Skip to content

Commit

Permalink
Merge pull request #12 from SuperFola/rewrite
Browse files Browse the repository at this point in the history
Rewrite
  • Loading branch information
SuperFola authored Jan 21, 2021
2 parents 8adb259 + 3c8ba42 commit ef22872
Show file tree
Hide file tree
Showing 8 changed files with 233 additions and 74 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@ __pycache__/
*.py[ouc]
.vscode/
venv/

*.ini
4 changes: 1 addition & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,8 @@ In a few steps I was able to configure my NS provider to set myself up as my own

For this examples, let's say my server is named `example.com`.

1. In my DNS Zone, I added a `NS` entry for `dns.example.com`, pointing to `dns.example.com.` (mind the final dot)
1. Then I added a `A` entry for `dns.example.com`, pointing to `my server ip here`
1. I added a `A` entry for `dns.example.com`, pointing to `my server ip here`
1. In the DNS servers configuration, I already had things like `ns1.provider.com`, I added myself as a DNS server: `dns.example.com`, pointing to `my server ip here`
1. For the redirections, I added `dns.example.com` as a type `A`, pointing to `my server ip here`
1. Then, just wait a bit (can be as long as 48 hours) and you're good to go

Now I just have to tell my client scripts to use the domain `dns.example.com` to send messages to it and it works like a charm, even when asking Google about it!
Expand Down
33 changes: 23 additions & 10 deletions client.sh
Original file line number Diff line number Diff line change
@@ -1,13 +1,23 @@
#!/usr/bin/env bash

# send message
stripped_b32=`echo $1 | base32 | tr -d =`
crafted_domain="${stripped_b32}.dns.12f.pl"
answer=`dig @12f.pl $crafted_domain TXT`
if [[ $# != 2 ]]; then
echo "Usage:"
echo " $0 hostname message"
exit 1
fi

StartDate=$(date -u +"%s.%N")

# create message, remove padding
stripped_b32=$(echo "$2" | base32 | tr -d =)
# create domain
crafted_domain="${stripped_b32}.$1"
# make the DNS query and retrieve the answers
answer=$(dig "$crafted_domain" TXT)
# decode answer
message=`echo $answer | grep -A 1 ";; ANSWER SECTION:" | tail -n 1 | egrep -o "\".+\"" | cut -c 2- | rev | cut -c 2- | rev`
length=$((4 - $(expr length "$message") % 4))
# add padding accordingly
message=$(echo "$answer" | grep -A 1 ";; ANSWER SECTION:" | tail -n 1 | grep -E -o "\".+\"" | cut -c 2- | rev | cut -c 2- | rev)
length=$((4 - $(echo -n "$message" | wc -c) % 4))
# add padding back accordingly
case "$length" in
"1")
message="${message}="
Expand All @@ -21,7 +31,10 @@ case "$length" in
*)
;;
esac
# decode
decoded=$(echo "$message" | base64 -d)

decoded=`echo $message | base64 -d`

echo "Received: $decoded"
FinalDate=$(date -u +"%s.%N")
elapsed=$(date -u -d "0 $FinalDate sec - $StartDate sec" +"%S.%N")
echo "Received in $elapsed seconds"
echo "$decoded"
16 changes: 16 additions & 0 deletions config.ini.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[server]
interface=
root=example.com
domain=dns.example.com
host_ip=

[packets]
ttl=1

[example.com]
ip=
ttl=3600

[nice.example.com]
ip=
ttl=3600
32 changes: 11 additions & 21 deletions src/chatserver.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
#!/usr/bin/env python3

import sys
import time
from typing import List

from server import Server
from utils import get_ip_from_hostname
from server import main


class Message:
Expand All @@ -15,50 +14,41 @@ def __init__(self, author: str, content: str):
self.seen_by = []


class ChatServer(Server):
def __init__(self, interface: str, domain: str, host_ip: str):
super().__init__(interface, domain, host_ip)

class ChatServer:
def __init__(self):
self.messages = []
self.users = {}

def on_query(self, message: str, src_ip: str) -> str:
def __call__(self, message: str, src_ip: str, domains: List[str]) -> str:
message = message.strip()

if src_ip not in self.users:
# associate each new ip with its user id
self.users[src_ip] = str(len(self.users))

