Skip to content

Commit

Permalink
[ftr] Add IPv6 support
Browse files Browse the repository at this point in the history
  • Loading branch information
SharUpOff committed Oct 4, 2024
1 parent b44e11f commit 217e147
Show file tree
Hide file tree
Showing 52 changed files with 1,620 additions and 466 deletions.
22 changes: 22 additions & 0 deletions .github/workflows/pytest.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: Pytest

on: [push]

jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v3
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install .[test]
- name: Running Pytest
run: |
pytest
21 changes: 21 additions & 0 deletions LICENSE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2024 SharUpOff

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
13 changes: 7 additions & 6 deletions gwhosts/dns/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from gwhosts.dns._parsers import parse, parse_qname
from gwhosts.dns._serializers import serialize
from gwhosts.dns._exceptions import DNSParserError
from gwhosts.dns._tools import remove_ipv6
from gwhosts.dns._types import Addition, Answer, Authority, DNSData, Header, QName, Question, RRType
from ._casts import qname_to_str, answer_to_str
from ._exceptions import DNSParserError
from ._parsers import parse, parse_qname
from ._serializers import serialize
from ._types import Addition, Answer, Authority, DNSData, Header, QName, Question, RRType

__all__ = [
"DNSData",
Expand All @@ -16,6 +16,7 @@
"RRType",
"parse",
"serialize",
"remove_ipv6",
"parse_qname",
"qname_to_str",
"answer_to_str",
]
26 changes: 26 additions & 0 deletions gwhosts/dns/_casts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from ._parsers import parse_qname
from ._types import QName, Answer, RRType
from ..network.ipv4 import ipv4_bytes_to_str
from ..network.ipv6 import ipv6_bytes_to_str


def qname_to_str(qname: QName) -> str:
return b".".join(qname).decode("utf8")


def qname_bytes_to_str(data: bytes) -> str:
return qname_to_str(parse_qname(data))


_RR_TO_STR = {
RRType.A.value: ipv4_bytes_to_str,
RRType.AAAA.value: ipv6_bytes_to_str,
RRType.CNAME.value: qname_bytes_to_str,
}


def answer_to_str(answer: Answer) -> str:
if answer.rr_type in _RR_TO_STR:
return f"{qname_to_str(answer.name)} -> {_RR_TO_STR[answer.rr_type](answer.rr_data)}"

return f"{qname_to_str(answer.name)} -> {answer.rr_data}"
8 changes: 8 additions & 0 deletions gwhosts/dns/_exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,11 @@ class DNSException(Exception):

class DNSParserError(DNSException):
pass


class DNSParserUnpackError(DNSParserError):
pass


class DNSParserRecursionError(DNSParserError):
pass
4 changes: 2 additions & 2 deletions gwhosts/dns/_parsers.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from ._exceptions import DNSParserRecursionError
from ._struct import unpack
from ._types import Addition, Answer, Authority, DNSData, Header, QName, Question
from ._types import Addition, Answer, Authority, DNSData, Header, QName, Question, RRType


def _parse_header(buffer: BinaryIO) -> Header:
Expand Down Expand Up @@ -48,7 +48,7 @@ def _parse_question(buffer: BinaryIO) -> Question:
return Question(name, rr_type, rr_class)


def _parse_resource(buffer: BinaryIO) -> Tuple[QName, int, int, int, int, bytes]:
def _parse_resource(buffer: BinaryIO) -> Tuple[QName, RRType, int, int, int, bytes]:
name = _parse_qname(buffer)
rr_type, rr_class, ttl, rr_data_length = unpack("!HHIH", buffer.read(10))
return name, rr_type, rr_class, ttl, rr_data_length, buffer.read(rr_data_length)
Expand Down
2 changes: 1 addition & 1 deletion gwhosts/dns/_serializers.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from dataclasses import astuple
from struct import pack

from gwhosts.dns._types import Addition, Answer, Authority, DNSData, Header, QName, Question
from ._types import Addition, Answer, Authority, DNSData, Header, QName, Question


def _encode_qname(qname: QName):
Expand Down
21 changes: 0 additions & 21 deletions gwhosts/dns/_tools.py

This file was deleted.

