func addPeer(data []string) { if len(data) == 0 { fmt.Println("You must enter the peering details surrounded by single qoutes '<peer details>'") return } input := data[0] // Strip comments, just in case, and surround with {} to make it valid JSON raw, err := stripComments([]byte("{" + input + "}")) if err != nil { fmt.Println("Comment errors: ", err) return } // Convert from JSON to an object var object map[string]interface{} err = json.Unmarshal(raw, &object) if err != nil { fmt.Println("JSON Error:", err) return } // Load the config file if File == "" { cjdAdmin, err := loadCjdnsadmin() if err != nil { fmt.Println("Unable to load configuration file:", err) return } File = cjdAdmin.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") if _, ok := conf["interfaces"]; !ok { fmt.Println("Your configuration file does not contain an 'interfaces' section") return } is := conf["interfaces"].(map[string]interface{}) if len(is) == 0 { fmt.Println("No valid interfaces found!") return } else if len(is) > 1 { fmt.Println("You have multiple interfaces to choose from, enter yes or no, or press enter for the default option:") } var useIface string var i []interface{} selectIF: for { for key, _ := range is { if len(is) > 1 { fmt.Printf("Add peer to '%v' [Y/n]: ", key) if gotYes(true) { i = is[key].([]interface{}) useIface = key break selectIF } } else if len(is) == 1 { i = is[key].([]interface{}) useIface = key } } if useIface == "" { fmt.Println("You must select an interface to add to!") continue } break } var iX map[string]interface{} if len(i) > 1 { fmt.Printf("You have multiple '%v' options to choose from, enter yes or no, or press enter for the default option\n", useIface) selectIF2: for _, iFace := range i { temp := iFace.(map[string]interface{}) fmt.Printf("Add peer to '%v %v' [Y/n]: ", useIface, temp["bind"]) if gotYes(true) { iX = iFace.(map[string]interface{}) break } } if iX == nil { fmt.Println("You must select an interface to add to!") goto selectIF2 } } else if len(i) == 1 { iX = i[0].(map[string]interface{}) } else { fmt.Printf("No valid settings for '%v' found!\n", useIface) return } peers := iX["connectTo"].(map[string]interface{}) for key, data := range object { var peer map[string]interface{} if peers[key] != nil { peer = peers[key].(map[string]interface{}) fmt.Printf("Peer '%v' exists with the following information:\n", key) for f, v := range peer { fmt.Printf("\t\"%v\":\"%v\"\n", f, v) } fmt.Printf("Update peer with new information? [Y/n]: ") if gotYes(true) { peer = data.(map[string]interface{}) fmt.Printf("Updating peer '%v'\n", key) } else { fmt.Printf("Skipped updating peer '%v'\n", key) continue } } else { fmt.Printf("Adding new peer '%v'\n", key) peer = data.(map[string]interface{}) } // Optionally add meta information for { r := bufio.NewReader(os.Stdin) fmt.Printf("Enter a field name for any extra information, or press enter to skip: ") fName, _ := r.ReadString('\n') fName = strings.TrimSpace(fName) if len(fName) == 0 { break } fmt.Printf("Enter a the content for field '%v' or press enter to cancel: ", fName) fData, _ := r.ReadString('\n') fData = strings.TrimSpace(fData) if len(fData) == 0 { continue } peer[fName] = fData continue } fmt.Println("Peer information:") for f, v := range peer { fmt.Printf("\t\"%v\":\"%v\"\n", f, v) } fmt.Printf("Add this peer? [Y/n]: ") if gotYes(true) { peers[key] = peer fmt.Println("Peer added") } else { fmt.Println("Skipped adding peer") } } // 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") }
func addPassword(data []string) { // Load the config file if File == "" { var cjdnsAdmin *admin.CjdnsAdminConfig var err error 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") var input string if len(data) == 0 { fmt.Printf("You didnt supply a password, should I generate one for you? [Y/n]: ") if gotYes(true) { for { input = randString(15, 50) fmt.Printf("Generated: '%v' Accept? [Y/n]: ", input) if gotYes(true) { break } } } else { // No password supplied, not going to generate one, I quit! return } } else { input = data[0] } if _, ok := conf["authorizedPasswords"]; !ok { conf["authorizedPasswords"] = make([]interface{}, 0) fmt.Println("Your configuration file does not contain an 'authorizedPasswords' section, so one was created for you") } passwords := conf["authorizedPasswords"].([]interface{}) for loc, p := range passwords { x := p.(map[string]interface{}) if x["password"] == input { fmt.Printf("Password '%v' exists with the following information:\n", input) for f, v := range x { fmt.Printf("\t\"%v\":\"%v\"\n", f, v) } fmt.Printf("Update password with new information? [Y/n]: ") if gotYes(true) { // Remove the entry we are replacing passwords = append(passwords[:loc], passwords[loc+1:]...) break } else { return } } } pass := make(map[string]interface{}) pass["password"] = input is := conf["interfaces"].(map[string]interface{}) if len(is) == 0 { fmt.Println("No valid interfaces found!") return } else if len(is) > 1 { fmt.Println("You have multiple interfaces to choose from, enter yes or no, or press enter for the default option:") } var useIface string var i []interface{} selectIF: for { for key, _ := range is { if len(is) > 1 { fmt.Printf("Add password to '%v' [Y/n]: ", key) if gotYes(true) { i = is[key].([]interface{}) useIface = key break selectIF } } else if len(is) == 1 { i = is[key].([]interface{}) useIface = key } } if useIface == "" { fmt.Println("You must select an interface to add to!") continue } break } var iX map[string]interface{} if len(i) > 1 { fmt.Printf("You have multiple '%v' options to choose from, enter yes or no, or press enter for the default option\n", useIface) selectIF2: for _, iFace := range i { temp := iFace.(map[string]interface{}) fmt.Printf("Add peer to '%v %v' [Y/n]: ", useIface, temp["bind"]) if gotYes(true) { iX = iFace.(map[string]interface{}) break } } if iX == nil { fmt.Println("You must select an interface to add to!") goto selectIF2 } } else if len(i) == 1 { iX = i[0].(map[string]interface{}) } else { fmt.Printf("No valid settings for '%v' found!\n", useIface) return } // Optionally add meta information for { r := bufio.NewReader(os.Stdin) fmt.Printf("Enter a field name for any extra information, or press enter to skip: ") fName, _ := r.ReadString('\n') fName = strings.TrimSpace(fName) if len(fName) == 0 { break } fmt.Printf("Enter a the content for field '%v' or press enter to cancel: ", fName) fData, _ := r.ReadString('\n') fData = strings.TrimSpace(fData) if len(fData) == 0 { continue } pass[fName] = fData continue } fmt.Println("Password information:") for f, v := range pass { fmt.Printf("\t\"%v\":\"%v\"\n", f, v) } fmt.Printf("Add this password? [Y/n]: ") if gotYes(true) { conf["authorizedPasswords"] = append(passwords, pass) fmt.Println("Password added") } else { fmt.Println("Cancelled adding password") return } // 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("\nError saving config:", err) return } fmt.Printf("Saved\n") var bind string if strings.ToLower(useIface) == "ethinterface" { iFace, err := net.InterfaceByName(iX["bind"].(string)) if err != nil { fmt.Println("Unable to get interface's MAC address, you'll have to enter it yourself") bind = "UNKNOWN" } else { bind = iFace.HardwareAddr.String() } } else { bind = iX["bind"].(string) } fmt.Println("Here are the details to be shared with your new peer:") fmt.Printf("\"%v\":{\n", bind) fmt.Printf("\t\"password\":\"%v\",\n", pass["password"].(string)) fmt.Printf("\t\"publicKey\":\"%v\"\n", conf["publicKey"].(string)) fmt.Printf("}\n") }
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() } }