api.go 48 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227
  1. /*
  2. * Copyright (c) 2023, Psiphon Inc.
  3. * All rights reserved.
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. *
  18. */
  19. package inproxy
  20. import (
  21. "crypto/rand"
  22. "crypto/subtle"
  23. "encoding/base64"
  24. "encoding/binary"
  25. "math"
  26. "strconv"
  27. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
  28. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/errors"
  29. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/protocol"
  30. )
  31. const (
  32. // ProtocolVersion1 represents protocol version 1, the initial in-proxy
  33. // protocol version number.
  34. ProtocolVersion1 = int32(1)
  35. // ProtocolVersion2 represents protocol version 2, which adds support for
  36. // proxying over WebRTC media streams.
  37. ProtocolVersion2 = int32(2)
  38. // LatestProtocolVersion is the current, default protocol version number.
  39. LatestProtocolVersion = ProtocolVersion2
  40. // MinimumProxyProtocolVersion is the minimum required protocol version
  41. // number for proxies.
  42. MinimumProxyProtocolVersion = ProtocolVersion1
  43. // MinimumClientProtocolVersion is the minimum supported protocol version
  44. // number for clients.
  45. MinimumClientProtocolVersion = ProtocolVersion1
  46. MaxCompartmentIDs = 10
  47. )
  48. // minimumProxyProtocolVersion and minimumClientProtocolVersion are variable
  49. // to enable overriding the values in tests. These value should not be
  50. // overridden outside of test cases.
  51. var (
  52. minimumProxyProtocolVersion = MinimumProxyProtocolVersion
  53. minimumClientProtocolVersion = MinimumClientProtocolVersion
  54. )
  55. // negotiateProtocolVersion selects the in-proxy protocol version for a new
  56. // proxy/client match, based on the client's and proxy's reported protocol
  57. // versions, and the client's selected protocol options. Returns false if no
  58. // protocol version selection is possible.
  59. //
  60. // The broker performs the negotiation on behalf of the proxy and client. Both
  61. // the proxy and client initially specify the latest protocol version they
  62. // support. The client specifies the protocol options to use, based on
  63. // tactics and replay.
  64. //
  65. // negotiateProtocolVersion is used by the matcher when searching for
  66. // potential matches; for this reason, the failure case is expected and
  67. // returns a simple boolean intead of formating an error message.
  68. //
  69. // Existing, legacy proxies have the equivalent of an "if
  70. // announceResponse.SelectedProtocolVersion != ProtocolVersion1" check, so
  71. // the SelectedProtocolVersion must be downgraded in that case, if a match is
  72. // possible.
  73. func negotiateProtocolVersion(
  74. proxyProtocolVersion int32,
  75. clientProtocolVersion int32,
  76. useMediaStreams bool) (int32, bool) {
  77. // When not using WebRTC media streams, introduced in ProtocolVersion2,
  78. // potentially downgrade if either the proxy or client supports only
  79. // ProtocolVersion1.
  80. if (proxyProtocolVersion == ProtocolVersion1 ||
  81. clientProtocolVersion == ProtocolVersion1) &&
  82. !useMediaStreams {
  83. return ProtocolVersion1, true
  84. }
  85. // Select the client's protocol version.
  86. if proxyProtocolVersion >= clientProtocolVersion {
  87. return clientProtocolVersion, true
  88. }
  89. // No selection is possible. This includes the case where the proxy
  90. // supports up to ProtocolVersion1 and the client has specified media
  91. // streams.
  92. return 0, false
  93. }
  94. // ID is a unique identifier used to identify inproxy connections and actors.
  95. type ID [32]byte
  96. // MakeID generates a new ID using crypto/rand.
  97. func MakeID() (ID, error) {
  98. var id ID
  99. for {
  100. _, err := rand.Read(id[:])
  101. if err != nil {
  102. return id, errors.Trace(err)
  103. }
  104. if !id.Zero() {
  105. return id, nil
  106. }
  107. }
  108. }
  109. // IDFromString returns an ID given its string encoding.
  110. func IDFromString(s string) (ID, error) {
  111. var id ID
  112. return id, errors.Trace(fromBase64String(s, id[:]))
  113. }
  114. func fromBase64String(s string, b []byte) error {
  115. value, err := base64.RawStdEncoding.DecodeString(s)
  116. if err != nil {
  117. return errors.Trace(err)
  118. }
  119. if len(value) != len(b) {
  120. return errors.TraceNew("invalid length")
  121. }
  122. copy(b, value)
  123. return nil
  124. }
  125. // IDsFromStrings returns a list of IDs given a list of string encodings.
  126. func IDsFromStrings(strs []string) ([]ID, error) {
  127. var ids []ID
  128. for _, str := range strs {
  129. id, err := IDFromString(str)
  130. if err != nil {
  131. return nil, errors.Trace(err)
  132. }
  133. ids = append(ids, id)
  134. }
  135. return ids, nil
  136. }
  137. // MarshalText emits IDs as base64.
  138. func (id ID) MarshalText() ([]byte, error) {
  139. return []byte(id.String()), nil
  140. }
  141. // String emits IDs as base64.
  142. func (id ID) String() string {
  143. return base64.RawStdEncoding.EncodeToString([]byte(id[:]))
  144. }
  145. // Equal indicates whether two IDs are equal. It uses a constant time
  146. // comparison.
  147. func (id ID) Equal(x ID) bool {
  148. return subtle.ConstantTimeCompare(id[:], x[:]) == 1
  149. }
  150. // Zero indicates whether the ID is the zero value.
  151. func (id ID) Zero() bool {
  152. var zero ID
  153. return id.Equal(zero)
  154. }
  155. // HaveCommonIDs indicates whether two lists of IDs have a common entry.
  156. func HaveCommonIDs(a, b []ID) bool {
  157. for _, x := range a {
  158. for _, y := range b {
  159. // Each comparison is constant time, but the number of comparisons
  160. // varies and might leak the size of a list.
  161. if x.Equal(y) {
  162. return true
  163. }
  164. }
  165. }
  166. return false
  167. }
  168. // NetworkType is the type of a network, such as WiFi or Mobile. This enum is
  169. // used for compact API message encoding.
  170. type NetworkType int32
  171. const (
  172. NetworkTypeUnknown NetworkType = iota
  173. NetworkTypeWiFi
  174. NetworkTypeMobile
  175. NetworkTypeWired
  176. NetworkTypeVPN
  177. )
  178. // NetworkProtocol is an Internet protocol, such as TCP or UDP. This enum is
  179. // used for compact API message encoding.
  180. type NetworkProtocol int32
  181. const (
  182. NetworkProtocolTCP NetworkProtocol = iota
  183. NetworkProtocolUDP
  184. )
  185. // NetworkProtocolFromString converts a "net" package network protocol string
  186. // value to a NetworkProtocol.
  187. func NetworkProtocolFromString(networkProtocol string) (NetworkProtocol, error) {
  188. switch networkProtocol {
  189. case "tcp":
  190. return NetworkProtocolTCP, nil
  191. case "udp":
  192. return NetworkProtocolUDP, nil
  193. }
  194. var p NetworkProtocol
  195. return p, errors.Tracef("unknown network protocol: %s", networkProtocol)
  196. }
  197. // String converts a NetworkProtocol to a "net" package network protocol string.
  198. func (p NetworkProtocol) String() string {
  199. switch p {
  200. case NetworkProtocolTCP:
  201. return "tcp"
  202. case NetworkProtocolUDP:
  203. return "udp"
  204. }
  205. // This case will cause net dials to fail.
  206. return ""
  207. }
  208. // IsStream indicates if the NetworkProtocol is stream-oriented (e.g., TCP)
  209. // and not packet-oriented (e.g., UDP).
  210. func (p NetworkProtocol) IsStream() bool {
  211. switch p {
  212. case NetworkProtocolTCP:
  213. return true
  214. case NetworkProtocolUDP:
  215. return false
  216. }
  217. return false
  218. }
  219. // ProxyMetrics are network topolology and resource metrics provided by a
  220. // proxy to a broker. The broker uses this information when matching proxies
  221. // and clients.
  222. // Limitation: Currently, there is no MaxReducedPersonalClients config, as
  223. // We assumed that users would not want the personal connections to be reduced.
  224. type ProxyMetrics struct {
  225. BaseAPIParameters protocol.PackedAPIParameters `cbor:"1,keyasint,omitempty"`
  226. ProtocolVersion int32 `cbor:"2,keyasint,omitempty"`
  227. NATType NATType `cbor:"3,keyasint,omitempty"`
  228. PortMappingTypes PortMappingTypes `cbor:"4,keyasint,omitempty"`
  229. MaxCommonClients int32 `cbor:"6,keyasint,omitempty"`
  230. ConnectingClients int32 `cbor:"7,keyasint,omitempty"`
  231. ConnectedClients int32 `cbor:"8,keyasint,omitempty"`
  232. LimitUpstreamBytesPerSecond int64 `cbor:"9,keyasint,omitempty"`
  233. LimitDownstreamBytesPerSecond int64 `cbor:"10,keyasint,omitempty"`
  234. PeakUpstreamBytesPerSecond int64 `cbor:"11,keyasint,omitempty"`
  235. PeakDownstreamBytesPerSecond int64 `cbor:"12,keyasint,omitempty"`
  236. MaxPersonalClients int32 `cbor:"13,keyasint,omitempty"`
  237. }
  238. // ClientMetrics are network topolology metrics provided by a client to a
  239. // broker. The broker uses this information when matching proxies and
  240. // clients.
  241. type ClientMetrics struct {
  242. BaseAPIParameters protocol.PackedAPIParameters `cbor:"1,keyasint,omitempty"`
  243. ProtocolVersion int32 `cbor:"2,keyasint,omitempty"`
  244. NATType NATType `cbor:"3,keyasint,omitempty"`
  245. PortMappingTypes PortMappingTypes `cbor:"4,keyasint,omitempty"`
  246. }
  247. // ProxyAnnounceRequest is an API request sent from a proxy to a broker,
  248. // announcing that it is available for a client connection. Proxies send one
  249. // ProxyAnnounceRequest for each available client connection. The broker will
  250. // match the proxy with a client and return WebRTC connection information
  251. // in the response.
  252. //
  253. // PersonalCompartmentIDs limits the clients to those that supply one of the
  254. // specified compartment IDs; personal compartment IDs are distributed from
  255. // proxy operators to client users out-of-band and provide optional access
  256. // control.
  257. //
  258. // When CheckTactics is set, the broker will check for new tactics or indicate
  259. // that the proxy's cached tactics TTL may be extended. Tactics information
  260. // is returned in the response TacticsPayload. To minimize broker processing
  261. // overhead, proxies with multiple workers should designate just one worker
  262. // to set CheckTactics.
  263. //
  264. // When PreCheckTactics is set, the broker checks tactics as with
  265. // CheckTactics, but responds immediately without awaiting a match. This
  266. // option enables the proxy to quickly establish the shared Noise protocol
  267. // session and launch all workers.
  268. //
  269. // The proxy's session public key is an implicit and cryptographically
  270. // verified proxy ID.
  271. type ProxyAnnounceRequest struct {
  272. PersonalCompartmentIDs []ID `cbor:"1,keyasint,omitempty"`
  273. Metrics *ProxyMetrics `cbor:"2,keyasint,omitempty"`
  274. CheckTactics bool `cbor:"3,keyasint,omitempty"`
  275. PreCheckTactics bool `cbor:"4,keyasint,omitempty"`
  276. }
  277. // WebRTCSessionDescription is compatible with pion/webrtc.SessionDescription
  278. // and facilitates the PSIPHON_DISABLE_INPROXY build tag exclusion of pion
  279. // dependencies.
  280. type WebRTCSessionDescription struct {
  281. Type int `cbor:"1,keyasint,omitempty"`
  282. SDP string `cbor:"2,keyasint,omitempty"`
  283. }
  284. // TODO: send ProxyAnnounceRequest/ClientOfferRequest.Metrics only with the
  285. // first request in a session and cache.
  286. // ProxyAnnounceResponse returns the connection information for a matched
  287. // client. To establish a WebRTC connection, the proxy uses the client's
  288. // offer SDP to create its own answer SDP and send that to the broker in a
  289. // subsequent ProxyAnswerRequest. The ConnectionID is a unique identifier for
  290. // this single connection and must be relayed back in the ProxyAnswerRequest.
  291. //
  292. // ClientRootObfuscationSecret is generated (or replayed) by the client and
  293. // sent to the proxy and used to drive obfuscation operations.
  294. //
  295. // DestinationAddress is the dial address for the Psiphon server the proxy is
  296. // to relay client traffic with. The broker validates that the dial address
  297. // corresponds to a valid Psiphon server.
  298. //
  299. // MustUpgrade is an optional flag that is set by the broker, based on the
  300. // submitted ProtocolVersion, when the proxy app must be upgraded in order to
  301. // function properly. Potential must-upgrade scenarios include changes to the
  302. // personal pairing broker rendezvous algorithm, where no protocol backwards
  303. // compatibility accommodations can ensure a rendezvous and match. When
  304. // MustUpgrade is set, NoMatch is implied.
  305. type ProxyAnnounceResponse struct {
  306. TacticsPayload []byte `cbor:"2,keyasint,omitempty"`
  307. Limited bool `cbor:"3,keyasint,omitempty"`
  308. NoMatch bool `cbor:"4,keyasint,omitempty"`
  309. MustUpgrade bool `cbor:"13,keyasint,omitempty"`
  310. ConnectionID ID `cbor:"5,keyasint,omitempty"`
  311. SelectedProtocolVersion int32 `cbor:"6,keyasint,omitempty"`
  312. ClientOfferSDP WebRTCSessionDescription `cbor:"7,keyasint,omitempty"`
  313. ClientRootObfuscationSecret ObfuscationSecret `cbor:"8,keyasint,omitempty"`
  314. DoDTLSRandomization bool `cbor:"9,keyasint,omitempty"`
  315. UseMediaStreams bool `cbor:"14,keyasint,omitempty"`
  316. TrafficShapingParameters *TrafficShapingParameters `cbor:"10,keyasint,omitempty"`
  317. NetworkProtocol NetworkProtocol `cbor:"11,keyasint,omitempty"`
  318. DestinationAddress string `cbor:"12,keyasint,omitempty"`
  319. ClientRegion string `cbor:"15,keyasint,omitempty"`
  320. }
  321. // ClientOfferRequest is an API request sent from a client to a broker,
  322. // requesting a proxy connection. The client sends its WebRTC offer SDP with
  323. // this request.
  324. //
  325. // Clients specify known compartment IDs and are matched with proxies in those
  326. // compartments. CommonCompartmentIDs are comparment IDs managed by Psiphon
  327. // and revealed through tactics or bundled with server lists.
  328. // PersonalCompartmentIDs are compartment IDs shared privately between users,
  329. // out-of-band.
  330. //
  331. // ClientRootObfuscationSecret is generated (or replayed) by the client and
  332. // sent to the proxy and used to drive obfuscation operations.
  333. //
  334. // To specify the Psiphon server it wishes to proxy to, the client sends the
  335. // full, digitally signed Psiphon server entry to the broker and also the
  336. // specific dial address that it has selected for that server. The broker
  337. // validates the server entry signature, the server in-proxy capability, and
  338. // that the dial address corresponds to the network protocol, IP address or
  339. // domain, and destination port for a valid Psiphon tunnel protocol run by
  340. // the specified server entry.
  341. type ClientOfferRequest struct {
  342. Metrics *ClientMetrics `cbor:"1,keyasint,omitempty"`
  343. CommonCompartmentIDs []ID `cbor:"2,keyasint,omitempty"`
  344. PersonalCompartmentIDs []ID `cbor:"3,keyasint,omitempty"`
  345. ClientOfferSDP WebRTCSessionDescription `cbor:"4,keyasint,omitempty"`
  346. ICECandidateTypes ICECandidateTypes `cbor:"5,keyasint,omitempty"`
  347. ClientRootObfuscationSecret ObfuscationSecret `cbor:"6,keyasint,omitempty"`
  348. DoDTLSRandomization bool `cbor:"7,keyasint,omitempty"`
  349. UseMediaStreams bool `cbor:"12,keyasint,omitempty"`
  350. TrafficShapingParameters *TrafficShapingParameters `cbor:"8,keyasint,omitempty"`
  351. PackedDestinationServerEntry []byte `cbor:"9,keyasint,omitempty"`
  352. NetworkProtocol NetworkProtocol `cbor:"10,keyasint,omitempty"`
  353. DestinationAddress string `cbor:"11,keyasint,omitempty"`
  354. }
  355. // TrafficShapingParameters specifies data channel or media stream traffic
  356. // shaping configuration, including random padding and decoy messages (or
  357. // packets). Clients determine their own traffic shaping configuration, and
  358. // generate and send a configuration for the peer proxy to use.
  359. type TrafficShapingParameters struct {
  360. MinPaddedMessages int `cbor:"1,keyasint,omitempty"`
  361. MaxPaddedMessages int `cbor:"2,keyasint,omitempty"`
  362. MinPaddingSize int `cbor:"3,keyasint,omitempty"`
  363. MaxPaddingSize int `cbor:"4,keyasint,omitempty"`
  364. MinDecoyMessages int `cbor:"5,keyasint,omitempty"`
  365. MaxDecoyMessages int `cbor:"6,keyasint,omitempty"`
  366. MinDecoySize int `cbor:"7,keyasint,omitempty"`
  367. MaxDecoySize int `cbor:"8,keyasint,omitempty"`
  368. DecoyMessageProbability float64 `cbor:"9,keyasint,omitempty"`
  369. }
  370. // ClientOfferResponse returns the connecting information for a matched proxy.
  371. // The proxy's WebRTC SDP is an answer to the offer sent in
  372. // ClientOfferRequest and is used to begin dialing the WebRTC connection.
  373. //
  374. // Once the client completes its connection to the Psiphon server, it must
  375. // relay a BrokerServerReport to the server on behalf of the broker. This
  376. // relay is conducted within a secure session. First, the client sends
  377. // RelayPacketToServer to the server. Then the client relays any responses to
  378. // the broker using ClientRelayedPacketRequests and continues to relay using
  379. // ClientRelayedPacketRequests until complete. ConnectionID identifies this
  380. // connection and its relayed BrokerServerReport.
  381. //
  382. // MustUpgrade is an optional flag that is set by the broker, based on the
  383. // submitted ProtocolVersion, when the client app must be upgraded in order
  384. // to function properly. Potential must-upgrade scenarios include changes to
  385. // the personal pairing broker rendezvous algorithm, where no protocol
  386. // backwards compatibility accommodations can ensure a rendezvous and match.
  387. // When MustUpgrade is set, NoMatch is implied.
  388. type ClientOfferResponse struct {
  389. Limited bool `cbor:"1,keyasint,omitempty"`
  390. NoMatch bool `cbor:"2,keyasint,omitempty"`
  391. MustUpgrade bool `cbor:"7,keyasint,omitempty"`
  392. ConnectionID ID `cbor:"3,keyasint,omitempty"`
  393. SelectedProtocolVersion int32 `cbor:"4,keyasint,omitempty"`
  394. ProxyAnswerSDP WebRTCSessionDescription `cbor:"5,keyasint,omitempty"`
  395. RelayPacketToServer []byte `cbor:"6,keyasint,omitempty"`
  396. }
  397. // TODO: Encode SDPs using CBOR without field names, simliar to packed metrics?
  398. // ProxyAnswerRequest is an API request sent from a proxy to a broker,
  399. // following ProxyAnnounceResponse, with the WebRTC answer SDP corresponding
  400. // to the client offer SDP received in ProxyAnnounceResponse. ConnectionID
  401. // identifies the connection begun in ProxyAnnounceResponse.
  402. //
  403. // If the proxy was unable to establish an answer SDP or failed for some other
  404. // reason, it should still send ProxyAnswerRequest with AnswerError
  405. // populated; the broker will signal the client to abort this connection.
  406. type ProxyAnswerRequest struct {
  407. ConnectionID ID `cbor:"1,keyasint,omitempty"`
  408. ProxyAnswerSDP WebRTCSessionDescription `cbor:"3,keyasint,omitempty"`
  409. ICECandidateTypes ICECandidateTypes `cbor:"4,keyasint,omitempty"`
  410. AnswerError string `cbor:"5,keyasint,omitempty"`
  411. // These fields are no longer used.
  412. //
  413. // SelectedProtocolVersion int32 `cbor:"2,keyasint,omitempty"`
  414. }
  415. // ProxyAnswerResponse is the acknowledgement for a ProxyAnswerRequest. If
  416. // NoAwaitingClient is indicated, then the client was no longer awaiting the
  417. // answer and the proxy should abandon the connection attempt.
  418. type ProxyAnswerResponse struct {
  419. NoAwaitingClient bool `cbor:"1,keyasint,omitempty"`
  420. }
  421. // ClientRelayedPacketRequest is an API request sent from a client to a
  422. // broker, relaying a secure session packet from the Psiphon server to the
  423. // broker. This relay is a continuation of the broker/server exchange begun
  424. // with ClientOfferResponse.RelayPacketToServer. PacketFromServer is the next
  425. // packet from the server.
  426. //
  427. // When a broker attempts to use an existing session which has expired on the
  428. // server, the packet from the server may contain a signed reset session
  429. // token, which is used to automatically reset and start establishing a new
  430. // session before relaying the payload.
  431. type ClientRelayedPacketRequest struct {
  432. ConnectionID ID `cbor:"1,keyasint,omitempty"`
  433. PacketFromServer []byte `cbor:"2,keyasint,omitempty"`
  434. }
  435. // ClientRelayedPacketResponse returns the next packet from the broker to the
  436. // server. When PacketToServer is empty, the broker/server exchange is done
  437. // and the client stops relaying packets.
  438. type ClientRelayedPacketResponse struct {
  439. PacketToServer []byte `cbor:"1,keyasint,omitempty"`
  440. }
  441. // BrokerServerReport is a one-way API call sent from a broker to a
  442. // Psiphon server. This delivers, to the server, information that neither the
  443. // client nor the proxy is trusted to report. ProxyID is the proxy ID to be
  444. // logged with server_tunnel to attribute traffic to a specific proxy.
  445. // ClientIP is the original client IP as seen by the broker; this is the IP
  446. // value to be used in GeoIP-related operations including traffic rules,
  447. // tactics, and OSL progress. ProxyIP is the proxy IP as seen by the broker;
  448. // this value should match the Psiphon's server observed client IP.
  449. // Additional fields are metrics to be logged with server_tunnel.
  450. //
  451. // Using a one-way message here means that, once a broker/server session is
  452. // established, the entire relay can be encasulated in a single additional
  453. // field sent in the Psiphon API handshake. This minimizes observable and
  454. // potentially fingerprintable traffic flows as the client does not need to
  455. // relay any further session packets before starting the tunnel. The
  456. // trade-off is that the broker doesn't get an indication from the server
  457. // that the message was accepted or rejects and cannot directly, in real time
  458. // log any tunnel error associated with the server rejecting the message, or
  459. // log that the relay was completed successfully. These events can be logged
  460. // on the server and logs reconciled using the in-proxy Connection ID.
  461. type BrokerServerReport struct {
  462. ProxyID ID `cbor:"1,keyasint,omitempty"`
  463. ConnectionID ID `cbor:"2,keyasint,omitempty"`
  464. MatchedCommonCompartments bool `cbor:"3,keyasint,omitempty"`
  465. MatchedPersonalCompartments bool `cbor:"4,keyasint,omitempty"`
  466. ClientNATType NATType `cbor:"7,keyasint,omitempty"`
  467. ClientPortMappingTypes PortMappingTypes `cbor:"8,keyasint,omitempty"`
  468. ClientIP string `cbor:"9,keyasint,omitempty"`
  469. ProxyIP string `cbor:"10,keyasint,omitempty"`
  470. ProxyMetrics *ProxyMetrics `cbor:"11,keyasint,omitempty"`
  471. ProxyIsPriority bool `cbor:"12,keyasint,omitempty"`
  472. // These legacy fields are now sent in ProxyMetrics.
  473. ProxyNATType NATType `cbor:"5,keyasint,omitempty"`
  474. ProxyPortMappingTypes PortMappingTypes `cbor:"6,keyasint,omitempty"`
  475. }
  476. // ClientDSLRequest is a client DSL request that the broker relays to the DSL
  477. // backend. The broker's role is to provide a blocking resistant initial
  478. // hop; DSL requests are not direct components of the in-proxy protocol.
  479. type ClientDSLRequest struct {
  480. RequestPayload []byte `cbor:"1,keyasint,omitempty"`
  481. }
  482. // ClientDSLResponse is a DSL response relayed back to the client.
  483. type ClientDSLResponse struct {
  484. ResponsePayload []byte `cbor:"1,keyasint,omitempty"`
  485. }
  486. // ProxyQualityKey is the key that proxy quality is indexed on a proxy ID and
  487. // a proxy ASN. Quality is tracked at a fine-grained level, with the proxy ID
  488. // representing, typically, an individual device, and the proxy ASN
  489. // representing the network the device used at the time a quality tunnel was
  490. // reported.
  491. type ProxyQualityKey [36]byte
  492. // MakeProxyQualityKey creates a ProxyQualityKey using the given proxy ID and
  493. // proxy ASN. In the key, the proxy ID remains encoded as-is, and the ASN is
  494. // encoded in the 4-byte representation (see RFC6793).
  495. func MakeProxyQualityKey(proxyID ID, proxyASN string) ProxyQualityKey {
  496. var key ProxyQualityKey
  497. copy(key[0:32], proxyID[:])
  498. ASN, err := strconv.ParseInt(proxyASN, 10, 0)
  499. if err != nil || ASN < 0 || ASN > math.MaxUint32 {
  500. // In cases including failed or misconfigured GeoIP lookups -- with
  501. // values such as server.GEOIP_UNKNOWN_VALUE or invalid AS numbers --
  502. // fall back to a reserved AS number (see RFC5398). This is, effectively, a less
  503. // fine-grained key.
  504. //
  505. // Note that GeoIP lookups are performed server-side and a proxy
  506. // itself cannot force this downgrade (to obtain false quality
  507. // classification across different networks).
  508. ASN = 65536
  509. }
  510. binary.BigEndian.PutUint32(key[32:36], uint32(ASN))
  511. return key
  512. }
  513. // ProxyQualityASNCounts is tunnel quality data, a map from client ASNs to
  514. // counts of quality tunnels that a proxy relayed for those client ASNs.
  515. type ProxyQualityASNCounts map[string]int
  516. // ProxyQualityRequestCounts is ProxyQualityASNCounts for a set of proxies.
  517. type ProxyQualityRequestCounts map[ProxyQualityKey]ProxyQualityASNCounts
  518. // ServerProxyQualityRequest is an API request sent from a server to a broker,
  519. // reporting a set of proxy IDs/ASNs that have relayed quality tunnels -- as
  520. // determined by bytes transferred and duration thresholds -- for clients in
  521. // the given ASNs. This quality data is used, by brokers, to prioritize
  522. // well-performing proxies, and to match clients with proxies that worked
  523. // successfully for the client's ASN.
  524. //
  525. // QualityCounts is a map from proxy ID/ASN to ASN quality tunnel counts.
  526. //
  527. // DialParameters specifies additional parameters to log with proxy quality
  528. // broker events, including any relevant server broker dial parameters.
  529. // Unlike clients and proxies, servers do not send BaseAPIParameters to
  530. // brokers.
  531. type ServerProxyQualityRequest struct {
  532. QualityCounts ProxyQualityRequestCounts `cbor:"1,keyasint,omitempty"`
  533. DialParameters protocol.PackedAPIParameters `cbor:"2,keyasint,omitempty"`
  534. }
  535. // ServerProxyQualityResponse is the acknowledgement for a
  536. // ServerProxyQualityRequest.
  537. type ServerProxyQualityResponse struct {
  538. }
  539. // GetNetworkType extracts the network_type from base API metrics and returns
  540. // a corresponding NetworkType. This is the one base metric that is used in
  541. // the broker logic, and not simply logged.
  542. func GetNetworkType(packedBaseParams protocol.PackedAPIParameters) NetworkType {
  543. baseNetworkType, ok := packedBaseParams.GetNetworkType()
  544. if !ok {
  545. return NetworkTypeUnknown
  546. }
  547. switch baseNetworkType {
  548. case "WIFI":
  549. return NetworkTypeWiFi
  550. case "MOBILE":
  551. return NetworkTypeMobile
  552. case "WIRED":
  553. return NetworkTypeWired
  554. case "VPN":
  555. return NetworkTypeVPN
  556. }
  557. return NetworkTypeUnknown
  558. }
  559. // Sanity check values.
  560. const (
  561. maxICECandidateTypes = 10
  562. maxPortMappingTypes = 10
  563. maxPaddedMessages = 100
  564. maxPaddingSize = 16384
  565. maxDecoyMessages = 100
  566. maxDecoySize = 16384
  567. maxQualityCounts = 10000
  568. )
  569. // ValidateAndGetParametersAndLogFields validates the ProxyMetrics and returns
  570. // Psiphon API parameters for processing and common.LogFields for logging.
  571. func (metrics *ProxyMetrics) ValidateAndGetParametersAndLogFields(
  572. baseAPIParameterValidator common.APIParameterValidator,
  573. formatter common.APIParameterLogFieldFormatter,
  574. logFieldPrefix string,
  575. geoIPData common.GeoIPData) (common.APIParameters, common.LogFields, error) {
  576. if metrics.BaseAPIParameters == nil {
  577. return nil, nil, errors.TraceNew("missing base API parameters")
  578. }
  579. baseParams, err := protocol.DecodePackedAPIParameters(metrics.BaseAPIParameters)
  580. if err != nil {
  581. return nil, nil, errors.Trace(err)
  582. }
  583. err = baseAPIParameterValidator(baseParams)
  584. if err != nil {
  585. return nil, nil, errors.Trace(err)
  586. }
  587. if metrics.ProtocolVersion < ProtocolVersion1 || metrics.ProtocolVersion > LatestProtocolVersion {
  588. return nil, nil, errors.Tracef("invalid protocol version: %v", metrics.ProtocolVersion)
  589. }
  590. if !metrics.NATType.IsValid() {
  591. return nil, nil, errors.Tracef("invalid NAT type: %v", metrics.NATType)
  592. }
  593. if len(metrics.PortMappingTypes) > maxPortMappingTypes {
  594. return nil, nil, errors.Tracef("invalid portmapping types length: %d", len(metrics.PortMappingTypes))
  595. }
  596. if !metrics.PortMappingTypes.IsValid() {
  597. return nil, nil, errors.Tracef("invalid portmapping types: %v", metrics.PortMappingTypes)
  598. }
  599. logFields := formatter(logFieldPrefix, geoIPData, baseParams)
  600. logFields[logFieldPrefix+"protocol_version"] = metrics.ProtocolVersion
  601. logFields[logFieldPrefix+"nat_type"] = metrics.NATType
  602. logFields[logFieldPrefix+"port_mapping_types"] = metrics.PortMappingTypes
  603. logFields[logFieldPrefix+"max_common_clients"] = metrics.MaxCommonClients
  604. logFields[logFieldPrefix+"max_personal_clients"] = metrics.MaxPersonalClients
  605. logFields[logFieldPrefix+"max_clients"] = metrics.MaxCommonClients + metrics.MaxPersonalClients
  606. logFields[logFieldPrefix+"connecting_clients"] = metrics.ConnectingClients
  607. logFields[logFieldPrefix+"connected_clients"] = metrics.ConnectedClients
  608. logFields[logFieldPrefix+"limit_upstream_bytes_per_second"] = metrics.LimitUpstreamBytesPerSecond
  609. logFields[logFieldPrefix+"limit_downstream_bytes_per_second"] = metrics.LimitDownstreamBytesPerSecond
  610. logFields[logFieldPrefix+"peak_upstream_bytes_per_second"] = metrics.PeakUpstreamBytesPerSecond
  611. logFields[logFieldPrefix+"peak_downstream_bytes_per_second"] = metrics.PeakDownstreamBytesPerSecond
  612. return baseParams, logFields, nil
  613. }
  614. // ValidateAndGetLogFields validates the ClientMetrics and returns
  615. // common.LogFields for logging.
  616. func (metrics *ClientMetrics) ValidateAndGetLogFields(
  617. baseAPIParameterValidator common.APIParameterValidator,
  618. formatter common.APIParameterLogFieldFormatter,
  619. geoIPData common.GeoIPData) (common.LogFields, error) {
  620. if metrics.BaseAPIParameters == nil {
  621. return nil, errors.TraceNew("missing base API parameters")
  622. }
  623. baseParams, err := protocol.DecodePackedAPIParameters(metrics.BaseAPIParameters)
  624. if err != nil {
  625. return nil, errors.Trace(err)
  626. }
  627. err = baseAPIParameterValidator(baseParams)
  628. if err != nil {
  629. return nil, errors.Trace(err)
  630. }
  631. if metrics.ProtocolVersion < ProtocolVersion1 || metrics.ProtocolVersion > LatestProtocolVersion {
  632. return nil, errors.Tracef("invalid protocol version: %v", metrics.ProtocolVersion)
  633. }
  634. if !metrics.NATType.IsValid() {
  635. return nil, errors.Tracef("invalid NAT type: %v", metrics.NATType)
  636. }
  637. if len(metrics.PortMappingTypes) > maxPortMappingTypes {
  638. return nil, errors.Tracef("invalid portmapping types length: %d", len(metrics.PortMappingTypes))
  639. }
  640. if !metrics.PortMappingTypes.IsValid() {
  641. return nil, errors.Tracef("invalid portmapping types: %v", metrics.PortMappingTypes)
  642. }
  643. logFields := formatter("", geoIPData, baseParams)
  644. logFields["protocol_version"] = metrics.ProtocolVersion
  645. logFields["nat_type"] = metrics.NATType
  646. logFields["port_mapping_types"] = metrics.PortMappingTypes
  647. return logFields, nil
  648. }
  649. // ValidateAndGetParametersAndLogFields validates the ProxyAnnounceRequest and
  650. // returns Psiphon API parameters for processing and common.LogFields for
  651. // logging.
  652. func (request *ProxyAnnounceRequest) ValidateAndGetParametersAndLogFields(
  653. maxCompartmentIDs int,
  654. baseAPIParameterValidator common.APIParameterValidator,
  655. formatter common.APIParameterLogFieldFormatter,
  656. geoIPData common.GeoIPData) (common.APIParameters, common.LogFields, error) {
  657. // A proxy may specify at most 1 personal compartment ID. This is
  658. // currently a limitation of the multi-queue implementation; see comment
  659. // in announcementMultiQueue.enqueue.
  660. if len(request.PersonalCompartmentIDs) > 1 {
  661. return nil, nil, errors.Tracef(
  662. "invalid compartment IDs length: %d", len(request.PersonalCompartmentIDs))
  663. }
  664. if request.Metrics == nil {
  665. return nil, nil, errors.TraceNew("missing metrics")
  666. }
  667. apiParams, logFields, err := request.Metrics.ValidateAndGetParametersAndLogFields(
  668. baseAPIParameterValidator, formatter, "", geoIPData)
  669. if err != nil {
  670. return nil, nil, errors.Trace(err)
  671. }
  672. // PersonalCompartmentIDs are user-generated and shared out-of-band;
  673. // values are not logged since they may link users.
  674. hasPersonalCompartmentIDs := len(request.PersonalCompartmentIDs) > 0
  675. logFields["has_personal_compartment_ids"] = hasPersonalCompartmentIDs
  676. return apiParams, logFields, nil
  677. }
  678. // ValidateAndGetLogFields validates the ClientOfferRequest and returns
  679. // common.LogFields for logging.
  680. func (request *ClientOfferRequest) ValidateAndGetLogFields(
  681. maxCompartmentIDs int,
  682. lookupGeoIP LookupGeoIP,
  683. baseAPIParameterValidator common.APIParameterValidator,
  684. formatter common.APIParameterLogFieldFormatter,
  685. geoIPData common.GeoIPData) ([]byte, common.LogFields, error) {
  686. // UseMediaStreams requires at least ProtocolVersion2.
  687. if request.UseMediaStreams &&
  688. request.Metrics.ProtocolVersion < ProtocolVersion2 {
  689. return nil, nil, errors.Tracef(
  690. "invalid protocol version: %d", request.Metrics.ProtocolVersion)
  691. }
  692. if len(request.CommonCompartmentIDs) > maxCompartmentIDs {
  693. return nil, nil, errors.Tracef(
  694. "invalid compartment IDs length: %d", len(request.CommonCompartmentIDs))
  695. }
  696. if len(request.PersonalCompartmentIDs) > maxCompartmentIDs {
  697. return nil, nil, errors.Tracef(
  698. "invalid compartment IDs length: %d", len(request.PersonalCompartmentIDs))
  699. }
  700. if len(request.CommonCompartmentIDs) > 0 && len(request.PersonalCompartmentIDs) > 0 {
  701. return nil, nil, errors.TraceNew("multiple compartment ID types")
  702. }
  703. // The client offer SDP may contain no ICE candidates.
  704. errorOnNoCandidates := false
  705. // The client offer SDP may include RFC 1918/4193 private IP addresses in
  706. // personal pairing mode. filterSDPAddresses should not filter out
  707. // private IP addresses based on the broker's local interfaces; this
  708. // filtering occurs on the proxy that receives the SDP.
  709. allowPrivateIPAddressCandidates :=
  710. len(request.PersonalCompartmentIDs) > 0 &&
  711. len(request.CommonCompartmentIDs) == 0
  712. filterPrivateIPAddressCandidates := false
  713. // Client offer SDP candidate addresses must match the country and ASN of
  714. // the client. Don't facilitate connections to arbitrary destinations.
  715. filteredSDP, sdpMetrics, err := filterSDPAddresses(
  716. []byte(request.ClientOfferSDP.SDP),
  717. errorOnNoCandidates,
  718. lookupGeoIP,
  719. geoIPData,
  720. allowPrivateIPAddressCandidates,
  721. filterPrivateIPAddressCandidates)
  722. if err != nil {
  723. return nil, nil, errors.Trace(err)
  724. }
  725. // The client's self-reported ICECandidateTypes are used instead of the
  726. // candidate types that can be derived from the SDP, since port mapping
  727. // types are edited into the SDP in a way that makes them
  728. // indistinguishable from host candidate types.
  729. if !request.ICECandidateTypes.IsValid() {
  730. return nil, nil, errors.Tracef(
  731. "invalid ICE candidate types: %v", request.ICECandidateTypes)
  732. }
  733. if request.Metrics == nil {
  734. return nil, nil, errors.TraceNew("missing metrics")
  735. }
  736. logFields, err := request.Metrics.ValidateAndGetLogFields(
  737. baseAPIParameterValidator, formatter, geoIPData)
  738. if err != nil {
  739. return nil, nil, errors.Trace(err)
  740. }
  741. if request.TrafficShapingParameters != nil {
  742. err := request.TrafficShapingParameters.Validate()
  743. if err != nil {
  744. return nil, nil, errors.Trace(err)
  745. }
  746. }
  747. // CommonCompartmentIDs are generated and managed and are a form of
  748. // obfuscation secret, so are not logged. PersonalCompartmentIDs are
  749. // user-generated and shared out-of-band; values are not logged since
  750. // they may link users.
  751. hasCommonCompartmentIDs := len(request.CommonCompartmentIDs) > 0
  752. hasPersonalCompartmentIDs := len(request.PersonalCompartmentIDs) > 0
  753. logFields["has_common_compartment_ids"] = hasCommonCompartmentIDs
  754. logFields["has_personal_compartment_ids"] = hasPersonalCompartmentIDs
  755. logFields["ice_candidate_types"] = request.ICECandidateTypes
  756. logFields["has_IPv4"] = sdpMetrics.hasIPv4
  757. logFields["has_IPv6"] = sdpMetrics.hasIPv6
  758. logFields["has_private_IP"] = sdpMetrics.hasPrivateIP
  759. logFields["filtered_ice_candidates"] = sdpMetrics.filteredICECandidates
  760. logFields["use_media_streams"] = request.UseMediaStreams
  761. return filteredSDP, logFields, nil
  762. }
  763. // Validate validates the that client has not specified excess traffic shaping
  764. // padding or decoy traffic.
  765. func (params *TrafficShapingParameters) Validate() error {
  766. if params.MinPaddedMessages < 0 ||
  767. params.MinPaddedMessages > params.MaxPaddedMessages ||
  768. params.MaxPaddedMessages > maxPaddedMessages {
  769. return errors.TraceNew("invalid padded messages")
  770. }
  771. if params.MinPaddingSize < 0 ||
  772. params.MinPaddingSize > params.MaxPaddingSize ||
  773. params.MaxPaddingSize > maxPaddingSize {
  774. return errors.TraceNew("invalid padding size")
  775. }
  776. if params.MinDecoyMessages < 0 ||
  777. params.MinDecoyMessages > params.MaxDecoyMessages ||
  778. params.MaxDecoyMessages > maxDecoyMessages {
  779. return errors.TraceNew("invalid decoy messages")
  780. }
  781. if params.MinDecoySize < 0 ||
  782. params.MinDecoySize > params.MaxDecoySize ||
  783. params.MaxDecoySize > maxDecoySize {
  784. return errors.TraceNew("invalid decoy size")
  785. }
  786. return nil
  787. }
  788. // ValidateAndGetLogFields validates the ProxyAnswerRequest and returns
  789. // common.LogFields for logging. A nil filteredSDP is returned when
  790. // ProxyAnswerRequest.AnswerError is set.
  791. func (request *ProxyAnswerRequest) ValidateAndGetLogFields(
  792. lookupGeoIP LookupGeoIP,
  793. baseAPIParameterValidator common.APIParameterValidator,
  794. formatter common.APIParameterLogFieldFormatter,
  795. geoIPData common.GeoIPData,
  796. proxyAnnouncementHasPersonalCompartmentIDs bool) ([]byte, common.LogFields, error) {
  797. var filteredSDP []byte
  798. var sdpMetrics *webRTCSDPMetrics
  799. var err error
  800. if request.AnswerError == "" {
  801. // The proxy answer SDP must contain at least one ICE candidate.
  802. errorOnNoCandidates := true
  803. // The proxy answer SDP may include RFC 1918/4193 private IP addresses in
  804. // personal pairing mode. filterSDPAddresses should not filter out
  805. // private IP addresses based on the broker's local interfaces; this
  806. // filtering occurs on the client that receives the SDP.
  807. allowPrivateIPAddressCandidates := proxyAnnouncementHasPersonalCompartmentIDs
  808. filterPrivateIPAddressCandidates := false
  809. // Proxy answer SDP candidate addresses must match the country and ASN of
  810. // the proxy. Don't facilitate connections to arbitrary destinations.
  811. filteredSDP, sdpMetrics, err = filterSDPAddresses(
  812. []byte(request.ProxyAnswerSDP.SDP),
  813. errorOnNoCandidates,
  814. lookupGeoIP,
  815. geoIPData,
  816. allowPrivateIPAddressCandidates,
  817. filterPrivateIPAddressCandidates)
  818. if err != nil {
  819. return nil, nil, errors.Trace(err)
  820. }
  821. }
  822. // The proxy's self-reported ICECandidateTypes are used instead of the
  823. // candidate types that can be derived from the SDP, since port mapping
  824. // types are edited into the SDP in a way that makes them
  825. // indistinguishable from host candidate types.
  826. if !request.ICECandidateTypes.IsValid() {
  827. return nil, nil, errors.Tracef(
  828. "invalid ICE candidate types: %v", request.ICECandidateTypes)
  829. }
  830. logFields := formatter("", geoIPData, common.APIParameters{})
  831. logFields["connection_id"] = request.ConnectionID
  832. logFields["ice_candidate_types"] = request.ICECandidateTypes
  833. logFields["answer_error"] = request.AnswerError
  834. if sdpMetrics != nil {
  835. logFields["has_IPv4"] = sdpMetrics.hasIPv4
  836. logFields["has_IPv6"] = sdpMetrics.hasIPv6
  837. logFields["has_private_IP"] = sdpMetrics.hasPrivateIP
  838. logFields["filtered_ice_candidates"] = sdpMetrics.filteredICECandidates
  839. }
  840. return filteredSDP, logFields, nil
  841. }
  842. // ValidateAndGetLogFields validates the ClientRelayedPacketRequest and returns
  843. // common.LogFields for logging.
  844. func (request *ClientRelayedPacketRequest) ValidateAndGetLogFields(
  845. baseAPIParameterValidator common.APIParameterValidator,
  846. formatter common.APIParameterLogFieldFormatter,
  847. geoIPData common.GeoIPData) (common.LogFields, error) {
  848. logFields := formatter("", geoIPData, common.APIParameters{})
  849. logFields["connection_id"] = request.ConnectionID
  850. return logFields, nil
  851. }
  852. // ValidateAndGetLogFields validates the BrokerServerReport and returns
  853. // common.LogFields for logging.
  854. func (report *BrokerServerReport) ValidateAndGetLogFields(
  855. baseAPIParameterValidator common.APIParameterValidator,
  856. formatter common.APIParameterLogFieldFormatter,
  857. proxyMetricsPrefix string) (common.LogFields, error) {
  858. // Neither ClientIP nor ProxyIP is logged.
  859. if !report.ClientNATType.IsValid() {
  860. return nil, errors.Tracef("invalid client NAT type: %v", report.ClientNATType)
  861. }
  862. if !report.ClientPortMappingTypes.IsValid() {
  863. return nil, errors.Tracef("invalid client portmapping types: %v", report.ClientPortMappingTypes)
  864. }
  865. var logFields common.LogFields
  866. if report.ProxyMetrics == nil {
  867. // Backwards compatibility for reports without ProxyMetrics.
  868. if !report.ProxyNATType.IsValid() {
  869. return nil, errors.Tracef("invalid proxy NAT type: %v", report.ProxyNATType)
  870. }
  871. if !report.ProxyPortMappingTypes.IsValid() {
  872. return nil, errors.Tracef("invalid proxy portmapping types: %v", report.ProxyPortMappingTypes)
  873. }
  874. logFields = common.LogFields{}
  875. logFields["inproxy_proxy_nat_type"] = report.ProxyNATType
  876. logFields["inproxy_proxy_port_mapping_types"] = report.ProxyPortMappingTypes
  877. } else {
  878. var err error
  879. _, logFields, err = report.ProxyMetrics.ValidateAndGetParametersAndLogFields(
  880. baseAPIParameterValidator,
  881. formatter,
  882. proxyMetricsPrefix,
  883. common.GeoIPData{}) // Proxy GeoIP data is added by the caller.
  884. if err != nil {
  885. return nil, errors.Trace(err)
  886. }
  887. }
  888. logFields["inproxy_proxy_id"] = report.ProxyID
  889. logFields["inproxy_connection_id"] = report.ConnectionID
  890. logFields["inproxy_matched_common_compartments"] = report.MatchedCommonCompartments
  891. logFields["inproxy_matched_personal_compartments"] = report.MatchedPersonalCompartments
  892. logFields["inproxy_client_nat_type"] = report.ClientNATType
  893. logFields["inproxy_client_port_mapping_types"] = report.ClientPortMappingTypes
  894. logFields["inproxy_proxy_is_priority"] = report.ProxyIsPriority
  895. // TODO:
  896. // - log IPv4 vs. IPv6 information
  897. // - relay and log broker transport stats, such as meek HTTP version
  898. return logFields, nil
  899. }
  900. // ValidateAndGetLogFields validates the ServerProxyQualityRequest and returns
  901. // common.LogFields for logging.
  902. func (request *ServerProxyQualityRequest) ValidateAndGetLogFields() (common.LogFields, error) {
  903. if len(request.QualityCounts) > maxQualityCounts {
  904. return nil, errors.Tracef("invalid quality count length: %d", len(request.QualityCounts))
  905. }
  906. // Currently, there is no custom validator or formatter for
  907. // DialParameters, as there is for the BaseAPIParameters sent by clients
  908. // and proxies:
  909. //
  910. // - The DialParameters inputs, used only to annotate logs, are from a
  911. // trusted Psiphon server.
  912. //
  913. // - Psiphon servers do not send fields required by the existing
  914. // BaseAPIParameters validators, such as sponsor ID.
  915. //
  916. // - No formatter transforms, such as "0"/"1" to bool, are currently
  917. // expected; and server.getRequestLogFields is inefficient when a
  918. // couple of log fields are expected; for an example for any future
  919. // special case formatter, see
  920. // server.getInproxyBrokerServerReportParameterLogFieldFormatter.
  921. dialParams, err := protocol.DecodePackedAPIParameters(request.DialParameters)
  922. if err != nil {
  923. return nil, errors.Trace(err)
  924. }
  925. logFields := common.LogFields(dialParams)
  926. return logFields, nil
  927. }
  928. func MarshalProxyAnnounceRequest(request *ProxyAnnounceRequest) ([]byte, error) {
  929. payload, err := marshalRecord(request, recordTypeAPIProxyAnnounceRequest)
  930. return payload, errors.Trace(err)
  931. }
  932. func UnmarshalProxyAnnounceRequest(payload []byte) (*ProxyAnnounceRequest, error) {
  933. var request *ProxyAnnounceRequest
  934. err := unmarshalRecord(recordTypeAPIProxyAnnounceRequest, payload, &request)
  935. return request, errors.Trace(err)
  936. }
  937. func MarshalProxyAnnounceResponse(response *ProxyAnnounceResponse) ([]byte, error) {
  938. payload, err := marshalRecord(response, recordTypeAPIProxyAnnounceResponse)
  939. return payload, errors.Trace(err)
  940. }
  941. func UnmarshalProxyAnnounceResponse(payload []byte) (*ProxyAnnounceResponse, error) {
  942. var response *ProxyAnnounceResponse
  943. err := unmarshalRecord(recordTypeAPIProxyAnnounceResponse, payload, &response)
  944. return response, errors.Trace(err)
  945. }
  946. func MarshalProxyAnswerRequest(request *ProxyAnswerRequest) ([]byte, error) {
  947. payload, err := marshalRecord(request, recordTypeAPIProxyAnswerRequest)
  948. return payload, errors.Trace(err)
  949. }
  950. func UnmarshalProxyAnswerRequest(payload []byte) (*ProxyAnswerRequest, error) {
  951. var request *ProxyAnswerRequest
  952. err := unmarshalRecord(recordTypeAPIProxyAnswerRequest, payload, &request)
  953. return request, errors.Trace(err)
  954. }
  955. func MarshalProxyAnswerResponse(response *ProxyAnswerResponse) ([]byte, error) {
  956. payload, err := marshalRecord(response, recordTypeAPIProxyAnswerResponse)
  957. return payload, errors.Trace(err)
  958. }
  959. func UnmarshalProxyAnswerResponse(payload []byte) (*ProxyAnswerResponse, error) {
  960. var response *ProxyAnswerResponse
  961. err := unmarshalRecord(recordTypeAPIProxyAnswerResponse, payload, &response)
  962. return response, errors.Trace(err)
  963. }
  964. func MarshalClientOfferRequest(request *ClientOfferRequest) ([]byte, error) {
  965. payload, err := marshalRecord(request, recordTypeAPIClientOfferRequest)
  966. return payload, errors.Trace(err)
  967. }
  968. func UnmarshalClientOfferRequest(payload []byte) (*ClientOfferRequest, error) {
  969. var request *ClientOfferRequest
  970. err := unmarshalRecord(recordTypeAPIClientOfferRequest, payload, &request)
  971. return request, errors.Trace(err)
  972. }
  973. func MarshalClientOfferResponse(response *ClientOfferResponse) ([]byte, error) {
  974. payload, err := marshalRecord(response, recordTypeAPIClientOfferResponse)
  975. return payload, errors.Trace(err)
  976. }
  977. func UnmarshalClientOfferResponse(payload []byte) (*ClientOfferResponse, error) {
  978. var response *ClientOfferResponse
  979. err := unmarshalRecord(recordTypeAPIClientOfferResponse, payload, &response)
  980. return response, errors.Trace(err)
  981. }
  982. func MarshalClientRelayedPacketRequest(request *ClientRelayedPacketRequest) ([]byte, error) {
  983. payload, err := marshalRecord(request, recordTypeAPIClientRelayedPacketRequest)
  984. return payload, errors.Trace(err)
  985. }
  986. func UnmarshalClientRelayedPacketRequest(payload []byte) (*ClientRelayedPacketRequest, error) {
  987. var request *ClientRelayedPacketRequest
  988. err := unmarshalRecord(recordTypeAPIClientRelayedPacketRequest, payload, &request)
  989. return request, errors.Trace(err)
  990. }
  991. func MarshalClientRelayedPacketResponse(response *ClientRelayedPacketResponse) ([]byte, error) {
  992. payload, err := marshalRecord(response, recordTypeAPIClientRelayedPacketResponse)
  993. return payload, errors.Trace(err)
  994. }
  995. func UnmarshalClientRelayedPacketResponse(payload []byte) (*ClientRelayedPacketResponse, error) {
  996. var response *ClientRelayedPacketResponse
  997. err := unmarshalRecord(recordTypeAPIClientRelayedPacketResponse, payload, &response)
  998. return response, errors.Trace(err)
  999. }
  1000. func MarshalBrokerServerReport(request *BrokerServerReport) ([]byte, error) {
  1001. payload, err := marshalRecord(request, recordTypeAPIBrokerServerReport)
  1002. return payload, errors.Trace(err)
  1003. }
  1004. func UnmarshalBrokerServerReport(payload []byte) (*BrokerServerReport, error) {
  1005. var request *BrokerServerReport
  1006. err := unmarshalRecord(recordTypeAPIBrokerServerReport, payload, &request)
  1007. return request, errors.Trace(err)
  1008. }
  1009. func MarshalServerProxyQualityRequest(request *ServerProxyQualityRequest) ([]byte, error) {
  1010. payload, err := marshalRecord(request, recordTypeAPIServerProxyQualityRequest)
  1011. return payload, errors.Trace(err)
  1012. }
  1013. func UnmarshalServerProxyQualityRequest(payload []byte) (*ServerProxyQualityRequest, error) {
  1014. var request *ServerProxyQualityRequest
  1015. err := unmarshalRecord(recordTypeAPIServerProxyQualityRequest, payload, &request)
  1016. return request, errors.Trace(err)
  1017. }
  1018. func MarshalServerProxyQualityResponse(response *ServerProxyQualityResponse) ([]byte, error) {
  1019. payload, err := marshalRecord(response, recordTypeAPIServerProxyQualityResponse)
  1020. return payload, errors.Trace(err)
  1021. }
  1022. func UnmarshalServerProxyQualityResponse(payload []byte) (*ServerProxyQualityResponse, error) {
  1023. var response *ServerProxyQualityResponse
  1024. err := unmarshalRecord(recordTypeAPIServerProxyQualityResponse, payload, &response)
  1025. return response, errors.Trace(err)
  1026. }
  1027. func MarshalClientDSLRequest(request *ClientDSLRequest) ([]byte, error) {
  1028. payload, err := marshalRecord(request, recordTypeAPIClientDSLRequest)
  1029. return payload, errors.Trace(err)
  1030. }
  1031. func UnmarshalClientDSLRequest(payload []byte) (*ClientDSLRequest, error) {
  1032. var request *ClientDSLRequest
  1033. err := unmarshalRecord(recordTypeAPIClientDSLRequest, payload, &request)
  1034. return request, errors.Trace(err)
  1035. }
  1036. func MarshalClientDSLResponse(response *ClientDSLResponse) ([]byte, error) {
  1037. payload, err := marshalRecord(response, recordTypeAPIClientDSLResponse)
  1038. return payload, errors.Trace(err)
  1039. }
  1040. func UnmarshalClientDSLResponse(payload []byte) (*ClientDSLResponse, error) {
  1041. var response *ClientDSLResponse
  1042. err := unmarshalRecord(recordTypeAPIClientDSLResponse, payload, &response)
  1043. return response, errors.Trace(err)
  1044. }