func usageFor(fs *flag.FlagSet, usage string) func() { return func() { var b bytes.Buffer b.WriteString(ansi.Bold("Usage:") + "\n " + usage + "\n") var options [][]string fs.VisitAll(func(f *flag.Flag) { var opt = " -" + f.Name if f.DefValue != "false" { opt += "=" + f.DefValue } options = append(options, []string{opt, f.Usage}) }) if len(options) > 0 { b.WriteString("\n" + ansi.Bold("Options:") + "\n") optionTable(&b, options) } fmt.Println(b.String()) } }
func lazySetupPrefixes() { if prefix == nil { if !loggingEnabled { panic("attempting setup before logging enabled") } prefix = map[string]string{ "debug": ansi.Magenta("debug "), // info has no prefix "ok": ansi.Bold(ansi.Green("ok ")), "warning": ansi.Bold(ansi.Yellow("warning ")), "fatal": ansi.Bold(ansi.Red("fatal ")), } } }
func sendForwards(fwdChan chan<- conf.ForwardLine, cfg *conf.Config) { for _, fwd := range cfg.Forwards { infoln(ansi.Bold(ansi.Cyan(fwd.Name))) for _, cmt := range fwd.Comments { infoln(ansi.Cyan(" ; " + cmt)) } for _, line := range fwd.Lines { infoln(" " + line.String()) fwdChan <- line } } }
func tableFormatter(cell string, row, col, flags int) string { if row == 0 { return ansi.Underline(cell) } else if col == 0 { return ansi.Bold(ansi.Cyan(cell)) } else if col == 1 && (strings.HasSuffix(cell, "U") || strings.HasSuffix(cell, "E")) { return ansi.Red(cell) } else if flags&table.Truncated != 0 { return cell[:len(cell)-1] + ansi.Red(">") } return cell }
func mainUsage(w io.Writer) { tw := tabwriter.NewWriter(w, 2, 4, 2, ' ', 0) fmt.Fprintln(w, ansi.Bold("Commands:")) var cmds []string for _, cmd := range commandList { cmds = append(cmds, cmd.name) } sort.Strings(cmds) for _, name := range cmds { cmd := commandMap[name] if sn := cmd.descr; sn != "" { alias := "" // Ignore undocumented commands if len(cmd.aliases) > 0 { alias = " (" + strings.Join(cmd.aliases, ", ") + ")" } tw.Write([]byte(fmt.Sprintf(" %s%s\t%s\n", ansi.Bold(ansi.Cyan(name)), ansi.Cyan(alias), sn))) } } tw.Flush() fmt.Fprintln(w, "\n Commands can be abbreviated to their unique prefix.\n") fmt.Fprintln(w, ansi.Bold("Examples:")) examples := [][]string{ {" mole ls", "# show all available tunnels"}, {" mole l foo", "# show all available tunnels matching the regexp \"foo\""}, {" mole show foo", "# show the hosts and forwards in the tunnel \"foo\""}, {" sudo mole dig foo", "# dig the tunnel \"foo\""}, {" sudo mole -d d foo", "# dig the tunnel \"foo\", while showing debug output"}, {" mole push foo.ini", "# create or update the \"foo\" tunnel from a local file"}, {" mole install", "# list packages available for installation"}, {" mole ins vpnc", "# install a package named vpnc"}, {" mole up -force", "# perform a forced up/downgrade to the server version"}, } optionTable(w, examples) w.Write([]byte("\n")) }
func shell(fwdChan chan<- conf.ForwardLine, cfg *conf.Config, dialer Dialer) { help := func() { infoln("Available commands:") infoln(" help, ? - show help") infoln(" quit, ^D - stop forwarding and exit") infoln(" test - test each forward for connection") infoln(" stat - show forwarding statistics") infoln(" debug - enable debugging") infoln(" fwd srcip:srcport dstip:dstport - add forward") } term := liner.NewLiner() atExit(func() { term.Close() }) // Receive commands commands := make(chan string) next := make(chan bool) go func() { for { prompt := "mole> " if debugEnabled { prompt = "(debug) mole> " } cmd, err := term.Prompt(prompt) if err == io.EOF { fmt.Println("quit") commands <- "quit" return } if cmd != "" { commands <- cmd term.AppendHistory(cmd) _, ok := <-next if !ok { return } } } }() // Catch ^C and treat as "quit" command sigchan := make(chan os.Signal, 1) signal.Notify(sigchan, os.Interrupt) go func() { <-sigchan fmt.Println("quit") commands <- "quit" }() // Handle commands for { cmd := <-commands parts := strings.SplitN(cmd, " ", -1) switch parts[0] { case "quit": close(next) return case "help", "?": help() case "stat": printStats() case "test": results := testForwards(dialer, cfg) for res := range results { infof(ansi.Bold(ansi.Cyan(res.name))) for _, line := range res.results { if line.err == nil { infof("%22s %s in %.02f ms", line.dst, ansi.Bold(ansi.Green("-ok-")), line.ms) } else { infof("%22s %s in %.02f ms (%s)", line.dst, ansi.Bold(ansi.Red("fail")), line.ms, line.err) } } } case "debug": infoln(msgDebugEnabled) debugEnabled = true case "fwd": if len(parts) != 3 { warnf(msgErrIncorrectFwd, cmd) break } src := strings.SplitN(parts[1], ":", 2) if len(src) != 2 { warnf(msgErrIncorrectFwdSrc, parts[1]) break } var ipExists bool for _, ip := range currentAddresses() { if ip == src[0] { ipExists = true break } } if !ipExists { warnf(msgErrIncorrectFwdIP, src[0]) break } dst := strings.SplitN(parts[2], ":", 2) if len(dst) != 2 { warnf(msgErrIncorrectFwdDst, parts[2]) break } srcp, err := strconv.Atoi(src[1]) if err != nil { warnln(err) break } if srcp < 1024 { warnf(msgErrIncorrectFwdPriv, srcp) break } dstp, err := strconv.Atoi(dst[1]) if err != nil { warnln(err) break } fwd := conf.ForwardLine{ SrcIP: src[0], SrcPort: srcp, DstIP: dst[0], DstPort: dstp, } okln("add", fwd) fwdChan <- fwd default: warnf(msgErrNoSuchCommand, parts[0]) } next <- true } }
func commandLs(args []string) { fs := flag.NewFlagSet("ls", flag.ContinueOnError) short := fs.Bool("s", false, "Short listing") long := fs.Bool("l", false, "Long listing") fs.Usage = usageFor(fs, msgLsUsage) err := fs.Parse(args) if err != nil { fmt.Println(ansi.Bold("Feature Flags:")) fmt.Println(msgLsFlags) exit(3) } args = fs.Args() var re *regexp.Regexp if len(args) == 1 { re, err = regexp.Compile("(?i)" + args[0]) fatalErr(err) } cl := NewClient(serverAddress(), moleIni.Get("server", "fingerprint")) res, err := authenticated(cl, func() (interface{}, error) { return cl.List() }) fatalErr(err) l := res.([]ListItem) var rows [][]string var header []string var format string var hasFeatureFlags bool for _, i := range l { if i.Features != 0 { hasFeatureFlags = true break } } if hasFeatureFlags { header = []string{"TUNNEL", "FLAGS", "DESCRIPTION"} format = "lll" } else { header = []string{"TUNNEL", "DESCRIPTION"} format = "ll" } if *long { header = append(header, "HOSTS", "VER") format += "lr" } rows = [][]string{header} tunnelCache, _ := os.Create(path.Join(homeDir, "tunnels.cache")) var matched int for _, i := range l { hosts := strings.Join(i.Hosts, ", ") if re == nil || re.MatchString(i.Name) || re.MatchString(i.Description) || re.MatchString(hosts) { if tunnelCache != nil { fmt.Fprintln(tunnelCache, i.Name) } if *short { fmt.Println(i.Name) } else { matched++ row := []string{i.Name} if hasFeatureFlags { flags := "" spacer := "ยท" unsupported := i.Features & ^(conf.FeatureError|conf.FeatureSshKey|conf.FeatureSshPassword|conf.FeatureLocalOnly|conf.FeatureVpnc|conf.FeatureOpenConnect|conf.FeatureSocks) != 0 if i.Features&conf.FeatureError != 0 { flags = strings.Repeat(spacer, 4) + "E" } else { if i.Features&conf.FeatureVpnc != 0 { flags += "v" unsupported = unsupported || !supportsVpn("vpnc") } else if i.Features&conf.FeatureOpenConnect != 0 { flags += "o" unsupported = unsupported || !supportsVpn("openconnect") } else { flags += spacer } if i.Features&conf.FeatureSshKey != 0 { flags += "k" } else { flags += spacer } if i.Features&conf.FeatureSshPassword != 0 { flags += "p" } else { flags += spacer } if i.Features&conf.FeatureSocks != 0 { flags += "s" } else if i.Features&conf.FeatureLocalOnly != 0 { flags += "l" } else { flags += spacer } if unsupported { flags += "U" } else { flags += spacer } } row = append(row, flags) } row = append(row, i.Description) if *long { ver := fmt.Sprintf("%.01f", i.Version) if hosts == "" { hosts = "-" } row = append(row, hosts, ver) } rows = append(rows, row) } } } if tunnelCache != nil { _ = tunnelCache.Close() } if !*short { // Never prefix table with log stuff fmt.Printf(table.FmtFunc(format, rows, tableFormatter)) if matched != len(l) { fmt.Printf(ansi.Faint(" - Matched %d out of %d records\n"), matched, len(l)) } } }