{"id":13801512,"url":"https://github.com/yschaeff/ICantBelieveItsNotDNS","last_synced_at":"2025-05-13T11:31:19.477Z","repository":{"id":77869834,"uuid":"65421344","full_name":"yschaeff/ICantBelieveItsNotDNS","owner":"yschaeff","description":"\"I Can't Believe It's Not DNS!\" (ICBIND) is an authoritative DNS server for the  ESP8266 written in MicroPython. ","archived":false,"fork":false,"pushed_at":"2016-08-19T10:21:38.000Z","size":35,"stargazers_count":19,"open_issues_count":1,"forks_count":2,"subscribers_count":6,"default_branch":"master","last_synced_at":"2024-04-22T12:33:09.364Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/yschaeff.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2016-08-10T22:45:05.000Z","updated_at":"2023-07-24T19:01:48.000Z","dependencies_parsed_at":"2023-04-08T02:46:52.848Z","dependency_job_id":null,"html_url":"https://github.com/yschaeff/ICantBelieveItsNotDNS","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yschaeff%2FICantBelieveItsNotDNS","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yschaeff%2FICantBelieveItsNotDNS/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yschaeff%2FICantBelieveItsNotDNS/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yschaeff%2FICantBelieveItsNotDNS/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/yschaeff","download_url":"https://codeload.github.com/yschaeff/ICantBelieveItsNotDNS/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253932866,"owners_count":21986467,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-04T00:01:23.709Z","updated_at":"2025-05-13T11:31:16.975Z","avatar_url":"https://github.com/yschaeff.png","language":"Python","funding_links":[],"categories":["Libraries"],"sub_categories":["Communications"],"readme":"I Can't Believe It's Not DNS!\n===\n\"I Can't Believe It's Not DNS!\" (ICBIND) is an authoritative DNS server for the \nESP8266 written in MicroPython. \n\nAnti-features\n---\n* No storage of zone files, populated by AXFR.\n* DNSSEC filtering.\n* TSIG-less AXFR support!\n* Notify handling.\n* Highly optimized: no sanity checks.\n\nPreamble\n---\nWhen I first received my ESP8266 I flashed the NodeMCU firmware on it. To make\nmyself familiar with the ESP and Lua tried writing an authoritative DNS server\nfor it. A crappy server only echoing the queries over TCP (UDP was broken on\nNodeMCU) could do _almost_ 2 qps. That project quickly stopped.\n\nI was excited when MicroPython came out and decided to start a similar project.\nMicroPython was much more capable than Lua and I got 150 to 160 qps! Soon I hit\nlimitations on memory consumption and code size. To get it running I needed to\n~~cut some corners~~ highly optimize the code.\n\nIntroduction\n---\nThe code is contained in three files: boot.py establishes Wifi connection.\nmain.py contains the DNS server code and, trusted_networks.py has just a\ndictionary of ESSID and password combinations. The latter is not included in\nthe git repository and you should create one yourself (see boot.py).\n\nThe other thing you need to edit before uploading the files is the first few\nlines of main.py. There the zone as well as the master server to get it from are\ndefined.\n\nFor convenience a Makefile is included. Though in order to use it you probably\nneed to fiddle with it.\n\nModus Operandi\n---\nAt boot ICBIND will select the strongest Wifi access point it has credentials\nfor and connect to it. It does _not_ start webrepl as that would prevent\nmain.py to load due to memory shortage.\n\nThen the daemon will start. First it will transfer the zone via AXFR. When that\nis done it starts to answer queries from its database. Finally when a notify is\nreceived it will retransfer the zone.\n\nImplementation Details\n---\nMy personal domain is rather small. Less than 20 records or thereabouts. While\nmy TLD does not offer upload of DS records I _did_ sign my zone. As a result a\nAXFR reply is about 13 KiB. After receiving the AXFR I tried to do something \nwith that data: ENOMEM. Crud.\n\nPython to the rescue! I can make an iterator that I would feed a socket which\nwould read from said socket and spit out parsed Resource records! Easy does it.\nExcept... DNS uses compression pointers. Compression pointers greatly reduce\nthe size of DNS packets by eliminating repeating of owner names of resource\nrecords.  BUT we don't have enough memory to buffer the entire AXFR. We need a\nplan.\n\nPlan A\n---\nSo a compression pointer is just an octet pointing back relative to its current\nposition right? (HINT: No it isn't you up mucking asshole codemonkey). So I\njust need to keep a sliding window of the last 256 bytes! In reality a\ncompression pointer is 14 bits wide and absolute from the start of the message.\nMost names will point to one of the very first resource records in the message.\nSo let us also keep a copy of the first 256 bytes. That surely must catch 99\npercent of all cases, we just drop any record we can't resolve the pointer for.\nWho cares!  Well, that is mostly true. But I wasn't satisfied with the amount\nof records dropped in my small zone. So nothing else to do but store the AXFR\non flash you say? Oh you don't know me! It's personal now, I have a plan B.\n\nPlan B\n---\nWhat if we don't resolve the compression pointers during the AXFR? That's right,\njust let them sit unresolved for a bit. In the mean time drop all those pesky\nDNSSEC records we are offered. Those are to big anyway and I really don't want to\ndeal with NSEC lookups on this tiny device. Also, while we are at it drop\nanything other than class IN, that does not exist in my world. We end up with\njust a small set of records.  But how do we resolve the owner names, we don't\nhave this data any more?\n\nI know somebody who has this data... the master! You know what? With that set\nof records in hand do _another_ AXFR a couple of bytes at the time and resolve\nthose pointers on the fly without the need to buffer anything longer than a\nlabel (max 63 bytes). Of course compression pointers can be nested so we need\nto repeat this process in a loop until every pointer is resolved!\n\nServing Queries\n---\nThis is the easy part. Lets do as little as possible. When a query comes in we\nchop of anything beyond the question section. BAM! We have most of our reply\ndone.  Fiddle a bit with the flags and section counts, assume query name is\nuncompressed and append our resource record. Our database only contains TYPE and\nRDATA. Query name?  Always a pointer to byte 12 in the packet. Class? always\nIN. TLL? always 15 minutes, deal with it.\n\nSOA Serial Management\n---\nFinally we need a mechanism to update our little DNS server if the zone has\nchanged. Serious software would keep track of the version of the zone via the\nSOA serial number. Poll for a new version on set times, listen to notifies\nfrom the master and make an intelligible decision when and how to update the zone.\n\nWe don't have the memory available to be intelligible. But we can listen for \nnotify queries. If we receive a notify, any notify -  we optimized out any ACL or\nchecking of the zone name, we simply reboot(). The ESP8266 will powercycle and\nthe new version of the zone will be transferred and served. SOA serial \nmanagement made easy!\n\nEpilogue\n---\nThis software is shit. It sort of mimics DNS but really it isn't. You should\nnot use this, I should not use this (but you know I will because DNS hosting\non my ESP8266 is freaking awesome!)\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyschaeff%2FICantBelieveItsNotDNS","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fyschaeff%2FICantBelieveItsNotDNS","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyschaeff%2FICantBelieveItsNotDNS/lists"}