-
Notifications
You must be signed in to change notification settings - Fork 226
Add DNS oracle protocol #917
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Jim8y
wants to merge
30
commits into
master
Choose a base branch
from
feature/dns-doh
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 11 commits
Commits
Show all changes
30 commits
Select commit
Hold shift + click to select a range
9a72c1a
Add DNS oracle protocol
Jim8y 6aa0a8e
Align oracle DNS protocol with RFC 4501
Jim8y 4118a19
Guard DoH certificate parsing against oversized payloads
Jim8y a40b561
Merge branch 'master' into feature/dns-doh
erikzhang 9e27fda
Merge branch 'master' into feature/dns-doh
ajara87 2b9f772
Merge branch 'master' into feature/dns-doh
ajara87 55a5023
Merge branch 'master' into feature/dns-doh
ajara87 ba3ed0f
Merge branch 'master' into feature/dns-doh
Jim8y 769f160
Update plugins/OracleService/OracleSettings.cs
Jim8y 9071047
Update docs/oracle-dns-protocol.md
Jim8y 6ec4813
Update DNS oracle to RFC 8484 application/dns-message format
Jim8y 22f4f0e
Simplify DNS DoH handling and tighten safeguards
Jim8y 12fcdbf
Merge branch 'master' into feature/dns-doh
Jim8y ba19dba
Align oracle DNS protocol with RFC4501
Jim8y 1f4d451
Merge branch 'master' into feature/dns-doh
ajara87 27e07cd
Oracle DNS: return struct result for contracts
Jim8y 6b23c71
Merge branch 'master' into feature/dns-doh
Jim8y 5ee06cc
Update plugins/OracleService/OracleService.cs
Jim8y 37604ce
Merge branch 'master' into feature/dns-doh
Jim8y 9b87928
style: format oracle dns files
Jim8y a1c1bf1
fix: restore compatibility with current Neo package
Jim8y 3e5c77f
Merge branch 'master' into feature/dns-doh
Jim8y 122f490
Use PipeReader for DoH responses
Jim8y 4725a47
check bool result
shargon d4c4380
Address Oracle DNS review comments
Jim8y 09d4a4b
Address DNS oracle review feedback
Jim8y 4c4583f
Pin DNS resolver connections
Jim8y 93c5c00
Merge branch 'master' into feature/dns-doh
shargon 9d1553d
Harden DNS oracle response handling
Jim8y f240675
Merge branch 'master' into feature/dns-doh
ajara87 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,142 @@ | ||
| # Oracle DNS Protocol | ||
|
|
||
| The Oracle plugin resolves RFC 4501 `dns:` URIs through a DNS-over-HTTPS (DoH) gateway. This lets oracle nodes read authoritative DNS data (TXT for DKIM/SPF/DIDs, CERT/TLSA, etc.) without sending plaintext DNS queries. | ||
|
|
||
| > **When should I use it?** | ||
| > Whenever you need DNS data on-chain and want the request to stay encrypted end-to-end. | ||
|
|
||
| ## Enable and configure | ||
|
|
||
| 1. Install or build the `OracleService` plugin and copy `OracleService.json` next to the plugin binary. | ||
| 2. Add the `Dns` section (defaults shown): | ||
|
|
||
| ```jsonc | ||
| { | ||
| "PluginConfiguration": { | ||
| // ... | ||
| "Dns": { | ||
| "EndPoint": "https://cloudflare-dns.com/dns-query", | ||
| "TimeoutMilliseconds": 5000 | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| - `EndPoint` must point to a DoH resolver that supports [RFC 8484](https://www.rfc-editor.org/rfc/rfc8484.html) with `application/dns-message` format. | ||
| - `Timeout` is the maximum milliseconds the oracle will wait for a DoH response before returning `OracleResponseCode.Timeout`. | ||
|
|
||
| > You can run your own DoH gateway and point the oracle to it if you need custom trust anchors or strict egress controls. | ||
|
|
||
| ### RFC 8484 Compliance | ||
|
|
||
| This implementation uses the standard `application/dns-message` content type as defined in RFC 8484. DNS queries are sent as POST requests with binary DNS wire format (RFC 1035). Compatible DoH endpoints include: | ||
|
|
||
| | Provider | Endpoint | | ||
| |----------|----------| | ||
| | Cloudflare | `https://cloudflare-dns.com/dns-query` | | ||
| | Google | `https://dns.google/dns-query` | | ||
| | Quad9 | `https://dns.quad9.net/dns-query` | | ||
|
|
||
| Any RFC 8484-compliant DoH server should work with this oracle protocol. | ||
|
|
||
| ## RFC 4501 URI format | ||
|
|
||
| ``` | ||
| dns:[//authority/]domain[?CLASS=class;TYPE=type][;FORMAT=x509] | ||
| ``` | ||
|
|
||
| - `domain` is the DNS owner name (relative or absolute). Percent-encoding and escaped dots (`%5c.`) follow RFC 4501 rules. | ||
| - `domain` must not include additional path segments; only the owner name belongs here. | ||
| - `authority` is the optional DoH server to use for this query (RFC 4501). When specified, the oracle connects to `https://{authority}/dns-query`. If omitted, the configured `EndPoint` is used. | ||
| - `CLASS` is optional and case-insensitive. Only `IN` (`1`) is supported; other classes are rejected. | ||
| - `TYPE` is optional and case-insensitive. Use mnemonics (`TXT`, `TLSA`, `CERT`, `A`, `AAAA`, …) or numeric values. Defaults to `A` per RFC 4501. | ||
| - `FORMAT` is an oracle extension; use `format=x509` (or `cert`) to parse TXT/CERT payloads into the `Certificate` field. | ||
| - `name` is an oracle extension; if present, it overrides `domain` entirely (useful for percent-encoding complex owner names). | ||
|
|
||
| Query parameters can be separated by `;` (RFC style) or `&`. | ||
|
|
||
| Examples: | ||
|
|
||
| - `dns:1alhai._domainkey.icloud.com?TYPE=TXT` — DKIM TXT record. | ||
| - `dns:simon.example.org?TYPE=CERT;FORMAT=x509` — extract the X.509 payload into `Certificate`. | ||
| - `dns://dns.google/ftp.example.org?TYPE=A` — uses Google's DoH server (`https://dns.google/dns-query`) instead of the configured endpoint. | ||
| - `dns://cloudflare-dns.com/example.org?TYPE=TXT` — uses Cloudflare's DoH server for this specific query. | ||
| - `dns:ignored?name=weird%5c.label.example&type=TXT` — uses the `name` override (decoded to `weird.label.example`). | ||
|
|
||
| ## Response schema | ||
|
|
||
| Successful queries return UTF-8 JSON. Attributes correspond to the `ResultEnvelope` produced by the oracle: | ||
|
|
||
| ```jsonc | ||
| { | ||
| "Name": "1alhai._domainkey.icloud.com", | ||
| "Type": "TXT", | ||
| "Answers": [ | ||
| { | ||
| "Name": "1alhai._domainkey.icloud.com", | ||
| "Type": "TXT", | ||
| "Ttl": 299, | ||
| "Data": "\"k=rsa; p=...IDAQAB\"" | ||
| } | ||
| ], | ||
| "Certificate": { | ||
| "Subject": "CN=example.com", | ||
| "Issuer": "CN=Example Root", | ||
| "Thumbprint": "ABCD1234...", | ||
| "NotBefore": "2024-01-16T00:00:00Z", | ||
| "NotAfter": "2025-01-16T00:00:00Z", | ||
| "Der": "MIIC...", | ||
| "PublicKey": { | ||
| "Algorithm": "RSA", | ||
| "Encoded": "MIIBIjANBg...", | ||
| "Modulus": "B968DE...", | ||
| "Exponent": "010001" | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| - `Answers` mirrors the DoH response but normalizes record types and names. | ||
| - `Certificate` is present only when `TYPE=CERT` or `FORMAT=x509`. `Der` is the base64-encoded certificate, while `PublicKey` provides both the encoded SubjectPublicKeyInfo (`Encoded`) and algorithm-specific fields (`Modulus`/`Exponent` for RSA, `Curve`/`X`/`Y` for EC). | ||
| - For RSA keys the modulus/exponent strings are big-endian hex. For EC keys the X/Y coordinates are hex-encoded affine coordinates on the reported `Curve`. | ||
| - If the DoH server responds with NXDOMAIN, the oracle returns `OracleResponseCode.NotFound`. | ||
| - Responses exceeding `OracleResponse.MaxResultSize` yield `OracleResponseCode.ResponseTooLarge`. | ||
|
|
||
| ## Contract usage example | ||
|
|
||
| ```csharp | ||
| public static void RequestAppleDkim() | ||
| { | ||
| const string url = "dns:1alhai._domainkey.icloud.com?TYPE=TXT"; | ||
| Oracle.Request(url, "", nameof(OnOracleCallback), Runtime.CallingScriptHash, 5_00000000); | ||
| } | ||
|
|
||
| public static void OnOracleCallback(string url, byte[] userData, int code, byte[] result) | ||
| { | ||
| if (code != (int)OracleResponseCode.Success) throw new Exception("Oracle query failed"); | ||
|
|
||
| var envelope = (Neo.SmartContract.Framework.Services.Neo.Json.JsonObject)StdLib.JsonDeserialize(result); | ||
| var answers = (Neo.SmartContract.Framework.Services.Neo.Json.JsonArray)envelope["Answers"]; | ||
| var txt = (Neo.SmartContract.Framework.Services.Neo.Json.JsonObject)answers[0]; | ||
| Storage.Put(Storage.CurrentContext, "dkim", txt["Data"].AsString()); | ||
| } | ||
| ``` | ||
|
|
||
| Tips: | ||
|
|
||
| 1. Always set `TYPE` when you need anything other than an A record. | ||
| 2. Budget enough `gasForResponse` to cover JSON payload size (TXT records are often kilobytes). | ||
| 3. Validate TTL or fingerprint data before trusting it. | ||
| 4. Combine oracle DNS data with existing filters (e.g., `Helper.JsonPath`/`OracleService.Filter`) if you only need a slice of the result. | ||
|
|
||
| ## Manual testing | ||
|
|
||
| Use the same resolver the oracle will contact to inspect responses: | ||
|
|
||
| ```bash | ||
| curl -s \ | ||
| -H 'accept: application/dns-json' \ | ||
| 'https://cloudflare-dns.com/dns-query?name=1alhai._domainkey.icloud.com&type=TXT' | ||
| ``` | ||
|
|
||
| Compare the JSON payload with the data returned by your contract callback to ensure parity. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.