rpc25519

package module
v1.0.40 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 15, 2024 License: MIT Imports: 32 Imported by: 1

README

rpc25519: Edwards curve ed25519 based identity RPC for Go/golang

Not generally available at the moment.

At the moment I wrote this to support goq moving to a sane RPC system. It does not have the bells and whistles needed for general purpose usability. But the bones are there. It works well under my goq distributed job queueing system as a transport.

The docs and examples and user facing API just need some polish to remove any rough edges.

It is public because my distributed job queuing facility goq is public and it needed it.

TLS-1.3 over TCP

Three transports are available: TLS-v1.3 over TCP, plain TCP, and QUIC which uses TLS-v1.3 over UDP.

QUIC is so much faster than even plain TCP, it should probably be your default choice.

The only difficulty comes to IPv6 networks. They are not very friendly to QUIC. Typically they have minimal 1280 MTU, so we'll have a problem if any kind of VPN layer is in use. Tailscale/Wireguard add an extra 28 bytes of ICMP/IP headers and try to keep packets 1:1, thus going over the MTU and not really working. IPv6 cannot fragment packets in the network, so they just get rejected outright. This is my surmize. I could be misunderstanding the root cause. The fact remains that in testing over IPv6 VPN links, rpc25519's QUIC nodes could not talk to each other.

Context: https://github.com/tailscale/tailscale/issues/2633

Comments/links: https://gist.github.com/jj1bdx/1adac3e305d0fb6dee90dd5b909513ed

At the moment we will detect and panic in the case an IPv6 network is used to warn you of this issue.

How to KYC or Know Your Clients

How do we identify our clients in an RPC situation?

We have clients and servers in RPC. For that matter, how do we authenticate the server too?

The identity of an both client and server, either end of a connection, is established with a private-key/public-key pair in which the remote party proves posession of a private-key and we can confirm that the associated public-key has been signed by our certificate authority. Public keys signed in this way are more commonly called certificates or "certs".

An email address for convenience of identification of jobs can be listed in the certificate.

This is a much saner approach than tying a work-load identity to a specific machine, domain name, or machine port. These incidental details are artifacts of the network design, easily spoofed or phished, and are not reliable or desirable identitiers. Only access to the private key corresponding to our cert should convey identity during a TLS handshake. The later part of the handshake verifies that the key was signed by our CA. This should suffice.

Such a requirement maintains the strongest security but still allows elastic clouds to grow and shrink and migrate the workloads of both clients and servers. It allows for sane development and deployment on any hardware a developer can access with ssh, without compromising on encryption on either end.

TOFU or not

By creating (touch will do) a known_client_keys file on the server directory where the server is running, you activate the key tracking system on the server.

Similarly, by touching the known_server_keys file in the directory where the client is running, you tell the client to record the server certificates it has seen.

Without these files, no history of seen certs is recorded. While certs are still checked (assuming you have left SkipVerifyKeys at the default false value in the config), they are not remembered.

In a typical use case, touch the file to record the certs in use, then make the known keys file read-only to block out any new certs.

This works because clients are accepted or rejected depending on the writability of the known_client_keys file, and the presence of their certificates in that file.

Trust on first use, or TOFU, is used if the known_client_keys file is writable. When the file is writable and a new client arrives, their public key and email (if available) are stored. This is similar to how the classic ~/.ssh/known_hosts file works, but with a twist.

The twist is that once the desired identities have been recorded, the file can be used to reject any new or unknown certs from new or unkown clients.

If the file has been made unwritable (say with chmod -w known_client_keys), then we are "locked down". In this case, TOFU is not allowed and only existing keys will be accepted. Any others whose public keys are not in the known_client_hosts file will be dropped during the TLS handshake.

These unknown clients are usually just attackers that should in fact be rejected soundly. In case of misconfiguration however, all clients should be prepared to timeout as, if they are not on the list, they will be shunned and shown no further attention or packets. Without a timeout, they may well hang indefinitely waiting for network activity.

encryption details

Modern Ed25519 keys are used with TLS-1.3. The TLS_CHACHA20_POLY1305_SHA256 cipher suite is the only one configured. This is similar to the crypto suite used in Wireguard (TailScale), the only difference being that Wireguard uses Blake2s as a hash function (apparently faster than SHA-256). The ChaCha20, Poly1305, and Curve25519 parts are the same.

