From 1bb43476e88afb4c507a2b6d1850c08650184d2f Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 26 Oct 2020 18:44:45 -0400 Subject: [PATCH] Build fix, add Go unit tests for CGo Locator interface. --- cmd/zerotier/cli/cert.go | 70 +++++++++++++++--------- cmd/zerotier/cli/help.go | 1 - cmd/zerotier/cli/identity.go | 16 ++++-- cmd/zerotier/cli/join.go | 24 ++++---- cmd/zerotier/cli/misc.go | 14 ++++- cmd/zerotier/cli/network.go | 4 +- cmd/zt_service_tests/locator.go | 53 ++++++++++++++++++ cmd/zt_service_tests/zt_service_tests.go | 3 + pkg/zerotier/identity.go | 8 +-- pkg/zerotier/locator.go | 6 +- pkg/zerotier/misc.go | 4 +- 11 files changed, 145 insertions(+), 58 deletions(-) create mode 100644 cmd/zt_service_tests/locator.go diff --git a/cmd/zerotier/cli/cert.go b/cmd/zerotier/cli/cert.go index 797aed6b7..0673b0354 100644 --- a/cmd/zerotier/cli/cert.go +++ b/cmd/zerotier/cli/cert.go @@ -48,20 +48,27 @@ func Cert(basePath string, authTokenGenerator func() string, args []string, json Help() return 1 } + uniqueId, uniqueIdPrivate, err := zerotier.NewCertificateSubjectUniqueId(zerotier.CertificateUniqueIdTypeNistP384) if err != nil { - fmt.Printf("ERROR: unable to create unique ID and private key: %s\n", err.Error()) + pErr("unable to create unique ID and private key: %s", err.Error()) return 1 } - sec, err := json.MarshalIndent(&zerotier.CertificateSubjectUniqueIDSecret{UniqueID: uniqueId, UniqueIDSecret: uniqueIdPrivate}, "", " ") + + sec, err := json.MarshalIndent(&zerotier.CertificateSubjectUniqueIDSecret{ + UniqueID: uniqueId, + UniqueIDSecret: uniqueIdPrivate, + }, "", " ") if err != nil { - fmt.Printf("ERROR: unable to create unique ID and private key: %s\n", err.Error()) + pErr("unable to create unique ID and private key: %s", err.Error()) return 1 } + if len(args) == 1 { fmt.Println(string(sec)) } else { _ = ioutil.WriteFile(args[1], sec, 0600) + pResult("%s", args[1]) } case "newcsr": @@ -69,28 +76,32 @@ func Cert(basePath string, authTokenGenerator func() string, args []string, json Help() return 1 } - var cs zerotier.CertificateSubject - err := readJSONFile(args[1], &cs) + + var subject zerotier.CertificateSubject + err := readJSONFile(args[1], &subject) if err != nil { - fmt.Printf("ERROR: unable to read subject from %s: %s\n", args[1], err.Error()) + pErr("unable to read subject from %s: %s", args[1], err.Error()) return 1 } - var subj zerotier.CertificateSubjectUniqueIDSecret - err = readJSONFile(args[2], &subj) + + var uniqueIdSecret zerotier.CertificateSubjectUniqueIDSecret + err = readJSONFile(args[2], &uniqueIdSecret) if err != nil { - fmt.Printf("ERROR: unable to read unique ID secret from %s: %s\n", args[2], err.Error()) + pErr("unable to read unique ID secret from %s: %s", args[2], err.Error()) return 1 } - csr, err := zerotier.NewCertificateCSR(&cs, subj.UniqueID, subj.UniqueIDSecret) + + csr, err := zerotier.NewCertificateCSR(&subject, uniqueIdSecret.UniqueID, uniqueIdSecret.UniqueIDSecret) if err != nil { - fmt.Printf("ERROR: problem creating CSR: %s\n", err.Error()) + pErr("problem creating CSR: %s", err.Error()) return 1 } + err = ioutil.WriteFile(args[3], csr, 0644) if err == nil { - fmt.Printf("Wrote CSR to %s\n", args[3]) + pResult("%s", args[3]) } else { - fmt.Printf("ERROR: unable to write CSR to %s: %s\n", args[3], err.Error()) + pErr("unable to write CSR to %s: %s", args[3], err.Error()) return 1 } @@ -102,40 +113,42 @@ func Cert(basePath string, authTokenGenerator func() string, args []string, json csrBytes, err := ioutil.ReadFile(args[1]) if err != nil { - fmt.Printf("ERROR: unable to read CSR from %s: %s\n", args[1], err.Error()) + pErr("unable to read CSR from %s: %s", args[1], err.Error()) return 1 } csr, err := zerotier.NewCertificateFromBytes(csrBytes, false) if err != nil { - fmt.Printf("ERROR: CSR in %s is invalid: %s\n", args[1], err.Error()) + pErr("CSR in %s is invalid: %s", args[1], err.Error()) return 1 } - id := readIdentity(args[2]) - if id == nil { - fmt.Printf("ERROR: unable to read identity from %s\n", args[2]) + signingIdentity := readIdentity(args[2]) + if signingIdentity == nil { + pErr("unable to read identity from %s", args[2]) return 1 } - if !id.HasPrivate() { - fmt.Printf("ERROR: signing identity in %s lacks private key\n", args[2]) + if !signingIdentity.HasPrivate() { + pErr("signing identity in %s lacks private key", args[2]) return 1 } - cert, err := csr.Sign(id) + cert, err := csr.Sign(signingIdentity) if err != nil { - fmt.Printf("ERROR: error signing CSR or generating certificate: %s\n", err.Error()) + pErr("error signing CSR or generating certificate: %s", err.Error()) return 1 } + cb, err := cert.Marshal() if err != nil { - fmt.Printf("ERROR: error marshaling signed certificate: %s\n", err.Error()) + pErr("error marshaling signed certificate: %s", err.Error()) return 1 } + err = ioutil.WriteFile(args[3], cb, 0644) if err == nil { - fmt.Printf("Wrote signed certificate to %s\n", args[3]) + pResult("%s", args[3]) } else { - fmt.Printf("ERROR: unable to write signed certificate to %s: %s\n", args[3], err.Error()) + pErr("unable to write signed certificate to %s: %s", args[3], err.Error()) return 1 } @@ -144,16 +157,19 @@ func Cert(basePath string, authTokenGenerator func() string, args []string, json Help() return 1 } + certBytes, err := ioutil.ReadFile(args[1]) if err != nil { - fmt.Printf("ERROR: unable to read certificate from %s: %s\n", args[1], err.Error()) + pErr("unable to read certificate from %s: %s", args[1], err.Error()) return 1 } + cert, err := zerotier.NewCertificateFromBytes(certBytes, true) if err != nil { - fmt.Printf("FAILED: certificate in %s invalid: %s\n", args[1], err.Error()) + pErr("certificate in %s invalid: %s", args[1], err.Error()) return 1 } + if args[0] == "dump" { fmt.Println(cert.JSON()) } else { diff --git a/cmd/zerotier/cli/help.go b/cmd/zerotier/cli/help.go index e1fde61bb..ef6e83b23 100644 --- a/cmd/zerotier/cli/help.go +++ b/cmd/zerotier/cli/help.go @@ -97,7 +97,6 @@ Advanced Operations: cert [args] · list List certificates at local node · show Show certificate details - newsubject Interactive subject creation newsid Create a new subject unique ID newcsr Create a subject CSR sign Sign a CSR to create a certificate diff --git a/cmd/zerotier/cli/identity.go b/cmd/zerotier/cli/identity.go index 5817fc8ae..9df75cf24 100644 --- a/cmd/zerotier/cli/identity.go +++ b/cmd/zerotier/cli/identity.go @@ -43,11 +43,13 @@ func Identity(args []string) int { return 1 } } + id, err := zerotier.NewIdentity(idType) if err != nil { - fmt.Printf("ERROR: internal error generating identity: %s\n", err.Error()) + pErr("internal error generating identity: %s", err.Error()) return 1 } + fmt.Println(id.PrivateKeyString()) return 0 @@ -56,20 +58,24 @@ func Identity(args []string) int { fmt.Println(readIdentity(args[1]).String()) return 0 } + pErr("no identity specified") + return 1 case "fingerprint": if len(args) == 2 { fmt.Println(readIdentity(args[1]).Fingerprint().String()) return 0 } + pErr("no identity specified") + return 1 case "validate": if len(args) == 2 { if readIdentity(args[1]).LocallyValidate() { - fmt.Println("OK") + fmt.Println("VALID") return 0 } - fmt.Println("FAILED") + fmt.Println("INVALID") return 1 } @@ -78,7 +84,7 @@ func Identity(args []string) int { id := readIdentity(args[1]) msg, err := ioutil.ReadFile(args[2]) if err != nil { - fmt.Printf("ERROR: unable to read input file: %s\n", err.Error()) + pErr("unable to read input file: %s", err.Error()) return 1 } @@ -99,7 +105,7 @@ func Identity(args []string) int { } else { sig, err := id.Sign(msg) if err != nil { - fmt.Printf("ERROR: internal error signing message: %s\n", err.Error()) + pErr("internal error signing message: %s", err.Error()) return 1 } fmt.Println(hex.EncodeToString(sig)) diff --git a/cmd/zerotier/cli/join.go b/cmd/zerotier/cli/join.go index 3fc8cc165..02032fb08 100644 --- a/cmd/zerotier/cli/join.go +++ b/cmd/zerotier/cli/join.go @@ -39,10 +39,17 @@ func Join(basePath string, authTokenGenerator func() string, args []string) int Help() return 1 } - if len(args[0]) != zerotier.NetworkIDStringLength { - fmt.Printf("ERROR: invalid network ID: %s\n", args[0]) + + if !isValidNetworkID(args[0]) { + pErr("invalid network ID: %s", args[0]) return 1 } + nwid, err := strconv.ParseUint(args[0], 16, 64) + if err != nil { + pErr("ERROR: invalid network ID: %s", args[0]) + return 1 + } + nwids := fmt.Sprintf("%.16x", nwid) _ = *controllerAuthToken // TODO: not implemented yet @@ -51,32 +58,25 @@ func Join(basePath string, authTokenGenerator func() string, args []string) int if strings.ContainsRune(*controllerFingerprint, '-') { fp, err = zerotier.NewFingerprintFromString(*controllerFingerprint) if err != nil { - fmt.Printf("ERROR: invalid network controller fingerprint: %s\n", *controllerFingerprint) + pErr("invalid network controller fingerprint: %s", *controllerFingerprint) return 1 } } else { id, err := zerotier.NewIdentityFromString(*controllerFingerprint) if err != nil { - fmt.Printf("ERROR: invalid network controller identity: %s\n", *controllerFingerprint) + pErr("invalid network controller identity: %s", *controllerFingerprint) return 1 } fp = id.Fingerprint() } } - nwid, err := strconv.ParseUint(args[0], 16, 64) - if err != nil { - fmt.Printf("ERROR: invalid network ID: %s\n", args[0]) - return 1 - } - nwids := fmt.Sprintf("%.16x", nwid) - var network zerotier.APINetwork network.ID = zerotier.NetworkID(nwid) network.ControllerFingerprint = fp if apiPost(basePath, authToken, "/network/"+nwids, &network, nil) <= 0 { - fmt.Printf("FAILED\n") + fmt.Println("FAILED") } else { if fp == nil { fmt.Printf("OK %s\n", nwids) diff --git a/cmd/zerotier/cli/misc.go b/cmd/zerotier/cli/misc.go index 0894a8548..2ec2d6b84 100644 --- a/cmd/zerotier/cli/misc.go +++ b/cmd/zerotier/cli/misc.go @@ -14,18 +14,26 @@ package cli import ( - "bufio" "encoding/json" "fmt" "io/ioutil" "net/http" "os" - "strconv" "strings" "zerotier/pkg/zerotier" ) +func pErr(format string, args ...interface{}) { + _, _ = fmt.Fprintf(os.Stdout, "ERROR: "+format, args...) + fmt.Println() +} + +func pResult(format string, args ...interface{}) { + _, _ = fmt.Printf(format, args...) + fmt.Println() +} + func apiGet(basePath, authToken, urlPath string, result interface{}) int64 { statusCode, clock, err := zerotier.APIGet(basePath, zerotier.APISocketName, authToken, urlPath, result) if err != nil { @@ -238,6 +246,7 @@ func isValidNetworkID(a string) bool { return false } +/* func prompt(str string, dfl string) string { if len(dfl) > 0 { fmt.Printf("%s [%s]: ", str, dfl) @@ -274,3 +283,4 @@ func promptFile(str string) []byte { } return nil } +*/ diff --git a/cmd/zerotier/cli/network.go b/cmd/zerotier/cli/network.go index 8391cc665..fc5226af3 100644 --- a/cmd/zerotier/cli/network.go +++ b/cmd/zerotier/cli/network.go @@ -124,12 +124,12 @@ func Network(basePath string, authTokenGenerator func() string, args []string, j } if len(args[0]) != zerotier.NetworkIDStringLength { - fmt.Printf("ERROR: invalid network ID: %s\n", args[0]) + pErr("ERROR: invalid network ID: %s", args[0]) return 1 } nwid, err := strconv.ParseUint(args[0], 16, 64) if err != nil { - fmt.Printf("ERROR: invalid network ID: %s\n", args[0]) + pErr("ERROR: invalid network ID: %s", args[0]) return 1 } nwids := fmt.Sprintf("%.16x", nwid) diff --git a/cmd/zt_service_tests/locator.go b/cmd/zt_service_tests/locator.go new file mode 100644 index 000000000..580a9c314 --- /dev/null +++ b/cmd/zt_service_tests/locator.go @@ -0,0 +1,53 @@ +/* + * Copyright (C)2013-2020 ZeroTier, Inc. + * + * Use of this software is governed by the Business Source License included + * in the LICENSE.TXT file in the project's root directory. + * + * Change Date: 2025-01-01 + * + * On the date above, in accordance with the Business Source License, use + * of this software will be governed by version 2.0 of the Apache License. + */ +/****/ + +package main + +import ( + "fmt" + "zerotier/pkg/zerotier" +) + +func TestLocator() bool { + fmt.Printf("Creating Endpoint instances... ") + ep0, err := zerotier.NewEndpointFromString("1.1.1.1/1") + if err != nil { + fmt.Printf("IPv4 FAILED (%s)\n",err.Error()) + return false + } + ep1, err := zerotier.NewEndpointFromString("2600:1901:0:4006::1234/2") + if err != nil { + fmt.Printf("IPv6 FAILED (%s)\n",err.Error()) + return false + } + eps := []*zerotier.Endpoint{ep0, ep1} + fmt.Printf("OK\n") + + fmt.Printf("Creating signing Identity... ") + signer, err := zerotier.NewIdentity(zerotier.IdentityTypeP384) + if err != nil { + fmt.Printf("FAILED (%s)\n", err.Error()) + return false + } + fmt.Printf("OK %s\n",signer.String()) + + fmt.Printf("Creating Locator instance... ") + loc, err := zerotier.NewLocator(zerotier.TimeMs(), eps, signer) + if err != nil { + fmt.Printf("FAILED (%s)\n",err.Error()) + return false + } + fmt.Printf("OK %s\n",loc.String()) + + return true +} diff --git a/cmd/zt_service_tests/zt_service_tests.go b/cmd/zt_service_tests/zt_service_tests.go index ce7b36269..3a7bcadb1 100644 --- a/cmd/zt_service_tests/zt_service_tests.go +++ b/cmd/zt_service_tests/zt_service_tests.go @@ -13,4 +13,7 @@ func main() { if !TestCertificate() { os.Exit(1) } + if !TestLocator() { + os.Exit(1) + } } diff --git a/pkg/zerotier/identity.go b/pkg/zerotier/identity.go index 3ca3ce0c3..90c5983d3 100644 --- a/pkg/zerotier/identity.go +++ b/pkg/zerotier/identity.go @@ -111,7 +111,7 @@ func NewIdentityFromString(s string) (*Identity, error) { } case 1: - id.publicKey, err = Base32StdLowerCase.DecodeString(ss[2]) + id.publicKey, err = Base32.DecodeString(ss[2]) if err != nil { return nil, err } @@ -119,7 +119,7 @@ func NewIdentityFromString(s string) (*Identity, error) { return nil, ErrInvalidKey } if len(ss) >= 4 { - id.privateKey, err = Base32StdLowerCase.DecodeString(ss[3]) + id.privateKey, err = Base32.DecodeString(ss[3]) if err != nil { return nil, err } @@ -190,7 +190,7 @@ func (id *Identity) PrivateKeyString() string { } case IdentityTypeP384: if len(id.publicKey) == IdentityTypeP384PublicKeySize && len(id.privateKey) == IdentityTypeP384PrivateKeySize { - return fmt.Sprintf("%.10x:1:%s:%s", uint64(id.address), Base32StdLowerCase.EncodeToString(id.publicKey), Base32StdLowerCase.EncodeToString(id.privateKey)) + return fmt.Sprintf("%.10x:1:%s:%s", uint64(id.address), Base32.EncodeToString(id.publicKey), Base32.EncodeToString(id.privateKey)) } } return "" @@ -206,7 +206,7 @@ func (id *Identity) String() string { } case IdentityTypeP384: if len(id.publicKey) == IdentityTypeP384PublicKeySize { - return fmt.Sprintf("%.10x:1:%s", uint64(id.address), Base32StdLowerCase.EncodeToString(id.publicKey)) + return fmt.Sprintf("%.10x:1:%s", uint64(id.address), Base32.EncodeToString(id.publicKey)) } } return "" diff --git a/pkg/zerotier/locator.go b/pkg/zerotier/locator.go index 4170d84e9..e851656bd 100644 --- a/pkg/zerotier/locator.go +++ b/pkg/zerotier/locator.go @@ -39,7 +39,7 @@ func newLocatorFromCLocator(cl unsafe.Pointer, needFinalizer bool) (*Locator, er return loc, nil } -func NewLocator(ts int64, endpoints []Endpoint, signer *Identity) (*Locator, error) { +func NewLocator(ts int64, endpoints []*Endpoint, signer *Identity) (*Locator, error) { if ts <= 0 || len(endpoints) == 0 || signer == nil { return nil, ErrInvalidParameter } @@ -110,8 +110,8 @@ func (loc *Locator) String() string { if loc.cl == nil { return "" } - var buf [4096]C.char - return C.GoString(C.ZT_Locator_toString(loc.cl, &buf[0], 4096)) + var buf [16384]C.char // 16384 == ZT_LOCATOR_STRING_SIZE_MAX + return C.GoString(C.ZT_Locator_toString(loc.cl, &buf[0], 16384)) } func (loc *Locator) MarshalJSON() ([]byte, error) { diff --git a/pkg/zerotier/misc.go b/pkg/zerotier/misc.go index ee77c3ae2..d5009b9bc 100644 --- a/pkg/zerotier/misc.go +++ b/pkg/zerotier/misc.go @@ -35,8 +35,8 @@ const pointerSize = unsafe.Sizeof(uintptr(0)) // Base32Alphabet is the Base32 alphabet used in ZeroTier. const Base32Alphabet = "abcdefghijklmnopqrstuvwxyz234567" -// Base32 is an encoder using the ZeroTier base32 encoding. -var Base32 = base32.NewEncoding(Base32Alphabet) +// Base32 is an encoder using the ZeroTier base32 encoding and no padding (same as core). +var Base32 = base32.NewEncoding(Base32Alphabet).WithPadding(base32.NoPadding) // unassignedPrivilegedPorts are ports below 1024 that do not appear to be assigned by IANA. // The new 2.0+ ZeroTier default is 793, which we will eventually seek to have assigned. These