mirror of
https://git.mills.io/saltyim/saltyim.git
synced 2024-06-16 11:58:24 +00:00
Fix Lookup to actually use SRV records (where available) and refactor (#62)
Co-authored-by: James Mills <prologic@shortcircuit.net.au> Reviewed-on: https://git.mills.io/saltyim/saltyim/pulls/62 Reviewed-by: xuu <xuu@noreply@mills.io>
This commit is contained in:
parent
fda4628414
commit
211f725986
26
client.go
26
client.go
@ -18,7 +18,7 @@ import (
|
||||
"go.mills.io/saltyim/internal/exec"
|
||||
)
|
||||
|
||||
type configCache map[string]Config
|
||||
type addrCache map[string]*Addr
|
||||
|
||||
// PackMessage formts an outoing message in the Message Format
|
||||
// <timestamp>\t(<sender>) <message>
|
||||
@ -42,7 +42,7 @@ func Send(endpoint, msg string) error {
|
||||
type Client struct {
|
||||
me *Addr
|
||||
key *keys.EdX25519Key
|
||||
cache configCache
|
||||
cache addrCache
|
||||
}
|
||||
|
||||
// NewClient returns a new Salty IM client for sending and receiving
|
||||
@ -72,24 +72,24 @@ func NewClient(me *Addr, options ...IdentityOption) (*Client, error) {
|
||||
return &Client{
|
||||
me: me,
|
||||
key: ident.key,
|
||||
cache: make(configCache),
|
||||
cache: make(addrCache),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (cli *Client) getConfig(user string) (Config, error) {
|
||||
config, ok := cli.cache[user]
|
||||
func (cli *Client) getAddr(user string) (*Addr, error) {
|
||||
addr, ok := cli.cache[user]
|
||||
if ok {
|
||||
return config, nil
|
||||
return addr, nil
|
||||
}
|
||||
|
||||
config, err := Lookup(user)
|
||||
addr, err := LookupAddr(user)
|
||||
if err != nil {
|
||||
return Config{}, fmt.Errorf("error: failed to lookup user %s: %w", user, err)
|
||||
return nil, fmt.Errorf("error: failed to lookup user %s: %w", user, err)
|
||||
}
|
||||
|
||||
cli.cache[user] = config
|
||||
cli.cache[user] = addr
|
||||
|
||||
return config, nil
|
||||
return addr, nil
|
||||
}
|
||||
|
||||
func (cli *Client) handleMessage(prehook, posthook string, msgs chan string) msgbus.HandlerFunc {
|
||||
@ -147,17 +147,17 @@ func (cli *Client) Read(ctx context.Context, prehook, posthook string) chan stri
|
||||
|
||||
// Send sends an encrypted message to the specified user
|
||||
func (cli *Client) Send(user, msg string) error {
|
||||
cfg, err := cli.getConfig(user)
|
||||
addr, err := cli.getAddr(user)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error looking up user %s: %w", user, err)
|
||||
}
|
||||
|
||||
b, err := salty.Encrypt(cli.key, PackMessage(cli.me, msg), []string{cfg.Key})
|
||||
b, err := salty.Encrypt(cli.key, PackMessage(cli.me, msg), []string{addr.Key().ID().String()})
|
||||
if err != nil {
|
||||
return fmt.Errorf("error encrypting message to %s: %w", user, err)
|
||||
}
|
||||
|
||||
if err := Send(cfg.Endpoint, string(b)); err != nil {
|
||||
if err := Send(addr.Endpoint().String(), string(b)); err != nil {
|
||||
return fmt.Errorf("error sending message to %s: %w", user, err)
|
||||
}
|
||||
|
||||
|
@ -44,7 +44,7 @@ func lookup(user string) {
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
config, err := saltyim.Lookup(user)
|
||||
config, err := saltyim.LookupAddr(user)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error looking up user %s: %s\n", user, err)
|
||||
os.Exit(2)
|
||||
|
@ -10,8 +10,8 @@ import (
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
"golang.org/x/crypto/ssh/terminal"
|
||||
"golang.org/x/net/context"
|
||||
"golang.org/x/term"
|
||||
|
||||
"go.mills.io/saltyim"
|
||||
)
|
||||
@ -92,7 +92,7 @@ func read(me *saltyim.Addr, identity string, prehook, posthook string, args ...s
|
||||
}()
|
||||
|
||||
for msg := range cli.Read(ctx, prehook, posthook) {
|
||||
if terminal.IsTerminal(syscall.Stdin) {
|
||||
if term.IsTerminal(syscall.Stdin) {
|
||||
fmt.Println(saltyim.FormatMessage(msg))
|
||||
} else {
|
||||
fmt.Println(msg)
|
||||
|
@ -1,8 +0,0 @@
|
||||
package saltyim
|
||||
|
||||
// Config represents a Salty Config for a User which at a minimum is required
|
||||
// to have an Endpoint and Key (Public Key)
|
||||
type Config struct {
|
||||
Endpoint string `json:"endpoint"`
|
||||
Key string `json:"key"`
|
||||
}
|
4
go.mod
4
go.mod
@ -16,7 +16,7 @@ require (
|
||||
github.com/spf13/viper v1.10.1
|
||||
github.com/stretchr/testify v1.7.0
|
||||
go.mills.io/salty v0.0.0-20220322161301-ce2b9f6573fa
|
||||
golang.org/x/crypto v0.0.0-20220321153916-2c7772ba3064
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211
|
||||
)
|
||||
|
||||
require (
|
||||
@ -63,9 +63,9 @@ require (
|
||||
github.com/vmihailenco/msgpack/v4 v4.3.12 // indirect
|
||||
github.com/vmihailenco/tagparser v0.1.2 // indirect
|
||||
github.com/writeas/go-strip-markdown/v2 v2.1.1 // indirect
|
||||
golang.org/x/crypto v0.0.0-20220321153916-2c7772ba3064 // indirect
|
||||
golang.org/x/exp v0.0.0-20220321173239-a90fa8a75705 // indirect
|
||||
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8 // indirect
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/protobuf v1.28.0 // indirect
|
||||
|
@ -156,7 +156,7 @@ func (h *SaltyChat) handleSendMessage(ctx app.Context, e app.Event) {
|
||||
if h.client != nil {
|
||||
friendAddress := strings.TrimSpace(h.friend.Value)
|
||||
h.friend.Value = friendAddress
|
||||
if _, err := saltyim.Lookup(friendAddress); err != nil {
|
||||
if _, err := saltyim.LookupAddr(friendAddress); err != nil {
|
||||
h.showDialog("problem with send-to address", err.Error())
|
||||
} else {
|
||||
if err := h.client.Send(friendAddress, msg); err == nil {
|
||||
|
@ -30,9 +30,9 @@ func getUserColor(s string) tcell.Color {
|
||||
type ChatTUI struct {
|
||||
mu sync.RWMutex
|
||||
|
||||
cli *saltyim.Client
|
||||
user string
|
||||
config saltyim.Config
|
||||
cli *saltyim.Client
|
||||
user string
|
||||
addr *saltyim.Addr
|
||||
|
||||
// Configurations.
|
||||
palette map[string]string
|
||||
@ -41,15 +41,15 @@ type ChatTUI struct {
|
||||
// NewChatTUI initializes a new chatApp.
|
||||
// Sets up connection with broker, and initializes UI.
|
||||
func NewChatTUI(cli *saltyim.Client, user string) (*ChatTUI, error) {
|
||||
config, err := saltyim.Lookup(user)
|
||||
addr, err := saltyim.LookupAddr(user)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error looking up user %s: %s", user, err)
|
||||
}
|
||||
|
||||
return &ChatTUI{
|
||||
cli: cli,
|
||||
user: user,
|
||||
config: config,
|
||||
cli: cli,
|
||||
user: user,
|
||||
addr: addr,
|
||||
|
||||
palette: map[string]string{
|
||||
"background": "000000",
|
||||
@ -128,7 +128,7 @@ func (c *ChatTUI) SetScreen(inCh <-chan string, outCh chan<- string) {
|
||||
|
||||
app := tview.NewApplication()
|
||||
|
||||
chatTitle := fmt.Sprintf("Chatting to %s via %s", c.user, c.config.Endpoint)
|
||||
chatTitle := fmt.Sprintf("Chatting to %s via %s", c.user, c.addr.Endpoint().String())
|
||||
inputTitle := fmt.Sprintf("Connected to %s as %s", c.cli.Me().Endpoint(), c.cli.Me())
|
||||
|
||||
// Generate UI components.
|
||||
|
119
lookup.go
119
lookup.go
@ -10,28 +10,72 @@ import (
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
sync "github.com/sasha-s/go-deadlock"
|
||||
"github.com/keys-pub/keys"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var (
|
||||
_ json.Marshaler = (*Addr)(nil)
|
||||
)
|
||||
|
||||
func fetchConfig(addr string) (Config, error) {
|
||||
// Attempt using hash
|
||||
res, err := Request(http.MethodGet, addr, nil, nil)
|
||||
if err != nil {
|
||||
return Config{}, err
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return Config{}, err
|
||||
}
|
||||
|
||||
var config Config
|
||||
if err := json.Unmarshal(data, &config); err != nil {
|
||||
return Config{}, err
|
||||
}
|
||||
|
||||
return config, err
|
||||
}
|
||||
|
||||
// Config represents a Salty Config for a User which at a minimum is required
|
||||
// to have an Endpoint and Key (Public Key)
|
||||
type Config struct {
|
||||
Endpoint string `json:"endpoint"`
|
||||
Key string `json:"key"`
|
||||
}
|
||||
|
||||
// Addr represents a Salty IM User's Address
|
||||
type Addr struct {
|
||||
mu sync.RWMutex
|
||||
|
||||
User string
|
||||
Domain string
|
||||
|
||||
key *keys.EdX25519PublicKey
|
||||
endpoint *url.URL
|
||||
discoveredDomain string
|
||||
}
|
||||
|
||||
// IsZero returns true if the address is empty
|
||||
func (a *Addr) IsZero() bool {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
|
||||
return a.User == "" && a.Domain == ""
|
||||
}
|
||||
|
||||
func (a *Addr) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
Addr string
|
||||
User string
|
||||
Domain string
|
||||
Key string
|
||||
Endpoint string
|
||||
}{
|
||||
User: a.User,
|
||||
Domain: a.Domain,
|
||||
Addr: a.String(),
|
||||
Key: a.key.ID().String(),
|
||||
Endpoint: a.Endpoint().String(),
|
||||
})
|
||||
}
|
||||
|
||||
func (a *Addr) String() string {
|
||||
return fmt.Sprintf("%s@%s", a.User, a.Domain)
|
||||
}
|
||||
@ -47,20 +91,19 @@ func (a *Addr) Formatted() string {
|
||||
return fmt.Sprintf("(%s)", a)
|
||||
}
|
||||
|
||||
// Key returns the Publib Kcy of this User (Salty Addr) as discovered
|
||||
func (a *Addr) Key() *keys.EdX25519PublicKey {
|
||||
return a.key
|
||||
}
|
||||
|
||||
// Endpoint returns the discovered Endpoint
|
||||
func (a *Addr) Endpoint() *url.URL {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
|
||||
return a.endpoint
|
||||
|
||||
}
|
||||
|
||||
// DiscoveredDomain returns the discovered domain (if any) of fallbacks to the Domain
|
||||
func (a *Addr) DiscoveredDomain() string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
|
||||
if a.discoveredDomain != "" {
|
||||
return a.discoveredDomain
|
||||
}
|
||||
@ -78,18 +121,26 @@ func (a *Addr) HashURI() string {
|
||||
}
|
||||
|
||||
func (a *Addr) Refresh() error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
|
||||
log.Debugf("Looking up SRV record for _salty._tcp.%s", a.Domain)
|
||||
_, records, err := net.LookupSRV("salty", "tcp", a.Domain)
|
||||
if err == nil && len(records) > 0 {
|
||||
a.discoveredDomain = records[0].Target
|
||||
log.Debugf("Discovered salty services %s", a.discoveredDomain)
|
||||
}
|
||||
|
||||
config, err := Lookup(a.String())
|
||||
config, err := fetchConfig(a.HashURI())
|
||||
if err != nil {
|
||||
return fmt.Errorf("error looking up endpoint for %s: %w", a, err)
|
||||
// Fallback to plain user nick
|
||||
config, err = fetchConfig(a.URI())
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("error looking up user %s: %w", a, err)
|
||||
}
|
||||
key, err := keys.NewEdX25519PublicKeyFromID(keys.ID(config.Key))
|
||||
if err != nil {
|
||||
return fmt.Errorf("error parsing public key %s: %w", config.Key, err)
|
||||
}
|
||||
a.key = key
|
||||
|
||||
u, err := url.Parse(config.Endpoint)
|
||||
if err != nil {
|
||||
@ -112,38 +163,16 @@ func ParseAddr(addr string) (*Addr, error) {
|
||||
return &Addr{User: parts[0], Domain: parts[1]}, nil
|
||||
}
|
||||
|
||||
// Lookup looks up a Salty Address for a User by parsing the user's domain and
|
||||
// LookupAddr looks up a Salty Address for a User by parsing the user's domain and
|
||||
// making a request to the user's Well-Known URI expected to be located at
|
||||
// https://domain/.well-known/salty/<user>.json
|
||||
func Lookup(addr string) (Config, error) {
|
||||
func LookupAddr(addr string) (*Addr, error) {
|
||||
a, err := ParseAddr(addr)
|
||||
if err != nil {
|
||||
return Config{}, err
|
||||
return nil, err
|
||||
}
|
||||
config, err := fetchConfig(a.HashURI())
|
||||
if err != nil {
|
||||
// Fallback to plain user nick
|
||||
config, err = fetchConfig(a.URI())
|
||||
if err := a.Refresh(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return config, err
|
||||
}
|
||||
|
||||
func fetchConfig(addr string) (Config, error) {
|
||||
// Attempt using hash
|
||||
res, err := Request(http.MethodGet, addr, nil, nil)
|
||||
if err != nil {
|
||||
return Config{}, err
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return Config{}, err
|
||||
}
|
||||
|
||||
var config Config
|
||||
if err := json.Unmarshal(data, &config); err != nil {
|
||||
return Config{}, err
|
||||
}
|
||||
|
||||
return config, err
|
||||
return a, nil
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user