security posture for both extremes

The strength of security is controlled by the Config options to NewServer() and NewClient().

See the cli.go file.

By default security is very strong, requiring TLS-1.3 and valid signed client certs, but allowing TOFU for previously unseen clients who come bearing valid certs; those signed by our CA.

This allows one to test one's initial setup with a minimum of fuss.

Further hardening (into a virtual fortress) can then be accomplished by making read-only the set of already seen clients; with chmod -w known_client_keys.

With this done, only those clients who we have already seen will be permitted in; and these are clients bearing proper certs signed by our CA private key; this will be verified during the TLS handshake. Any others will be rejected.

On the other extreme, setting TCPonly_no_TLS to true means we will use only TCP, no TLS at all, and everything will be transmitted in clear text.

In the middle of the road, setting Config.SkipVerifyKeys to true means the server will act like an HTTPS web server: any client with any key will be allowed to talk to the server, but TLS-1.3 will encrypt the traffic. Whenever TLS is used, it is always TLS-1.3 and set to the tls.TLS_CHACHA20_POLY1305_SHA256 cipher suite. Thus this setting is of no use for authenticating clients; it should almost never be used since it opens up your server to the world. But for web servers, this may be desirable.

Note that both sides of the connection (client and server) must agree to -skip-verify (Config.SkipVerifyKeys = true); otherwise the signing authority (CA) must agree. Under the default, SkipVerifyKeys = false, by signing only your own keys, with your own CA, that you keep private from the world, you maintain very tight control over access to your server.

We only create and only accept Ed25519 keys (with SkipVerifyKeys off/false, the default). (See selfy below, or the included gen.sh script to create keys).

// Config says who to contact (for a client), or
// where to listen (for a server); and sets how
// strong a security posture we adopt.
type Config struct {

	// ServerAddr host:port of the rpc25519.Server to contact.
	ServerAddr string

	// false means TLS-1.3 secured. true here means do TCP only.
	TCPonly_no_TLS bool

	// path to certs/ like certificate directory 
	// on the live filesystem. If left
	// empty then the embedded certs/ from build-time, those 
	// copied from the on-disk certs/ directory and baked 
	// into the executable as a virtual file system with
	// the go:embed directive are used.
	CertPath string

	// SkipVerifyKeys true allows any incoming
	// key to be signed by
	// any CA; it does not have to be ours. Obviously
	// this discards almost all access control; it
	// should rarely be used unless communication
	// with the any random agent/hacker/public person
	// is desired.
	SkipVerifyKeys bool
	
	// This is not a Config option, but creating
	// the known key file on the client/server is
	// typically the last security measure in hardening.
	//
	// If known_client_keys exists on the server,
	// then we will read from it.
	// Likewise, if known_server_keys exists on
	// the client, then we will read from it.
	//
	// If the known keys file is read-only: Read-only
	// means we are in lockdown mode and no unknown
	// client certs will be accepted, even if they
	// have been properly signed by our CA.
	//
	// If the known keys file is writable then we are
	// Trust On First Use mode, and new remote parties
	// are recorded in the file if their certs are valid (signed
	// by us/our CA).
	//
	// Note if the known_client_keys is read-only, it
	// had better not be empty or nobody will be
	// able to contact us. The server will notice
	// this and crash since why bother being up.

	...
}

generating keys with emails inside

See the selfy command below for details.

Delightfully, email addresses can be stored in certificates!

This provides a fantastically convenient way to identify the job and/or the owner of the job. Importantly, if you are supporting work loads from many people, this can be critical in telling who needs to know when something goes wrong with a job.

the private parts, embedded

On running make, by default the 'certs' directory will be embedded in client and server executables. This means that the binary executables contain sensitive private keys, and should be kept confidential and secure.

While this is convenient for deployment, just a single binary, it does make it difficult to rotate keys. Normally one would just compile a new binary with new keys, but if one wishes to rotate keys only, then there is also the ability to read keys from disk rather from the embedded filesystem.

