jczic/MicroDNSSrv

Dns answers to partial FQDN match

Closed this issue · 18 comments

I did an improvement in order to match a word in the fqdn asked.
Now in the domain list, you can put something like : {'"test" : "192.168.4.1"}
And making a DNS request on www.test.com, test.fr, test, will replay with "192.168.4.1".
This is useful for configuration web portal.

`
# ============================================================================
# ===( Server Thread )========================================================
# ============================================================================

def _serverProcess(self) :
    self._started = True
    debug = True
    while True :
        try :
            packet, cliAddr = self._server.recvfrom(256)
            domName = MicroDNSSrv._getAskedDomainName(packet)
            if domName:
                ipB = self._domList.get(domName.lower(), None)
                if debug and ipB:
                    print("DNS: full domain matched: " + domName.lower())

                if not ipB:
                    for word_a in domName.lower().split("."):
                        for word_b in self._domList.keys():
                            if word_a == word_b:
                                if debug:
                                    print("DNS: word matched: " + word_b)
                                ipB = self._domList.get(word_b, None)
                                break
                        if ipB:
                            break

                if not ipB:
                    ipB = self._domList.get('*', None)
                    if debug and ipB:
                        print("DNS: catchall matched.")

                if not ipB:
                    if debug:
                        print("DNS: no match found.")

                if ipB:
                    packet = MicroDNSSrv._getPacketAnswerA(packet, ipB)
                    if packet :
                        self._server.sendto(packet, cliAddr)

        except :
            if not self._started :
                break

`

jczic commented

Code updated in initial post.

jczic commented

I have not updated the code :

                if not ipB:
                    for word_a in domName.lower().split("."):
                        for word_b in self._domList.keys():
                            if word_a == word_b:
                                if debug:
                                    print("DNS: word matched: " + word_b)
                                ipB = self._domList.get(word_b, None)
                                break
                        if ipB:
                            break

Because it can be causes a bad match.
The top will be to recognize partials wildcards like "*.toto.com".
What do you think about that ?

What do you think as bad match ? Can you give an example ?

jczic commented

Yes and it is the purpose :-)

Documentation:
The DNS resolver algorithm resolves entries of the configuration table in a determined order. As soon as a result is found, it is sent to the DNS client. The order is :

  • full match of FQDN (ex : www.github.com"),
  • the request (splited by dots) contains en entry of the configuration table (ex : github),
  • the catchall (*), if present is returned.

I don't see a case where this algorithm could cause problems. Maybe there will be some non addressed cases (ex : matching *.toto.com).
After that, we could use regexps for matching configuration. But this will take much more CPU to compute...
Choice is yours JC :-)

jczic commented

Hello :)
I've added a wildcards domains parts rules (using regex).

You can check :
*.test.com
www.*.com
titi.toto.*

and also :
*.toto.*.titi.com
www.*.*

What do you think about that and the code ?

                        for domChk in self._domList.keys() :
                            if domChk.find('*') >= 0 :
                                r = domChk.replace('.', '\.').replace('*', '.+') + '$'
                                if match(r, domName) :
                                    ipB = self._domList.get(domChk, None)
                                    break

Ok nice :-) I have still 2 remarks:

  • wildcard is matched before regexp, for me it is a problem
  • while using regexp, why to replace dot and star with escaped version and not use expression as this ? While doing it you assis the users of your code but you remove some possibilities (ex : maching titi.com and tata.com with "t.t..com").
jczic commented

wildcard is matched before regexp, for me it is a problem
Why ? It's for not check all regex for nothing :o

In my case I want to do a configuration portal. For this I need to have DNS answers to some words, listed here for example : DNS_ANSWERS = ['ncsi', 'msftconnecttest', 'connectivitycheck', 'clients3', 'google', 'login', 'gvt3', 'apple', 'gvt2'] (from : https://github.com/lemariva/uPyPortal/blob/master/captive_portal/config.py). This is for telling various devices that there is a captive portal, it is normal. Then I want that any FQDN to be resolved to the IP of the ESP32 in order to go to the MicroWebSRV root (index.html) in order to serve the configuration page.

Yes, I don't want add the direct possibility to set regex but just humain writable rules.
You can add "toto.com" and "*.toto.com" for exemple.

Ok but in the case of captive portal, the mobile devices can change the target FQDN, but it always contains the same word.

jczic commented

Ok, yes, I understand.
I've just changed that : wildcards (*) match any char zero or more times in domains rules
And not just one or more times.
Now, you can set *google* directly to match any who contain this word :)
You can also set google.* and *.google.* if you want.
It's ok ? :)

jczic commented

Sorry for late response. I was quite busy these days.
Your proposal seems to be a good compromise.
But you still match the catchall before the regexp, which I think is not good. The systematic behaviour that is most specific first to less specific last seems better for me.
I will test that monday or tuesday.
Thank you JC !

jczic commented

For me not :-)
The catchall should come as last chance :-)

jczic commented

Ok you're right, I pushed the new wildcards filters order :)

It seems to be great !
`
dnssrv = microDNSSrv.MicroDNSSrv.Create({
"www.testa.com" : "192.168.4.1",
"testb" : "192.168.4.2",
"testc." : "192.168.4.3",
"
" : "192.168.4.4"
})

www.testa.com => 192.168.4.1

www.testb.com => 192.168.4.2
www.testb.fr => 192.168.4.2
testb.me => 192.168.4.2
testb => 192.168.4.2

testc.fr => 192.168.4.3

testc => 192.168.4.4
www.testc.com => 192.168.4.4
www.perdu.com => 192.168.4.4
`

The only not logical case is : testc. => 192.168.4.4
But as this is not a valid FQDN we don't care so much :-)

Thank you JC !

jczic commented