📶 [WIP] RouterOS WinBox bruteforce
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
mtbf/config.go

344 lines
9.2 KiB

2 years ago
package main
import (
"os"
"sort"
"strconv"
"strings"
"time"
)
// ConfigParameterOptions represents additional options for a ConfigParameter.
type ConfigParameterOptions struct {
sw, hidden, command bool
callback func()
}
// ConfigParameter represents a single configuration parameter.
type ConfigParameter struct {
name string // duplicated in configMap, but also saved here for convenience
value, def interface{} // value and default value
description string // description for this parameter
parsed bool // true if it was successfully parsed from commandline
opts ConfigParameterOptions
}
var configMap map[string]ConfigParameter = make(map[string]ConfigParameter, 16)
var configAliasMap map[string]string = make(map[string]string, 4)
var ConfigParsingFinished bool = false
// registration
func genericRegister(name string, def interface{}, description string, opts ConfigParameterOptions) {
name = strings.ToLower(name)
_, ok := configMap[name]
failIf(ok, "cannot register config parameter (already exists): \"%v\"", name)
_, ok = configAliasMap[name]
failIf(ok, "cannot register config parameter (already exists as an alias): \"%v\"", name)
failIf(opts.command && opts.callback == nil, "\"%v\" is defined as a command but callback is missing", name)
p := ConfigParameter{}
p.name = name
p.value = def
p.def = def
p.description = description
p.opts = opts
configMap[name] = p
}
func CfgRegister(name string, def interface{}, description string) {
genericRegister(name, def, description, ConfigParameterOptions{})
}
func CfgRegisterEx(name string, def interface{}, description string, options ConfigParameterOptions) {
genericRegister(name, def, description, options)
}
func CfgRegisterCallback(name string, def interface{}, description string, callback func()) {
genericRegister(name, def, description, ConfigParameterOptions{callback: callback})
}
func CfgRegisterCommand(name string, description string, callback func()) {
genericRegister(name, false, description, ConfigParameterOptions{command: true, callback: callback})
}
func CfgRegisterSwitch(name string, description string) {
genericRegister(name, false, description, ConfigParameterOptions{sw: true})
}
func CfgRegisterHidden(name string, def interface{}) {
genericRegister(name, def, "", ConfigParameterOptions{hidden: true})
}
func CfgRegisterAlias(alias, target string) {
alias, target = strings.ToLower(alias), strings.ToLower(target)
_, ok := configAliasMap[alias]
failIf(ok, "cannot register alias (already exists): \"%v\"", alias)
_, ok = configMap[alias]
failIf(ok, "cannot register alias (already exists as a config parameter): \"%v\"", alias)
_, ok = configMap[target]
failIf(!ok, "cannot register alias \"%v\": target \"%v\" does not exist", alias, target)
configAliasMap[alias] = target
}
// acquisition
func CfgGet(name string) interface{} {
name = strings.ToLower(name)
parm, ok := configMap[name]
failIf(!ok, "unknown config parameter: \"%v\"", name)
if parm.opts.sw {
return parm.parsed // switches always return true if they were parsed
} else if parm.parsed || parm.opts.hidden {
return parm.value // parsed and hidden parms return their current value
} else {
return parm.def // otherwise, use default value
}
}
func CfgGetInt(name string) int {
return CfgGet(name).(int)
}
func CfgGetFloat(name string) float64 {
return CfgGet(name).(float64)
}
func CfgGetIntSlice(name string) []int {
return CfgGet(name).([]int)
}
func CfgGetBool(name string) bool {
return CfgGet(name).(bool)
}
func CfgGetSwitch(name string) bool {
return CfgGet(name).(bool)
}
func CfgGetString(name string) string {
return CfgGet(name).(string)
}
func CfgGetStringSlice(name string) []string {
return CfgGet(name).([]string)
}
func CfgGetDurationMS(name string) time.Duration {
tm := CfgGet(name).(int)
failIf(tm < -1, "\"%v\" can only be set to -1 or a positive value", name)
if tm == -1 {
tm = 0
}
return time.Duration(tm) * time.Millisecond
}
// setting
func CfgSet(name string, value interface{}) {
name = strings.ToLower(name)
parm, ok := configMap[name]
failIf(!ok, "unknown config parameter: \"%v\"", name)
failIf(!parm.opts.hidden, "tried to set \"%v\", but it is not a hidden parameter", name)
parm.value = value
if parm.opts.callback != nil {
parm.opts.callback()
}
configMap[name] = parm
}
// parsing
// getCmdlineParm returns a trimmed commandline parameter with specified index.
func getCmdlineParm(i int) string {
return strings.TrimSpace(os.Args[i])
}
// isSlice checks if a ConfigParameter value is a slice.
func (parm *ConfigParameter) isSlice() bool {
switch parm.value.(type) {
case []int, []uint, []string:
return true
default:
return false
}
}
// writeParmValue saves raw commandline value into a ConfigParameter.
func (parm *ConfigParameter) writeParmValue(value string) {
var err error
switch parm.value.(type) {
case bool:
parm.value, err = strconv.ParseBool(value)
failIf(err != nil, "config parameter \"%v\" must be a boolean", parm.name)
case int:
v, err := strconv.ParseInt(value, 10, 0)
failIf(err != nil, "config parameter \"%v\" must be an integer", parm.name)
parm.value = int(v)
case uint:
v, err := strconv.ParseUint(value, 10, 0)
failIf(err != nil, "config parameter \"%v\" must be an unsigned integer", parm.name)
parm.value = uint(v)
case string:
parm.value = value
case []bool:
b, err := strconv.ParseBool(value)
failIf(err != nil, "config parameter \"%v\" must be a boolean", parm.name)
parm.value = append(parm.value.([]bool), b)
case []int:
i, err := strconv.ParseInt(value, 10, 0)
failIf(err != nil, "config parameter \"%v\" must be an integer", parm.name)
parm.value = append(parm.value.([]int), int(i))
case []uint:
u, err := strconv.ParseUint(value, 10, 0)
failIf(err != nil, "config parameter \"%v\" must be an unsigned integer", parm.name)
parm.value = append(parm.value.([]uint), uint(u))
case []string:
parm.value = append(parm.value.([]string), value)
default:
fail("unknown config parameter \"%v\" type: %T", parm.name, parm.value)
}
}
// finalizeParm marks a ConfigParameter as parsed, adds it to a global config map
// and calls its callback, if one is present.
func (parm *ConfigParameter) finalizeParm() {
parm.parsed = true
configMap[parm.name] = *parm
if parm.opts.callback != nil {
parm.opts.callback()
}
log("cfg", 2, "parse: %T \"%v\" -> def %v, now %v", parm.value, parm.name, parm.def, parm.value)
}
func parseAppConfig() {
log("cfg", 1, "parsing config")
totalFinalized := 0
for i := 1; i < len(os.Args); i++ {
arg := getCmdlineParm(i)
if len(arg) == 0 {
continue
}
failIf(arg[0] != '-', "\"%v\" is not a commandline parameter", arg)
arg = strings.TrimPrefix(arg, "-")
arg = strings.TrimPrefix(arg, "-")
failIf(len(arg) == 0, "\"%v\" is not a commandline parameter", getCmdlineParm(i))
parm, ok := configMap[strings.ToLower(arg)]
if !ok {
alias, ok := configAliasMap[strings.ToLower(arg)]
failIf(!ok, "unknown commandline parameter: \"%v\"", arg)
parm, ok = configMap[alias]
failIf(!ok, "alias \"%v\" references unknown commandline parameter", arg)
log("cfg", 3, "\"%v\" is aliased to \"%v\"", alias, parm.name)
}
failIf(parm.opts.hidden, "\"%v\" is not a commandline parameter", getCmdlineParm(i))
failIf(parm.parsed && !parm.isSlice(), "multiple occurrences of commandline parameter \"%v\" are not allowed", parm.name)
if !parm.opts.command {
if parm.opts.sw {
parm.writeParmValue("true")
} else {
i++
parm.writeParmValue(getCmdlineParm(i))
}
}
finalizeParm(&parm)
totalFinalized++
}
log("cfg", 1, "parsed %v commandline parameters", totalFinalized)
ConfigParsingFinished = true
}
func showHelp() {
log("", 0, "options:")
parms := make([]string, 0, len(configMap))
for key := range configMap {
parms = append(parms, key)
}
sort.Strings(parms)
for _, parmName := range parms {
parm := configMap[parmName]
if parm.opts.hidden {
continue
}
header := "-" + parm.name
if len(parm.name) > 1 {
header = "-" + header
}
aliases := []string{}
for alias, target := range configAliasMap {
if target == parm.name {
if len(alias) == 1 {
aliases = append(aliases, "-"+alias)
} else {
aliases = append(aliases, "--"+alias)
}
break
}
}
if len(aliases) > 0 {
sort.Strings(aliases)
header = header + " (aliases: " + strings.Join(aliases, ", ") + ")"
}
header = header + ":"
description := " (description missing)"
if parm.description != "" {
description = " " + parm.description
}
if parm.opts.command || parm.opts.sw {
log("", 0, "%s\n%s", header, description)
} else {
log("", 0, "%s\n%s\n default: %v", header, description, parm.value)
}
}
log("", 0, "")
log("", 0, "examples:")
log("", 0, " single target:")
log("", 0, " ./mtbf --ip 127.0.0.1 --port 8291 --login admin --password 12345678 --out-file good.txt")
log("", 0, " multiple targets with multiple passwords:")
log("", 0, " ./mtbf --ip-list ips.txt --port 8291 --login admin --password-list passwords.txt --out-file good.txt")
os.Exit(0)
}
func init() {
CfgRegisterCommand("help", "show program usage", showHelp)
CfgRegisterAlias("?", "help")
CfgRegisterAlias("h", "help")
}