The certificate authority private key is never included in the binary. However, the private keys for the client and server must be available in order to handshake, and so they are included. This means that anyone possessing the binary (just as anyone possessing the private key files deployed on disk) could use that private key to man-in-the-middle the communication. Mostly this is moot: if you don't trust the computer you are running the binary on, you equally cannot trust the private key sitting on the filesystem. Hence there is generally no loss of security from embedding the keys into the binary, as long as one protects the binary as would one's private keys; do not publish to the world the compiled binary with the embedded keys.

A lovely side effect of embedding is that the binary can be run from any directory. For example, submitting a job to a work-management queue will frequently need to be in the context of the work to be done; the current directory matters a great deal, and is unlikely to be nearby the original certs/ directory where the rpc client was built.

A localhost by any other name would not smell as sweet

The server name will always be 'localhost' on these certificates, and this is critical to allowing our processes to run on any machine. By doing so, we leverage the SNI technology[1], to break the troublesome adhesion of IP address and certificate. SNI was developed to let a single IP address host many different web sites. We want multiple IP addresses to run many different jobs, and to be able to migrate jobs between hosts. Brilliantly, SNI lets us move our clients and servers between machines without having to re-issue the certificates. The certs are always issued to 'localhost' and clients always request ServerName 'localhost' in their tls.Config.ServerName field.

This is much more like the convenience and usability ssh. To me ssh has always been so much easier to deal with than certificates. rpc25519 aims for usability on par with ssh. Leveraging SNI is one way we get there. Anther is by providing the bespoke tool, selfy, which is described in the next section. It takes alot of the pain out of creating certficates.

[1] https://en.wikipedia.org/wiki/Server_Name_Indication ,

The selfy tool: create new keys quickly; view certificates

The selfy command is an easy way to create private keys, certificates, and self-signed certficate authories. It is vastly more usable than mountain of complexity that is openssl, but more limited in scope. In our opinion, this a good thing.

If you are in a hurry to get started, the most basic use of selfy is to create a new ed25519 key-pair:

$ selfy -k name_of_your_identity -e your@email.here.org

With other tools you would need to already have a CA (Certificate Authority).

But we've got your back. If you lack a CA in the default directory (see -p below), then, for your convenience, a self-signed CA will be auto-generated for you. It will then be used to sign the new cert. Hence the above command is all that a typical developer needs to get started. Yay(!)

If you want to first create a CA manually, you can do that with the -ca flag. The -p flag will let you put it somewhere other than the default directory.

selfy -ca # make a new self-signed Certificate Authority

Your newly created cert can be viewed with the selfy -v flag, just give it the path to your .crt file.

$ selfy -v certs/name_of_your_identity.crt

By default, the CA is stored in the ./my-keep-private-dir/ca.{crt,key}, while the -k named identifying cert is stored in certs/name.crt. The corresponding private key is stored in certs/name.key.

Update: we have added pass-phrase protection to the private keys by default. In order to forgoe this protection and use the original behavior, supply the selfy --nopass flag.

$ selfy -h

Usage of selfy:
  -ca
    	create a new self-signed certificate authority (1st of 2 
		steps in making new certs). Written to the -p directory 
		(see selfy -p flag, where -p is for private).
		The CA uses ed25519 keys too.
		
  -e string
    	email to write into the certificate (who to contact 
		about this job) (strongly encouraged when making 
		new certs! defaults to name@host if not given)
		
  -k string
    	2nd of 2 steps: -k {key_name} ; create a new ed25519 
		key pair (key and cert), and save it to this name. 
		The pair will be saved under the -o directory; 
		we strongly suggest you also use the 
		-e your_email@actually.org flag to 
		describe the job and/or provide the owner's email 
		to contact when needed. A CA will be auto-generated 
		if none is found in the -p directory, which has 
		a default name which warns the user to protect it.

  -nopass
    	by default we request a password and use 
		it with Argon2id to encrypt private key files (CA & identity). 
		Setting -nopass means we generate an un-encrypted 
		private key; this is not recommended.

  -o string
    	directory to save newly created certs into. (default "certs")
		
  -p string
    	directory to find the CA in. If doing -ca, we will save 
		the newly created Certificate Authority (CA) private 
		key to this directory. If doing -k to create a new key, 
		we'll look for the CA here. (default "my-keep-private-dir")  
		
  -v string
    	path to cert to view. Similar output as the openssl 
		command: 'openssl x509 -in certs/client.crt  -text -noout', 
		which you could use instead; just replace certs/client.crt 
		with the path to your cert.

The openssl commands in the included gen.sh script do the same things as selfy does, but it is more painful to incorporate an email because you have to modify the openssl-san.cnf file to do so each time.


Author: Jason E. Aten, Ph.D.

License: MIT

Documentation

Index

Constants

View Source
const RFC3339MsecTz0 = "2006-01-02T15:04:05.000Z07:00"
View Source
const RFC3339NanoNumericTZ0pad = "2006-01-02T15:04:05.000000000-07:00"
View Source
const VersionByteBase59Checked byte = 255

we always use 255, which is -1 in 8-bit 2's compliment.

Variables

View Source
var Chicago *time.Location
View Source
var ErrDone = fmt.Errorf("done channel closed")
View Source
var ErrNetConnectionNotFound = fmt.Errorf("error: net.Conn not found")
View Source
var ErrNotFound = fmt.Errorf("known_tls_hosts file not found")
View Source
var ErrPubKeyMismath = fmt.Errorf("remote host pubkey does not match that on file!")
View Source
var ErrPubKeyUnknown = fmt.Errorf("remote host pubkey is not on file, and TOFU is off!")
View Source
var ErrShutdown = fmt.Errorf("shutting down")
View Source
var ForceQuiet = false

useful during git bisect

View Source
var Frankfurt *time.Location
View Source
var London *time.Location
View Source
var MyPid = os.Getpid()
View Source
var OurStdout io.Writer = os.Stdout

so we can multi write easily, use our own printf

View Source
var ShowPid bool
View Source
var Verbose bool = false

for tons of debug output

View Source
var VerboseVerbose bool = false

Functions

func AlwaysPrintf

func AlwaysPrintf(format string, a ...interface{})

func Caller

func Caller(upStack int) string

func DirExists

func DirExists(name string) bool

func FileExists

func FileExists(name string) bool

func FileLine

func FileLine(depth int) string

func FileSize

func FileSize(name string) (int64, error)

func GenAddress

func GenAddress() string

GenAddress generates a local address by calling GetAvailPort() and GetExternalIP(), then prefixing them with 'tcp://'.

func GetAvailPort

func GetAvailPort() int

GetAvailPort asks the OS for an unused port. There's a race here, where the port could be grabbed by someone else before the caller gets to Listen on it, but in practice such races are rare. Uses net.Listen("tcp", ":0") to determine a free port, then releases it back to the OS with Listener.Close().

func GetExternalIP

func GetExternalIP() string

GetExternalIP tries to determine the external IP address used on this host.

func GetExternalIPAsInt

func GetExternalIPAsInt() int

GetExternalIPAsInt calls GetExternalIP() and then converts the resulting IPv4 string into an integer.

func HostKeyVerifies

func HostKeyVerifies(
	knownKeysPath string,
	connState *tls.ConnectionState,
	remoteAddr string) (good, bad []string, wasNew bool, err0 error)

server will want stripPort true since client's port will change all the time. tofu true means we add any unknown cert to our knownKeysPath. We don't really care what the IP or hostname is, as long as we recognized a certified public key (in one of the identities), we accept. The IP or port could change, we don't care.

NB only ed25519 keys are permitted, any others will result in an immediate error and no further keys will be evaluated.

func IsLocalhost added in v1.0.12

func IsLocalhost(ipStr string) (isLocal bool, hostOnlyNoPort string)

func IsRoutableIPv4

func IsRoutableIPv4(ip string) bool

IsRoutableIPv4 returns true if the string in ip represents an IPv4 address that is not private. See http://en.wikipedia.org/wiki/Private_network#Private_IPv4_address_spaces for the numeric ranges that are private. 127.0.0.1, 192.168.0.1, and 172.16.0.1 are examples of non-routables IP addresses.

func IsWritable

func IsWritable(path string) bool

IsWritable returns true if the file does not exist. Otherwise it checks the write bits. If any write bits (owner, group, others) are set, then we return true. Otherwise false.

func LoadClientTLSConfig

func LoadClientTLSConfig(embedded bool, sslCA, sslCert, sslCertKey string) (*tls.Config, error)

LoadClientTLSConfig creates a client TLSConfig by loading the CA and client certs. The following paths must be passed: - sslCA: path to the CA certificate - sslCert: path to the client certificate - sslCertKey: path to the client key If the path is prefixed with "embedded=", load the embedded certs.

embedded true means load from the static embedded files, created at compile time. If embedded is false, then we read from the filesystem.

func LoadClientTLSConfigFilesystem

func LoadClientTLSConfigFilesystem(sslCA, sslCert, sslCertKey string) (*tls.Config, error)

func LoadServerTLSConfig

func LoadServerTLSConfig(embedded bool, sslCA, sslClientCA, sslCert, sslCertKey string) (*tls.Config, error)

LoadServerTLSConfig creates a server TLSConfig by loading the CA and server certs. The following paths must be passed:

  • sslCA: path to the CA certificate
  • sslClientCA: path to the CA certificate to verify client certificates, can be the same as sslCA
  • sslCert: path to the server certificate
  • sslCertKey: path to the server key

embedded true means load from the static embedded files, created at compile time. If embedded is false, then we read from the filesystem.

func LoadServerTLSConfigFilesystem

func LoadServerTLSConfigFilesystem(sslCA, sslClientCA, sslCert, sslCertKey string) (*tls.Config, error)

func LocalAddrMatching added in v1.0.7

func LocalAddrMatching(addr string) (local string, err error)

LocalAddrMatching finds a matching interface IP to a server destination address

addr should b "host:port" of server, we'll find the local IP to use.

func P

func P(format string, a ...interface{})

func PB

func PB(w io.Writer, format string, a ...interface{})

func PP

func PP(format string, a ...interface{})

func PPP

func PPP(format string, a ...interface{})

without the file/line, otherwise the same as PP

func Printf

func Printf(format string, a ...interface{}) (n int, err error)

Printf formats according to a format specifier and writes to standard output. It returns the number of bytes written and any write error encountered.

func QPrintf

func QPrintf(format string, a ...interface{}) (n int, err error)

func SelfyNewKey added in v1.0.3

func SelfyNewKey(createKeyPairNamed, odir string) error

odir/my-keep-private-dir and odir/certs will be created.

func StripNanomsgAddressPrefix

func StripNanomsgAddressPrefix(nanomsgAddr string) (suffix string, err error)

StripNanomsgAddressPrefix removes the 'tcp://' prefix from nanomsgAddr.

func TSPrintf

func TSPrintf(format string, a ...interface{})

time-stamped printf

func VPrintf

func VPrintf(format string, a ...interface{}) (n int, err error)

quieted for now, uncomment below to display

func VV

func VV(format string, a ...interface{})

func WaitUntilCanConnect

func WaitUntilCanConnect(addr string)

func WrapWithBrackets added in v1.0.7

func WrapWithBrackets(local string) string

if it needs [] ipv6 brackets, add them

Types

type Client

type Client struct {
	Conn uConnLR
	//	Conn     net.Conn // the default.
	QuicConn quic.Connection

	// if connecting suceeds, a nil will be sent; else the error.
	Connected chan error
	// contains filtered or unexported fields
}

Clients write requests, and maybe wait for responses.

func NewClient

func NewClient(name string, config *Config) (c *Client, err error)

func (*Client) Close

func (c *Client) Close() error

func (*Client) Err

func (c *Client) Err() error

func (*Client) GetOneRead

func (c *Client) GetOneRead(seqno uint64, ch chan *Message)

auto unregister after a single send on ch.

func (*Client) GetReadIncomingCh

func (c *Client) GetReadIncomingCh() (ch chan *Message)

func (*Client) GetReads

func (c *Client) GetReads(ch chan *Message)

register to get any received messages on ch.

func (*Client) LocalAddr

func (c *Client) LocalAddr() string

func (*Client) Name added in v1.0.39

func (c *Client) Name() string

func (*Client) OneWaySend

func (c *Client) OneWaySend(msg *Message, doneCh <-chan struct{}) (err error)

doneCh is optional, can be nil.

func (*Client) RunClientMain

func (c *Client) RunClientMain(serverAddr string, tcp_only bool, certPath string)

eg. serverAddr = "localhost:8443" serverAddr = "192.168.254.151:8443"

func (*Client) RunClientTCP

func (c *Client) RunClientTCP(serverAddr string)

