Add NULL record (#840)
Sorely missing from this library. Add it. As there is no presentation format the String method for this type puts a comment in front of it. Signed-off-by: Miek Gieben <miek@miek.nl>
This commit is contained in:
parent
5f5f2380fc
commit
56516cf4de
|
@ -153,7 +153,8 @@ if rr.%s != "-" {
|
||||||
fallthrough
|
fallthrough
|
||||||
case st.Tag(i) == `dns:"hex"`:
|
case st.Tag(i) == `dns:"hex"`:
|
||||||
o("off, err = packStringHex(rr.%s, msg, off)\n")
|
o("off, err = packStringHex(rr.%s, msg, off)\n")
|
||||||
|
case st.Tag(i) == `dns:"any"`:
|
||||||
|
o("off, err = packStringAny(rr.%s, msg, off)\n")
|
||||||
case st.Tag(i) == `dns:"octet"`:
|
case st.Tag(i) == `dns:"octet"`:
|
||||||
o("off, err = packStringOctet(rr.%s, msg, off)\n")
|
o("off, err = packStringOctet(rr.%s, msg, off)\n")
|
||||||
case st.Tag(i) == "":
|
case st.Tag(i) == "":
|
||||||
|
@ -261,6 +262,8 @@ return rr, off, err
|
||||||
o("rr.%s, off, err = unpackStringBase64(msg, off, rdStart + int(rr.Hdr.Rdlength))\n")
|
o("rr.%s, off, err = unpackStringBase64(msg, off, rdStart + int(rr.Hdr.Rdlength))\n")
|
||||||
case `dns:"hex"`:
|
case `dns:"hex"`:
|
||||||
o("rr.%s, off, err = unpackStringHex(msg, off, rdStart + int(rr.Hdr.Rdlength))\n")
|
o("rr.%s, off, err = unpackStringHex(msg, off, rdStart + int(rr.Hdr.Rdlength))\n")
|
||||||
|
case `dns:"any"`:
|
||||||
|
o("rr.%s, off, err = unpackStringAny(msg, off, rdStart + int(rr.Hdr.Rdlength))\n")
|
||||||
case `dns:"octet"`:
|
case `dns:"octet"`:
|
||||||
o("rr.%s, off, err = unpackStringOctet(msg, off)\n")
|
o("rr.%s, off, err = unpackStringOctet(msg, off)\n")
|
||||||
case "":
|
case "":
|
||||||
|
|
|
@ -363,6 +363,22 @@ func packStringHex(s string, msg []byte, off int) (int, error) {
|
||||||
return off, nil
|
return off, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func unpackStringAny(msg []byte, off, end int) (string, int, error) {
|
||||||
|
if end > len(msg) {
|
||||||
|
return "", len(msg), &Error{err: "overflow unpacking anything"}
|
||||||
|
}
|
||||||
|
return string(msg[off:end]), end, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func packStringAny(s string, msg []byte, off int) (int, error) {
|
||||||
|
if off+len(s) > len(msg) {
|
||||||
|
return len(msg), &Error{err: "overflow packing anything"}
|
||||||
|
}
|
||||||
|
copy(msg[off:off+len(s)], s)
|
||||||
|
off += len(s)
|
||||||
|
return off, nil
|
||||||
|
}
|
||||||
|
|
||||||
func unpackStringTxt(msg []byte, off int) ([]string, int, error) {
|
func unpackStringTxt(msg []byte, off int) ([]string, int, error) {
|
||||||
txt, off, err := unpackTxt(msg, off)
|
txt, off, err := unpackTxt(msg, off)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -341,7 +341,7 @@ func TestParseDirectiveMisc(t *testing.T) {
|
||||||
|
|
||||||
func TestNSEC(t *testing.T) {
|
func TestNSEC(t *testing.T) {
|
||||||
nsectests := map[string]string{
|
nsectests := map[string]string{
|
||||||
"nl. IN NSEC3PARAM 1 0 5 30923C44C6CBBB8F": "nl.\t3600\tIN\tNSEC3PARAM\t1 0 5 30923C44C6CBBB8F",
|
"nl. IN NSEC3PARAM 1 0 5 30923C44C6CBBB8F": "nl.\t3600\tIN\tNSEC3PARAM\t1 0 5 30923C44C6CBBB8F",
|
||||||
"p2209hipbpnm681knjnu0m1febshlv4e.nl. IN NSEC3 1 1 5 30923C44C6CBBB8F P90DG1KE8QEAN0B01613LHQDG0SOJ0TA NS SOA TXT RRSIG DNSKEY NSEC3PARAM": "p2209hipbpnm681knjnu0m1febshlv4e.nl.\t3600\tIN\tNSEC3\t1 1 5 30923C44C6CBBB8F P90DG1KE8QEAN0B01613LHQDG0SOJ0TA NS SOA TXT RRSIG DNSKEY NSEC3PARAM",
|
"p2209hipbpnm681knjnu0m1febshlv4e.nl. IN NSEC3 1 1 5 30923C44C6CBBB8F P90DG1KE8QEAN0B01613LHQDG0SOJ0TA NS SOA TXT RRSIG DNSKEY NSEC3PARAM": "p2209hipbpnm681knjnu0m1febshlv4e.nl.\t3600\tIN\tNSEC3\t1 1 5 30923C44C6CBBB8F P90DG1KE8QEAN0B01613LHQDG0SOJ0TA NS SOA TXT RRSIG DNSKEY NSEC3PARAM",
|
||||||
"localhost.dnssex.nl. IN NSEC www.dnssex.nl. A RRSIG NSEC": "localhost.dnssex.nl.\t3600\tIN\tNSEC\twww.dnssex.nl. A RRSIG NSEC",
|
"localhost.dnssex.nl. IN NSEC www.dnssex.nl. A RRSIG NSEC": "localhost.dnssex.nl.\t3600\tIN\tNSEC\twww.dnssex.nl. A RRSIG NSEC",
|
||||||
"localhost.dnssex.nl. IN NSEC www.dnssex.nl. A RRSIG NSEC TYPE65534": "localhost.dnssex.nl.\t3600\tIN\tNSEC\twww.dnssex.nl. A RRSIG NSEC TYPE65534",
|
"localhost.dnssex.nl. IN NSEC www.dnssex.nl. A RRSIG NSEC TYPE65534": "localhost.dnssex.nl.\t3600\tIN\tNSEC\twww.dnssex.nl. A RRSIG NSEC TYPE65534",
|
||||||
|
@ -398,16 +398,16 @@ func TestQuotes(t *testing.T) {
|
||||||
`t.example.com. IN TXT "a bc"`: "t.example.com.\t3600\tIN\tTXT\t\"a bc\"",
|
`t.example.com. IN TXT "a bc"`: "t.example.com.\t3600\tIN\tTXT\t\"a bc\"",
|
||||||
`t.example.com. IN TXT "a
|
`t.example.com. IN TXT "a
|
||||||
bc"`: "t.example.com.\t3600\tIN\tTXT\t\"a\\010 bc\"",
|
bc"`: "t.example.com.\t3600\tIN\tTXT\t\"a\\010 bc\"",
|
||||||
`t.example.com. IN TXT ""`: "t.example.com.\t3600\tIN\tTXT\t\"\"",
|
`t.example.com. IN TXT ""`: "t.example.com.\t3600\tIN\tTXT\t\"\"",
|
||||||
`t.example.com. IN TXT "a"`: "t.example.com.\t3600\tIN\tTXT\t\"a\"",
|
`t.example.com. IN TXT "a"`: "t.example.com.\t3600\tIN\tTXT\t\"a\"",
|
||||||
`t.example.com. IN TXT "aa"`: "t.example.com.\t3600\tIN\tTXT\t\"aa\"",
|
`t.example.com. IN TXT "aa"`: "t.example.com.\t3600\tIN\tTXT\t\"aa\"",
|
||||||
`t.example.com. IN TXT "aaa" ;`: "t.example.com.\t3600\tIN\tTXT\t\"aaa\"",
|
`t.example.com. IN TXT "aaa" ;`: "t.example.com.\t3600\tIN\tTXT\t\"aaa\"",
|
||||||
`t.example.com. IN TXT "abc" "DEF"`: "t.example.com.\t3600\tIN\tTXT\t\"abc\" \"DEF\"",
|
`t.example.com. IN TXT "abc" "DEF"`: "t.example.com.\t3600\tIN\tTXT\t\"abc\" \"DEF\"",
|
||||||
`t.example.com. IN TXT "abc" ( "DEF" )`: "t.example.com.\t3600\tIN\tTXT\t\"abc\" \"DEF\"",
|
`t.example.com. IN TXT "abc" ( "DEF" )`: "t.example.com.\t3600\tIN\tTXT\t\"abc\" \"DEF\"",
|
||||||
`t.example.com. IN TXT aaa ;`: "t.example.com.\t3600\tIN\tTXT\t\"aaa\"",
|
`t.example.com. IN TXT aaa ;`: "t.example.com.\t3600\tIN\tTXT\t\"aaa\"",
|
||||||
`t.example.com. IN TXT aaa aaa;`: "t.example.com.\t3600\tIN\tTXT\t\"aaa\" \"aaa\"",
|
`t.example.com. IN TXT aaa aaa;`: "t.example.com.\t3600\tIN\tTXT\t\"aaa\" \"aaa\"",
|
||||||
`t.example.com. IN TXT aaa aaa`: "t.example.com.\t3600\tIN\tTXT\t\"aaa\" \"aaa\"",
|
`t.example.com. IN TXT aaa aaa`: "t.example.com.\t3600\tIN\tTXT\t\"aaa\" \"aaa\"",
|
||||||
`t.example.com. IN TXT aaa`: "t.example.com.\t3600\tIN\tTXT\t\"aaa\"",
|
`t.example.com. IN TXT aaa`: "t.example.com.\t3600\tIN\tTXT\t\"aaa\"",
|
||||||
"cid.urn.arpa. NAPTR 100 50 \"s\" \"z3950+I2L+I2C\" \"\" _z3950._tcp.gatech.edu.": "cid.urn.arpa.\t3600\tIN\tNAPTR\t100 50 \"s\" \"z3950+I2L+I2C\" \"\" _z3950._tcp.gatech.edu.",
|
"cid.urn.arpa. NAPTR 100 50 \"s\" \"z3950+I2L+I2C\" \"\" _z3950._tcp.gatech.edu.": "cid.urn.arpa.\t3600\tIN\tNAPTR\t100 50 \"s\" \"z3950+I2L+I2C\" \"\" _z3950._tcp.gatech.edu.",
|
||||||
"cid.urn.arpa. NAPTR 100 50 \"s\" \"rcds+I2C\" \"\" _rcds._udp.gatech.edu.": "cid.urn.arpa.\t3600\tIN\tNAPTR\t100 50 \"s\" \"rcds+I2C\" \"\" _rcds._udp.gatech.edu.",
|
"cid.urn.arpa. NAPTR 100 50 \"s\" \"rcds+I2C\" \"\" _rcds._udp.gatech.edu.": "cid.urn.arpa.\t3600\tIN\tNAPTR\t100 50 \"s\" \"rcds+I2C\" \"\" _rcds._udp.gatech.edu.",
|
||||||
"cid.urn.arpa. NAPTR 100 50 \"s\" \"http+I2L+I2C+I2R\" \"\" _http._tcp.gatech.edu.": "cid.urn.arpa.\t3600\tIN\tNAPTR\t100 50 \"s\" \"http+I2L+I2C+I2R\" \"\" _http._tcp.gatech.edu.",
|
"cid.urn.arpa. NAPTR 100 50 \"s\" \"http+I2L+I2C+I2R\" \"\" _http._tcp.gatech.edu.": "cid.urn.arpa.\t3600\tIN\tNAPTR\t100 50 \"s\" \"http+I2L+I2C+I2R\" \"\" _http._tcp.gatech.edu.",
|
||||||
|
@ -1567,3 +1567,17 @@ func TestBad(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNULLRecord(t *testing.T) {
|
||||||
|
// packet captured from iodine
|
||||||
|
packet := `8116840000010001000000000569627a6c700474657374046d69656b026e6c00000a0001c00c000a0001000000000005497f000001`
|
||||||
|
data, _ := hex.DecodeString(packet)
|
||||||
|
msg := new(Msg)
|
||||||
|
err := msg.Unpack(data)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to unpack NULL record")
|
||||||
|
}
|
||||||
|
if _, ok := msg.Answer[0].(*NULL); !ok {
|
||||||
|
t.Fatalf("Expected packet to contain NULL record")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
11
types.go
11
types.go
|
@ -241,6 +241,17 @@ type ANY struct {
|
||||||
|
|
||||||
func (rr *ANY) String() string { return rr.Hdr.String() }
|
func (rr *ANY) String() string { return rr.Hdr.String() }
|
||||||
|
|
||||||
|
// NULL RR. See RFC 1035.
|
||||||
|
type NULL struct {
|
||||||
|
Hdr RR_Header
|
||||||
|
Anything string `dns:"any"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rr *NULL) String() string {
|
||||||
|
// There is no presentation format; prefix string with a comment.
|
||||||
|
return ";" + rr.Hdr.String() + rr.Anything
|
||||||
|
}
|
||||||
|
|
||||||
// CNAME RR. See RFC 1034.
|
// CNAME RR. See RFC 1034.
|
||||||
type CNAME struct {
|
type CNAME struct {
|
||||||
Hdr RR_Header
|
Hdr RR_Header
|
||||||
|
|
|
@ -193,6 +193,8 @@ func main() {
|
||||||
fallthrough
|
fallthrough
|
||||||
case st.Tag(i) == `dns:"hex"`:
|
case st.Tag(i) == `dns:"hex"`:
|
||||||
o("l += len(rr.%s)/2 + 1\n")
|
o("l += len(rr.%s)/2 + 1\n")
|
||||||
|
case st.Tag(i) == `dns:"any"`:
|
||||||
|
o("l += len(rr.%s)\n")
|
||||||
case st.Tag(i) == `dns:"a"`:
|
case st.Tag(i) == `dns:"a"`:
|
||||||
o("l += net.IPv4len // %s\n")
|
o("l += net.IPv4len // %s\n")
|
||||||
case st.Tag(i) == `dns:"aaaa"`:
|
case st.Tag(i) == `dns:"aaaa"`:
|
||||||
|
|
|
@ -85,6 +85,8 @@ func isDuplicateRdata(r1, r2 RR) bool {
|
||||||
return isDuplicateNSEC3(r1.(*NSEC3), r2.(*NSEC3))
|
return isDuplicateNSEC3(r1.(*NSEC3), r2.(*NSEC3))
|
||||||
case TypeNSEC3PARAM:
|
case TypeNSEC3PARAM:
|
||||||
return isDuplicateNSEC3PARAM(r1.(*NSEC3PARAM), r2.(*NSEC3PARAM))
|
return isDuplicateNSEC3PARAM(r1.(*NSEC3PARAM), r2.(*NSEC3PARAM))
|
||||||
|
case TypeNULL:
|
||||||
|
return isDuplicateNULL(r1.(*NULL), r2.(*NULL))
|
||||||
case TypeOPENPGPKEY:
|
case TypeOPENPGPKEY:
|
||||||
return isDuplicateOPENPGPKEY(r1.(*OPENPGPKEY), r2.(*OPENPGPKEY))
|
return isDuplicateOPENPGPKEY(r1.(*OPENPGPKEY), r2.(*OPENPGPKEY))
|
||||||
case TypePTR:
|
case TypePTR:
|
||||||
|
@ -616,6 +618,13 @@ func isDuplicateNSEC3PARAM(r1, r2 *NSEC3PARAM) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isDuplicateNULL(r1, r2 *NULL) bool {
|
||||||
|
if r1.Anything != r2.Anything {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func isDuplicateOPENPGPKEY(r1, r2 *OPENPGPKEY) bool {
|
func isDuplicateOPENPGPKEY(r1, r2 *OPENPGPKEY) bool {
|
||||||
if r1.PublicKey != r2.PublicKey {
|
if r1.PublicKey != r2.PublicKey {
|
||||||
return false
|
return false
|
||||||
|
|
30
zmsg.go
30
zmsg.go
|
@ -802,6 +802,18 @@ func (rr *NSEC3PARAM) pack(msg []byte, off int, compression compressionMap, comp
|
||||||
return headerEnd, off, nil
|
return headerEnd, off, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (rr *NULL) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) {
|
||||||
|
headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress)
|
||||||
|
if err != nil {
|
||||||
|
return headerEnd, off, err
|
||||||
|
}
|
||||||
|
off, err = packStringAny(rr.Anything, msg, off)
|
||||||
|
if err != nil {
|
||||||
|
return headerEnd, off, err
|
||||||
|
}
|
||||||
|
return headerEnd, off, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (rr *OPENPGPKEY) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) {
|
func (rr *OPENPGPKEY) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) {
|
||||||
headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress)
|
headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -2549,6 +2561,23 @@ func unpackNSEC3PARAM(h RR_Header, msg []byte, off int) (RR, int, error) {
|
||||||
return rr, off, err
|
return rr, off, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func unpackNULL(h RR_Header, msg []byte, off int) (RR, int, error) {
|
||||||
|
rr := new(NULL)
|
||||||
|
rr.Hdr = h
|
||||||
|
if noRdata(h) {
|
||||||
|
return rr, off, nil
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
rdStart := off
|
||||||
|
_ = rdStart
|
||||||
|
|
||||||
|
rr.Anything, off, err = unpackStringAny(msg, off, rdStart+int(rr.Hdr.Rdlength))
|
||||||
|
if err != nil {
|
||||||
|
return rr, off, err
|
||||||
|
}
|
||||||
|
return rr, off, err
|
||||||
|
}
|
||||||
|
|
||||||
func unpackOPENPGPKEY(h RR_Header, msg []byte, off int) (RR, int, error) {
|
func unpackOPENPGPKEY(h RR_Header, msg []byte, off int) (RR, int, error) {
|
||||||
rr := new(OPENPGPKEY)
|
rr := new(OPENPGPKEY)
|
||||||
rr.Hdr = h
|
rr.Hdr = h
|
||||||
|
@ -3448,6 +3477,7 @@ var typeToUnpack = map[uint16]func(RR_Header, []byte, int) (RR, int, error){
|
||||||
TypeNSEC: unpackNSEC,
|
TypeNSEC: unpackNSEC,
|
||||||
TypeNSEC3: unpackNSEC3,
|
TypeNSEC3: unpackNSEC3,
|
||||||
TypeNSEC3PARAM: unpackNSEC3PARAM,
|
TypeNSEC3PARAM: unpackNSEC3PARAM,
|
||||||
|
TypeNULL: unpackNULL,
|
||||||
TypeOPENPGPKEY: unpackOPENPGPKEY,
|
TypeOPENPGPKEY: unpackOPENPGPKEY,
|
||||||
TypeOPT: unpackOPT,
|
TypeOPT: unpackOPT,
|
||||||
TypePTR: unpackPTR,
|
TypePTR: unpackPTR,
|
||||||
|
|
10
ztypes.go
10
ztypes.go
|
@ -54,6 +54,7 @@ var TypeToRR = map[uint16]func() RR{
|
||||||
TypeNSEC: func() RR { return new(NSEC) },
|
TypeNSEC: func() RR { return new(NSEC) },
|
||||||
TypeNSEC3: func() RR { return new(NSEC3) },
|
TypeNSEC3: func() RR { return new(NSEC3) },
|
||||||
TypeNSEC3PARAM: func() RR { return new(NSEC3PARAM) },
|
TypeNSEC3PARAM: func() RR { return new(NSEC3PARAM) },
|
||||||
|
TypeNULL: func() RR { return new(NULL) },
|
||||||
TypeOPENPGPKEY: func() RR { return new(OPENPGPKEY) },
|
TypeOPENPGPKEY: func() RR { return new(OPENPGPKEY) },
|
||||||
TypeOPT: func() RR { return new(OPT) },
|
TypeOPT: func() RR { return new(OPT) },
|
||||||
TypePTR: func() RR { return new(PTR) },
|
TypePTR: func() RR { return new(PTR) },
|
||||||
|
@ -209,6 +210,7 @@ func (rr *NSAPPTR) Header() *RR_Header { return &rr.Hdr }
|
||||||
func (rr *NSEC) Header() *RR_Header { return &rr.Hdr }
|
func (rr *NSEC) Header() *RR_Header { return &rr.Hdr }
|
||||||
func (rr *NSEC3) Header() *RR_Header { return &rr.Hdr }
|
func (rr *NSEC3) Header() *RR_Header { return &rr.Hdr }
|
||||||
func (rr *NSEC3PARAM) Header() *RR_Header { return &rr.Hdr }
|
func (rr *NSEC3PARAM) Header() *RR_Header { return &rr.Hdr }
|
||||||
|
func (rr *NULL) Header() *RR_Header { return &rr.Hdr }
|
||||||
func (rr *OPENPGPKEY) Header() *RR_Header { return &rr.Hdr }
|
func (rr *OPENPGPKEY) Header() *RR_Header { return &rr.Hdr }
|
||||||
func (rr *OPT) Header() *RR_Header { return &rr.Hdr }
|
func (rr *OPT) Header() *RR_Header { return &rr.Hdr }
|
||||||
func (rr *PTR) Header() *RR_Header { return &rr.Hdr }
|
func (rr *PTR) Header() *RR_Header { return &rr.Hdr }
|
||||||
|
@ -473,6 +475,11 @@ func (rr *NSEC3PARAM) len(off int, compression map[string]struct{}) int {
|
||||||
l += len(rr.Salt) / 2
|
l += len(rr.Salt) / 2
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
|
func (rr *NULL) len(off int, compression map[string]struct{}) int {
|
||||||
|
l := rr.Hdr.len(off, compression)
|
||||||
|
l += len(rr.Anything)
|
||||||
|
return l
|
||||||
|
}
|
||||||
func (rr *OPENPGPKEY) len(off int, compression map[string]struct{}) int {
|
func (rr *OPENPGPKEY) len(off int, compression map[string]struct{}) int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len(off, compression)
|
||||||
l += base64.StdEncoding.DecodedLen(len(rr.PublicKey))
|
l += base64.StdEncoding.DecodedLen(len(rr.PublicKey))
|
||||||
|
@ -783,6 +790,9 @@ func (rr *NSEC3) copy() RR {
|
||||||
func (rr *NSEC3PARAM) copy() RR {
|
func (rr *NSEC3PARAM) copy() RR {
|
||||||
return &NSEC3PARAM{rr.Hdr, rr.Hash, rr.Flags, rr.Iterations, rr.SaltLength, rr.Salt}
|
return &NSEC3PARAM{rr.Hdr, rr.Hash, rr.Flags, rr.Iterations, rr.SaltLength, rr.Salt}
|
||||||
}
|
}
|
||||||
|
func (rr *NULL) copy() RR {
|
||||||
|
return &NULL{rr.Hdr, rr.Anything}
|
||||||
|
}
|
||||||
func (rr *OPENPGPKEY) copy() RR {
|
func (rr *OPENPGPKEY) copy() RR {
|
||||||
return &OPENPGPKEY{rr.Hdr, rr.PublicKey}
|
return &OPENPGPKEY{rr.Hdr, rr.PublicKey}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue