📶 [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

368 lines
9.8 KiB

package main
import (
"os"
"sort"
"strconv"
"strings"
"time"
)
var configMap = map[string]configParameter{}
var configAliasMap = map[string]string{}
func init() {
registerCommand("help", "show program usage", func() {
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.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)
}
}
}
if len(aliases) > 0 {
sort.Strings(aliases)
header = header + " (aliases: " + strings.Join(aliases, ", ") + ")"
}
header = header + ":"
full := header
if parm.description != "" {
full = header + "\n " + parm.description
}
if parm.command || parm.sw {
log("", 0, "%s", full)
} else {
log("", 0, "%s\n default: %v", full, 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)
})
registerAlias("?", "help")
registerAlias("h", "help")
}
// 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 any // value and default value
description string // description for this parameter
parsed bool // true if it was successfully parsed from commandline
configParameterOptions
}
type configParameterTypeUnion = interface {
bool | int | uint | float64 | string | []int | []uint | []float64 | []string | []bool | map[string]bool
}
// --------------
// parsing
// --------------
func ParseConfig() {
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.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.command {
if parm.sw {
parm.writeParmValue("true")
} else {
i++
failIf(i >= len(os.Args), "missing value for \"%v\"", parm.name)
parm.writeParmValue(getCmdlineParm(i))
}
}
parm.finalize()
totalFinalized++
}
log("cfg", 1, "ok: finished parsing config, got %v parameters", totalFinalized)
}
// getCmdlineParm retrieves a commandline parameter with index i.
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, []bool, []string:
return true
default:
return false
}
}
func (parm *configParameter) clearSlice() {
if !parm.parsed {
switch parm.value.(type) {
case []int:
parm.value = []int{}
case []uint:
parm.value = []uint{}
case []bool:
parm.value = []bool{}
case []string:
parm.value = []string{}
}
}
}
// 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.clearSlice()
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.clearSlice()
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.clearSlice()
parm.value = append(parm.value.([]uint), uint(u))
case []string:
parm.clearSlice()
parm.value = append(parm.value.([]string), value)
default:
fail("unknown config parameter \"%v\" type: %T", parm.name, parm.value)
}
}
// finalize marks a configParameter as parsed, adds it to a global config map
// and calls its callback, if one is present.
func (parm *configParameter) finalize() {
parm.parsed = true
configMap[parm.name] = *parm
if parm.callback != nil {
parm.callback()
}
log("cfg", 2, "parse: %T \"%v\" -> def %v, now %v", parm.value, parm.name, parm.def, parm.value)
}
// --------------
// registration
// --------------
func registerConfigParameter[T configParameterTypeUnion](name string, def T, 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)
configMap[name] = configParameter{name: name, value: def, def: def, description: description,
configParameterOptions: opts}
}
func registerParam[T configParameterTypeUnion](name string, def T, description string) {
registerConfigParameter(name, def, description, configParameterOptions{})
}
func registerParamHidden[T configParameterTypeUnion](name string, def T) {
registerConfigParameter(name, def, "", configParameterOptions{hidden: true})
}
func registerParamWithCallback[T configParameterTypeUnion](name string, def T, description string, callback func()) {
registerConfigParameter(name, def, description, configParameterOptions{callback: callback})
}
func registerCommand(name string, description string, callback func()) {
registerConfigParameter(name, false, description, configParameterOptions{command: true, callback: callback})
}
func registerSwitch(name string, description string) {
registerConfigParameter(name, false, description, configParameterOptions{sw: true})
}
func registerAlias(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 getParamGeneric(name string) any {
name = strings.ToLower(name)
parm, ok := configMap[name]
failIf(!ok, "unknown config parameter: \"%v\"", name)
failIf(parm.command, "config parameter \"%v\" is a command", name)
if parm.sw {
return parm.parsed // switches always return true if they were parsed
} else if parm.parsed || parm.hidden {
return parm.value // parsed and hidden parms return their current value
} else {
return parm.def // otherwise, use default value
}
}
func getParam[T configParameterTypeUnion](name string) T {
return getParamGeneric(name).(T)
}
func getParamInt(name string) int {
return getParam[int](name)
}
func getParamFloat(name string) float64 {
return getParam[float64](name)
}
func getParamIntSlice(name string) []int {
return getParam[[]int](name)
}
func getParamBool(name string) bool {
return getParam[bool](name)
}
func getParamSwitch(name string) bool {
return getParamBool(name)
}
func getParamString(name string) string {
return getParam[string](name)
}
func getParamStringSlice(name string) []string {
return getParam[[]string](name)
}
func getParamDurationMS(name string) time.Duration {
tm := getParam[int](name)
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 setParam(name string, value any) {
name = strings.ToLower(name)
parm, ok := configMap[name]
failIf(!ok, "unknown config parameter: \"%v\"", name)
failIf(parm.command, "config parameter \"%v\" is a command and cannot be set", name)
failIf(parm.sw && !value.(bool), "config parameter \"%v\" is a switch and only accepts boolean arguments", name)
parm.value = value
if parm.callback != nil {
parm.callback()
}
configMap[name] = parm
}