func (*Client) RunQUIC added in v1.0.5

func (c *Client) RunQUIC(localHostPort, quicServerAddr string, tlsConfig *tls.Config)

func (*Client) RunReadLoop

func (c *Client) RunReadLoop(conn net.Conn)

func (*Client) RunSendLoop

func (c *Client) RunSendLoop(conn net.Conn)

func (*Client) SendAndGetReply

func (c *Client) SendAndGetReply(req *Message, doneCh <-chan struct{}) (reply *Message, err error)

doneCh is optional; can be nil.

func (*Client) SendAndGetReplyWithTimeout

func (c *Client) SendAndGetReplyWithTimeout(timeout time.Duration, req *Message) (reply *Message, err error)

func (*Client) SetLocalAddr added in v1.0.17

func (c *Client) SetLocalAddr(local string)

func (*Client) UngetReads

func (c *Client) UngetReads(ch chan *Message)

un-register to get any received messages on ch.

type Config

type Config struct {

	// ServerAddr host:port that the rpc25519.Server should bind to.
	ServerAddr string

	// optional. Can be used to force client to use a specific host:port;
	// say for port sharing with a server.
	ClientHostPort string

	// Who the client should contact
	ClientDialToHostPort string

	// TCP false means TLS-1.3 secured. true here means do TCP only.
	TCPonly_no_TLS bool

	// UseQUIC cannot be true if TCPonly_no_TLS is true.
	UseQUIC bool

	// path to certs/ like certificate
	// directory on the live filesystem. If left
	// empty then the embedded certs/ from build-time, those
	// copied from the on-disk certs/ directory and baked
	// into the executable as a virtual file system with
	// the go:embed directive are used.
	CertPath string

	// SkipVerifyKeys true allows any incoming
	// key to be signed by
	// any CA; it does not have to be ours. Obviously
	// this discards almost all access control; it
	// should rarely be used unless communication
	// with the any random agent/hacker/public person
	// is desired.
	SkipVerifyKeys bool

	ClientKeyPairName string // default "client" means use certs/client.crt and certs/client.key
	ServerKeyPairName string // default "node" means use certs/node.crt and certs/node.key

	// These are timeouts for connection and transport tuning.
	// The defaults of 0 mean wait forever.
	ConnectTimeout time.Duration
	ReadTimeout    time.Duration
	WriteTimeout   time.Duration

	LocalAddress string
	// contains filtered or unexported fields
}

Config says who to contact (for a client), or where to listen (for a server); and sets how strong a security posture we adopt.

Copying a Config is fine, but it should be a simple shallow copy to preserve the shared *SharedTransport struct.

This shared pointer is the basis of port (and file handle) reuse where a single process can maintain a server and multiple clients in a "star" pattern. This only works with QUIC of course, and is one of the main reasons to use QUIC. The shared pointer is reference counted and the underlying net.UDPConn is only closed when the last instance in use is Close()-ed.

func NewConfig added in v1.0.14

func NewConfig() *Config

type Known

type Known struct {
	Path  string
	Hosts []*KnownKey

	// "6v0qwCgyE6SrR7DYsuvbjih67HppzHBLPfSxnE" -> *KnownKey
	PubKeyMap map[string]*KnownKey
}

func NewKnown

func NewKnown(path string) *Known

func (*Known) Add

func (k *Known) Add(kh *KnownKey)

func (*Known) WriteOut

func (k *Known) WriteOut() (err error)

type KnownKey

type KnownKey struct {
	Addr    string // 192.168.254.151:8443
	KeyType string // ed25519
	PubKey  string // 9aTjVYv1K7vj3WYX3EktjaGPycNwym5Rn5Vo1WuxLdF7bxpMDV6
	Emails  string
	Line    int
}

KnownKey saved to a file results in lines like 127.0.0.1 pubkey@edwardsRPC-ed25519-b58c-9ZrrEXxvoqmj9UkgiPjHNZP41N9wuLyQTEUCg5S7VjPuJbXXL8a:froggy@example.com

func (*KnownKey) IdentityString

func (kh *KnownKey) IdentityString() string

e.g. "pubkey@edwardsRPC-ed25519-b58c-9ZrrEXxvoqmj9UkgiPjHNZP41N9wuLyQTEUCg5S7VjPuJbXXL8a:froggy@example.com"

