New Feature: HASH() adds hashing functions to dnsconfig.js language (#3085)

Co-authored-by: Tom Limoncelli <tlimoncelli@stackoverflow.com>
This commit is contained in:
fuero 2024-08-21 15:21:39 +02:00 committed by GitHub
parent 864d45290f
commit 94a0cfcba3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 122 additions and 0 deletions

View file

@ -1136,6 +1136,35 @@ declare function DnsProvider(name: string, nsCount?: number): DomainModifier;
*/
declare function FRAME(name: string, target: string, ...modifiers: RecordModifier[]): DomainModifier;
/**
* `HASH` hashes `value` using the hashing algorithm given in `algorithm`
* (accepted values `SHA1`, `SHA256`, and `SHA512`) and returns the hex encoded
* hash value.
*
* example `HASH("SHA1", "abc")` returns `a9993e364706816aba3e25717850c26c9cd0d89d`.
*
* `HASH()`'s primary use case is for managing [catalog zones](https://datatracker.ietf.org/doc/html/rfc9432):
*
* > a method for automatic DNS zone provisioning among DNS primary and secondary name
* > servers by storing and transferring the catalog of zones to be provisioned as one
* > or more regular DNS zones.
*
* Here's an example of a catalog zone:
*
* ```javascript
* foo_name_suffix = HASH("SHA1", "foo.name") + ".zones"
* D("catalog.example"
* [...]
* , TXT("version", "2")
* , PTR(foo_name_suffix, "foo.name.")
* , A("primaries.ext." + foo_name_suffix, "192.168.1.1")
* )
* ```
*
* @see https://docs.dnscontrol.org/language-reference/top-level-functions/hash
*/
declare function HASH(algorithm: "SHA1" | "SHA256" | "SHA512", value: string): string;
/**
* HTTPS adds an HTTPS record to a domain. The name should be the relative label for the record. Use `@` for the domain apex. The HTTPS record is a special form of the SVCB resource record.
*

View file

@ -20,6 +20,7 @@
* [DOMAIN_ELSEWHERE_AUTO](language-reference/top-level-functions/DOMAIN_ELSEWHERE_AUTO.md)
* [D_EXTEND](language-reference/top-level-functions/D_EXTEND.md)
* [FETCH](language-reference/top-level-functions/FETCH.md)
* [HASH](language-reference/top-level-functions/HASH.md)
* [IP](language-reference/top-level-functions/IP.md)
* [NewDnsProvider](language-reference/top-level-functions/NewDnsProvider.md)
* [NewRegistrar](language-reference/top-level-functions/NewRegistrar.md)

View file

@ -0,0 +1,36 @@
---
name: HASH
parameters:
- algorithm
- value
parameter_types:
algorithm: '"SHA1" | "SHA256" | "SHA512"'
value: string
ts_return: string
---
`HASH` hashes `value` using the hashing algorithm given in `algorithm`
(accepted values `SHA1`, `SHA256`, and `SHA512`) and returns the hex encoded
hash value.
example `HASH("SHA1", "abc")` returns `a9993e364706816aba3e25717850c26c9cd0d89d`.
`HASH()`'s primary use case is for managing [catalog zones](https://datatracker.ietf.org/doc/html/rfc9432):
> a method for automatic DNS zone provisioning among DNS primary and secondary name
> servers by storing and transferring the catalog of zones to be provisioned as one
> or more regular DNS zones.
Here's an example of a catalog zone:
{% code title="dnsconfig.js" %}
```javascript
foo_name_suffix = HASH("SHA1", "foo.name") + ".zones"
D("catalog.example"
[...]
, TXT("version", "2")
, PTR(foo_name_suffix, "foo.name.")
, A("primaries.ext." + foo_name_suffix, "192.168.1.1")
)
```
{% endcode %}

41
pkg/js/hash.go Normal file
View file

@ -0,0 +1,41 @@
package js
import (
"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
"encoding/hex"
"fmt"
"github.com/robertkrimen/otto"
)
// Exposes sha1, sha256, and sha512 hashing functions to Javascript
func hashFunc(call otto.FunctionCall) otto.Value {
if len(call.ArgumentList) != 2 {
throw(call.Otto, "require takes exactly two arguments")
}
algorithm := call.Argument(0).String() // The algorithm to use for hashing
value := call.Argument(1).String() // The value to hash
result := otto.Value{}
fmt.Printf("%s\n", value)
switch algorithm {
case "SHA1", "sha1":
tmp := sha1.New()
tmp.Write([]byte(value))
fmt.Printf("%s\n", hex.EncodeToString(tmp.Sum(nil)))
result, _ = otto.ToValue(hex.EncodeToString(tmp.Sum(nil)))
case "SHA256", "sha256":
tmp := sha256.New()
tmp.Write([]byte(value))
result, _ = otto.ToValue(hex.EncodeToString(tmp.Sum(nil)))
case "SHA512", "sha512":
tmp := sha512.New()
tmp.Write([]byte(value))
result, _ = otto.ToValue(hex.EncodeToString(tmp.Sum(nil)))
default:
throw(call.Otto, fmt.Sprintf("invalid algorithm %s given", algorithm))
}
return result
}

View file

@ -74,6 +74,7 @@ func ExecuteJavascriptString(script []byte, devMode bool, variables map[string]s
vm.Set("REVCOMPAT", reverseCompat)
vm.Set("glob", listFiles) // used for require_glob()
vm.Set("PANIC", jsPanic)
vm.Set("HASH", hashFunc)
// add cli variables to otto
for key, value := range variables {

View file

@ -141,6 +141,7 @@ func TestErrors(t *testing.T) {
{"Bad cidr", `D(reverse("foo.com"), "reg")`},
{"Dup domains", `D("example.org", "reg"); D("example.org", "reg")`},
{"Bad NAMESERVER", `D("example.com","reg", NAMESERVER("@","ns1.foo.com."))`},
{"Bad Hash function", `D(HASH("123", "abc"),"reg")`},
}
for _, tst := range tests {
t.Run(tst.desc, func(t *testing.T) {

View file

@ -0,0 +1 @@
D(HASH("SHA1", "abc"), "reg")

View file

@ -0,0 +1,12 @@
{
"registrars": [],
"dns_providers": [],
"domains": [
{
"name": "a9993e364706816aba3e25717850c26c9cd0d89d",
"registrar": "reg",
"dnsProviders": {},
"records": []
}
]
}