mirror of
https://github.com/bettercap/bettercap
synced 2025-07-06 04:52:10 -07:00
misc: refactored caplets code in a dedicated package
This commit is contained in:
parent
d16b0c7cf5
commit
9721c1d6e0
7 changed files with 209 additions and 157 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -2,7 +2,6 @@
|
||||||
*.tar.gz
|
*.tar.gz
|
||||||
*.prof*
|
*.prof*
|
||||||
pcaps
|
pcaps
|
||||||
caplets
|
|
||||||
build
|
build
|
||||||
bettercap
|
bettercap
|
||||||
bettercap.history
|
bettercap.history
|
||||||
|
|
53
caplets/caplet.go
Normal file
53
caplets/caplet.go
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
package caplets
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Caplet struct {
|
||||||
|
Name string
|
||||||
|
Path string
|
||||||
|
Size int64
|
||||||
|
Code []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cap *Caplet) Eval(argv []string, lineCb func(line string) error) error {
|
||||||
|
// the caplet might include other files (include directive, proxy modules, etc),
|
||||||
|
// temporarily change the working directory
|
||||||
|
cwd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error while getting current working directory: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
capPath := filepath.Dir(cap.Path)
|
||||||
|
if err := os.Chdir(capPath); err != nil {
|
||||||
|
return fmt.Errorf("error while changing current working directory: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if err := os.Chdir(cwd); err != nil {
|
||||||
|
fmt.Printf("error while restoring working directory: %v\n", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if argv == nil {
|
||||||
|
argv = []string{}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, line := range cap.Code {
|
||||||
|
// replace $0 with argv[0], $1 with argv[1] and so on
|
||||||
|
for i, arg := range argv {
|
||||||
|
what := fmt.Sprintf("$%d", i)
|
||||||
|
line = strings.Replace(line, what, arg, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = lineCb(line); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
2
caplets/doc.go
Normal file
2
caplets/doc.go
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
// Package caplets contains functions to enumerate, load and execute caplets.
|
||||||
|
package caplets
|
32
caplets/env.go
Normal file
32
caplets/env.go
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
package caplets
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/bettercap/bettercap/core"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
Suffix = ".cap"
|
||||||
|
InstallPath = "/usr/local/share/bettercap/caplets/"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
LoadPaths = []string{
|
||||||
|
"./caplets/",
|
||||||
|
InstallPath,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
for _, path := range core.SepSplit(core.Trim(os.Getenv("CAPSPATH")), ":") {
|
||||||
|
if path = core.Trim(path); len(path) > 0 {
|
||||||
|
LoadPaths = append(LoadPaths, path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, path := range LoadPaths {
|
||||||
|
LoadPaths[i], _ = filepath.Abs(path)
|
||||||
|
}
|
||||||
|
}
|
93
caplets/manager.go
Normal file
93
caplets/manager.go
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
package caplets
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/bettercap/bettercap/core"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
cache = make(map[string]*Caplet)
|
||||||
|
cacheLock = sync.Mutex{}
|
||||||
|
)
|
||||||
|
|
||||||
|
func List() []Caplet {
|
||||||
|
caplets := make([]Caplet, 0)
|
||||||
|
cwd, _ := filepath.Abs(".")
|
||||||
|
|
||||||
|
for _, searchPath := range append([]string{cwd}, LoadPaths...) {
|
||||||
|
files, _ := filepath.Glob(searchPath + "/*" + Suffix)
|
||||||
|
files2, _ := filepath.Glob(searchPath + "/*/*" + Suffix)
|
||||||
|
|
||||||
|
for _, fileName := range append(files, files2...) {
|
||||||
|
if stats, err := os.Stat(fileName); err == nil {
|
||||||
|
base := strings.Replace(fileName, searchPath+"/", "", -1)
|
||||||
|
base = strings.Replace(base, Suffix, "", -1)
|
||||||
|
|
||||||
|
caplets = append(caplets, Caplet{
|
||||||
|
Name: base,
|
||||||
|
Path: fileName,
|
||||||
|
Size: stats.Size(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return caplets
|
||||||
|
}
|
||||||
|
|
||||||
|
func Load(name string) (error, *Caplet) {
|
||||||
|
cacheLock.Lock()
|
||||||
|
defer cacheLock.Unlock()
|
||||||
|
|
||||||
|
if caplet, found := cache[name]; found {
|
||||||
|
return nil, caplet
|
||||||
|
}
|
||||||
|
|
||||||
|
names := []string{name}
|
||||||
|
if !strings.HasSuffix(name, Suffix) {
|
||||||
|
names = append(names, name+Suffix)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, path := range LoadPaths {
|
||||||
|
if !strings.HasSuffix(name, Suffix) {
|
||||||
|
name += Suffix
|
||||||
|
}
|
||||||
|
names = append(names, filepath.Join(path, name))
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, filename := range names {
|
||||||
|
if core.Exists(filename) {
|
||||||
|
cap := &Caplet{
|
||||||
|
Path: filename,
|
||||||
|
Code: make([]string, 0),
|
||||||
|
}
|
||||||
|
|
||||||
|
input, err := os.Open(filename)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error reading caplet %s: %v", filename, err), nil
|
||||||
|
}
|
||||||
|
defer input.Close()
|
||||||
|
|
||||||
|
scanner := bufio.NewScanner(input)
|
||||||
|
scanner.Split(bufio.ScanLines)
|
||||||
|
for scanner.Scan() {
|
||||||
|
line := core.Trim(scanner.Text())
|
||||||
|
if line == "" || line[0] == '#' {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
cap.Code = append(cap.Code, line)
|
||||||
|
}
|
||||||
|
|
||||||
|
cache[name] = cap
|
||||||
|
return nil, cap
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("caplet %s not found", name), nil
|
||||||
|
}
|
|
@ -1,153 +0,0 @@
|
||||||
package session
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/bettercap/bettercap/core"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
CapletSuffix = ".cap"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Caplet struct {
|
|
||||||
Path string
|
|
||||||
Code []string
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
CapletLoadPaths = []string{
|
|
||||||
"./caplets/",
|
|
||||||
"/usr/local/share/bettercap/caplets/",
|
|
||||||
}
|
|
||||||
|
|
||||||
cache = make(map[string]*Caplet)
|
|
||||||
cacheLock = sync.Mutex{}
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
for _, path := range core.SepSplit(core.Trim(os.Getenv("CAPSPATH")), ":") {
|
|
||||||
if path = core.Trim(path); len(path) > 0 {
|
|
||||||
CapletLoadPaths = append(CapletLoadPaths, path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, path := range CapletLoadPaths {
|
|
||||||
CapletLoadPaths[i], _ = filepath.Abs(path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func LoadCaplet(name string) (error, *Caplet) {
|
|
||||||
cacheLock.Lock()
|
|
||||||
defer cacheLock.Unlock()
|
|
||||||
|
|
||||||
if caplet, found := cache[name]; found {
|
|
||||||
return nil, caplet
|
|
||||||
}
|
|
||||||
|
|
||||||
names := []string{name}
|
|
||||||
if !strings.HasSuffix(name, CapletSuffix) {
|
|
||||||
names = append(names, name+CapletSuffix)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, path := range CapletLoadPaths {
|
|
||||||
if !strings.HasSuffix(name, CapletSuffix) {
|
|
||||||
name += CapletSuffix
|
|
||||||
}
|
|
||||||
names = append(names, filepath.Join(path, name))
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, filename := range names {
|
|
||||||
if core.Exists(filename) {
|
|
||||||
cap := &Caplet{
|
|
||||||
Path: filename,
|
|
||||||
Code: make([]string, 0),
|
|
||||||
}
|
|
||||||
|
|
||||||
I.Events.Log(core.INFO, "reading from caplet %s ...", filename)
|
|
||||||
input, err := os.Open(filename)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error reading caplet %s: %v", filename, err), nil
|
|
||||||
}
|
|
||||||
defer input.Close()
|
|
||||||
|
|
||||||
scanner := bufio.NewScanner(input)
|
|
||||||
scanner.Split(bufio.ScanLines)
|
|
||||||
for scanner.Scan() {
|
|
||||||
line := core.Trim(scanner.Text())
|
|
||||||
if line == "" || line[0] == '#' {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
cap.Code = append(cap.Code, line)
|
|
||||||
}
|
|
||||||
|
|
||||||
cache[name] = cap
|
|
||||||
return nil, cap
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Errorf("caplet %s not found", name), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseCapletCommand(line string) (is bool, caplet *Caplet, argv []string) {
|
|
||||||
file := core.Trim(line)
|
|
||||||
parts := strings.Split(file, " ")
|
|
||||||
argc := len(parts)
|
|
||||||
argv = make([]string, 0)
|
|
||||||
// check for any arguments
|
|
||||||
if argc > 1 {
|
|
||||||
file = core.Trim(parts[0])
|
|
||||||
if argc >= 2 {
|
|
||||||
argv = parts[1:]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err, cap := LoadCaplet(file); err == nil {
|
|
||||||
return true, cap, argv
|
|
||||||
}
|
|
||||||
|
|
||||||
return false, nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cap *Caplet) Eval(s *Session, argv []string) error {
|
|
||||||
// the caplet might include other files (include directive, proxy modules, etc),
|
|
||||||
// temporarily change the working directory
|
|
||||||
cwd, err := os.Getwd()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error while getting current working directory: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
capPath := filepath.Dir(cap.Path)
|
|
||||||
if err := os.Chdir(capPath); err != nil {
|
|
||||||
return fmt.Errorf("error while changing current working directory: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
if err := os.Chdir(cwd); err != nil {
|
|
||||||
s.Events.Log(core.ERROR, "error while restoring working directory: %v", err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
if argv == nil {
|
|
||||||
argv = []string{}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, line := range cap.Code {
|
|
||||||
// replace $0 with argv[0], $1 with argv[1] and so on
|
|
||||||
for i, arg := range argv {
|
|
||||||
what := fmt.Sprintf("$%d", i)
|
|
||||||
line = strings.Replace(line, what, arg, -1)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = s.Run(line + "\n"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -9,10 +9,12 @@ import (
|
||||||
"runtime"
|
"runtime"
|
||||||
"runtime/pprof"
|
"runtime/pprof"
|
||||||
"sort"
|
"sort"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/bettercap/readline"
|
"github.com/bettercap/readline"
|
||||||
|
|
||||||
|
"github.com/bettercap/bettercap/caplets"
|
||||||
"github.com/bettercap/bettercap/core"
|
"github.com/bettercap/bettercap/core"
|
||||||
"github.com/bettercap/bettercap/firewall"
|
"github.com/bettercap/bettercap/firewall"
|
||||||
"github.com/bettercap/bettercap/network"
|
"github.com/bettercap/bettercap/network"
|
||||||
|
@ -255,12 +257,34 @@ func (s *Session) ReadLine() (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Session) RunCaplet(filename string) error {
|
func (s *Session) RunCaplet(filename string) error {
|
||||||
err, caplet := LoadCaplet(filename)
|
err, caplet := caplets.Load(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return caplet.Eval(s, nil)
|
return caplet.Eval(nil, func(line string) error {
|
||||||
|
return s.Run(line + "\n")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseCapletCommand(line string) (is bool, caplet *caplets.Caplet, argv []string) {
|
||||||
|
file := core.Trim(line)
|
||||||
|
parts := strings.Split(file, " ")
|
||||||
|
argc := len(parts)
|
||||||
|
argv = make([]string, 0)
|
||||||
|
// check for any arguments
|
||||||
|
if argc > 1 {
|
||||||
|
file = core.Trim(parts[0])
|
||||||
|
if argc >= 2 {
|
||||||
|
argv = parts[1:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err, cap := caplets.Load(file); err == nil {
|
||||||
|
return true, cap, argv
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Session) Run(line string) error {
|
func (s *Session) Run(line string) error {
|
||||||
|
@ -294,7 +318,9 @@ func (s *Session) Run(line string) error {
|
||||||
|
|
||||||
// is it a caplet command?
|
// is it a caplet command?
|
||||||
if parsed, caplet, argv := parseCapletCommand(line); parsed {
|
if parsed, caplet, argv := parseCapletCommand(line); parsed {
|
||||||
return caplet.Eval(s, argv)
|
return caplet.Eval(argv, func(line string) error {
|
||||||
|
return s.Run(line + "\n")
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// is it a proxy module custom command?
|
// is it a proxy module custom command?
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue