package protocol // Message types const ( MsgRegister = "register" MsgDeviceList = "device_list" MsgRequestDevice = "request_device" MsgDeviceGranted = "device_granted" MsgDeviceDenied = "device_denied" MsgReleaseDevice = "release_device" MsgDeviceReleased = "device_released" MsgClientJoined = "client_joined" MsgClientLeft = "client_left" MsgForceRelease = "force_release" MsgPing = "ping" MsgPong = "pong" MsgError = "error" ) // Client modes. // // ModeBoth lets a single client offer its own devices and consume other // clients' devices at the same time, which is the normal case for a peer // group where every machine both lends and borrows hardware. const ( ModeShare = "share" ModeUse = "use" ModeBoth = "both" ) // ValidMode reports whether mode is one this build understands. func ValidMode(mode string) bool { switch mode { case ModeShare, ModeUse, ModeBoth: return true } return false } // CanShare reports whether a client in this mode offers devices to others. func CanShare(mode string) bool { return mode == ModeShare || mode == ModeBoth } // CanUse reports whether a client in this mode consumes devices from others. func CanUse(mode string) bool { return mode == ModeUse || mode == ModeBoth } // Device status const ( StatusAvailable = "available" StatusInUse = "in_use" ) // Envelope is the top-level message wrapper type Envelope struct { Type string `json:"type"` } // Register is sent by a client when connecting to the relay type Register struct { Type string `json:"type"` Hash string `json:"hash"` Mode string `json:"mode"` ClientID string `json:"client_id"` Name string `json:"name"` // DirectPort is the TCP port this client listens on for direct tunnel // connections, or 0 if it accepts none. Peers use it to skip the relay. DirectPort int `json:"direct_port,omitempty"` // LocalEndpoints are host:port addresses on this client's own interfaces. // They let two machines on the same network find each other directly // instead of sending USB traffic out to a relay and back. LocalEndpoints []string `json:"local_endpoints,omitempty"` } // USBDevice describes a USB device type USBDevice struct { BusID string `json:"bus_id"` BusNum uint32 `json:"bus_num"` DevNum uint32 `json:"dev_num"` Speed uint32 `json:"speed"` VendorID string `json:"vendor_id"` ProductID string `json:"product_id"` DeviceBCD string `json:"device_bcd,omitempty"` Class uint8 `json:"class"` SubClass uint8 `json:"sub_class"` Protocol uint8 `json:"protocol"` Name string `json:"name"` Manufacturer string `json:"manufacturer,omitempty"` NumInterfaces uint8 `json:"num_interfaces"` Status string `json:"status"` UsedBy string `json:"used_by,omitempty"` } // DeviceList is sent by share clients to announce available devices type DeviceList struct { Type string `json:"type"` ClientID string `json:"client_id"` ClientName string `json:"client_name"` Devices []USBDevice `json:"devices"` AllowForceDetach bool `json:"allow_force_detach,omitempty"` } // RequestDevice is sent by use clients to request a specific device type RequestDevice struct { Type string `json:"type"` TargetClient string `json:"target_client"` BusID string `json:"bus_id"` RequestID string `json:"request_id"` } // DeviceGranted is sent by share clients when a device is ready type DeviceGranted struct { Type string `json:"type"` BusID string `json:"bus_id"` TunnelID string `json:"tunnel_id"` RequestID string `json:"request_id"` DevID uint32 `json:"dev_id"` // (busnum << 16) | devnum Speed uint32 `json:"speed"` // Endpoints are addresses at which the granting client accepts a direct // tunnel connection for this device. The client contributes its own // interface addresses; the relay appends the public address it sees, // which is the only part neither peer can determine for itself. Endpoints []string `json:"endpoints,omitempty"` // Encrypted reports whether the granting client will encrypt tunnel // frames. It is false only for clients configured with a bare group hash // and no tokens, which cannot derive the key. Encrypted bool `json:"encrypted,omitempty"` } // DeviceDenied is sent when a device request is rejected type DeviceDenied struct { Type string `json:"type"` BusID string `json:"bus_id"` RequestID string `json:"request_id"` Reason string `json:"reason"` } // ReleaseDevice is sent by use clients to release a device type ReleaseDevice struct { Type string `json:"type"` TargetClient string `json:"target_client"` BusID string `json:"bus_id"` } // DeviceReleased is sent when a device is released type DeviceReleased struct { Type string `json:"type"` BusID string `json:"bus_id"` ClientID string `json:"client_id,omitempty"` } // ForceRelease is sent by use clients to force-release a device from another user type ForceRelease struct { Type string `json:"type"` TargetClient string `json:"target_client"` BusID string `json:"bus_id"` } // ClientJoined is broadcast when a new client joins the group type ClientJoined struct { Type string `json:"type"` ClientID string `json:"client_id"` Mode string `json:"mode"` Name string `json:"name"` } // ClientLeft is broadcast when a client leaves the group type ClientLeft struct { Type string `json:"type"` ClientID string `json:"client_id"` } // Ping/Pong for keepalive type Ping struct { Type string `json:"type"` } type Pong struct { Type string `json:"type"` } // Error message type ErrorMsg struct { Type string `json:"type"` Message string `json:"message"` } // TunnelHeader is prepended to binary WebSocket frames for tunnel data. // Format: [16 bytes UUID][payload] const TunnelHeaderSize = 16 // ShortID truncates an identifier for logging without panicking on short or // empty input. Slicing IDs directly is a real hazard here: a client that // registers with an empty hash would otherwise take down the relay. func ShortID(id string) string { if len(id) <= 8 { return id } return id[:8] }