func (*KnownKey) String

func (kh *KnownKey) String() string

type MID

type MID struct {
	Created string `zid:"0"`
	From    string `zid:"1"`
	To      string `zid:"2"`
	Subject string `zid:"3"`
	IsRPC   bool   `zid:"4"`
	IsLeg2  bool   `zid:"5"`
	Serial  int64  `zid:"6"`
	CallID  string `zid:"7"` // able to match call and response on this alone.
	PID     int64  `zid:"8"`
	Seqno   uint64 `zid:"9"`
}

The Multiverse Identitifer: for when there are multiple universes and so a UUID just won't do.

func MIDFromBytes

func MIDFromBytes(jsonData []byte) (*MID, error)

func MIDFromGreenpack

func MIDFromGreenpack(header []byte) (*MID, error)

workspace can be nil or reused to avoid allocation.

func MIDFromOpaqueURLFriendly

func MIDFromOpaqueURLFriendly(s string) (*MID, error)

func NewMID

func NewMID(from, to, subject string, isRPC bool, isLeg2 bool) (m *MID)

func Unbytes

func Unbytes(jsonData []byte) *MID

Unbytes reverses Bytes.

func (*MID) AsGreenpack

func (mid *MID) AsGreenpack(scratch []byte) (o []byte, err error)

the scrach workspace can be nil or reused to avoid allocation.

func (*MID) Bytes

func (m *MID) Bytes() []byte

Bytes serializes to compact JSON formatted bytes.

func (*MID) Compact

func (m *MID) Compact() string

Compact is all on one line.

func (*MID) DecodeMsg

func (z *MID) DecodeMsg(dc *msgp.Reader) (err error)

DecodeMsg implements msgp.Decodable We treat empty fields as if we read a Nil from the wire.

func (*MID) EncodeMsg

func (z *MID) EncodeMsg(en *msgp.Writer) (err error)

EncodeMsg implements msgp.Encodable

func (*MID) Equal

func (a *MID) Equal(b *MID) bool

Equal compares two *MID structs field by field for structural equality

func (*MID) JSON

func (m *MID) JSON() []byte

JSON serializes to JSON.

func (*MID) MarshalMsg

func (z *MID) MarshalMsg(b []byte) (o []byte, err error)

MarshalMsg implements msgp.Marshaler

func (*MID) Msgsize

func (z *MID) Msgsize() (s int)

Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message

func (*MID) OpaqueURLFriendly

func (m *MID) OpaqueURLFriendly() string

func (*MID) Pretty

func (m *MID) Pretty() string

Pretty shows in pretty-printed JSON format.

func (*MID) String

func (m *MID) String() string

func (*MID) UnmarshalMsg

func (z *MID) UnmarshalMsg(bts []byte) (o []byte, err error)

UnmarshalMsg implements msgp.Unmarshaler

func (*MID) UnmarshalMsgWithCfg

func (z *MID) UnmarshalMsgWithCfg(bts []byte, cfg *msgp.RuntimeConfig) (o []byte, err error)

type Message

type Message struct {
	Nc    net.Conn `msg:"-"`
	Seqno uint64   `zid:"0"`

	Subject string `zid:"1"`
	MID     MID    `zid:"2"`

	JobSerz []byte `zid:"3"`

	JobErrs string `zid:"4"`

	// Err is not serialized on the wire by the server,
	// so communicates only local information. Callback
	// functions should convey errors in-band within
	// JobSerz.
	Err error `msg:"-"`

	DoneCh chan *Message `msg:"-"`
}

Message basic substrate.

func MessageFromGreenpack added in v1.0.28

func MessageFromGreenpack(by []byte) (*Message, error)

func NewMessage

func NewMessage() *Message

func NewMessageFromBytes

func NewMessageFromBytes(by []byte) (msg *Message)

func (*Message) AsGreenpack added in v1.0.29

func (m *Message) AsGreenpack(scratch []byte) (o []byte, err error)

the scrach workspace can be nil or reused to avoid allocation.

func (*Message) DecodeMsg added in v1.0.26

func (z *Message) DecodeMsg(dc *msgp.Reader) (err error)

DecodeMsg implements msgp.Decodable We treat empty fields as if we read a Nil from the wire.

