unixserver.go 1.65 KB
Newer Older
ale's avatar
ale committed
1 2 3 4 5 6
package server

import (
	"bytes"
	"context"
	"errors"
7
	"fmt"
ale's avatar
ale committed
8

ale's avatar
ale committed
9
	"git.autistici.org/ai3/go-common/unix"
ale's avatar
ale committed
10 11 12 13 14 15 16
	"git.autistici.org/id/auth"
)

// SocketServer accepts connections on a UNIX socket, speaking the
// line-based wire protocol, and dispatches incoming requests to the
// wrapped Server.
type SocketServer struct {
17 18
	codec auth.Codec
	auth  *Server
ale's avatar
ale committed
19 20 21
}

// NewSocketServer returns a new SocketServer listening on the given path.
22
func NewSocketServer(authServer *Server) *SocketServer {
ale's avatar
ale committed
23
	return &SocketServer{
24 25
		auth:  authServer,
		codec: auth.DefaultCodec,
ale's avatar
ale committed
26 27 28
	}
}

ale's avatar
ale committed
29 30
// ServeLine handles a single request and writes a
// response. Implements the unix.LineHandler interface.
ale's avatar
ale committed
31
func (s *SocketServer) ServeLine(ctx context.Context, lw unix.LineResponseWriter, line []byte) error {
32 33 34 35 36 37 38
	// Parse the incoming command. The only two known
	// commands are 'auth' for an authentication request,
	// and 'quit' to terminate the connection (closing the
	// connection also works). We shut down the connection
	// on any protocol error.
	parts := bytes.SplitN(line, []byte{' '}, 2)
	nargs := len(parts)
ale's avatar
ale committed
39 40 41
	if nargs < 1 {
		return errors.New("no command given")
	}
42 43 44
	cmd := string(parts[0])
	switch {
	case nargs == 1 && cmd == "quit":
ale's avatar
ale committed
45
		return unix.ErrCloseConnection
46
	case nargs == 2 && cmd == "auth":
ale's avatar
ale committed
47
		return s.handleAuth(ctx, lw, parts[1])
48
	default:
ale's avatar
ale committed
49
		return errors.New("syntax error")
ale's avatar
ale committed
50 51 52
	}
}

ale's avatar
ale committed
53
func (s *SocketServer) handleAuth(ctx context.Context, lw unix.LineResponseWriter, arg []byte) error {
ale's avatar
ale committed
54
	var req auth.Request
55
	if err := s.codec.Decode(arg, &req); err != nil {
ale's avatar
ale committed
56
		return fmt.Errorf("decoding error: %v", err)
ale's avatar
ale committed
57 58 59 60
	}

	resp := s.auth.Authenticate(ctx, &req)

ale's avatar
ale committed
61
	return lw.WriteLineCRLF(s.codec.Encode(resp))
ale's avatar
ale committed
62
}