14 changes: 14 additions & 0 deletions gwhosts/dns/_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,14 @@ class RRType(Enum):
:param A: IPv4 Address [https://www.iana.org/go/rfc1035]
:param AAAA: IPv6 Address [https://www.iana.org/go/rfc3596]
:param CNAME: the canonical name for an alias [https://www.iana.org/go/rfc1035]
:param OPT: a pseudo-record type [https://www.iana.org/go/rfc6891]
:see: https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-4
"""

A: int = 1
AAAA: int = 28
CNAME: int = 5
OPT: int = 41


@dataclass
Expand All @@ -61,6 +63,18 @@ def qr(self) -> bool:
def aa(self) -> bool:
return bool(self.flags & Flags.AA.value)

@property
def tc(self) -> bool:
return bool(self.flags & Flags.TC.value)

@property
def rd(self) -> bool:
return bool(self.flags & Flags.RD.value)

@property
def ra(self) -> bool:
return bool(self.flags & Flags.RA.value)


class QName(Tuple[bytes]):
pass
Expand Down
2 changes: 1 addition & 1 deletion gwhosts/dns/parser.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from sys import stdin

from gwhosts.dns import parse
from . import parse

if __name__ == "__main__":
print(parse(stdin.buffer.read()))
12 changes: 7 additions & 5 deletions gwhosts/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
import sys
from argparse import ArgumentParser

from gwhosts.dns import QName
from gwhosts.network import Address
from gwhosts.performance import no_gc
from gwhosts.proxy import DNSProxy
from .dns import QName
from .network import Address
from .performance import no_gc
from .proxy import DNSProxy

if __name__ == "__main__":
parser = ArgumentParser()
Expand All @@ -19,9 +19,10 @@
"debug": logging.DEBUG,
}

parser.add_argument("gateway", help="Gateway IP")
parser.add_argument("gateway", help="Gateway IPv4")
parser.add_argument("hostsfile", help="Host List", nargs="?")
parser.add_argument("--host", dest="host", help="Listening address", default="127.0.0.1")
parser.add_argument("--ipv6-gateway", dest="ipv6_gateway", help="Gateway IPv6", default=None)
parser.add_argument("--port", dest="port", help="Listening port", default="8053", type=int)
parser.add_argument("--dns-host", dest="dns_host", help="Remote DNS address", default="127.0.0.1")
parser.add_argument("--dns-port", dest="dns_port", help="Remote DNS port", default="65053", type=int)
Expand Down Expand Up @@ -55,6 +56,7 @@

proxy = DNSProxy(
gateway=args.gateway,
ipv6_gateway=args.ipv6_gateway,
to_addr=Address(args.dns_host, args.dns_port),
hostnames=_hostnames,
logger=logger,
Expand Down
46 changes: 3 additions & 43 deletions gwhosts/network/__init__.py
Original file line number Diff line number Diff line change
@@ -1,63 +1,23 @@
from gwhosts.network._casts import (
ipv4_bytes_to_int,
ipv4_int_to_bytes,
ipv4_bytes_to_str,
ipv4_str_to_bytes,
ipv4_str_to_int,
ipv4_int_to_str,
ipv6_bytes_to_int,
ipv6_int_to_bytes,
ipv6_bytes_to_str,
ipv6_str_to_bytes,
ipv6_str_to_int,
ipv6_int_to_str,
network_to_str,
str_to_network,
netmask_to_network_size,
network_size_to_netmask,
)
from gwhosts.network._types import (
from ._types import (
Address,
Datagram,
ExpiringAddress,
IPAddress,
IPBinary,
Network,
NetworkSize,
UDPSocket,
NETSIZE_MAX,
NETMASK_MAX,
NETMASK_MIN,
RT_CLASS_MAIN,
)
from gwhosts.network._utils import reduce_subnets

__all__ = [
"ipv4_bytes_to_int",
"ipv4_int_to_bytes",
"ipv4_bytes_to_str",
"ipv4_str_to_bytes",
"ipv4_str_to_int",
"ipv4_int_to_str",
"ipv6_bytes_to_int",
"ipv6_int_to_bytes",
"ipv6_bytes_to_str",
"ipv6_str_to_bytes",
"ipv6_str_to_int",
"ipv6_int_to_str",
"network_to_str",
"str_to_network",
"netmask_to_network_size",
"network_size_to_netmask",
"Address",
"Datagram",
"ExpiringAddress",
"IPAddress",
"IPBinary",
"Network",
"NetworkSize",
"UDPSocket",
"NETSIZE_MAX",
"NETMASK_MAX",
"NETMASK_MIN",
"RT_CLASS_MAIN",
"reduce_subnets",
]
76 changes: 0 additions & 76 deletions gwhosts/network/_casts.py

This file was deleted.

5 changes: 0 additions & 5 deletions gwhosts/network/_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,6 @@
NetworkSize = int
IPBinary = int


NETSIZE_MAX = 32
NETMASK_MAX: IPBinary = 0xFFFFFFFF
NETMASK_MIN: IPBinary = 0xFF000000

# all normal routes are put there by default
# https://www.kernel.org/doc/Documentation/networking/policy-routing.txt
RT_CLASS_MAIN: RouteClass = 254
Expand Down
Loading

0 comments on commit 217e147

Please sign in to comment.