misc: refactored caplets code in a dedicated package

This commit is contained in:
evilsocket 2018-09-21 15:15:27 +02:00
parent d16b0c7cf5
commit 9721c1d6e0
7 changed files with 209 additions and 157 deletions

1
.gitignore vendored
View file

@ -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
View 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
View file

@ -0,0 +1,2 @@
// Package caplets contains functions to enumerate, load and execute caplets.
package caplets

32
caplets/env.go Normal file
View 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
View 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
}

View file

@ -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
}

View file

@ -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?