FIXED:
* tplx/sprigx would throw errors on dnsx.IsPtr, because it returns a
  bool and net.IP instead of a boolen and optional error. Fixed by
  adding dnsx.IsPtrChk and using that instead.
This commit is contained in:
brent saner
2026-08-08 00:13:34 -04:00
parent cbfaaddf34
commit 45b3d38283
12 changed files with 202 additions and 78 deletions
+4
View File
@@ -0,0 +1,4 @@
/*
Package fsx provides some enhancements to [io/fs].
*/
package fsx
+102
View File
@@ -0,0 +1,102 @@
package fsx
import (
"go/types"
"io/fs"
"path/filepath"
)
/*
FsPaths returns all paths in [io/fs.FS] `fsys`.
If `fsys` is nil, both `fsPaths` and `err` will be nil.
`t` should be an OR'd [io/fs.FileMode] containing all filetypes that should be included.
Use [io/fs.ModeType] to match any non-regular-file object, or 0 to explicitly match regular files.
If `t` is non-zero but you wish to ALSO include regular files, then specify `inclFiles` as true.
(`inclFiles` is ignored if `t` == 0, as this inherently matches regular files only/explicitly.)
See [HasType] for more information on this filtering.
`maxDepth` can be used to limit recursion depth.
- If `maxDepth` == 0, only direct [io/fs.DirEntry] descendants of `fsys`
will be included in `fsPaths`.
- If `maxDepth` < 0, ALL [io/fs.DirEntry] descendants of `fsys`
will be included in `fsPaths`.
- If `maxDepth` > 0, then only children at a depth of this level or lower
("higher" in a hierarchy) will be included in `fsPaths`.
*/
func FsPaths(fsys fs.FS, t fs.FileMode, inclFiles bool, maxDepth int) (fsPaths map[string]fs.DirEntry, err error) {
var idx int
var entries []fs.DirEntry
var t fs.FileMode = types.Type()
if fsys == nil {
return
}
if fsPaths, err = getEntries(fsys, t, inclFiles, maxDepth, "."); err != nil {
return
}
return
}
/*
HasType is just a convenience wrapper for:
hasType = m & t != 0
The type(s) will be explicitly extracted from `m` and `t` first.
Note that if `t` is an OR'd set of filetypes, then HasType will return true if ANY of the types
in `t` match `m`'s type (see [r00t2.io/goutils/bitmask] for more information on bitmasking).
If you wish to check if `m` is A SINGLE type, either ensure that `t` is not OR'd
(i.e. is a single fs.FileMode type) or use [IsType] instead.
*/
func HasType(m, t fs.FileMode) (hasType bool) { return m.Type()&t.Type() != 0 }
/*
IsType is just a convenience wrapper for:
isType = m & t == t
The type(s) will be explicitly extracted from `m` and `t` first.
Note that if `t` is an OR'd set of filetypes, then IsType will *never* return true
as its type is single-valued/not bitmasked (see [r00t2.io/goutils/bitmask] for more information
on bitmasking).
If you wish to check if `m` is ONE OF multiple types in `t`, use [HasType] instead.
*/
func IsType(m, t fs.FileMode) (isType bool) { return m&t == t }
/*
getEntries is used to recursively fetch entries from `fsys`.
It mostly serves as the recursion for [FsPaths], and the arguments
retain their exact meaning except that it requires `relPath`
and `maxDepth` (if > 0) will be decremanted in each recursion.
*/
func getEntries(fsys fs.FS, t fs.FileMode, inclFiles bool, maxDepth int, relPath string) (fsPaths map[string]fs.DirEntry, err error) {
var idx int
var p string
var e []fs.DirEntry
var subs map[string]fs.DirEntry = make(map[string]fs.DirEntry)
if e, err = fs.ReadDir(fsys, relPath); err != nil {
return
}
for idx = range e {
p = filepath.Join(relPath, e[idx].Name())
if e[idx].IsDir() {
if subs, err = getEntries(fsys, t, inclFiles, maxDepth, p); err != nil {
return
}
}
}
return
}
+1
View File
@@ -0,0 +1 @@
- does IsAdd4Compat work properly with loopback ::1?
+56 -40
View File
@@ -1,16 +1,16 @@
package dnsx package dnsx
import ( import (
`bytes` "bytes"
`encoding/base32` "encoding/base32"
`fmt` "fmt"
`math` "math"
`net` "net"
`net/netip` "net/netip"
`strings` "strings"
`go4.org/netipx` "go4.org/netipx"
`r00t2.io/goutils/stringsx` "r00t2.io/goutils/stringsx"
) )
/* /*
@@ -238,19 +238,19 @@ TXT records, etc. - not A/AAAA/CNAME, etc. - see RFC 8553 for details.
See the following functions for allowing additional syntax/rule validation See the following functions for allowing additional syntax/rule validation
that have record-type-specific accommodations made: that have record-type-specific accommodations made:
* [IsFqdnDefinedTxt] - [IsFqdnDefinedTxt]
* [IsFqdnNsec3] - [IsFqdnNsec3]
* [IsFqdnSrv] - [IsFqdnSrv]
* [IsFqdnWildcard] - [IsFqdnWildcard]
# RFC Coverage # RFC Coverage
This function should conform properly to: This function should conform properly to:
* RFC 952 - RFC 952
* RFC 1034 and RFC 1035 - RFC 1034 and RFC 1035
* RFC 1123 - RFC 1123
* RFC 2181 (selectively, see above) - RFC 2181 (selectively, see above)
preferring the most up-to-date rules where relevant (e.g. labels may start with digits, as per RFC 1123). preferring the most up-to-date rules where relevant (e.g. labels may start with digits, as per RFC 1123).
It enforces/checks label and overall length limits as defined by RFC. It enforces/checks label and overall length limits as defined by RFC.
@@ -265,12 +265,12 @@ the caller must perform translation first
To reiterate, IDN/IDNA: To reiterate, IDN/IDNA:
* RFC 3490 - RFC 3490
* RFC 5890 - RFC 5890
* RFC 5891 - RFC 5891
* RFC 5892 - RFC 5892
* RFC 5893 - RFC 5893
* RFC 5894 - RFC 5894
and Punycode (RFC 3492) *MUST* use their ASCII forms, NOT the localized/Unicode formats. and Punycode (RFC 3492) *MUST* use their ASCII forms, NOT the localized/Unicode formats.
@@ -299,18 +299,18 @@ func IsFqdn(s string) (fqdn bool) {
IsFqdnDefinedTxt is like [IsFqdn] but explicitly *only* allows fully-qualified IsFqdnDefinedTxt is like [IsFqdn] but explicitly *only* allows fully-qualified
RFC-defined TXT "subtypes": RFC-defined TXT "subtypes":
* ACME DNS-01 (RFC 8555) - ACME DNS-01 (RFC 8555)
* BIMI (RFC draft [bimi]) - BIMI (RFC draft [bimi])
* DKIM (RFC 6376, RFC 8301, RFC 8463) - DKIM (RFC 6376, RFC 8301, RFC 8463)
* DKIM ATPS (RFC 6541) - DKIM ATPS (RFC 6541)
* DMARC (RFC 7489, RFC 9091, RFC 9616) - DMARC (RFC 7489, RFC 9091, RFC 9616)
* MTA-STS (RFC 8461) - MTA-STS (RFC 8461)
* TLSRPT (RFC 8460) - TLSRPT (RFC 8460)
Note that the following TXT "subtypes" do not have special formatting in labels/name, Note that the following TXT "subtypes" do not have special formatting in labels/name,
and thus are not covered by this function: and thus are not covered by this function:
* SPF (RFC 4408, RFC 7208) - SPF (RFC 4408, RFC 7208)
[bimi]: https://datatracker.ietf.org/doc/html/draft-brand-indicators-for-message-identification [bimi]: https://datatracker.ietf.org/doc/html/draft-brand-indicators-for-message-identification
*/ */
@@ -359,15 +359,15 @@ can't be 100% confirmed with certainty - only basic checks can be done.
NSEC3 can be found via: NSEC3 can be found via:
* RFC 5155 - RFC 5155
* RFC 6840 - RFC 6840
* RFC 6944 - RFC 6944
* RFC 7129 - RFC 7129
* RFC 8198 - RFC 8198
* RFC 9077 - RFC 9077
* RFC 9157 - RFC 9157
* RFC 9276 - RFC 9276
* RFC 9905 - RFC 9905
At the time of writing, only one hashing algorithm (SHA-1) has been specified. At the time of writing, only one hashing algorithm (SHA-1) has been specified.
However, because this function does not check against the IANA registration at runtime, However, because this function does not check against the IANA registration at runtime,
@@ -524,6 +524,22 @@ func IsPtr(s string) (isPtr bool, addr net.IP) {
return return
} }
// IsPtrAddr is like IsPtr but only returns the `addr` value.
func IsPtrAddr(s string) (addr net.IP) {
_, addr = IsPtr(s)
return
}
// IsPtrChk is like IsPtr but only returns the `isPtr` value.
func IsPtrChk(s string) (isPtr bool) {
isPtr, _ = IsPtr(s)
return
}
// commonFqdn is used to validate some rules common to all record names. // commonFqdn is used to validate some rules common to all record names.
func commonFqdn(s string) (isOk bool) { func commonFqdn(s string) (isOk bool) {
+1 -1
View File
@@ -636,7 +636,7 @@ pre.rouge .gs {
<div class="details"> <div class="details">
<span id="author" class="author">Brent Saner</span><br> <span id="author" class="author">Brent Saner</span><br>
<span id="email" class="email"><a href="mailto:bts@square-r00t.net">bts@square-r00t.net</a></span><br> <span id="email" class="email"><a href="mailto:bts@square-r00t.net">bts@square-r00t.net</a></span><br>
<span id="revdate">Last rendered 2026-07-29 15:05:41 -0400</span> <span id="revdate">Last rendered 2026-08-08 00:13:35 -0400</span>
</div> </div>
<div id="toc" class="toc2"> <div id="toc" class="toc2">
<div id="toctitle">Table of Contents</div> <div id="toctitle">Table of Contents</div>
+1 -1
View File
@@ -6,7 +6,7 @@
<span id="author" class="author">Brent Saner</span> <span id="author" class="author">Brent Saner</span>
<span id="email" class="email"><bts@square-r00t.net></span> <span id="email" class="email"><bts@square-r00t.net></span>
<span id="revdate">Last rendered 2026-07-29 15:05:45 -0400</span> <span id="revdate">Last rendered 2026-08-08 00:13:40 -0400</span>
</div> </div>
+37 -36
View File
@@ -1,30 +1,30 @@
package sprigx package sprigx
import ( import (
`net` "net"
`net/netip` "net/netip"
`os` "os"
`os/user` "os/user"
`path` "path"
`path/filepath` "path/filepath"
`runtime` "runtime"
`time` "time"
`github.com/davecgh/go-spew/spew` "github.com/davecgh/go-spew/spew"
`github.com/shirou/gopsutil/v4/cpu` "github.com/shirou/gopsutil/v4/cpu"
`github.com/shirou/gopsutil/v4/disk` "github.com/shirou/gopsutil/v4/disk"
`github.com/shirou/gopsutil/v4/host` "github.com/shirou/gopsutil/v4/host"
`github.com/shirou/gopsutil/v4/load` "github.com/shirou/gopsutil/v4/load"
`github.com/shirou/gopsutil/v4/mem` "github.com/shirou/gopsutil/v4/mem"
psnet `github.com/shirou/gopsutil/v4/net` psnet "github.com/shirou/gopsutil/v4/net"
`github.com/shirou/gopsutil/v4/process` "github.com/shirou/gopsutil/v4/process"
`github.com/shirou/gopsutil/v4/sensors` "github.com/shirou/gopsutil/v4/sensors"
`go4.org/netipx` "go4.org/netipx"
`r00t2.io/goutils/netx` "r00t2.io/goutils/netx"
`r00t2.io/goutils/netx/dnsx` "r00t2.io/goutils/netx/dnsx"
`r00t2.io/goutils/stringsx` "r00t2.io/goutils/stringsx"
`r00t2.io/goutils/timex` "r00t2.io/goutils/timex"
`r00t2.io/sysutils` "r00t2.io/sysutils"
) )
var ( var (
@@ -98,19 +98,20 @@ var (
/* /*
Networking (r00t.io/goutils/netx/dnsx) Networking (r00t.io/goutils/netx/dnsx)
*/ */
"dnsxPtrAddr": dnsx.AddrFromPtr, "dnsxPtrAddr": dnsx.AddrFromPtr,
"dnsxAddrPtr": dnsx.AddrToPtr, "dnsxAddrPtr": dnsx.AddrToPtr,
"dnsxStrWire": dnsx.DnsStrToWire, "dnsxStrWire": dnsx.DnsStrToWire,
"dnsxWireStr": dnsx.DnsWireToStr, "dnsxWireStr": dnsx.DnsWireToStr,
"dnsxPtrIp": dnsx.IpFromPtr, "dnsxPtrIp": dnsx.IpFromPtr,
"dnsxIpPtr": dnsx.IpToPtr, "dnsxIpPtr": dnsx.IpToPtr,
"dnsxIsFqdn": dnsx.IsFqdn, "dnsxIsFqdn": dnsx.IsFqdn,
"dnsxIsTxt": dnsx.IsFqdnDefinedTxt, "dnsxIsTxt": dnsx.IsFqdnDefinedTxt,
"dnsxIsNsec3": dnsx.IsFqdnNsec3, "dnsxIsNsec3": dnsx.IsFqdnNsec3,
"dnsxIsSrv": dnsx.IsFqdnSrv, "dnsxIsSrv": dnsx.IsFqdnSrv,
"dnsxIsWild": dnsx.IsFqdnWildcard, "dnsxIsWild": dnsx.IsFqdnWildcard,
"dnsxIsLbl": dnsx.IsLabel, "dnsxIsLbl": dnsx.IsLabel,
"dnsxIsPtr": dnsx.IsPtr, "dnsxIsPtrAddr": dnsx.IsPtrAddr,
"dnsxIsPtr": dnsx.IsPtrChk,
/* /*
Numbers/Math Numbers/Math
*/ */