bettercap/session/module_param.go

204 lines
4.9 KiB
Go

package session
import (
"crypto/rand"
"encoding/json"
"fmt"
"net"
"regexp"
"strconv"
"strings"
"github.com/evilsocket/islazy/tui"
)
type ParamType int
const (
STRING ParamType = iota
BOOL = iota
INT = iota
FLOAT = iota
)
type ModuleParam struct {
Name string
Type ParamType
Value string
Description string
Validator *regexp.Regexp
}
func NewModuleParameter(name string, def_value string, t ParamType, validator string, desc string) *ModuleParam {
p := &ModuleParam{
Name: name,
Type: t,
Description: desc,
Value: def_value,
Validator: nil,
}
if validator != "" {
p.Validator = regexp.MustCompile(validator)
}
return p
}
func NewStringParameter(name string, def_value string, validator string, desc string) *ModuleParam {
return NewModuleParameter(name, def_value, STRING, validator, desc)
}
func NewBoolParameter(name string, def_value string, desc string) *ModuleParam {
return NewModuleParameter(name, def_value, BOOL, "^(true|false)$", desc)
}
func NewIntParameter(name string, def_value string, desc string) *ModuleParam {
return NewModuleParameter(name, def_value, INT, `^[\-\+]?[\d]+$`, desc)
}
func NewDecimalParameter(name string, def_value string, desc string) *ModuleParam {
return NewModuleParameter(name, def_value, FLOAT, `^[\-\+]?[\d]+(\.\d+)?$`, desc)
}
func (p ModuleParam) validate(value string) (error, interface{}) {
if p.Validator != nil {
if !p.Validator.MatchString(value) {
return fmt.Errorf("Parameter %s not valid: '%s' does not match rule '%s'.", tui.Bold(p.Name), value, p.Validator.String()), nil
}
}
switch p.Type {
case STRING:
return nil, value
case BOOL:
lvalue := strings.ToLower(value)
if lvalue == "true" {
return nil, true
} else if lvalue == "false" {
return nil, false
} else {
return fmt.Errorf("Can't typecast '%s' to boolean.", value), nil
}
case INT:
i, err := strconv.Atoi(value)
return err, i
case FLOAT:
i, err := strconv.ParseFloat(value, 64)
return err, i
}
return fmt.Errorf("Unhandled module parameter type %d.", p.Type), nil
}
const ParamIfaceName = "<interface name>"
const ParamIfaceAddress = "<interface address>"
const ParamIfaceAddress6 = "<interface address6>"
const ParamIfaceMac = "<interface mac>"
const ParamSubnet = "<entire subnet>"
const ParamRandomMAC = "<random mac>"
var ParamIfaceNameParser = regexp.MustCompile("<([a-zA-Z0-9]{2,16})>")
func (p ModuleParam) parse(s *Session, v string) string {
switch v {
case ParamIfaceName:
v = s.Interface.Name()
case ParamIfaceAddress:
v = s.Interface.IpAddress
case ParamIfaceAddress6:
v = s.Interface.Ip6Address
case ParamIfaceMac:
v = s.Interface.HwAddress
case ParamSubnet:
v = s.Interface.CIDR()
case ParamRandomMAC:
hw := make([]byte, 6)
rand.Read(hw)
v = net.HardwareAddr(hw).String()
default:
// check the <iface> case
if m := ParamIfaceNameParser.FindStringSubmatch(v); len(m) == 2 {
// does it match any interface?
name := m[1]
if iface, err := net.InterfaceByName(name); err == nil {
if addrs, err := iface.Addrs(); err == nil {
var ipv4, ipv6 *net.IP
// get first ipv4 and ipv6 addresses
for _, addr := range addrs {
if ipv4 == nil {
if ipv4Addr := addr.(*net.IPNet).IP.To4(); ipv4Addr != nil {
ipv4 = &ipv4Addr
}
} else if ipv6 == nil {
if ipv6Addr := addr.(*net.IPNet).IP.To16(); ipv6Addr != nil {
ipv6 = &ipv6Addr
}
} else {
break
}
}
// prioritize ipv4, fallback on ipv6 if assigned
if ipv4 != nil {
v = ipv4.String()
} else if ipv6 != nil {
v = ipv6.String()
}
}
}
}
}
return v
}
func (p ModuleParam) getUnlocked(s *Session) string {
_, v := s.Env.GetUnlocked(p.Name)
return p.parse(s, v)
}
func (p ModuleParam) Get(s *Session) (error, interface{}) {
_, v := s.Env.Get(p.Name)
v = p.parse(s, v)
return p.validate(v)
}
func (p ModuleParam) Help(padding int) string {
return fmt.Sprintf(" "+tui.YELLOW+"%"+strconv.Itoa(padding)+"s"+tui.RESET+
" : "+
"%s "+tui.DIM+"(default=%s"+tui.RESET+")\n", p.Name, p.Description, p.Value)
}
func (p ModuleParam) Register(s *Session) {
s.Env.Set(p.Name, p.Value)
}
func (p ModuleParam) RegisterObserver(s *Session, cb EnvironmentChangedCallback) {
s.Env.WithCallback(p.Name, p.Value, cb)
}
type JSONModuleParam struct {
Name string `json:"name"`
Type ParamType `json:"type"`
Description string `json:"description"`
Value string `json:"default_value"`
Current string `json:"current_value"`
Validator string `json:"validator"`
}
func (p ModuleParam) MarshalJSON() ([]byte, error) {
j := JSONModuleParam{
Name: p.Name,
Type: p.Type,
Description: p.Description,
Value: p.Value,
Current: p.getUnlocked(I), // if we're here, Env is already locked
}
if p.Validator != nil {
j.Validator = p.Validator.String()
}
return json.Marshal(j)
}