0

The dns doesnt work if i do this it gives this error and idk whats the matter it just gives me this error:

Traceback (most recent call last):
  File "c:\Users\engin\Downloads\All files\dns.py", line 160, in <module>
    r = buildresponse(data)
  File "c:\Users\engin\Downloads\All files\dns.py", line 155, in buildresponse
    dnsbody += rectobytes(domainname, rectype, record["ttl"], record["value"])
TypeError: string indices must be integers

and i did write this code for my DNS Server:

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind((ip, port))

def load_zone():
    jsonzone = {}
    zonefiles = glob.glob('zones/*.zone')
    
    
    for zone in zonefiles:
        with open(zone) as zonedata:
            data = json.load(zonedata)
            zonename = data["$origin"]
            jsonzone[zonename] = data
    return jsonzone

zonedata = load_zone()

def getFlags(flags):
    byte1 = bytes(flags[:1])
    byte2 = bytes(flags[1:2])

    rflags = ''

    QR = '1'

    OPCODE = ''

    for bit in range(1,5):
        OPCODE += str(ord(byte1) & (1<<bit))
    
    AA = '1'

    TC = '0'

    RD = '0'

    RA = '0'

    Z= '000'

    RCODE = '0000'

    return(int(QR + OPCODE + AA + TC + RD, 2).to_bytes(1, byteorder='big') + int(RA + Z + RCODE, 2).to_bytes(1, byteorder='big'))

def getquestiondomain(data):
    state = 0
    expectedlenght = 0
    domainsting = ''
    domainparts = []
    x = 0
    y = 0

    for byte in data:
        if state == 1:
            if byte != 0:
                domainsting += chr(byte)
            x += 1
            if x == expectedlenght:
                domainparts.append(domainsting)
                domainsting = ''
                state = 0
                x = 0
            if byte == 0:
                domainparts.append(domainsting)
                break
        else:
            state = 1
            expectedlenght = byte
        # x += 1
        y += 1

    questiontype = data[y + 1 : y + 3]
            
    return(domainparts, questiontype)

def getzone(domain):
    global zonedata
    zone_name = '.'.join(domain)
    return zonedata[zone_name]

def getrecs(data):
    domain, questiontype = getquestiondomain(data)
    qt = ''

    if questiontype == b'\x00\x01':
        qt = 'a'
    
    zone = getzone(domain)

    return (zone, qt, domain)

def rectobytes(domainname, rectype, recttl, recval):
    rbytes = b'\xc0\x0c'
    if rectype == 'a':
        rbytes = rbytes + bytes([0]) + bytes([1])
    
    rbytes = rbytes + bytes([0]) + bytes([1])

    rbytes += int(recttl).to_bytes(4, byteorder='big')

    if rectype == 'a':
        rbytes = rbytes + bytes([0]) + bytes([4])

        for part in recval.split('.'):
            rbytes += bytes([int(part)])
    return rbytes

def buildquestion(domainname, rectype):
    qbytes = b''

    for part in domainname:
        lenght = len (part)
        qbytes += bytes([lenght])

        for char in part:
            qbytes += ord(char).to_bytes(1, byteorder='big')
        if rectype == 'a':
            qbytes += (1).to_bytes(2, byteorder='big')
        qbytes += (1).to_bytes(2, byteorder='big')
    return qbytes

def buildresponse(data):
    TransactionID = data[:2]
    
    # TID = ''
    # for byte in TransactionID:
        # TID += hex(byte)
    
    Flags = getFlags(data[2:4])

    QDCOUNT = b'\x00\x01'

    # getquestiondomain(data[12:])

    ANCOUNT = len(getrecs(data[12:])[0]).to_bytes(2, byteorder='big')

    NSCOUNT = (0).to_bytes(2, byteorder='big')

    ARCOUNT = (0).to_bytes(2, byteorder='big')

    dnsheader = TransactionID + Flags + QDCOUNT + ANCOUNT + NSCOUNT + ARCOUNT
    
    dnsbody = b''

    records, rectype, domainname = getrecs(data[12:])

    dnsquestion = buildquestion(domainname, rectype)

    for record in records:
        dnsbody += rectobytes(domainname, rectype, record["ttl"], record["value"])
    return dnsheader + dnsquestion + dnsbody

