func main() { //Define the flags, and parse them //Clearly a hack but it works for now //TODO(inhies): Re-implement flag parsing so flags can have multiple meanings based on the base command (ping, route, etc) if len(os.Args) <= 1 { usage() return } else if len(os.Args) == 2 { if string(os.Args[1]) == "--help" { fs.PrintDefaults() return } } else { fs.Parse(os.Args[2:]) } //TODO(inhies): check argv[0] for trailing commands. //For example, to run ctraceroute: //ln -s /path/to/cjdcmd /usr/bin/ctraceroute like things command := os.Args[1] arguments := fs.Args() data := arguments[fs.NFlag()-fs.NFlag():] //Setup variables now so that if the program is killed we can still finish what we're doing ping := &Ping{} globalData := &Data{&admin.Admin{}, ""} var err error if File != "" { File, err = filepath.Abs(File) if err != nil { fmt.Println(err) return } } if OutFile != "" { OutFile, err = filepath.Abs(OutFile) if err != nil { fmt.Println(err) return } } // Check to see if the user specified a cjdnsadmin file to use instead of // the default if userCjdnsadmin != "" { userSpecifiedCjdnsadmin = true } // capture ctrl+c (actually any kind of kill signal...) c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt) go func() { for _ = range c { fmt.Printf("\n") if command == "log" { // Unsubscribe from logging _, err := admin.AdminLog_unsubscribe(globalData.User, globalData.LoggingStreamID) if err != nil { fmt.Printf("%v\n", err) return } } if command == "ping" { //stop pinging and print results outputPing(ping) } // Close all the channels for _, c := range globalData.User.Channels { close(c) } // If we have an open connection, close it if globalData.User.Conn != nil { globalData.User.Conn.Close() } // Exit with no error os.Exit(0) } }() switch command { // Generates a .cjdnsadmin file case cjdnsadminCmd: if File == "" { var cjdnsAdmin *CjdnsAdmin if !userSpecifiedCjdnsadmin { cjdnsAdmin, err = loadCjdnsadmin() if err != nil { fmt.Println("Unable to load configuration file:", err) return } } else { cjdnsAdmin, err = readCjdnsadmin(userCjdnsadmin) if err != nil { fmt.Println("Error loading cjdnsadmin file:", err) return } } File = cjdnsAdmin.Config if File == "" { fmt.Println("Please specify the configuration file in your .cjdnsadmin file or pass the --file flag.") return } } fmt.Printf("Loading configuration from: %v... ", File) conf, err := readConfig() if err != nil { fmt.Println("Error loading config:", err) return } fmt.Printf("Loaded\n") split := strings.LastIndex(conf.Admin.Bind, ":") addr := conf.Admin.Bind[:split] port := conf.Admin.Bind[split+1:] portInt, err := strconv.Atoi(port) if err != nil { fmt.Println("Error with cjdns admin bind settings") return } adminOut := CjdnsAdmin{ Address: addr, Port: portInt, Password: conf.Admin.Password, Config: File, } jsonout, err := json.MarshalIndent(adminOut, "", "\t") if err != nil { fmt.Println("Unable to create JSON for .cjdnsadmin") return } if OutFile == "" { tUser, err := user.Current() if err != nil { fmt.Println("I was unable to get your home directory, please manually specify where to save the file with --outfile") return } OutFile = tUser.HomeDir + "/.cjdnsadmin" } // Check if the output file exists and prompt befoer overwriting if _, err := os.Stat(OutFile); err == nil { fmt.Printf("Overwrite %v? [y/N]: ", OutFile) if !gotYes(false) { return } } else { fmt.Println("Saving to", OutFile) } ioutil.WriteFile(OutFile, jsonout, 0600) case cleanCfgCmd: // Load the config file if File == "" { var cjdnsAdmin *CjdnsAdmin if !userSpecifiedCjdnsadmin { cjdnsAdmin, err = loadCjdnsadmin() if err != nil { fmt.Println("Unable to load configuration file:", err) return } } else { cjdnsAdmin, err = readCjdnsadmin(userCjdnsadmin) if err != nil { fmt.Println("Error loading cjdnsadmin file:", err) return } } File = cjdnsAdmin.Config if File == "" { fmt.Println("Please specify the configuration file in your .cjdnsadmin file or pass the --file flag.") return } } fmt.Printf("Loading configuration from: %v... ", File) conf, err := config.LoadExtConfig(File) if err != nil { fmt.Println("Error loading config:", err) return } fmt.Printf("Loaded\n") // Get the permissions from the input file stats, err := os.Stat(File) if err != nil { fmt.Println("Error getting permissions for original file:", err) return } if File != "" && OutFile == "" { OutFile = File } // Check if the output file exists and prompt befoer overwriting if _, err := os.Stat(OutFile); err == nil { fmt.Printf("Overwrite %v? [y/N]: ", OutFile) if !gotYes(false) { return } } fmt.Printf("Saving configuration to: %v... ", OutFile) err = config.SaveConfig(OutFile, conf, stats.Mode()) if err != nil { fmt.Println("Error saving config:", err) return } fmt.Printf("Saved\n") case addPassCmd: addPassword(data) case addPeerCmd: addPeer(data) case hostNameCmd: if len(data) == 0 { setHypeDNS("") return } if len(data) == 1 { setHypeDNS(data[0]) return } if len(data) > 1 { fmt.Println("Too many arguments.") return } return case hostCmd: if len(data) == 0 { fmt.Println("Invalid hostname or IPv6 address specified") return } input := data[0] validIP, _ := regexp.MatchString(ipRegex, input) validHost, _ := regexp.MatchString(hostRegex, input) if validIP { hostname, err := resolveIP(input) if err != nil { fmt.Printf("Error: %v\n", err) return } fmt.Printf("%v\n", hostname) } else if validHost { ips, err := resolveHost(input) if err != nil { fmt.Printf("Error: %v\n", err) return } for _, addr := range ips { fmt.Printf("%v has IPv6 address %v\n", data[0], addr) } } else { fmt.Println("Invalid hostname or IPv6 address specified") return } case passGenCmd: // TODO(inies): Make more good fmt.Println(randString(15, 50)) case pubKeyToIPcmd: var ip []byte if len(data) > 0 { if len(data[0]) == 52 || len(data[0]) == 54 { ip = []byte(data[0]) } else { fmt.Println("Invalid public key") return } } else { fmt.Println("Invalid public key") return } parsed, err := admin.PubKeyToIP(ip) if err != nil { fmt.Println(err) return } var tText string hostname, _ := resolveIP(string(parsed)) if hostname != "" { tText = string(parsed) + " (" + hostname + ")" } else { tText = string(parsed) } fmt.Printf("%v\n", tText) case traceCmd: user, err := adminConnect() if err != nil { fmt.Println("Error:", err) return } globalData.User = user target, err := setTarget(data, true) if err != nil { fmt.Println("Error:", err) return } doTraceroute(globalData.User, target) case routeCmd: target, err := setTarget(data, true) if err != nil { fmt.Println(err) return } user, err := adminConnect() if err != nil { fmt.Println(err) return } var tText string hostname, _ := resolveIP(target.Target) if hostname != "" { tText = target.Target + " (" + hostname + ")" } else { tText = target.Target } fmt.Printf("Showing all routes to %v\n", tText) globalData.User = user table := getTable(globalData.User) sort.Sort(ByQuality{table}) count := 0 for _, v := range table { if v.IP == target.Target || v.Path == target.Target { if v.Link > 1 { fmt.Printf("IP: %v -- Version: %d -- Path: %s -- Link: %.0f\n", v.IP, v.Version, v.Path, v.Link) count++ } } } fmt.Println("Found", count, "routes") case pingCmd: // TODO: allow pinging of entire routing table target, err := setTarget(data, true) if err != nil { fmt.Println(err) return } user, err := adminConnect() if err != nil { fmt.Println(err) return } globalData.User = user ping.Target = target.Target var tText string // If we were given an IP then try to resolve the hostname if validIP(target.Supplied) { hostname, _ := resolveIP(target.Target) if hostname != "" { tText = target.Supplied + " (" + hostname + ")" } else { tText = target.Supplied } // If we were given a path, resolve the IP } else if validPath(target.Supplied) { tText = target.Supplied table := getTable(globalData.User) for _, v := range table { if v.Path == target.Supplied { // We have the IP now tText = target.Supplied + " (" + v.IP + ")" // Try to get the hostname hostname, _ := resolveIP(v.IP) if hostname != "" { tText = target.Supplied + " (" + v.IP + " (" + hostname + "))" } } } // We were given a hostname, everything is already done for us! } else if validHost(target.Supplied) { tText = target.Supplied + " (" + target.Target + ")" } fmt.Printf("PING %v \n", tText) if PingCount != defaultPingCount { // ping only as much as the user asked for for i := 1; i <= PingCount; i++ { start := time.Duration(time.Now().UTC().UnixNano()) err := pingNode(globalData.User, ping) if err != nil { if err.Error() != "Socket closed" { fmt.Println(err) } return } fmt.Println(ping.Response) // Send 1 ping per second now := time.Duration(time.Now().UTC().UnixNano()) time.Sleep(start + (time.Duration(PingInterval) * time.Second) - now) } } else { // ping until we're told otherwise for { start := time.Duration(time.Now().UTC().UnixNano()) err := pingNode(globalData.User, ping) if err != nil { // Ignore these errors, as they are returned when we kill an in-progress ping if err.Error() != "Socket closed" && err.Error() != "use of closed network connection" { fmt.Println("ermagherd:", err) } return } fmt.Println(ping.Response) // Send 1 ping per second now := time.Duration(time.Now().UTC().UnixNano()) time.Sleep(start + (time.Duration(PingInterval) * time.Second) - now) } } outputPing(ping) case logCmd: user, err := adminConnect() if err != nil { fmt.Println(err) return } globalData.User = user var response chan map[string]interface{} response, globalData.LoggingStreamID, err = admin.AdminLog_subscribe(globalData.User, LogFile, LogLevel, LogFileLine) if err != nil { fmt.Printf("Error: %v\n", err) return } format := "%d %d %s %s:%d %s\n" // TODO: add user formatted output counter := 1 // Spawn a routine to ping cjdns every 10 seconds to keep the connection alive go func() { for { timeout := 10 * time.Second time.Sleep(timeout) ok, err := admin.SendPing(globalData.User, 1000) if err != nil { fmt.Println("Error sending periodic ping to cjdns:", err) return } else if !ok { fmt.Println("Cjdns did not respond to the periodic ping.") return } } }() for { input, ok := <-response if !ok { fmt.Println("Error reading log response from cjdns.") return } fmt.Printf(format, counter, input["time"], input["level"], input["file"], input["line"], input["message"]) counter++ } case peerCmd: user, err := adminConnect() if err != nil { fmt.Println(err) return } // no target specified, use ourselves if len(data) == 0 { data = append(data, "0000.0000.0000.0001") } target, err := setTarget(data, true) if err != nil { fmt.Println(err) return } globalData.User = user doPeers(user, target) case versionCmd: // TODO(inhies): Ping a specific node and return it's cjdns version, or // ping all nodes in the routing table and get their versions // git log -1 --date=iso --pretty=format:"%ad" <hash> case killCmd: user, err := adminConnect() if err != nil { fmt.Println(err) return } globalData.User = user _, err = admin.Core_exit(globalData.User) if err != nil { fmt.Printf("%v\n", err) return } alive := true for ; alive; alive, _ = admin.SendPing(globalData.User, 1000) { runtime.Gosched() //play nice } fmt.Println("cjdns is shutting down...") case dumpCmd: user, err := adminConnect() if err != nil { fmt.Println(err) return } globalData.User = user // TODO: add flag to show zero link quality routes, by default hide them table := getTable(globalData.User) sort.Sort(ByQuality{table}) k := 1 for _, v := range table { if v.Link >= 1 { fmt.Printf("%d IP: %v -- Version: %d -- Path: %s -- Link: %.0f\n", k, v.IP, v.Version, v.Path, v.Link) k++ } } case memoryCmd: user, err := adminConnect() if err != nil { fmt.Println(err) return } globalData.User = user response, err := admin.Memory(globalData.User) if err != nil { fmt.Printf("%v\n", err) return } fmt.Println(response, "bytes") default: fmt.Println("Invalid command", command) usage() } }
func main() { //Define the flags, and parse them //Clearly a hack but it works for now //TODO(inhies): Re-implement flag parsing so flags can have multiple meanings based on the base command (ping, route, etc) if len(os.Args) <= 1 { usage() return } else if len(os.Args) == 2 { if string(os.Args[1]) == "--help" { fs.PrintDefaults() return } } else { fs.Parse(os.Args[2:]) } //TODO(inhies): check argv[0] for trailing commands. //For example, to run ctraceroute: //ln -s /path/to/cjdcmd /usr/bin/ctraceroute like things command := os.Args[1] if AdminPassword == defaultPass { conf, err := config.LoadMinConfig(File) fmt.Printf("\nReading config file from %v\n", File) if err != nil || len(conf.Admin.Password) == 0 { fmt.Printf("Error: %v\n", err) return } AdminPassword = conf.Admin.Password AdminBind = conf.Admin.Bind } else { AdminBind = defaultAdminBind } fmt.Printf("Attempting to connect to cjdns...") user, err := admin.Connect(AdminBind, AdminPassword) if err != nil { if e, ok := err.(net.Error); ok { if e.Timeout() { fmt.Println("\nConnection timed out") } else if e.Temporary() { fmt.Println("\nTemporary error (not sure what that means!)") } else { fmt.Println("\nUnable to connect to cjdns:", e) } } else { fmt.Println("\nError:", err) } return } println("Connected") defer user.Conn.Close() arguments := fs.Args() data := arguments[fs.NFlag()-fs.NFlag():] //Setup variables now so that if the program is killed we can still finish what we're doing ping := &Ping{} var loggingStreamID string // capture ctrl+c c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt) go func() { for _ = range c { fmt.Printf("\n") if command == "log" { //unsubscribe from logging _, err := admin.AdminLog_unsubscribe(user, loggingStreamID) if err != nil { fmt.Printf("%v\n", err) return } } if command == "ping" { //stop pinging and print results outputPing(ping) } //close all the channels for _, c := range user.Channels { close(c) } user.Conn.Close() return } }() switch command { case traceCmd: target, err := setTarget(data, false) if err != nil { fmt.Println(err) return } doTraceroute(user, target) case routeCmd: target, err := setTarget(data, true) if err != nil { fmt.Println(err) return } table := getTable(user) sort.Sort(ByQuality{table}) count := 0 for _, v := range table { if v.IP == target || v.Path == target { if v.Link > 1 { fmt.Printf("IP: %v -- Version: %d -- Path: %s -- Link: %.0f\n", v.IP, v.Version, v.Path, v.Link) count++ } } } fmt.Println("Found", count, "routes") case pingCmd: // TODO: allow input of IP, hex path with and without dots and leading zeros, and binary path // TODO: allow pinging of entire routing table target, err := setTarget(data, true) if err != nil { fmt.Println(err) return } ping.Target = target if PingCount != defaultPingCount { // ping only as much as the user asked for for i := 1; i <= PingCount; i++ { err := pingNode(user, ping) if err != nil { fmt.Println(err) return } println(ping.Response) } } else { // ping until we're told otherwise for { err := pingNode(user, ping) if err != nil { fmt.Println(err) return } println(ping.Response) } } outputPing(ping) case logCmd: var response chan map[string]interface{} response, loggingStreamID, err = admin.AdminLog_subscribe(user, LogFile, LogLevel, LogFileLine) if err != nil { fmt.Printf("Error: %v\n", err) return } format := "%d %d %s %s:%d %s\n" // TODO: add user formatted output counter := 1 for { input, ok := <-response if !ok { break } fmt.Printf(format, counter, input["time"], input["level"], input["file"], input["line"], input["message"]) counter++ } case peerCmd: peers := make([]*Route, 0) table := getTable(user) sort.Sort(ByQuality{table}) fmt.Println("Finding all connected peers") for i := range table { if table[i].Link < 1 { continue } if table[i].RawPath == 1 { continue } response, err := getHops(table, table[i].RawPath) if err != nil { fmt.Println(err) } sort.Sort(ByPath{response}) var peer *Route if len(response) > 1 { peer = response[1] } else { peer = response[0] } found := false for _, p := range peers { if p == peer { found = true break } } if !found { peers = append(peers, peer) } } for _, p := range peers { fmt.Printf("IP: %v -- Path: %s -- Link: %.0f\n", p.IP, p.Path, p.Link) } case versionCmd: // TODO(inhies): Ping a specific node and return it's cjdns version, or // ping all nodes in the routing table and get their versions // git log -1 --date=iso --pretty=format:"%ad" <hash> case killCmd: _, err := admin.Core_exit(user) if err != nil { fmt.Printf("%v\n", err) return } alive := true for ; alive; alive, _ = admin.SendPing(user, 1000) { runtime.Gosched() //play nice } println("cjdns is shutting down...") case dumpCmd: // TODO: add flag to show zero link quality routes, by default hide them table := getTable(user) sort.Sort(ByQuality{table}) k := 1 for _, v := range table { if v.Link >= 1 { fmt.Printf("%d IP: %v -- Version: %d -- Path: %s -- Link: %.0f\n", k, v.IP, v.Version, v.Path, v.Link) k++ } } case "memory": println("Bye bye cjdns! This command causes a crash. Keep trying and maybe one day cjd will fix it :)") response, err := admin.Memory(user) if err != nil { fmt.Printf("%v\n", err) return } fmt.Println(response) default: fmt.Println("Invalid command", command) usage() } }