From ec8904cdcb2a22b53fdbb5f44405ec7723d42d54 Mon Sep 17 00:00:00 2001 From: Mehrdad Arshad Rad Date: Sun, 28 Aug 2016 22:45:16 -0700 Subject: [PATCH] added new feature to set command option(s) resolved #3 --- cli/config.go | 320 ++++++++++++++++++++++++++++++++++++++++++++++ http/ping/ping.go | 35 +++-- icmp/icmp.go | 25 ++-- mylg.go | 28 +++- scan/scan.go | 17 +-- scan/scan_test.go | 10 +- 6 files changed, 394 insertions(+), 41 deletions(-) create mode 100644 cli/config.go diff --git a/cli/config.go b/cli/config.go new file mode 100644 index 00000000..0bfa59e5 --- /dev/null +++ b/cli/config.go @@ -0,0 +1,320 @@ +package cli + +import ( + "encoding/json" + "fmt" + "os" + "os/user" + "reflect" + "strconv" + "strings" +) + +var defaultConfig = `{ + "ping" : { + "timeout" : "2s", + "interval": "1s", + "count": 4 + }, + "hping" : { + "timeout" : "2s", + "method" : "HEAD", + "data" : "mylg", + "count" : 5 + }, + "web" : { + "port" : 8080, + "address" : "127.0.0.1" + }, + "scan" : { + "port" : "1-500" + } +}` + +// Config represents configuration +type Config struct { + Ping Ping `json:"ping"` + Hping HPing `json:"hping"` + Web Web `json:"web"` + Scan Scan `json:"scan"` +} + +// Ping represents ping command options +type Ping struct { + Timeout string `json:"timeout"` + Interval string `json:"interval"` + Count int `json:"count"` +} + +// HPing represents ping command options +type HPing struct { + Timeout string `json:"timeout"` + Method string `json:"method"` + Data string `json:"data"` + Count int `json:"count"` +} + +// Web represents web command options +type Web struct { + Port int `json:port` + Address string `json:address` +} + +// Scan represents scan command options +type Scan struct { + Port string `json:port` +} + +// UpdateConfig +func WriteConfig(cfg Config) error { + f, err := cfgFile() + if err != nil { + return err + } + + h, err := os.Create(f) + if err != nil { + return err + } + + b, err := json.Marshal(cfg) + if err != nil { + return err + } + _, err = h.Write(b) + if err != nil { + return err + } + h.Close() + + return nil +} + +// UpgradeConfig adds / removes new command(s)/option(s) +func UpgradeConfig() { + // TODO + var conf map[string]interface{} + b := make([]byte, 2048) + f, err := cfgFile() + if err != nil { + + } + h, err := os.Open(f) + n, _ := h.Read(b) + b = b[:n] + + json.Unmarshal(b, &conf) + if v, ok := conf["ping"].(interface{}); ok { + if _, ok = v.(map[string]interface{})["timeout"]; !ok { + // there is new option + } + } else { + // there is new command + } + +} + +// LoadConfig loads configuration +func LoadConfig() Config { + var cfg Config + + cfg = ReadConfig() + + return cfg +} + +// InitConfig creates new config file +func InitConfig(f string) ([]byte, error) { + h, err := os.Create(f) + if err != nil { + return []byte(""), err + } + + h.Chmod(os.FileMode(int(0600))) + h.WriteString(defaultConfig) + h.Close() + + return []byte(defaultConfig), nil +} + +// ReadConfig reads configuration from existing +// or default configuration +func ReadConfig() Config { + var ( + b = make([]byte, 2048) + conf Config + err error + ) + f, err := cfgFile() + if err != nil { + + } + + h, err := os.Open(f) + + if err != nil { + switch { + case os.IsNotExist(err): + if b, err = InitConfig(f); err != nil { + println(err.Error()) + } + case os.IsPermission(err): + println("cannot read configuration file due to insufficient permissions") + b = []byte(defaultConfig) + default: + println(err.Error()) + b = []byte(defaultConfig) + } + } else { + n, _ := h.Read(b) + b = b[:n] + } + + err = json.Unmarshal(b, &conf) + if err != nil { + println(err.Error()) + b = []byte(defaultConfig) + json.Unmarshal(b, &conf) + } + + return conf +} + +// ReadDefaultConfig returns default configuration +func ReadDefaultConfig() (Config, error) { + var ( + b = make([]byte, 2048) + conf Config + ) + b = []byte(defaultConfig) + err := json.Unmarshal(b, &conf) + return conf, err +} + +// cfgFile returns config file +func cfgFile() (string, error) { + user, err := user.Current() + if err != nil { + return "", err + } + return user.HomeDir + "/.mylg.config", nil +} + +// SetConfig handles update option's value +func SetConfig(args string, s *Config) { + var ( + v reflect.Value + i int64 + float float64 + err error + ) + + args = strings.ToLower(args) + f := strings.Fields(args) + if len(f) < 1 { + helpSet() + return + } + + v = reflect.ValueOf(s) + v = reflect.Indirect(v) + v = v.FieldByName(strings.Title(f[0])) + + if v.IsValid() { + if i, err = strconv.ParseInt(f[2], 10, 64); err == nil { + // integer + err = SetValue(v.Addr(), strings.Title(f[1]), i) + } else if float, err = strconv.ParseFloat(f[2], 64); err == nil { + // float + err = SetValue(v.Addr(), strings.Title(f[1]), float) + } else { + // string + err = SetValue(v.Addr(), strings.Title(f[1]), f[2]) + } + } else { + err = fmt.Errorf("invalid") + } + + if err != nil { + println(err.Error()) + } else { + if err = WriteConfig(*s); err != nil { + println(err.Error()) + } + } + +} + +// SetConfig set optioni's value +func SetValue(v reflect.Value, rec string, val interface{}) error { + + if v.Kind() != reflect.Ptr { + return fmt.Errorf("not a pointer value") + } + + v = reflect.Indirect(v) + switch v.Kind() { + case reflect.Int: + if value, ok := val.(int64); ok { + v.SetInt(value) + } else { + return fmt.Errorf("the value should be integer") + } + case reflect.Float64: + if value, ok := val.(float64); ok { + v.SetFloat(value) + } else { + return fmt.Errorf("the value should be float") + } + case reflect.String: + if value, ok := val.(string); ok { + v.SetString(value) + } else { + return fmt.Errorf("the value shouldn't be number") + } + case reflect.Struct: + for i := 0; i < v.NumField(); i++ { + if v.Type().Field(i).Name == rec { + err := SetValue(v.Field(i).Addr(), rec, val) + if err != nil { + return err + } + } + } + } + return nil +} + +// ShowConfig prints the configuration +func ShowConfig(s *Config) { + var v reflect.Value + + v = reflect.ValueOf(s) + v = reflect.Indirect(v) + + for i := 0; i < v.NumField(); i++ { + cmd := v.Type().Field(i).Name + cmd = strings.ToLower(cmd) + + vv := v.Field(i).Addr() + vv = reflect.Indirect(vv) + + for j := 0; j < vv.NumField(); j++ { + subCmd := vv.Type().Field(j).Name + subCmd = strings.ToLower(subCmd) + value := vv.Field(j) + fmt.Printf("set %-8s %-10s %v\n", cmd, subCmd, value) + } + } +} + +// helpSet shows set command +func helpSet() { + println(` + usage: + set command option value + example: + set ping timeout 2s + `) + +} diff --git a/http/ping/ping.go b/http/ping/ping.go index 71acb61f..742a8e6a 100644 --- a/http/ping/ping.go +++ b/http/ping/ping.go @@ -42,11 +42,11 @@ type Result struct { } // NewPing validate and constructs request object -func NewPing(args string) (*Ping, error) { +func NewPing(args string, cfg cli.Config) (*Ping, error) { URL, flag := cli.Flag(args) // help if _, ok := flag["help"]; ok || URL == "" { - help() + help(cfg) return nil, fmt.Errorf("") } URL = Normalize(URL) @@ -68,12 +68,15 @@ func NewPing(args string) (*Ping, error) { } // set count - p.count = cli.SetFlag(flag, "c", 4).(int) + p.count = cli.SetFlag(flag, "c", cfg.Hping.Count).(int) // set timeout - timeout := cli.SetFlag(flag, "t", 2).(int) - p.timeout = time.Duration(timeout) + timeout := cli.SetFlag(flag, "t", cfg.Hping.Timeout).(string) + p.timeout, err = time.ParseDuration(timeout) + if err != nil { + return p, err + } // set method - p.method = cli.SetFlag(flag, "m", "HEAD").(string) + p.method = cli.SetFlag(flag, "m", cfg.Hping.Method).(string) p.method = strings.ToUpper(p.method) // set buff (post) buf := cli.SetFlag(flag, "d", "mylg").(string) @@ -223,15 +226,19 @@ func (p *Ping) Ping() (Result, error) { } // help shows ping help -func help() { - fmt.Println(` +func help(cfg cli.Config) { + fmt.Printf(` usage: - hping [-c count][-t timeout][-m method][-d data] url + hping url [options] options: - -c count Send 'count' requests (default: 4) - -t timeout Specifies a time limit for requests in second (default is 2) - -m method HTTP methods: GET/POST/HEAD (default: HEAD) - -d data Sending the given data (text/json) (default: "mylg") - `) + -c count Send 'count' requests (default: %d) + -t timeout Specifies a time limit for requests in ms/s (default is %s) + -m method HTTP methods: GET/POST/HEAD (default: %s) + -d data Sending the given data (text/json) (default: "%s") + `, + cfg.Hping.Count, + cfg.Hping.Timeout, + cfg.Hping.Method, + cfg.Hping.Data) } diff --git a/icmp/icmp.go b/icmp/icmp.go index f9bd009a..ca9abd3b 100644 --- a/icmp/icmp.go +++ b/icmp/icmp.go @@ -67,7 +67,7 @@ type Response struct { } // NewPing creates a new ping object -func NewPing(args string) (*Ping, error) { +func NewPing(args string, cfg cli.Config) (*Ping, error) { var ( err error ) @@ -75,7 +75,7 @@ func NewPing(args string) (*Ping, error) { // show help if _, ok := flag["help"]; ok || len(target) < 3 { - help() + help(cfg) return nil, nil } @@ -87,7 +87,7 @@ func NewPing(args string) (*Ping, error) { isV4Avail: false, isV6Avail: false, isCIDR: isCIDR(target), - count: cli.SetFlag(flag, "c", 4).(int), + count: cli.SetFlag(flag, "c", cfg.Ping.Count).(int), forceV4: cli.SetFlag(flag, "4", false).(bool), forceV6: cli.SetFlag(flag, "6", false).(bool), network: "ip", @@ -108,13 +108,13 @@ func NewPing(args string) (*Ping, error) { } // set timeout - timeoutStr := cli.SetFlag(flag, "t", "2s").(string) + timeoutStr := cli.SetFlag(flag, "t", cfg.Ping.Timeout).(string) timeoutStr = NormalizeDuration(timeoutStr) if p.timeout, err = time.ParseDuration(timeoutStr); err != nil { return nil, fmt.Errorf("timeout options is not valid") } // set interval - intervalStr := cli.SetFlag(flag, "i", "1s").(string) + intervalStr := cli.SetFlag(flag, "i", cfg.Ping.Interval).(string) intervalStr = NormalizeDuration(intervalStr) if p.interval, err = time.ParseDuration(intervalStr); err != nil { return nil, fmt.Errorf("interval options is not valid") @@ -444,14 +444,14 @@ func NormalizeDuration(d string) string { } // help represents ping help -func help() { - fmt.Println(` +func help(cfg cli.Config) { + fmt.Printf(` usage: ping IP address / domain name [options] options: - -c count Send 'count' requests (default: 4) - -t timeout Specify a timeout in format "ms", "s", "m" (default: 2s) - -i interval Wait interval between sending each packet (default: 1s) + -c count Send 'count' requests (default: %d) + -t timeout Specify a timeout in format "ms", "s", "m" (default: %s) + -i interval Wait interval between sending each packet (default: %s) -4 Forces the ping command to use IPv4 (target should be hostname) -6 Forces the ping command to use IPv6 (target should be hostname) Example: @@ -459,5 +459,8 @@ func help() { ping 8.8.8.8 -c 10 ping google.com -6 ping mylg.io -i 5s - `) + `, + cfg.Ping.Count, + cfg.Ping.Timeout, + cfg.Ping.Interval) } diff --git a/mylg.go b/mylg.go index 1e03fcf5..39797962 100644 --- a/mylg.go +++ b/mylg.go @@ -53,6 +53,7 @@ var ( prompt string cPName string noIf bool = true + cfg cli.Config nsr *ns.Request c *cli.Readline @@ -83,6 +84,8 @@ var ( "help": help, // help "exit": cleanUp, // clean up "quit": cleanUp, // clean up + "show": show, // show config + "set": setConfig, // set config "lg": setLG, // prepare looking glass "ns": setNS, // prepare name server } @@ -90,12 +93,15 @@ var ( // init func init() { + // load configuration + cfg = cli.LoadConfig() + // with interface if len(eArgs) == 1 { // initialize cli c = cli.Init("local", version) go c.Run(req, nxt) // start web server - go httpd.Run() + go httpd.Run(cfg) // set interface enabled noIf = false } @@ -210,7 +216,7 @@ func web() { if runtime.GOOS != "darwin" { openCmd = "xdg-open" } - cmd := exec.Command(openCmd, "http://localhost:8080") + cmd := exec.Command(openCmd, fmt.Sprintf("http://%s:%d", cfg.Web.Address, cfg.Web.Port)) err := cmd.Start() if err != nil { println("error opening default browser") @@ -298,7 +304,7 @@ func hping() { if cPName != "local" { return } - p, err := ping.NewPing(args) + p, err := ping.NewPing(args, cfg) if err != nil { println(err.Error()) } else { @@ -331,7 +337,7 @@ func pingLG() { // pingLocal tries to ping from local source ip func pingLocal() { - p, err := icmp.NewPing(args) + p, err := icmp.NewPing(args, cfg) if err != nil { println(err.Error()) } @@ -346,7 +352,7 @@ func pingLocal() { // scanPorts tries to scan tcp/ip ports func scanPorts() { - scan, err := scan.NewScan(args) + scan, err := scan.NewScan(args, cfg) if err != nil { println(err.Error()) } else { @@ -402,6 +408,18 @@ func discovery() { d.PrintPretty() } +// setConfig +func setConfig() { + cli.SetConfig(args, &cfg) +} + +// showConfig +func show() { + if args == "config" { + cli.ShowConfig(&cfg) + } +} + // setLG set lg prompt and completer func setLG() { c.SetPrompt("lg") diff --git a/scan/scan.go b/scan/scan.go index c7dcf124..af9895d0 100644 --- a/scan/scan.go +++ b/scan/scan.go @@ -22,7 +22,7 @@ type Scan struct { } // NewScan creats scan object -func NewScan(args string) (Scan, error) { +func NewScan(args string, cfg cli.Config) (Scan, error) { var ( scan Scan flag map[string]interface{} @@ -31,12 +31,12 @@ func NewScan(args string) (Scan, error) { args, flag = cli.Flag(args) // help - if _, ok := flag["help"]; ok { - help() + if _, ok := flag["help"]; ok || args == "" { + help(cfg) return scan, fmt.Errorf("") } - pRange := cli.SetFlag(flag, "p", "1-500").(string) + pRange := cli.SetFlag(flag, "p", cfg.Scan.Port).(string) re := regexp.MustCompile(`(\d+)-(\d+)`) f := re.FindStringSubmatch(pRange) @@ -116,11 +116,12 @@ func host(ipAddr string, minPort, maxPort int) { } // help represents guide to user -func help() { - println(` +func help(cfg cli.Config) { + fmt.Printf(` usage: scan ip/host [-p portrange] example: - scan www.google.com -p 1-500 - `) + scan www.google.com -p %s + `, + cfg.Scan.Port) } diff --git a/scan/scan_test.go b/scan/scan_test.go index b7c1e07d..657f7474 100644 --- a/scan/scan_test.go +++ b/scan/scan_test.go @@ -1,22 +1,26 @@ package scan_test import ( + "github.com/mehrdadrad/mylg/cli" "github.com/mehrdadrad/mylg/scan" "testing" ) -var s scan.Scan +var ( + s scan.Scan + cfg, _ = cli.ReadDefaultConfig() +) func TestIsCIDR(t *testing.T) { var err error - s, err = scan.NewScan("8.8.8.0/24") + s, err = scan.NewScan("8.8.8.0/24", cfg) if err != nil { t.Error("NewScan failed") } if !s.IsCIDR() { t.Error("IsCIDR failed") } - s, err = scan.NewScan("8.8.8.8") + s, err = scan.NewScan("8.8.8.8", cfg) if err != nil { t.Error("NewScan failed") }