while 1:
    data, addr = sock.recvfrom(512)
    r = buildresponse(data)
    sock.sendto(r, addr)

and this value record in the for at the very bottom of buildresponse it just prints out @origin Idk whats the problem or so and then i created this Post and pls help.

pls help!

CodingFox
  • 43
  • 9
  • If you put a breakpoint in that loop, what's the value of `record` when you get the error? – Random Davis Nov 20 '20 at 20:04
  • The error means that `records` is a list of strings, not a list of dictionaries. – Barmar Nov 20 '20 at 20:11
  • its just @origin – CodingFox Nov 21 '20 at 08:15
  • It doesnt go give all data @RandomDavis – CodingFox Nov 21 '20 at 08:15
  • and this what just records gives: {'$origin': 'status.minecraft.de.', '$ttl': 3600, 'soa': {'mname': 'ns1.status.minecraft.de.', 'rname': 'admin.status.minecraft.de.', 'serial': '{time}', 'refresh': 3600, 'retry': 600, 'expire': 604800, 'minimum': 86400}, 'ns': [{'host': 'ns1.status.minecraft.de.'}, {'host': 'ns2.status.minecraft.de.'}], 'a': [{'name': '@', 'ttl': 400, 'value': '255.255.255.255'}, {'name': '@', 'ttl': 400, 'value': '127.0.0.1'}, {'name': '@', 'ttl': 400, 'value': '127.0.0.1'}]} and the record just gives @origin – CodingFox Nov 21 '20 at 08:18

1 Answers1

1

Okay so now you've shown that your records value is the following dictionary:

records =  {'$origin': 'status.minecraft.de.', '$ttl': 3600, 'soa': {'mname': 'ns1.status.minecraft.de.', 'rname': 'admin.status.minecraft.de.', 'serial': '{time}', 'refresh': 3600, 'retry': 600, 'expire': 604800, 'minimum': 86400}, 'ns': [{'host': 'ns1.status.minecraft.de.'}, {'host': 'ns2.status.minecraft.de.'}], 'a': [{'name': '@', 'ttl': 400, 'value': '255.255.255.255'}, {'name': '@', 'ttl': 400, 'value': '127.0.0.1'}, {'name': '@', 'ttl': 400, 'value': '127.0.0.1'}]}

This means that if you were to loop through it how you currently do - for record in records - we know that, since it's a dictionary, each record will be a key of that dictionary (as per this). So, the following code:

for record in records:
    print(record)
    print(type(record))

Gives the following outpupt - which is just all the keys in the dictionary:

$origin
<class 'str'>
$ttl
<class 'str'>
soa
<class 'str'>
ns
<class 'str'>
a
<class 'str'>

So, that means that if you try to access record["ttl"] or record["value"], since record is a string, this will be like trying to do something like "$ttl"["ttl"], which would give the error string indices must be integers.

However, the issue wouldn't be solved just be replacing record with records, since the records object has no "value" or "ttl" key. It appears that you're actually trying to loop through each value in the "a" key in your records dictionary, since its value is an array of dictionaries that have both a "ttl" and "value" key:

[{'name': '@', 'ttl': 400, 'value': '255.255.255.255'}, {'name': '@', 'ttl': 400, 'value': '127.0.0.1'}, {'name': '@', 'ttl': 400, 'value': '127.0.0.1'}]

Therefore, your fix would simply be:

for record in records["a"]:
    dnsbody += rectobytes(domainname, rectype, record["ttl"], record["value"])
Random Davis
  • 6,662
  • 4
  • 14
  • 24