# check for commands
if len(message) > 1 and message[0] != '/':
if len(message) > 1 and message[0] != "/":
self.messages.append(Message(self.users[src_ip], message))
self.messages[-1].seen_by.append(self.users[src_ip])
return "/ok"
elif message == "/consult":
# get the user unread messages list
history = []
for msg in self.messages:
# get the last 5 messages in reverse order
for msg in self.messages[:-6:-1]:
if self.users[src_ip] not in msg.seen_by:
history.append(msg)
# mark the message as seen
msg.seen_by.append(self.users[src_ip])

# create the unread message list
output = ""
for msg in history:
# append message in ascending order
for msg in history[::-1]:
output += f"@{msg.author} [{msg.timestamp}]: {msg.content}\n"
return output
return "/error"


if __name__ == "__main__":
if len(sys.argv) < 3:
print("Usage: %s interface hostname" % sys.argv[0])
sys.exit(-1)

ip = get_ip_from_hostname(sys.argv[2])
if ip is None:
sys.exit(-1)

server = ChatServer(sys.argv[1], sys.argv[2], ip)
server.run()
main(chat=ChatServer())
1 change: 0 additions & 1 deletion src/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@

logger = None

import base64

class Client:
def __init__(self, domain: str, ip: str, verbosity: int = 0):
Expand Down
59 changes: 54 additions & 5 deletions src/packet.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,59 @@
from functools import reduce
from random import randint

from scapy.layers.dns import DNS, DNSQR, DNSRR
from scapy.layers.dns import DNS, DNSQR, DNSRR, dnstypes
from scapy.layers.inet import IP, UDP

from utils import DNSHeaders


def build_tos(
precedence: int, lowdelay: bool, throughput: bool, reliability: bool, lowcost: bool
) -> int:
"""Building IP Type of Service value
Args:
precedence (int): intended to denote the importance or priority of the datagram
0b1000 -- minimize delay
0b0100 -- maximize throughput
0b0010 -- maximize reliability
0b0001 -- minimize monetary cost
0b0000 -- normal service
lowdelay (bool): low (True), normal (False)
throughput (bool): high (True) or low (False)
reliability (bool): high (True) or normal (False)
lowcost (bool): minimize memory cost (True)
Returns:
int: type of service as describe in the RFC 1349 and 791
"""
return (
(lowcost << 1)
+ (reliability << 2)
+ (throughput << 3)
+ (lowdelay << 4)
+ (max(min(precedence, 0b111), 0b000) << 5)
)


class Packet:
@staticmethod
def build_query(layer: dict, domain: str) -> object:
pkt = IP(dst=layer["dst"], tos=0x28)
"""Build a DNS query packet
Args:
layer (dict): dict of the different layer properties and values
domain (str): the domain the packet is from
Returns:
object: a Packet object
"""
pkt = IP(dst=layer["dst"], tos=build_tos(1, 0, 1, 0, 0))
pkt /= UDP(sport=randint(0, 2 ** 16 - 1), dport=53)
pkt /= DNS(
# random transaction id
id=randint(0, 2 ** 16 - 1),
rd=0, # no recursion desired
rd=1, # recursion desired
qr=DNSHeaders.QR.Query,
# requests must be of type TXT otherwise our answers (of type TXT)
# don't get transmitted if recursion occured
Expand All @@ -28,6 +67,15 @@ def build_query(layer: dict, domain: str) -> object:

@staticmethod
def build_reply(layer: dict, domain: str) -> object:
"""Build a DNS reply packet
Args:
layer (dict): dict of the different layer properties and values
domain (str): the domain the packet is from
Returns:
object: a Packet object
"""
pkt = IP(dst=layer["dst"], src=layer["src"])
pkt /= UDP(dport=layer["dport"], sport=53)
pkt /= DNS(
Expand All @@ -44,14 +92,15 @@ def __init__(self, pkt: IP, domain: str):
self._pkt = pkt
self._domain = domain

def is_valid_dnsquery(self) -> bool:
def is_valid_dnsquery(self, qtype: str, domain: str = "") -> bool:
def check_qname(q: str) -> str:
return q[len(q) - len(self._domain) - 2 : len(q) - 2]
return q.endswith(f"{domain if domain else self._domain}.")

return (
DNS in self._pkt
and self._pkt[DNS].opcode == DNSHeaders.OpCode.StdQuery
and self._pkt[DNS].qr == DNSHeaders.QR.Query
and dnstypes[self._pkt[DNSQR].qtype] == qtype
and check_qname(self._pkt[DNSQR].qname.decode("utf-8"))
and self._pkt[DNS].ancount == 0
)
Expand Down
Loading

0 comments on commit ef22872

Please sign in to comment.