func (*Message) EncodeMsg added in v1.0.26

func (z *Message) EncodeMsg(en *msgp.Writer) (err error)

EncodeMsg implements msgp.Encodable

func (*Message) MarshalMsg added in v1.0.26

func (z *Message) MarshalMsg(b []byte) (o []byte, err error)

MarshalMsg implements msgp.Marshaler

func (*Message) Msgsize added in v1.0.26

func (z *Message) Msgsize() (s int)

Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message

func (*Message) String

func (msg *Message) String() string

func (*Message) UnmarshalMsg added in v1.0.26

func (z *Message) UnmarshalMsg(bts []byte) (o []byte, err error)

UnmarshalMsg implements msgp.Unmarshaler

func (*Message) UnmarshalMsgWithCfg added in v1.0.26

func (z *Message) UnmarshalMsgWithCfg(bts []byte, cfg *msgp.RuntimeConfig) (o []byte, err error)

type NetConnWrapper added in v1.0.5

type NetConnWrapper struct {
	quic.Stream
	quic.Connection
}

type OneWayFunc added in v1.0.39

type OneWayFunc func(req *Message)

OneWayFunc is the simpler sibling to the above. A OneWayFunc will not return anything to the sender.

As above req.JobSerz [] byte contains the job payload.

type QUIC_RWPair added in v1.0.5

type QUIC_RWPair struct {
	RWPair
	Stream quic.Stream
}

type RWPair

type RWPair struct {
	// our parent Server
	Server *Server

	Conn   net.Conn
	SendCh chan *Message
	// contains filtered or unexported fields
}

keep the pair of goroutines running the read loop and the write loop for a given connection together so we can figure out who to SendCh to and how to halt each other.

type Server

type Server struct {

	// remote when server gets a new client,
	// So test 004 can avoid a race/panic.
	RemoteConnectedCh chan string
	// contains filtered or unexported fields
}

Servers read and respond to requests. Server.Register() says which callback to call. Only one call back func is supported at the moment.

func NewServer

func NewServer(name string, config *Config) *Server

NewServer will keep its own copy of config. If config is nil, the server will make its own upon Start().

func (*Server) Close

func (s *Server) Close() error

func (*Server) NewQUIC_RWPair added in v1.0.5

func (s *Server) NewQUIC_RWPair(stream quic.Stream, conn quic.Connection) *QUIC_RWPair

func (*Server) NewRWPair

func (s *Server) NewRWPair(conn net.Conn) *RWPair

func (*Server) Register1Func added in v1.0.39

func (s *Server) Register1Func(callme1 OneWayFunc)

func (*Server) Register2Func added in v1.0.39

func (s *Server) Register2Func(callme2 TwoWayFunc)

func (*Server) RunQUICServer added in v1.0.5

func (s *Server) RunQUICServer(quicServerAddr string, tlsConfig *tls.Config, boundCh chan net.Addr)

func (*Server) RunServerMain

func (s *Server) RunServerMain(serverAddress string, tcp_only bool, certPath string, boundCh chan net.Addr)

boundCh should be buffered, at least 1, if it is not nil. If not nil, we will send the bound net.Addr back on it after we have started listening.

func (*Server) RunTCP

func (s *Server) RunTCP(serverAddress string, boundCh chan net.Addr)

func (*Server) SendMessage

func (s *Server) SendMessage(callID, subject, destAddr string, by []byte, seqno uint64) error

func (*Server) Start

func (s *Server) Start() (serverAddr net.Addr, err error)

type SharedTransport added in v1.0.14

type SharedTransport struct {
	// contains filtered or unexported fields
}

type TwoWayFunc added in v1.0.39

type TwoWayFunc func(req *Message, reply *Message) error

TwoWayFunc is the user's own function that they register with the server for remote procedure calls.

The user's Func may not want to return anything. In that case they should register a OneWayFunc instead.

req.JobSerz []byte contains the job payload.

Implementers of TwoWayFunc should assign their return []byte to reply.JobSerz. reply.Jobserz can also be left nil, of course.

Any errors can be returned on reply.JobErrs; this is optional. Not that JobErrs is a string value.

The system will overwrite the reply.MID field when sending the reply, so the user should not bother trying to alter it.

Directories

Path Synopsis
cmd
cli
srv

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL