package asahttp import ( "crypto/tls" "errors" "fmt" "net" "net/url" "strings" "time" "github.rackspace.com/gorax/webutil" ) const defaultHTTPTimeout = 15 * time.Second // GetCommand method takes an asaHttp receiver as initialization, then executes // the cmd (string) against the ASA. Response value will be a tuple (string, error) // with the string containing the response value and the error if any, or nil. func (a *ASAHttp) GetCommand(cmd string) (string, error) { path := url.QueryEscape(cmd) // Validate that the passed in char string is within limits. if err := validateRequest(path); err != nil { return "", err } headers := webutil.Headers{"User-Agent": "ASDM"} resp, code, err := webutil.BasicAuthGet(a.baseUrl+path, a.user, a.password, headers, defaultHTTPTimeout) if err != nil { return "", err } if code == 401 { return "", fmt.Errorf("ACS credentials invalid for user %s", a.user) } if code != 200 { return "", fmt.Errorf("Received error code: %d", code) } return string(resp), nil } // PostCommand method takes an asaHttp receiver as initialization, then executes // the cmd (string) against the ASA. The PostCommand works differently than the // GetCommand in that it expects that there will not be a response value. If there // is a response value, it assumes that the response value is an error. // Response value will be a tuple (string, error) with the string containing // the response value and the error if any, or nil. func (a *ASAHttp) PostCommand(cmd string) (string, error) { path := url.QueryEscape(cmd) // Validate that the passed in char string is within limits. if err := validateRequest(path); err != nil { return "", err } headers := webutil.Headers{"User-Agent": "ASDM"} resp, code, err := webutil.BasicAuthGet(a.baseUrl+path, a.user, a.password, headers, defaultHTTPTimeout) if err != nil { return "", err } if code == 401 { return "", fmt.Errorf("ACS credentials invalid for user %s", a.user) } if code != 200 { return "", fmt.Errorf("Received error code: %d", code) } r := string(resp) if r != "" { return "", errors.New(r) } return r, nil } // PostCommands method takes an asaHttp receiver as initialization, then executes // the cmd (string) against the ASA. The PostCommands works differently than the // GetCommand in that it expects that there will not be a response value. If there // is a response value, it assumes that the response value is an error. // Response value will be a tuple (string, error) with the string containing // the response value and the error if any, or nil. func (a *ASAHttp) PostCommands(cmds []string) (string, error) { var c []string for _, cmd := range cmds { c = append(c, url.QueryEscape(cmd)) } path := strings.Join(c, "/") // Validate that the passed in char string is within limits. if err := validateRequest(path); err != nil { return "", err } headers := webutil.Headers{"User-Agent": "ASDM"} resp, code, err := webutil.BasicAuthGet(a.baseUrl+path, a.user, a.password, headers) if err != nil { return "", err } if code == 401 { return "", fmt.Errorf("ACS credentials invalid for user %s", a.user) } if code != 200 { return "", fmt.Errorf("Received error code: %d", code) } r := string(resp) if r != "" { return "", errors.New(r) } return r, nil } // TestConnection method is used for making a connection to an ASA to validate that // the device is listening. NOTE: This function will add an additional 5 second latency // if the target device does not refuse the socket connection. func (a *ASAHttp) TestConnection(ip string, port int) error { conf := &tls.Config{ InsecureSkipVerify: true, } dialer := &net.Dialer{ Timeout: 5 * time.Second, } conn, err := tls.DialWithDialer(dialer, "tcp", fmt.Sprintf("%v:%v", ip, port), conf) if err != nil { return err } defer func() { _ = conn.Close() }() _, err = conn.Write([]byte("HEAD /admin/public/index.html HTTP/1.0\r\nConnection: close\r\n\r\n")) if err != nil { return err } buf := make([]byte, 100) _, err = conn.Read(buf) if err != nil { return err } return nil } // validateRequest function will test the passed in commands to ensure that they // do not exceed the http query length value. Return will be an error or nil. func validateRequest(path string) error { if len(path) > 1000 { return errors.New("Query string cannot be longer than 1000 characters.") } return nil }