| 
									
										
										
										
											2020-02-05 14:16:58 -08:00
										 |  |  | // Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved. | 
					
						
							|  |  |  | // Use of this source code is governed by a BSD-style | 
					
						
							|  |  |  | // license that can be found in the LICENSE file. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | package ipn | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2020-11-06 13:12:52 -08:00
										 |  |  | 	"bytes" | 
					
						
							| 
									
										
										
										
											2021-01-15 08:43:23 -08:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2020-02-05 14:16:58 -08:00
										 |  |  | 	"encoding/binary" | 
					
						
							|  |  |  | 	"encoding/json" | 
					
						
							|  |  |  | 	"errors" | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"io" | 
					
						
							|  |  |  | 	"log" | 
					
						
							|  |  |  | 	"time" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-13 22:13:11 +02:00
										 |  |  | 	"golang.org/x/oauth2" | 
					
						
							| 
									
										
										
										
											2020-02-14 19:23:16 -08:00
										 |  |  | 	"tailscale.com/types/logger" | 
					
						
							| 
									
										
										
										
											2020-05-03 13:58:39 -07:00
										 |  |  | 	"tailscale.com/types/structs" | 
					
						
							| 
									
										
										
										
											2020-02-05 14:16:58 -08:00
										 |  |  | 	"tailscale.com/version" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-15 08:43:23 -08:00
										 |  |  | type readOnlyContextKey struct{} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // IsReadonlyContext reports whether ctx is a read-only context, as currently used | 
					
						
							|  |  |  | // by Unix non-root users running the "tailscale" CLI command. They can run "status", | 
					
						
							|  |  |  | // but not much else. | 
					
						
							|  |  |  | func IsReadonlyContext(ctx context.Context) bool { | 
					
						
							|  |  |  | 	return ctx.Value(readOnlyContextKey{}) != nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ReadonlyContextOf returns ctx wrapped with a context value that | 
					
						
							|  |  |  | // will make IsReadonlyContext reports true. | 
					
						
							|  |  |  | func ReadonlyContextOf(ctx context.Context) context.Context { | 
					
						
							|  |  |  | 	if IsReadonlyContext(ctx) { | 
					
						
							|  |  |  | 		return ctx | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return context.WithValue(ctx, readOnlyContextKey{}, readOnlyContextKey{}) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-06 13:12:52 -08:00
										 |  |  | var jsonEscapedZero = []byte(`\u0000`) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-05 14:16:58 -08:00
										 |  |  | type NoArgs struct{} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type StartArgs struct { | 
					
						
							|  |  |  | 	Opts Options | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type SetPrefsArgs struct { | 
					
						
							| 
									
										
										
										
											2020-02-20 11:07:00 -08:00
										 |  |  | 	New *Prefs | 
					
						
							| 
									
										
										
										
											2020-02-05 14:16:58 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type FakeExpireAfterArgs struct { | 
					
						
							|  |  |  | 	Duration time.Duration | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-09 14:49:42 -07:00
										 |  |  | type PingArgs struct { | 
					
						
							|  |  |  | 	IP string | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-15 18:14:50 -08:00
										 |  |  | // Command is a command message that is JSON encoded and sent by a | 
					
						
							|  |  |  | // frontend to a backend. | 
					
						
							| 
									
										
										
										
											2020-02-05 14:16:58 -08:00
										 |  |  | type Command struct { | 
					
						
							| 
									
										
										
										
											2020-05-09 13:50:13 -07:00
										 |  |  | 	_ structs.Incomparable | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Version is the binary version of the frontend (the client). | 
					
						
							| 
									
										
										
										
											2020-02-15 18:14:50 -08:00
										 |  |  | 	Version string | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-09 13:50:13 -07:00
										 |  |  | 	// AllowVersionSkew controls whether it's permitted for the | 
					
						
							|  |  |  | 	// client and server to have a different version. The default | 
					
						
							|  |  |  | 	// (false) means to be strict. | 
					
						
							|  |  |  | 	AllowVersionSkew bool | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-15 18:14:50 -08:00
										 |  |  | 	// Exactly one of the following must be non-nil. | 
					
						
							| 
									
										
										
										
											2020-02-05 14:16:58 -08:00
										 |  |  | 	Quit                  *NoArgs | 
					
						
							|  |  |  | 	Start                 *StartArgs | 
					
						
							|  |  |  | 	StartLoginInteractive *NoArgs | 
					
						
							| 
									
										
										
										
											2020-07-13 22:13:11 +02:00
										 |  |  | 	Login                 *oauth2.Token | 
					
						
							| 
									
										
										
										
											2020-02-05 14:16:58 -08:00
										 |  |  | 	Logout                *NoArgs | 
					
						
							|  |  |  | 	SetPrefs              *SetPrefsArgs | 
					
						
							| 
									
										
										
										
											2020-08-10 19:42:04 -07:00
										 |  |  | 	SetWantRunning        *bool | 
					
						
							| 
									
										
										
										
											2020-02-05 14:16:58 -08:00
										 |  |  | 	RequestEngineStatus   *NoArgs | 
					
						
							| 
									
										
										
										
											2020-03-27 13:26:35 -07:00
										 |  |  | 	RequestStatus         *NoArgs | 
					
						
							| 
									
										
										
										
											2020-02-05 14:16:58 -08:00
										 |  |  | 	FakeExpireAfter       *FakeExpireAfterArgs | 
					
						
							| 
									
										
										
										
											2020-08-09 14:49:42 -07:00
										 |  |  | 	Ping                  *PingArgs | 
					
						
							| 
									
										
										
										
											2020-02-05 14:16:58 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type BackendServer struct { | 
					
						
							|  |  |  | 	logf          logger.Logf | 
					
						
							| 
									
										
										
										
											2020-02-25 07:36:32 -08:00
										 |  |  | 	b             Backend              // the Backend we are serving up | 
					
						
							|  |  |  | 	sendNotifyMsg func(jsonMsg []byte) // send a notification message | 
					
						
							|  |  |  | 	GotQuit       bool                 // a Quit command was received | 
					
						
							| 
									
										
										
										
											2020-02-05 14:16:58 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func NewBackendServer(logf logger.Logf, b Backend, sendNotifyMsg func(b []byte)) *BackendServer { | 
					
						
							|  |  |  | 	return &BackendServer{ | 
					
						
							|  |  |  | 		logf:          logf, | 
					
						
							|  |  |  | 		b:             b, | 
					
						
							|  |  |  | 		sendNotifyMsg: sendNotifyMsg, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (bs *BackendServer) send(n Notify) { | 
					
						
							| 
									
										
										
										
											2020-10-27 04:23:58 +00:00
										 |  |  | 	n.Version = version.Long | 
					
						
							| 
									
										
										
										
											2020-02-05 14:16:58 -08:00
										 |  |  | 	b, err := json.Marshal(n) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2020-04-11 08:35:34 -07:00
										 |  |  | 		log.Fatalf("Failed json.Marshal(notify): %v\n%#v", err, n) | 
					
						
							| 
									
										
										
										
											2020-02-05 14:16:58 -08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-11-06 13:12:52 -08:00
										 |  |  | 	if bytes.Contains(b, jsonEscapedZero) { | 
					
						
							|  |  |  | 		log.Printf("[unexpected] zero byte in BackendServer.send notify message: %q", b) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-02-05 14:16:58 -08:00
										 |  |  | 	bs.sendNotifyMsg(b) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-08 14:15:33 -07:00
										 |  |  | func (bs *BackendServer) SendErrorMessage(msg string) { | 
					
						
							|  |  |  | 	bs.send(Notify{ErrMessage: &msg}) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-02 09:52:59 -08:00
										 |  |  | // SendInUseOtherUserErrorMessage sends a Notify message to the client that | 
					
						
							|  |  |  | // both sets the state to 'InUseOtherUser' and sets the associated reason | 
					
						
							|  |  |  | // to msg. | 
					
						
							|  |  |  | func (bs *BackendServer) SendInUseOtherUserErrorMessage(msg string) { | 
					
						
							|  |  |  | 	inUse := InUseOtherUser | 
					
						
							|  |  |  | 	bs.send(Notify{ | 
					
						
							|  |  |  | 		State:      &inUse, | 
					
						
							|  |  |  | 		ErrMessage: &msg, | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-25 07:36:32 -08:00
										 |  |  | // GotCommandMsg parses the incoming message b as a JSON Command and | 
					
						
							|  |  |  | // calls GotCommand with it. | 
					
						
							| 
									
										
										
										
											2021-01-15 08:43:23 -08:00
										 |  |  | func (bs *BackendServer) GotCommandMsg(ctx context.Context, b []byte) error { | 
					
						
							| 
									
										
										
										
											2020-02-25 07:36:32 -08:00
										 |  |  | 	cmd := &Command{} | 
					
						
							| 
									
										
										
										
											2020-04-29 04:22:43 -04:00
										 |  |  | 	if len(b) == 0 { | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-02-25 07:36:32 -08:00
										 |  |  | 	if err := json.Unmarshal(b, cmd); err != nil { | 
					
						
							| 
									
										
										
										
											2020-02-05 14:16:58 -08:00
										 |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-01-15 08:43:23 -08:00
										 |  |  | 	return bs.GotCommand(ctx, cmd) | 
					
						
							| 
									
										
										
										
											2020-02-05 14:16:58 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-15 08:43:23 -08:00
										 |  |  | func (bs *BackendServer) GotFakeCommand(ctx context.Context, cmd *Command) error { | 
					
						
							| 
									
										
										
										
											2020-10-27 04:23:58 +00:00
										 |  |  | 	cmd.Version = version.Long | 
					
						
							| 
									
										
										
										
											2021-01-15 08:43:23 -08:00
										 |  |  | 	return bs.GotCommand(ctx, cmd) | 
					
						
							| 
									
										
										
										
											2020-04-29 04:22:43 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-01 13:52:01 -08:00
										 |  |  | // ErrMsgPermissionDenied is the Notify.ErrMessage value used an | 
					
						
							|  |  |  | // operation was done from a user/context that didn't have permission. | 
					
						
							|  |  |  | const ErrMsgPermissionDenied = "permission denied" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-15 08:43:23 -08:00
										 |  |  | func (bs *BackendServer) GotCommand(ctx context.Context, cmd *Command) error { | 
					
						
							| 
									
										
										
										
											2020-10-27 04:23:58 +00:00
										 |  |  | 	if cmd.Version != version.Long && !cmd.AllowVersionSkew { | 
					
						
							| 
									
										
										
										
											2020-04-29 04:22:43 -04:00
										 |  |  | 		vs := fmt.Sprintf("GotCommand: Version mismatch! frontend=%#v backend=%#v", | 
					
						
							| 
									
										
										
										
											2020-10-27 04:23:58 +00:00
										 |  |  | 			cmd.Version, version.Long) | 
					
						
							| 
									
										
										
										
											2020-04-11 08:35:34 -07:00
										 |  |  | 		bs.logf("%s", vs) | 
					
						
							| 
									
										
										
										
											2020-02-05 14:16:58 -08:00
										 |  |  | 		// ignore the command, but send a message back to the | 
					
						
							|  |  |  | 		// caller so it can realize the version mismatch too. | 
					
						
							|  |  |  | 		// We don't want to exit because it might cause a crash | 
					
						
							|  |  |  | 		// loop, and restarting won't fix the problem. | 
					
						
							|  |  |  | 		bs.send(Notify{ | 
					
						
							|  |  |  | 			ErrMessage: &vs, | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-01-15 08:43:23 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// TODO(bradfitz): finish plumbing context down to all the methods below; | 
					
						
							|  |  |  | 	// currently we just check for read-only contexts in this method and | 
					
						
							|  |  |  | 	// then never use contexts again. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Actions permitted with a read-only context: | 
					
						
							|  |  |  | 	if c := cmd.RequestEngineStatus; c != nil { | 
					
						
							|  |  |  | 		bs.b.RequestEngineStatus() | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} else if c := cmd.RequestStatus; c != nil { | 
					
						
							|  |  |  | 		bs.b.RequestStatus() | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} else if c := cmd.Ping; c != nil { | 
					
						
							|  |  |  | 		bs.b.Ping(c.IP) | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if IsReadonlyContext(ctx) { | 
					
						
							| 
									
										
										
										
											2021-02-01 13:52:01 -08:00
										 |  |  | 		msg := ErrMsgPermissionDenied | 
					
						
							| 
									
										
										
										
											2021-01-15 08:43:23 -08:00
										 |  |  | 		bs.send(Notify{ErrMessage: &msg}) | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-05 14:16:58 -08:00
										 |  |  | 	if cmd.Quit != nil { | 
					
						
							|  |  |  | 		bs.GotQuit = true | 
					
						
							|  |  |  | 		return errors.New("Quit command received") | 
					
						
							| 
									
										
										
										
											2021-01-15 08:43:23 -08:00
										 |  |  | 	} else if c := cmd.Start; c != nil { | 
					
						
							| 
									
										
										
										
											2020-02-05 14:16:58 -08:00
										 |  |  | 		opts := c.Opts | 
					
						
							|  |  |  | 		opts.Notify = bs.send | 
					
						
							|  |  |  | 		return bs.b.Start(opts) | 
					
						
							|  |  |  | 	} else if c := cmd.StartLoginInteractive; c != nil { | 
					
						
							|  |  |  | 		bs.b.StartLoginInteractive() | 
					
						
							|  |  |  | 		return nil | 
					
						
							| 
									
										
										
										
											2020-07-13 22:13:11 +02:00
										 |  |  | 	} else if c := cmd.Login; c != nil { | 
					
						
							|  |  |  | 		bs.b.Login(c) | 
					
						
							|  |  |  | 		return nil | 
					
						
							| 
									
										
										
										
											2020-02-05 14:16:58 -08:00
										 |  |  | 	} else if c := cmd.Logout; c != nil { | 
					
						
							|  |  |  | 		bs.b.Logout() | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} else if c := cmd.SetPrefs; c != nil { | 
					
						
							|  |  |  | 		bs.b.SetPrefs(c.New) | 
					
						
							|  |  |  | 		return nil | 
					
						
							| 
									
										
										
										
											2020-08-10 19:42:04 -07:00
										 |  |  | 	} else if c := cmd.SetWantRunning; c != nil { | 
					
						
							|  |  |  | 		bs.b.SetWantRunning(*c) | 
					
						
							|  |  |  | 		return nil | 
					
						
							| 
									
										
										
										
											2020-02-05 14:16:58 -08:00
										 |  |  | 	} else if c := cmd.FakeExpireAfter; c != nil { | 
					
						
							|  |  |  | 		bs.b.FakeExpireAfter(c.Duration) | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-01-15 08:43:23 -08:00
										 |  |  | 	return fmt.Errorf("BackendServer.Do: no command specified") | 
					
						
							| 
									
										
										
										
											2020-02-05 14:16:58 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-15 08:43:23 -08:00
										 |  |  | func (bs *BackendServer) Reset(ctx context.Context) error { | 
					
						
							| 
									
										
										
										
											2020-02-05 14:16:58 -08:00
										 |  |  | 	// Tell the backend we got a Logout command, which will cause it | 
					
						
							|  |  |  | 	// to forget all its authentication information. | 
					
						
							| 
									
										
										
										
											2021-01-15 08:43:23 -08:00
										 |  |  | 	return bs.GotFakeCommand(ctx, &Command{Logout: &NoArgs{}}) | 
					
						
							| 
									
										
										
										
											2020-02-05 14:16:58 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type BackendClient struct { | 
					
						
							|  |  |  | 	logf           logger.Logf | 
					
						
							| 
									
										
										
										
											2020-02-25 07:36:32 -08:00
										 |  |  | 	sendCommandMsg func(jsonb []byte) | 
					
						
							|  |  |  | 	notify         func(Notify) | 
					
						
							| 
									
										
										
										
											2020-05-09 13:50:13 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// AllowVersionSkew controls whether to allow mismatched | 
					
						
							|  |  |  | 	// frontend & backend versions. | 
					
						
							|  |  |  | 	AllowVersionSkew bool | 
					
						
							| 
									
										
										
										
											2020-02-05 14:16:58 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-25 07:36:32 -08:00
										 |  |  | func NewBackendClient(logf logger.Logf, sendCommandMsg func(jsonb []byte)) *BackendClient { | 
					
						
							| 
									
										
										
										
											2020-02-05 14:16:58 -08:00
										 |  |  | 	return &BackendClient{ | 
					
						
							|  |  |  | 		logf:           logf, | 
					
						
							|  |  |  | 		sendCommandMsg: sendCommandMsg, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (bc *BackendClient) GotNotifyMsg(b []byte) { | 
					
						
							| 
									
										
										
										
											2020-04-29 04:22:43 -04:00
										 |  |  | 	if len(b) == 0 { | 
					
						
							|  |  |  | 		// not interesting | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-11-06 13:12:52 -08:00
										 |  |  | 	if bytes.Contains(b, jsonEscapedZero) { | 
					
						
							|  |  |  | 		log.Printf("[unexpected] zero byte in BackendClient.GotNotifyMsg message: %q", b) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-02-05 14:16:58 -08:00
										 |  |  | 	n := Notify{} | 
					
						
							|  |  |  | 	if err := json.Unmarshal(b, &n); err != nil { | 
					
						
							| 
									
										
										
										
											2020-04-29 04:22:43 -04:00
										 |  |  | 		log.Fatalf("BackendClient.Notify: cannot decode message (length=%d)\n%#v", len(b), string(b)) | 
					
						
							| 
									
										
										
										
											2020-02-05 14:16:58 -08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-10-27 04:23:58 +00:00
										 |  |  | 	if n.Version != version.Long && !bc.AllowVersionSkew { | 
					
						
							| 
									
										
										
										
											2020-04-29 04:22:43 -04:00
										 |  |  | 		vs := fmt.Sprintf("GotNotify: Version mismatch! frontend=%#v backend=%#v", | 
					
						
							| 
									
										
										
										
											2020-10-27 04:23:58 +00:00
										 |  |  | 			version.Long, n.Version) | 
					
						
							| 
									
										
										
										
											2020-04-11 08:35:34 -07:00
										 |  |  | 		bc.logf("%s", vs) | 
					
						
							| 
									
										
										
										
											2020-02-05 14:16:58 -08:00
										 |  |  | 		// delete anything in the notification except the version, | 
					
						
							|  |  |  | 		// to prevent incorrect operation. | 
					
						
							|  |  |  | 		n = Notify{ | 
					
						
							|  |  |  | 			Version:    n.Version, | 
					
						
							|  |  |  | 			ErrMessage: &vs, | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if bc.notify != nil { | 
					
						
							|  |  |  | 		bc.notify(n) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (bc *BackendClient) send(cmd Command) { | 
					
						
							| 
									
										
										
										
											2020-10-27 04:23:58 +00:00
										 |  |  | 	cmd.Version = version.Long | 
					
						
							| 
									
										
										
										
											2020-02-05 14:16:58 -08:00
										 |  |  | 	b, err := json.Marshal(cmd) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		log.Fatalf("Failed json.Marshal(cmd): %v\n%#v\n", err, cmd) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-11-06 13:12:52 -08:00
										 |  |  | 	if bytes.Contains(b, jsonEscapedZero) { | 
					
						
							|  |  |  | 		log.Printf("[unexpected] zero byte in BackendClient.send command: %q", b) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-02-05 14:16:58 -08:00
										 |  |  | 	bc.sendCommandMsg(b) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-27 13:26:35 -07:00
										 |  |  | func (bc *BackendClient) SetNotifyCallback(fn func(Notify)) { | 
					
						
							|  |  |  | 	bc.notify = fn | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-05 14:16:58 -08:00
										 |  |  | func (bc *BackendClient) Quit() error { | 
					
						
							|  |  |  | 	bc.send(Command{Quit: &NoArgs{}}) | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (bc *BackendClient) Start(opts Options) error { | 
					
						
							|  |  |  | 	bc.notify = opts.Notify | 
					
						
							|  |  |  | 	opts.Notify = nil // server can't call our function pointer | 
					
						
							|  |  |  | 	bc.send(Command{Start: &StartArgs{Opts: opts}}) | 
					
						
							|  |  |  | 	return nil // remote Start() errors must be handled remotely | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (bc *BackendClient) StartLoginInteractive() { | 
					
						
							|  |  |  | 	bc.send(Command{StartLoginInteractive: &NoArgs{}}) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-13 22:13:11 +02:00
										 |  |  | func (bc *BackendClient) Login(token *oauth2.Token) { | 
					
						
							|  |  |  | 	bc.send(Command{Login: token}) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-05 14:16:58 -08:00
										 |  |  | func (bc *BackendClient) Logout() { | 
					
						
							|  |  |  | 	bc.send(Command{Logout: &NoArgs{}}) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-20 11:07:00 -08:00
										 |  |  | func (bc *BackendClient) SetPrefs(new *Prefs) { | 
					
						
							| 
									
										
										
										
											2020-02-05 14:16:58 -08:00
										 |  |  | 	bc.send(Command{SetPrefs: &SetPrefsArgs{New: new}}) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (bc *BackendClient) RequestEngineStatus() { | 
					
						
							|  |  |  | 	bc.send(Command{RequestEngineStatus: &NoArgs{}}) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-27 13:26:35 -07:00
										 |  |  | func (bc *BackendClient) RequestStatus() { | 
					
						
							| 
									
										
										
										
											2020-05-09 13:50:13 -07:00
										 |  |  | 	bc.send(Command{AllowVersionSkew: true, RequestStatus: &NoArgs{}}) | 
					
						
							| 
									
										
										
										
											2020-03-27 13:26:35 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-05 14:16:58 -08:00
										 |  |  | func (bc *BackendClient) FakeExpireAfter(x time.Duration) { | 
					
						
							|  |  |  | 	bc.send(Command{FakeExpireAfter: &FakeExpireAfterArgs{Duration: x}}) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-09 14:49:42 -07:00
										 |  |  | func (bc *BackendClient) Ping(ip string) { | 
					
						
							|  |  |  | 	bc.send(Command{Ping: &PingArgs{IP: ip}}) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-10 19:42:04 -07:00
										 |  |  | func (bc *BackendClient) SetWantRunning(v bool) { | 
					
						
							|  |  |  | 	bc.send(Command{SetWantRunning: &v}) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-25 07:36:32 -08:00
										 |  |  | // MaxMessageSize is the maximum message size, in bytes. | 
					
						
							| 
									
										
										
										
											2020-08-06 15:36:10 -07:00
										 |  |  | const MaxMessageSize = 10 << 20 | 
					
						
							| 
									
										
										
										
											2020-02-05 14:16:58 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // TODO(apenwarr): incremental json decode? | 
					
						
							|  |  |  | //  That would let us avoid storing the whole byte array uselessly in RAM. | 
					
						
							|  |  |  | func ReadMsg(r io.Reader) ([]byte, error) { | 
					
						
							|  |  |  | 	cb := make([]byte, 4) | 
					
						
							|  |  |  | 	_, err := io.ReadFull(r, cb) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	n := binary.LittleEndian.Uint32(cb) | 
					
						
							| 
									
										
										
										
											2020-02-25 07:36:32 -08:00
										 |  |  | 	if n > MaxMessageSize { | 
					
						
							| 
									
										
										
										
											2020-02-05 14:16:58 -08:00
										 |  |  | 		return nil, fmt.Errorf("ipn.Read: message too large: %v bytes", n) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	b := make([]byte, n) | 
					
						
							| 
									
										
										
										
											2020-04-29 04:22:43 -04:00
										 |  |  | 	nn, err := io.ReadFull(r, b) | 
					
						
							| 
									
										
										
										
											2020-02-05 14:16:58 -08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-04-29 04:22:43 -04:00
										 |  |  | 	if nn != int(n) { | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("ipn.Read: expected %v bytes, got %v", n, nn) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-02-05 14:16:58 -08:00
										 |  |  | 	return b, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // TODO(apenwarr): incremental json encode? | 
					
						
							|  |  |  | //  That would save RAM, at the expense of having to encode once so that | 
					
						
							|  |  |  | //  we can produce the initial byte count. | 
					
						
							|  |  |  | func WriteMsg(w io.Writer, b []byte) error { | 
					
						
							| 
									
										
										
										
											2020-02-25 07:36:32 -08:00
										 |  |  | 	// TODO(bradfitz): this does two writes to w, which likely | 
					
						
							|  |  |  | 	// does two writes on the wire, two frame generations, etc. We | 
					
						
							|  |  |  | 	// should take a concrete buffered type, or use a sync.Pool to | 
					
						
							|  |  |  | 	// allocate a buf and do one write. | 
					
						
							| 
									
										
										
										
											2020-02-05 14:16:58 -08:00
										 |  |  | 	cb := make([]byte, 4) | 
					
						
							| 
									
										
										
										
											2020-02-25 07:36:32 -08:00
										 |  |  | 	if len(b) > MaxMessageSize { | 
					
						
							| 
									
										
										
										
											2020-02-05 14:16:58 -08:00
										 |  |  | 		return fmt.Errorf("ipn.Write: message too large: %v bytes", len(b)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	binary.LittleEndian.PutUint32(cb, uint32(len(b))) | 
					
						
							|  |  |  | 	n, err := w.Write(cb) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if n != 4 { | 
					
						
							|  |  |  | 		return fmt.Errorf("ipn.Write: short write: %v bytes (wanted 4)", n) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	n, err = w.Write(b) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if n != len(b) { | 
					
						
							|  |  |  | 		return fmt.Errorf("ipn.Write: short write: %v bytes (wanted %v)", n, len(b)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } |