aerc/lib/socket.go
Daniel Martí 72f55b857b lib: fix an out of bounds panic in the server
If the message doesn't contain ':', we don't properly discard the
message, so we end up slicing it like msg[:-1].

This can be reproduced if one runs 'aerc foo', as the server receives
'foo' as the message.

'aerc foo' still doesn't do anything very user friendly, but at least it
doesn't panic horribly.

While at it, do the 'got message' log at the very beginning, so that the
user can see what message the server got before reporting the command as
invalid.

Signed-off-by: Daniel Martí <mvdan@mvdan.cc>
2020-02-13 10:26:12 -05:00

105 lines
2.2 KiB
Go

package lib
import (
"bufio"
"errors"
"fmt"
"log"
"net"
"net/url"
"path"
"strings"
"sync/atomic"
"time"
"github.com/kyoh86/xdg"
)
type AercServer struct {
logger *log.Logger
listener net.Listener
OnMailto func(addr *url.URL) error
}
func StartServer(logger *log.Logger) (*AercServer, error) {
sockpath := path.Join(xdg.RuntimeDir(), "aerc.sock")
l, err := net.Listen("unix", sockpath)
if err != nil {
return nil, err
}
as := &AercServer{
logger: logger,
listener: l,
}
// TODO: stash clients and close them on exit... bleh racey
go func() {
for {
conn, err := l.Accept()
if err != nil {
// TODO: Something more useful, in some cases, on wednesdays,
// after 2 PM, I guess?
as.logger.Printf("Closing Unix server: %v", err)
return
}
go as.handleClient(conn)
}
}()
return as, nil
}
func (as *AercServer) Close() {
as.listener.Close()
}
var lastId int64 = 0 // access via atomic
func (as *AercServer) handleClient(conn net.Conn) {
clientId := atomic.AddInt64(&lastId, 1)
as.logger.Printf("Accepted Unix connection %d", clientId)
scanner := bufio.NewScanner(conn)
conn.SetDeadline(time.Now().Add(1 * time.Minute))
for scanner.Scan() {
conn.SetDeadline(time.Now().Add(1 * time.Minute))
msg := scanner.Text()
as.logger.Printf("unix:%d: got message %s", clientId, msg)
if !strings.ContainsRune(msg, ':') {
conn.Write([]byte("error: invalid command\n"))
continue
}
prefix := msg[:strings.IndexRune(msg, ':')]
switch prefix {
case "mailto":
mailto, err := url.Parse(msg)
if err != nil {
conn.Write([]byte(fmt.Sprintf("error: %v\n", err)))
break
}
if as.OnMailto != nil {
err = as.OnMailto(mailto)
}
if err != nil {
conn.Write([]byte(fmt.Sprintf("result: %v\n", err)))
} else {
conn.Write([]byte(fmt.Sprint("result: success\n")))
}
}
}
as.logger.Printf("Closed Unix connection %d", clientId)
}
func ConnectAndExec(msg string) error {
sockpath := path.Join(xdg.RuntimeDir(), "aerc.sock")
conn, err := net.Dial("unix", sockpath)
if err != nil {
return err
}
conn.Write([]byte(msg + "\n"))
scanner := bufio.NewScanner(conn)
if !scanner.Scan() {
return errors.New("No response from server")
}
result := scanner.Text()
fmt.Println(result)
return nil
}