diff --git a/cloudflare.go b/cloudflare.go new file mode 100644 index 0000000..61a8157 --- /dev/null +++ b/cloudflare.go @@ -0,0 +1,298 @@ +package main + +import ( + "context" + "fmt" + "log" + "os" + "strconv" + "strings" + + "github.com/cloudflare/cloudflare-go" + + "github.com/goccy/go-json" +// "golang.org/x/net/idna" + + // "github.com/urfave/cli/v2" +) + +// App is the main structure of a cli application. +type app struct { + // The name of the program. Defaults to path.Base(os.Args[0]) + Name string + // Full name of command for help, defaults to Name + HelpName string + // Description of the program. + Usage string + + // Description of the program. + Port int +} + +var api *cloudflare.API +var c *app + +func (a *app) String(b string) string { + log.Println(a, b) + return b +} + +func (a *app) Int(b string) int { + log.Println(a, b) + return a.Port +} + +func (a *app) Uint(b string) uint { + return uint(a.Int(b)) +} + +func (a *app) Bool(b string) bool { + log.Println(a, b) + return true +} + +func testCloudflare() { + // Construct a new API object using a global API key + api, err := cloudflare.New(os.Getenv("CLOUDFLARE_API_KEY"), os.Getenv("CLOUDFLARE_API_EMAIL")) + // alternatively, you can use a scoped API token + // api, err := cloudflare.NewWithAPIToken(os.Getenv("CLOUDFLARE_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + // Most API calls require a Context + ctx := context.Background() + + // Fetch user details on the account + u, err := api.UserDetails(ctx) + if err != nil { + log.Fatal(err) + } + // Print user details + fmt.Println(u) +} + +func formatDNSRecord(record cloudflare.DNSRecord) []string { + return []string{ + record.ID, + record.Name, + record.Type, + record.Content, + strconv.FormatInt(int64(record.TTL), 10), + strconv.FormatBool(record.Proxiable), + strconv.FormatBool(*record.Proxied), + strconv.FormatBool(record.Locked), + } +} + +func dnsCreate() error { + zone := c.String("zone") + name := c.String("name") + rtype := c.String("type") + content := c.String("content") + ttl := c.Int("ttl") + proxy := c.Bool("proxy") + priority := uint16(c.Uint("priority")) + + zoneID, err := api.ZoneIDByName(zone) + if err != nil { + fmt.Println(err) + return err + } + + record := cloudflare.CreateDNSRecordParams{ + Name: name, + Type: strings.ToUpper(rtype), + Content: content, + TTL: ttl, + Proxied: &proxy, + Priority: &priority, + } + result, err := api.CreateDNSRecord(context.Background(), cloudflare.ZoneIdentifier(zoneID), record) + if err != nil { + fmt.Fprintln(os.Stderr, "Error creating DNS record: ", err) + return err + } + + output := [][]string{ + formatDNSRecord(result), + } + + writeTable(c, output, "ID", "Name", "Type", "Content", "TTL", "Proxiable", "Proxy", "Locked") + + return nil +} + +func dnsCreateOrUpdate() error { + zone := c.String("zone") + name := c.String("name") + rtype := strings.ToUpper(c.String("type")) + content := c.String("content") + ttl := c.Int("ttl") + proxy := c.Bool("proxy") + priority := uint16(c.Uint("priority")) + + zoneID, err := api.ZoneIDByName(zone) + if err != nil { + fmt.Fprintln(os.Stderr, "Error updating DNS record: ", err) + return err + } + + records, _, err := api.ListDNSRecords(context.Background(), cloudflare.ZoneIdentifier(zoneID), cloudflare.ListDNSRecordsParams{Name: name + "." + zone}) + if err != nil { + fmt.Fprintln(os.Stderr, "Error fetching DNS records: ", err) + return err + } + + var result cloudflare.DNSRecord + if len(records) > 0 { + // Record exists - find the ID and update it. + // This is imprecise without knowing the original content; if a label + // has multiple RRs we'll just update the first one. + for _, r := range records { + if r.Type == rtype { + rr := cloudflare.UpdateDNSRecordParams{} + rr.ID = r.ID + rr.Type = r.Type + rr.Content = content + rr.TTL = ttl + rr.Proxied = &proxy + rr.Priority = &priority + + result, err = api.UpdateDNSRecord(context.Background(), cloudflare.ZoneIdentifier(zoneID), rr) + if err != nil { + fmt.Println("Error updating DNS record:", err) + return err + } + } + } + } else { + // Record doesn't exist - create it + rr := cloudflare.CreateDNSRecordParams{ + Name: name, + Type: rtype, + Content: content, + TTL: ttl, + Proxied: &proxy, + Priority: &priority, + } + + // TODO: Print the response. + result, err = api.CreateDNSRecord(context.Background(), cloudflare.ZoneIdentifier(zoneID), rr) + if err != nil { + fmt.Println("Error creating DNS record:", err) + return err + } + } + + output := [][]string{ + formatDNSRecord(result), + } + + writeTable(c, output, "ID", "Name", "Type", "Content", "TTL", "Proxiable", "Proxy", "Locked") + + return nil +} + +func dnsUpdate() error { + zone := c.String("zone") + recordID := c.String("id") + name := c.String("name") + rtype := c.String("type") + content := c.String("content") + ttl := c.Int("ttl") + proxy := c.Bool("proxy") + priority := uint16(c.Uint("priority")) + + zoneID, err := api.ZoneIDByName(zone) + if err != nil { + fmt.Println(err) + return err + } + + record := cloudflare.UpdateDNSRecordParams{ + ID: recordID, + Name: name, + Type: strings.ToUpper(rtype), + Content: content, + TTL: ttl, + Proxied: &proxy, + Priority: &priority, + } + _, err = api.UpdateDNSRecord(context.Background(), cloudflare.ZoneIdentifier(zoneID), record) + if err != nil { + fmt.Fprintln(os.Stderr, "Error updating DNS record: ", err) + return err + } + + return nil +} + +func dnsDelete() error { + zone := c.String("zone") + recordID := c.String("id") + + zoneID, err := api.ZoneIDByName(zone) + if err != nil { + fmt.Println(err) + return err + } + + err = api.DeleteDNSRecord(context.Background(), cloudflare.ZoneIdentifier(zoneID), recordID) + if err != nil { + fmt.Fprintln(os.Stderr, "Error deleting DNS record: ", err) + return err + } + + return nil +} + +// writeTableTabular outputs tabular data to STDOUT. +func writeTableTabular(data [][]string, cols ...string) { + // table := tablewriter.NewWriter(os.Stdout) + // table.SetHeader(cols) + // table.SetBorder(false) + // table.AppendBulk(data) + + // table.Render() +} + +// writeTableJSON outputs JSON data to STDOUT. +func writeTableJSON(data [][]string, cols ...string) { + mappedData := make([]map[string]string, 0) + for i := range data { + rowData := make(map[string]string) + for j := range data[i] { + rowData[cols[j]] = data[i][j] + } + mappedData = append(mappedData, rowData) + } + jsonData, err := json.Marshal(mappedData) + if err != nil { + fmt.Println(err) + return + } + fmt.Println(string(jsonData)) +} + +// writeTable outputs JSON or tabular data to STDOUT. +func writeTable(c *app, data [][]string, cols ...string) { + if c.Bool("json") { + writeTableJSON(data, cols...) + } else { + writeTableTabular(data, cols...) + } +} + +// Utility function to check if CLI flags were given. +func checkFlags(c *app, flags ...string) error { + for _, flag := range flags { + if c.String(flag) == "" { + err := fmt.Errorf("error: the required flag %q was empty or not provided", flag) + fmt.Fprintln(os.Stderr, err) + return err + } + } + + return nil +} diff --git a/gui.go b/gui.go index cbf482e..0806d97 100644 --- a/gui.go +++ b/gui.go @@ -99,6 +99,9 @@ func debugTab(title string) { DumpPublicDNSZone("apple.com") dumpIPs("www.apple.com") }) + g2.NewButton("test Cloudflare", func () { + testCloudflare() + }) g2 = tab.NewGroup("debugging options") @@ -121,7 +124,7 @@ func debugTab(title string) { // various timeout settings g2.NewLabel("control panel TTL (in tenths of seconds)") ttl := g2.NewSlider("dnsTTL", 1, 100) - ttl.Set(me.dnsTTL * 10) + ttl.Set(int(me.dnsTTL * 10)) ttl.Custom = func () { me.dnsTTL = ttl.I / 10 log.Println("dnsTTL =", me.dnsTTL) @@ -129,7 +132,7 @@ func debugTab(title string) { g2.NewLabel("control panel loop delay (in tenths of seconds)") ttl2 := g2.NewSlider("dnsTTL", 1, 100) - ttl2.Set(me.dnsTTLsleep) + ttl2.Set(int(me.dnsTTLsleep * 10)) ttl2.Custom = func () { me.dnsTTLsleep = float64(ttl2.I) / 10 log.Println("dnsTTLsleep =", me.dnsTTLsleep) diff --git a/main.go b/main.go index a6b5e2b..bc1c54b 100644 --- a/main.go +++ b/main.go @@ -18,7 +18,6 @@ var myGui *gui.Node //go:embed plugins/*.so var resToolkit embed.FS -var tenth time.Duration = 1 // in tenths of seconds func main() { // parsedown() @@ -27,7 +26,9 @@ func main() { me.ipmap = make(map[string]*IPtype) me.dnsmap = make(map[string]*IPtype) me.ifmap = make(map[int]*IFtype) - me.dnsTTL = 2 // recheck DNS is working every 2 minutes // TODO: watch rx packets? + + me.dnsTTL = 2 // how often to recheck DNS + me.dnsTTLsleep = .4 // sleep between loops // will set all debugging flags // gui.SetDebug(true) @@ -63,14 +64,16 @@ func checkNetworkChanges() { duration := timeFunction(dnsTTL) log.Println("dnsTTL() execution Time: ", duration) var s string - if (duration > 50 * tenth) { + if (duration > 5000 * time.Millisecond ) { s = fmt.Sprint("VERY BAD\n", duration) - } else if (duration > 20 * tenth) { + } else if (duration > 2000 * time.Millisecond ) { s = fmt.Sprint("BAD\n", duration) - } else if (duration > 5 * tenth) { + } else if (duration > 500 * time.Millisecond ) { s = fmt.Sprint("SLOW\n", duration) - } else { + } else if (duration > 100 * time.Millisecond ) { s = fmt.Sprint("OK\n", duration) + } else { + s = fmt.Sprint("FAST\n", duration) } log.Println(true, "getHostname()", s) me.DnsSpeed.SetText(s)