inproxy.go 73 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182
  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 psiphon
  20. import (
  21. "bytes"
  22. "context"
  23. "encoding/binary"
  24. "fmt"
  25. "io"
  26. "net"
  27. "net/http"
  28. "net/netip"
  29. "runtime"
  30. "strconv"
  31. "sync"
  32. "sync/atomic"
  33. "syscall"
  34. "time"
  35. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
  36. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/errors"
  37. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/fragmentor"
  38. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/inproxy"
  39. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/parameters"
  40. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/prng"
  41. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/protocol"
  42. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/resolver"
  43. "github.com/cespare/xxhash"
  44. "golang.org/x/net/bpf"
  45. )
  46. // InproxyBrokerClientManager manages an InproxyBrokerClientInstance, an
  47. // in-proxy broker client, and its associated broker dial parameters, that
  48. // may be shared by multiple client dials or proxy instances. There is no
  49. // explicit close operation for the managed InproxyBrokerClientInstance.
  50. //
  51. // Once used, the current InproxyBrokerClientInstance and its broker client is
  52. // left actively connected to the broker, to minimize transport round trips
  53. // for additional requests.
  54. //
  55. // The InproxyBrokerClientManager and its components implement a replay system
  56. // for broker client dials. As one broker client is shared access multiple
  57. // client in-proxy dials, the broker dial parameters are replayed
  58. // independently from tunnel dial parameters.
  59. //
  60. // The NewInproxyBrokerClientInstance layer provides a fixed association
  61. // between a broker client and its broker dial parameters, ensuring that
  62. // in-proxy success/failure callbacks reference the correct replay parameters
  63. // when setting or clearing replay.
  64. //
  65. // A new InproxyBrokerClientInstance, including the broker dial parameters and
  66. // broker client, is instantiated when the active network ID changes, using
  67. // tactics for the new network.
  68. type InproxyBrokerClientManager struct {
  69. config *Config
  70. isProxy bool
  71. mutex sync.Mutex
  72. networkID string
  73. brokerClientInstance *InproxyBrokerClientInstance
  74. }
  75. // NewInproxyBrokerClientManager creates a new InproxyBrokerClientManager.
  76. // NewInproxyBrokerClientManager does not perform any network operations; the
  77. // managed InproxyBrokerClientInstance is initialized when used for a round
  78. // trip.
  79. func NewInproxyBrokerClientManager(
  80. config *Config, isProxy bool) *InproxyBrokerClientManager {
  81. b := &InproxyBrokerClientManager{
  82. config: config,
  83. isProxy: isProxy,
  84. }
  85. // b.brokerClientInstance is initialized on demand, when getBrokerClient
  86. // is called.
  87. return b
  88. }
  89. // TacticsApplied implements the TacticsAppliedReceiver interface, and is
  90. // called when tactics have changed, which triggers a broker client reset in
  91. // order to apply potentially changed parameters.
  92. func (b *InproxyBrokerClientManager) TacticsApplied() error {
  93. // TODO: as a future future enhancement, don't reset when the tactics
  94. // brokerSpecs.Hash() is unchanged?
  95. return errors.Trace(b.reset())
  96. }
  97. // GetBrokerClient returns the current, shared broker client and its
  98. // corresponding dial parametrers (for metrics logging). If there is no
  99. // current broker client, if the network ID differs from the network ID
  100. // associated with the previous broker client, a new broker client is
  101. // initialized.
  102. func (b *InproxyBrokerClientManager) GetBrokerClient(
  103. networkID string) (*inproxy.BrokerClient, *InproxyBrokerDialParameters, error) {
  104. b.mutex.Lock()
  105. defer b.mutex.Unlock()
  106. if b.brokerClientInstance == nil || b.networkID != networkID {
  107. err := b.reset()
  108. if err != nil {
  109. return nil, nil, errors.Trace(err)
  110. }
  111. }
  112. // The b.brokerClientInstance.brokerClient is wired up to refer back to
  113. // b.brokerClientInstance.brokerDialParams/roundTripper, etc.
  114. return b.brokerClientInstance.brokerClient,
  115. b.brokerClientInstance.brokerDialParams,
  116. nil
  117. }
  118. func (b *InproxyBrokerClientManager) resetBrokerClientOnRoundTripFailed() error {
  119. b.mutex.Lock()
  120. defer b.mutex.Unlock()
  121. return errors.Trace(b.reset())
  122. }
  123. func (b *InproxyBrokerClientManager) reset() error {
  124. // Assumes b.mutex lock is held.
  125. // Any existing broker client is removed, even if
  126. // NewInproxyBrokerClientInstance fails. This ensures, for example, that
  127. // an existing broker client is removed when its spec is no longer
  128. // available in tactics.
  129. b.networkID = ""
  130. b.brokerClientInstance = nil
  131. networkID := b.config.GetNetworkID()
  132. brokerClientInstance, err := NewInproxyBrokerClientInstance(
  133. b.config, b, networkID, b.isProxy)
  134. if err != nil {
  135. return errors.Trace(err)
  136. }
  137. b.networkID = networkID
  138. b.brokerClientInstance = brokerClientInstance
  139. return nil
  140. }
  141. // InproxyBrokerClientInstance pairs an inproxy.BrokerClient instance with an
  142. // implementation of the inproxy.BrokerDialCoordinator interface and the
  143. // associated, underlying broker dial paramaters. InproxyBrokerClientInstance
  144. // implements broker client dial replay.
  145. type InproxyBrokerClientInstance struct {
  146. config *Config
  147. brokerClientManager *InproxyBrokerClientManager
  148. networkID string
  149. brokerClientPrivateKey inproxy.SessionPrivateKey
  150. brokerClient *inproxy.BrokerClient
  151. brokerPublicKey inproxy.SessionPublicKey
  152. brokerRootObfuscationSecret inproxy.ObfuscationSecret
  153. brokerDialParams *InproxyBrokerDialParameters
  154. replayEnabled bool
  155. isReplay bool
  156. roundTripper *InproxyBrokerRoundTripper
  157. personalCompartmentIDs []inproxy.ID
  158. commonCompartmentIDs []inproxy.ID
  159. announceRequestTimeout time.Duration
  160. announceRetryDelay time.Duration
  161. announceRetryJitter float64
  162. answerRequestTimeout time.Duration
  163. offerRequestTimeout time.Duration
  164. offerRetryDelay time.Duration
  165. offerRetryJitter float64
  166. relayedPacketRequestTimeout time.Duration
  167. replayRetainFailedProbability float64
  168. replayUpdateFrequency time.Duration
  169. mutex sync.Mutex
  170. lastStoreReplay time.Time
  171. }
  172. // NewInproxyBrokerClientInstance creates a new InproxyBrokerClientInstance.
  173. // NewInproxyBrokerClientManager does not perform any network operations; the
  174. // new InproxyBrokerClientInstance is initialized when used for a round
  175. // trip.
  176. func NewInproxyBrokerClientInstance(
  177. config *Config,
  178. brokerClientManager *InproxyBrokerClientManager,
  179. networkID string,
  180. isProxy bool) (*InproxyBrokerClientInstance, error) {
  181. p := config.GetParameters().Get()
  182. defer p.Close()
  183. // Select common or personal compartment IDs.
  184. commonCompartmentIDs, personalCompartmentIDs, err := prepareCompartmentIDs(config, p, isProxy)
  185. if err != nil {
  186. return nil, errors.Trace(err)
  187. }
  188. // Select the broker to use, optionally favoring brokers with replay
  189. // data.
  190. var brokerSpecs parameters.InproxyBrokerSpecsValue
  191. if isProxy {
  192. brokerSpecs = p.InproxyBrokerSpecs(
  193. parameters.InproxyProxyBrokerSpecs, parameters.InproxyBrokerSpecs)
  194. } else {
  195. brokerSpecs = p.InproxyBrokerSpecs(
  196. parameters.InproxyClientBrokerSpecs, parameters.InproxyBrokerSpecs)
  197. }
  198. if len(brokerSpecs) == 0 {
  199. return nil, errors.TraceNew("no broker specs")
  200. }
  201. // To ensure personal compartment ID client/proxy rendezvous at same
  202. // broker, simply pick the first configured broker.
  203. //
  204. // Limitations: there's no failover or load balancing for the personal
  205. // compartment ID case; and this logic assumes that the broker spec
  206. // tactics are the same for the client and proxy.
  207. if len(personalCompartmentIDs) > 0 {
  208. brokerSpecs = brokerSpecs[:1]
  209. }
  210. now := time.Now()
  211. // Prefer a broker with replay data.
  212. // Replay is disabled when the TTL, InproxyReplayBrokerDialParametersTTL,
  213. // is 0.
  214. ttl := p.Duration(parameters.InproxyReplayBrokerDialParametersTTL)
  215. replayEnabled := ttl > 0 &&
  216. !config.DisableReplay &&
  217. prng.FlipWeightedCoin(p.Float(parameters.InproxyReplayBrokerDialParametersProbability))
  218. brokerSpec, brokerDialParams, err :=
  219. ShuffleAndGetNetworkReplayParameters[parameters.InproxyBrokerSpec, InproxyBrokerDialParameters](
  220. networkID,
  221. replayEnabled,
  222. brokerSpecs,
  223. func(spec *parameters.InproxyBrokerSpec) string { return spec.BrokerPublicKey },
  224. func(spec *parameters.InproxyBrokerSpec, dialParams *InproxyBrokerDialParameters) bool {
  225. return dialParams.LastUsedTimestamp.After(now.Add(-ttl)) &&
  226. bytes.Equal(dialParams.LastUsedBrokerSpecHash, hashBrokerSpec(spec))
  227. })
  228. if err != nil {
  229. NoticeWarning("ShuffleAndGetNetworkReplayParameters failed: %v", errors.Trace(err))
  230. // When there's an error, try to continue, using a random broker spec
  231. // and no replay dial parameters.
  232. brokerSpec = brokerSpecs[prng.Intn(len(brokerSpecs)-1)]
  233. }
  234. // Generate new broker dial parameters if not replaying. Later, isReplay
  235. // is used to report the replay metric.
  236. isReplay := brokerDialParams != nil
  237. if !isReplay {
  238. brokerDialParams, err = MakeInproxyBrokerDialParameters(config, p, networkID, brokerSpec)
  239. if err != nil {
  240. return nil, errors.Trace(err)
  241. }
  242. } else {
  243. brokerDialParams.brokerSpec = brokerSpec
  244. err := brokerDialParams.prepareDialConfigs(config, p, networkID, true, nil)
  245. if err != nil {
  246. return nil, errors.Trace(err)
  247. }
  248. }
  249. // Load broker key material.
  250. brokerPublicKey, err := inproxy.SessionPublicKeyFromString(brokerSpec.BrokerPublicKey)
  251. if err != nil {
  252. return nil, errors.Trace(err)
  253. }
  254. brokerRootObfuscationSecret, err := inproxy.ObfuscationSecretFromString(brokerSpec.BrokerRootObfuscationSecret)
  255. if err != nil {
  256. return nil, errors.Trace(err)
  257. }
  258. roundTripper := NewInproxyBrokerRoundTripper(brokerDialParams)
  259. // Clients always generate an ephemeral session key pair. Proxies may opt
  260. // to use a long-lived key pair for proxied traffic attribution.
  261. var brokerClientPrivateKey inproxy.SessionPrivateKey
  262. if isProxy && config.InproxyProxySessionPrivateKey != "" {
  263. brokerClientPrivateKey, err = inproxy.SessionPrivateKeyFromString(config.InproxyProxySessionPrivateKey)
  264. if err != nil {
  265. return nil, errors.Trace(err)
  266. }
  267. } else {
  268. brokerClientPrivateKey, err = inproxy.GenerateSessionPrivateKey()
  269. if err != nil {
  270. return nil, errors.Trace(err)
  271. }
  272. }
  273. // InproxyBrokerClientInstance implements the
  274. // inproxy.BrokerDialCoordinator interface and passes itself to
  275. // inproxy.NewBrokerClient in order to provide the round tripper, key
  276. // material, compartment IDs, timeouts, and other configuration to the
  277. // in-proxy broker client.
  278. //
  279. // Timeouts are not replayed, but snapshots are stored in the
  280. // InproxyBrokerClientInstance for efficient lookup.
  281. b := &InproxyBrokerClientInstance{
  282. config: config,
  283. brokerClientManager: brokerClientManager,
  284. networkID: networkID,
  285. brokerClientPrivateKey: brokerClientPrivateKey,
  286. brokerPublicKey: brokerPublicKey,
  287. brokerRootObfuscationSecret: brokerRootObfuscationSecret,
  288. brokerDialParams: brokerDialParams,
  289. replayEnabled: replayEnabled,
  290. isReplay: isReplay,
  291. roundTripper: roundTripper,
  292. personalCompartmentIDs: personalCompartmentIDs,
  293. commonCompartmentIDs: commonCompartmentIDs,
  294. announceRequestTimeout: p.Duration(parameters.InproxyProxyAnnounceRequestTimeout),
  295. announceRetryDelay: p.Duration(parameters.InproxyProxyAnnounceRetryDelay),
  296. announceRetryJitter: p.Float(parameters.InproxyProxyAnnounceRetryJitter),
  297. answerRequestTimeout: p.Duration(parameters.InproxyProxyAnswerRequestTimeout),
  298. offerRequestTimeout: p.Duration(parameters.InproxyClientOfferRequestTimeout),
  299. offerRetryDelay: p.Duration(parameters.InproxyClientOfferRetryDelay),
  300. offerRetryJitter: p.Float(parameters.InproxyClientOfferRetryJitter),
  301. relayedPacketRequestTimeout: p.Duration(parameters.InproxyClientRelayedPacketRequestTimeout),
  302. replayRetainFailedProbability: p.Float(parameters.InproxyReplayBrokerRetainFailedProbability),
  303. replayUpdateFrequency: p.Duration(parameters.InproxyReplayBrokerUpdateFrequency),
  304. }
  305. // Initialize broker client. This will start with a fresh broker session.
  306. //
  307. // When resetBrokerClientOnRoundTripFailed is invoked due to a failure at
  308. // the transport level -- TLS or domain fronting --
  309. // NewInproxyBrokerClientInstance is invoked, resetting both the broker
  310. // client round tripper and the broker session. As a future enhancement,
  311. // consider distinguishing between transport and session errors and
  312. // retaining a valid established session when only the transport needs to
  313. // be reset/retried.
  314. b.brokerClient, err = inproxy.NewBrokerClient(b)
  315. if err != nil {
  316. return nil, errors.Trace(err)
  317. }
  318. // Set a finalizer to close any open network resources associated with the
  319. // round tripper once the InproxyBrokerClientInstance is no longer
  320. // referenced. Note that there's no explicit call to close in
  321. // InproxyBrokerClientManager.reset when a new instance is created in
  322. // case the old insstance is still in use.
  323. runtime.SetFinalizer(b, func(b *InproxyBrokerClientInstance) {
  324. _ = b.roundTripper.Close()
  325. })
  326. return b, nil
  327. }
  328. func prepareCompartmentIDs(
  329. config *Config,
  330. p parameters.ParametersAccessor,
  331. isProxy bool) ([]inproxy.ID, []inproxy.ID, error) {
  332. // Personal compartment IDs are loaded from the tunnel-core config; these
  333. // are set by the external app based on user input/configuration of IDs
  334. // generated by or obtained from personal proxies. Both clients and
  335. // proxies send personal compartment IDs to the in-proxy broker. For
  336. // clients, when personal compartment IDs are configured, no common
  337. // compartment IDs are prepared, ensuring matches with only proxies that
  338. // supply the corresponding personal compartment IDs.
  339. //
  340. // Common compartment IDs are obtained from tactics and merged with
  341. // previously learned IDs stored in the local datastore. When new IDs are
  342. // obtained from tactics, the merged list is written back to the
  343. // datastore. This allows for schemes where common compartment IDs are
  344. // distributed to sets of clients, then removed from distibution, and
  345. // still used to match proxies to those sets of clients. Only clients
  346. // send common compartment IDs to the in-proxy broker. Proxies are
  347. // automatically assigned to common compartments by the broker.
  348. //
  349. // Maximum compartment ID list lengths are enforced to ensure broker
  350. // request sizes don't grow unbounded.
  351. //
  352. // Limitation: currently, in max length trimming, new common compartment
  353. // IDs take precedence over older IDs.
  354. maxCompartmentIDListLength := p.Int(parameters.InproxyMaxCompartmentIDListLength)
  355. configPersonalCompartmentIDs := config.InproxyProxyPersonalCompartmentIDs
  356. if !isProxy {
  357. configPersonalCompartmentIDs = config.InproxyClientPersonalCompartmentIDs
  358. }
  359. personalCompartmentIDs, err := inproxy.IDsFromStrings(configPersonalCompartmentIDs)
  360. if err != nil {
  361. return nil, nil, errors.Trace(err)
  362. }
  363. if len(personalCompartmentIDs) > maxCompartmentIDListLength {
  364. // Trim the list. It's not expected that user-configured personal
  365. // compartment ID lists will exceed the max length.
  366. //
  367. // TODO: shuffle before trimming? Prioritize previous matches?
  368. personalCompartmentIDs = personalCompartmentIDs[:maxCompartmentIDListLength]
  369. }
  370. var commonCompartmentIDs []inproxy.ID
  371. if !isProxy && len(personalCompartmentIDs) == 0 {
  372. tacticsCommonCompartmentIDs := p.InproxyCompartmentIDs(parameters.InproxyCommonCompartmentIDs)
  373. knownCommonCompartmentIDs, err := LoadInproxyCommonCompartmentIDs()
  374. if err != nil {
  375. NoticeWarning("LoadInproxyCommonCompartmentIDs failed: %v", errors.Trace(err))
  376. // Continue with only the tactics common compartment IDs.
  377. }
  378. newCompartmentIDs := make([]string, 0, len(tacticsCommonCompartmentIDs))
  379. for _, compartmentID := range tacticsCommonCompartmentIDs {
  380. // TODO: faster lookup?
  381. if !common.Contains(knownCommonCompartmentIDs, compartmentID) {
  382. newCompartmentIDs = append(newCompartmentIDs, compartmentID)
  383. }
  384. }
  385. if len(newCompartmentIDs) > 0 {
  386. newCompartmentIDs = append(newCompartmentIDs, knownCommonCompartmentIDs...)
  387. // Locally store more than InproxyMaxCompartmentIDListLength known
  388. // common compartment IDs, in case the request limit parameter is
  389. // increased in the future.
  390. // maxPersistedCommonCompartmentIDListLength still limits the
  391. // length of the list to cap local memory and disk impact.
  392. maxPersistedCommonCompartmentIDListLength := 500 // ~16K
  393. if maxCompartmentIDListLength > maxPersistedCommonCompartmentIDListLength {
  394. maxPersistedCommonCompartmentIDListLength = maxCompartmentIDListLength
  395. }
  396. if len(newCompartmentIDs) > maxPersistedCommonCompartmentIDListLength {
  397. newCompartmentIDs = newCompartmentIDs[:maxPersistedCommonCompartmentIDListLength]
  398. }
  399. err := StoreInproxyCommonCompartmentIDs(newCompartmentIDs)
  400. if err != nil {
  401. NoticeWarning("StoreInproxyCommonCompartmentIDs failed: %v", errors.Trace(err))
  402. // Continue without persisting new common compartment IDs.
  403. }
  404. knownCommonCompartmentIDs = newCompartmentIDs
  405. }
  406. commonCompartmentIDs, err = inproxy.IDsFromStrings(knownCommonCompartmentIDs)
  407. if err != nil {
  408. return nil, nil, errors.Trace(err)
  409. }
  410. if len(commonCompartmentIDs) > maxCompartmentIDListLength {
  411. // TODO: shuffle before trimming? Prioritize previous matches?
  412. commonCompartmentIDs = commonCompartmentIDs[:maxCompartmentIDListLength]
  413. }
  414. }
  415. return commonCompartmentIDs, personalCompartmentIDs, nil
  416. }
  417. // Implements the inproxy.BrokerDialCoordinator interface.
  418. func (b *InproxyBrokerClientInstance) NetworkID() string {
  419. return b.networkID
  420. }
  421. // Implements the inproxy.BrokerDialCoordinator interface.
  422. func (b *InproxyBrokerClientInstance) NetworkType() inproxy.NetworkType {
  423. return getInproxyNetworkType(GetNetworkType(b.networkID))
  424. }
  425. // Implements the inproxy.BrokerDialCoordinator interface.
  426. func (b *InproxyBrokerClientInstance) CommonCompartmentIDs() []inproxy.ID {
  427. return b.commonCompartmentIDs
  428. }
  429. // Implements the inproxy.BrokerDialCoordinator interface.
  430. func (b *InproxyBrokerClientInstance) PersonalCompartmentIDs() []inproxy.ID {
  431. return b.personalCompartmentIDs
  432. }
  433. // Implements the inproxy.BrokerDialCoordinator interface.
  434. func (b *InproxyBrokerClientInstance) BrokerClientPrivateKey() inproxy.SessionPrivateKey {
  435. return b.brokerClientPrivateKey
  436. }
  437. // Implements the inproxy.BrokerDialCoordinator interface.
  438. func (b *InproxyBrokerClientInstance) BrokerPublicKey() inproxy.SessionPublicKey {
  439. return b.brokerPublicKey
  440. }
  441. // Implements the inproxy.BrokerDialCoordinator interface.
  442. func (b *InproxyBrokerClientInstance) BrokerRootObfuscationSecret() inproxy.ObfuscationSecret {
  443. return b.brokerRootObfuscationSecret
  444. }
  445. // Implements the inproxy.BrokerDialCoordinator interface.
  446. func (b *InproxyBrokerClientInstance) BrokerClientRoundTripper() (inproxy.RoundTripper, error) {
  447. // Returns the same round tripper for the lifetime of the
  448. // inproxy.BrokerDialCoordinator, ensuring all requests for one in-proxy
  449. // dial or proxy relay use the same broker, as is necessary due to the
  450. // broker state for the proxy announce/answer, client broker/server
  451. // relay, etc.
  452. return b.roundTripper, nil
  453. }
  454. // Implements the inproxy.BrokerDialCoordinator interface.
  455. func (b *InproxyBrokerClientInstance) BrokerClientRoundTripperSucceeded(roundTripper inproxy.RoundTripper) {
  456. b.mutex.Lock()
  457. defer b.mutex.Unlock()
  458. if roundTripper != b.roundTripper {
  459. // Passing in the round tripper obtained from BrokerClientRoundTripper
  460. // is just used for sanity check in this implementation, since each
  461. // InproxyBrokerClientInstance has exactly one round tripper.
  462. NoticeError("BrokerClientRoundTripperSucceeded: roundTripper instance mismatch")
  463. return
  464. }
  465. // Set replay or extend the broker dial parameters replay TTL after a
  466. // success. With tunnel dial parameters, the replay TTL is extended after
  467. // every successful tunnel connection. Since there are potentially more
  468. // and more frequent broker round trips one tunnel dial, the TTL is only
  469. // extended after some target duration has elapsed, to avoid excessive
  470. // datastore writes.
  471. now := time.Now()
  472. if b.replayEnabled && now.Sub(b.lastStoreReplay) > b.replayUpdateFrequency {
  473. b.brokerDialParams.LastUsedTimestamp = time.Now()
  474. err := SetNetworkReplayParameters[InproxyBrokerDialParameters](
  475. b.networkID, b.brokerDialParams.brokerSpec.BrokerPublicKey, b.brokerDialParams)
  476. if err != nil {
  477. NoticeWarning("StoreBrokerDialParameters failed: %v", errors.Trace(err))
  478. // Continue without persisting replay changes.
  479. } else {
  480. b.lastStoreReplay = now
  481. }
  482. }
  483. // Verify/extend the resolver cache entry for any resolved domain after a
  484. // success.
  485. //
  486. // Limitation: currently this re-extends regardless of how long ago the DNS
  487. // resolve happened.
  488. resolver := b.config.GetResolver()
  489. if resolver != nil {
  490. resolver.VerifyCacheExtension(b.brokerDialParams.FrontingDialAddress)
  491. }
  492. }
  493. // Implements the inproxy.BrokerDialCoordinator interface.
  494. func (b *InproxyBrokerClientInstance) BrokerClientRoundTripperFailed(roundTripper inproxy.RoundTripper) {
  495. b.mutex.Lock()
  496. defer b.mutex.Unlock()
  497. if roundTripper != b.roundTripper {
  498. // Passing in the round tripper obtained from BrokerClientRoundTripper
  499. // is just used for sanity check in this implementation, since each
  500. // InproxyBrokerClientInstance has exactly one round tripper.
  501. NoticeError("BrokerClientRoundTripperFailed: roundTripper instance mismatch")
  502. return
  503. }
  504. // Delete any persistent replay dial parameters. Unlike with the success
  505. // case, consecutive, repeated deletes shouldn't write to storage, so
  506. // they are not avoided.
  507. if b.replayEnabled &&
  508. !prng.FlipWeightedCoin(b.replayRetainFailedProbability) {
  509. // Limitation: there's a race condition with multiple
  510. // InproxyBrokerClientInstances writing to the replay datastore for
  511. // the same broker, such as in the case where there's a dual-mode
  512. // in-proxy client and proxy; this delete could potentially clobber a
  513. // concurrent fresh replay store after a success.
  514. //
  515. // TODO: add an additional storage key distinguisher for each instance?
  516. err := DeleteNetworkReplayParameters[InproxyBrokerDialParameters](
  517. b.networkID, b.brokerDialParams.brokerSpec.BrokerPublicKey)
  518. if err != nil {
  519. NoticeWarning("DeleteBrokerDialParameters failed: %v", errors.Trace(err))
  520. // Continue without resetting replay.
  521. }
  522. }
  523. // Invoke resetBrokerClientOnRoundTripFailed to signal the
  524. // InproxyBrokerClientManager to create a new
  525. // InproxyBrokerClientInstance, with new dial parameters and a new round
  526. // tripper, after a failure.
  527. //
  528. // This InproxyBrokerClientInstance doesn't change its dial parameters or
  529. // round tripper to ensure that any concurrent usage retains affinity
  530. // with the same parameters and broker.
  531. //
  532. // Limitation: a transport-level failure may unnecessarily reset the
  533. // broker session state; see comment in NewInproxyBrokerClientInstance.
  534. err := b.brokerClientManager.resetBrokerClientOnRoundTripFailed()
  535. if err != nil {
  536. NoticeWarning("reset broker client failed: %v", errors.Trace(err))
  537. // Continue with old broker client instance.
  538. }
  539. }
  540. // Implements the inproxy.BrokerDialCoordinator interface.
  541. func (b *InproxyBrokerClientInstance) AnnounceRequestTimeout() time.Duration {
  542. return b.announceRequestTimeout
  543. }
  544. // Implements the inproxy.BrokerDialCoordinator interface.
  545. func (b *InproxyBrokerClientInstance) AnnounceRetryDelay() time.Duration {
  546. return b.announceRetryDelay
  547. }
  548. // Implements the inproxy.BrokerDialCoordinator interface.
  549. func (b *InproxyBrokerClientInstance) AnnounceRetryJitter() float64 {
  550. return b.announceRetryJitter
  551. }
  552. // Implements the inproxy.BrokerDialCoordinator interface.
  553. func (b *InproxyBrokerClientInstance) AnswerRequestTimeout() time.Duration {
  554. return b.answerRequestTimeout
  555. }
  556. // Implements the inproxy.BrokerDialCoordinator interface.
  557. func (b *InproxyBrokerClientInstance) OfferRequestTimeout() time.Duration {
  558. return b.offerRequestTimeout
  559. }
  560. // Implements the inproxy.BrokerDialCoordinator interface.
  561. func (b *InproxyBrokerClientInstance) OfferRetryDelay() time.Duration {
  562. return b.offerRetryDelay
  563. }
  564. // Implements the inproxy.BrokerDialCoordinator interface.
  565. func (b *InproxyBrokerClientInstance) OfferRetryJitter() float64 {
  566. return b.offerRetryJitter
  567. }
  568. // Implements the inproxy.BrokerDialCoordinator interface.
  569. func (b *InproxyBrokerClientInstance) RelayedPacketRequestTimeout() time.Duration {
  570. return b.relayedPacketRequestTimeout
  571. }
  572. // InproxyBrokerDialParameters represents a selected broker transport and dial
  573. // paramaters.
  574. //
  575. // InproxyBrokerDialParameters is used to configure dialers; as a persistent
  576. // record to store successful dial parameters for replay; and to report dial
  577. // stats in notices and Psiphon API calls.
  578. //
  579. // InproxyBrokerDialParameters is similar to tunnel DialParameters, but is
  580. // specific to the in-proxy broker dial phase.
  581. type InproxyBrokerDialParameters struct {
  582. brokerSpec *parameters.InproxyBrokerSpec `json:"-"`
  583. isReplay bool `json:"-"`
  584. LastUsedTimestamp time.Time
  585. LastUsedBrokerSpecHash []byte
  586. NetworkLatencyMultiplier float64
  587. BrokerTransport string
  588. DialAddress string
  589. FrontingProviderID string
  590. FrontingDialAddress string
  591. SNIServerName string
  592. TransformedHostName bool
  593. VerifyServerName string
  594. VerifyPins []string
  595. HostHeader string
  596. ResolvedIPAddress atomic.Value `json:"-"`
  597. TLSProfile string
  598. TLSVersion string
  599. RandomizedTLSProfileSeed *prng.Seed
  600. NoDefaultTLSSessionID bool
  601. TLSFragmentClientHello bool
  602. SelectedUserAgent bool
  603. UserAgent string
  604. BPFProgramName string
  605. BPFProgramInstructions []bpf.RawInstruction
  606. FragmentorSeed *prng.Seed
  607. ResolveParameters *resolver.ResolveParameters
  608. dialConfig *DialConfig `json:"-"`
  609. meekConfig *MeekConfig `json:"-"`
  610. }
  611. // MakeInproxyBrokerDialParameters creates a new InproxyBrokerDialParameters.
  612. func MakeInproxyBrokerDialParameters(
  613. config *Config,
  614. p parameters.ParametersAccessor,
  615. networkID string,
  616. brokerSpec *parameters.InproxyBrokerSpec) (*InproxyBrokerDialParameters, error) {
  617. // This function duplicates some code from MakeDialParameters and
  618. // makeFrontedHTTPClient. To simplify the logic, the Replay<Component>
  619. // tactic flags for individual dial components are ignored.
  620. //
  621. // TODO: merge common functionality?
  622. if config.UseUpstreamProxy() {
  623. return nil, errors.TraceNew("upstream proxy unsupported")
  624. }
  625. currentTimestamp := time.Now()
  626. var brokerDialParams *InproxyBrokerDialParameters
  627. // Select new broker dial parameters
  628. brokerDialParams = &InproxyBrokerDialParameters{
  629. brokerSpec: brokerSpec,
  630. LastUsedTimestamp: currentTimestamp,
  631. LastUsedBrokerSpecHash: hashBrokerSpec(brokerSpec),
  632. }
  633. // Network latency multiplier
  634. brokerDialParams.NetworkLatencyMultiplier = prng.ExpFloat64Range(
  635. p.Float(parameters.NetworkLatencyMultiplierMin),
  636. p.Float(parameters.NetworkLatencyMultiplierMax),
  637. p.Float(parameters.NetworkLatencyMultiplierLambda))
  638. // Select fronting configuration
  639. var err error
  640. brokerDialParams.FrontingProviderID,
  641. brokerDialParams.BrokerTransport,
  642. brokerDialParams.FrontingDialAddress,
  643. brokerDialParams.SNIServerName,
  644. brokerDialParams.VerifyServerName,
  645. brokerDialParams.VerifyPins,
  646. brokerDialParams.HostHeader,
  647. err = brokerDialParams.brokerSpec.BrokerFrontingSpecs.SelectParameters()
  648. if err != nil {
  649. return nil, errors.Trace(err)
  650. }
  651. // At this time, the broker client, the transport is limited to fronted
  652. // HTTPS.
  653. //
  654. // MeekModePlaintextRoundTrip currently disallows HTTP, as it must for
  655. // Conjure's request payloads, but the in-proxy broker session payload is
  656. // obfuscated. As a future enhancement, allow HTTP for the in-proxy
  657. // broker case, skip selecting TLS tactics and select HTTP tactics such
  658. // as HTTPTransformerParameters.
  659. if brokerDialParams.BrokerTransport == protocol.FRONTING_TRANSPORT_HTTP {
  660. return nil, errors.TraceNew("unsupported fronting transport")
  661. }
  662. // Determine and use the equivilent tunnel protocol for tactics
  663. // selections. For example, for the broker transport FRONTED-HTTPS, use
  664. // the tactics for FRONTED-MEEK-OSSH.
  665. equivilentTunnelProtocol, err := protocol.EquivilentTunnelProtocol(brokerDialParams.BrokerTransport)
  666. if err != nil {
  667. return nil, errors.Trace(err)
  668. }
  669. // FrontSpec.Addresses may include a port; default to 443 if none.
  670. if _, _, err := net.SplitHostPort(brokerDialParams.FrontingDialAddress); err == nil {
  671. brokerDialParams.DialAddress = brokerDialParams.FrontingDialAddress
  672. } else {
  673. brokerDialParams.DialAddress = net.JoinHostPort(brokerDialParams.FrontingDialAddress, "443")
  674. }
  675. // SNI configuration
  676. //
  677. // For a FrontingSpec, an SNI value of "" indicates to disable/omit SNI, so
  678. // never transform in that case.
  679. if brokerDialParams.SNIServerName != "" {
  680. if p.WeightedCoinFlip(parameters.TransformHostNameProbability) {
  681. brokerDialParams.SNIServerName = selectHostName(equivilentTunnelProtocol, p)
  682. brokerDialParams.TransformedHostName = true
  683. }
  684. }
  685. // TLS configuration
  686. //
  687. // The requireTLS13 flag is set to true in order to use only modern TLS
  688. // fingerprints which should support HTTP/2 in the ALPN.
  689. //
  690. // TODO: TLS padding, NoDefaultTLSSessionID
  691. brokerDialParams.TLSProfile,
  692. brokerDialParams.TLSVersion,
  693. brokerDialParams.RandomizedTLSProfileSeed,
  694. err = SelectTLSProfile(false, true, true, brokerDialParams.FrontingProviderID, p)
  695. brokerDialParams.NoDefaultTLSSessionID = p.WeightedCoinFlip(
  696. parameters.NoDefaultTLSSessionIDProbability)
  697. if brokerDialParams.SNIServerName != "" && net.ParseIP(brokerDialParams.SNIServerName) == nil {
  698. tlsFragmentorLimitProtocols := p.TunnelProtocols(parameters.TLSFragmentClientHelloLimitProtocols)
  699. if len(tlsFragmentorLimitProtocols) == 0 || common.Contains(tlsFragmentorLimitProtocols, equivilentTunnelProtocol) {
  700. brokerDialParams.TLSFragmentClientHello = p.WeightedCoinFlip(parameters.TLSFragmentClientHelloProbability)
  701. }
  702. }
  703. // User Agent configuration
  704. dialCustomHeaders := makeDialCustomHeaders(config, p)
  705. brokerDialParams.SelectedUserAgent, brokerDialParams.UserAgent = selectUserAgentIfUnset(p, dialCustomHeaders)
  706. // BPF configuration
  707. if ClientBPFEnabled() &&
  708. protocol.TunnelProtocolMayUseClientBPF(equivilentTunnelProtocol) {
  709. if p.WeightedCoinFlip(parameters.BPFClientTCPProbability) {
  710. brokerDialParams.BPFProgramName = ""
  711. brokerDialParams.BPFProgramInstructions = nil
  712. ok, name, rawInstructions := p.BPFProgram(parameters.BPFClientTCPProgram)
  713. if ok {
  714. brokerDialParams.BPFProgramName = name
  715. brokerDialParams.BPFProgramInstructions = rawInstructions
  716. }
  717. }
  718. }
  719. // Fragmentor configuration
  720. brokerDialParams.FragmentorSeed, err = prng.NewSeed()
  721. if err != nil {
  722. return nil, errors.Trace(err)
  723. }
  724. // Resolver configuration
  725. //
  726. // The custom resolcer is wired up only when there is a domain to be
  727. // resolved; GetMetrics will log resolver metrics when the resolver is set.
  728. if net.ParseIP(brokerDialParams.FrontingDialAddress) == nil {
  729. resolver := config.GetResolver()
  730. if resolver == nil {
  731. return nil, errors.TraceNew("missing resolver")
  732. }
  733. brokerDialParams.ResolveParameters, err = resolver.MakeResolveParameters(
  734. p, brokerDialParams.FrontingProviderID, brokerDialParams.FrontingDialAddress)
  735. if err != nil {
  736. return nil, errors.Trace(err)
  737. }
  738. }
  739. // Initialize Dial/MeekConfigs to be passed to the corresponding dialers.
  740. err = brokerDialParams.prepareDialConfigs(config, p, networkID, false, dialCustomHeaders)
  741. if err != nil {
  742. return nil, errors.Trace(err)
  743. }
  744. return brokerDialParams, nil
  745. }
  746. // prepareDialConfigs is called for both new and replayed broker dial parameters.
  747. func (brokerDialParams *InproxyBrokerDialParameters) prepareDialConfigs(
  748. config *Config,
  749. p parameters.ParametersAccessor,
  750. networkID string,
  751. isReplay bool,
  752. dialCustomHeaders http.Header) error {
  753. brokerDialParams.isReplay = isReplay
  754. equivilentTunnelProtocol, err := protocol.EquivilentTunnelProtocol(brokerDialParams.BrokerTransport)
  755. if err != nil {
  756. return errors.Trace(err)
  757. }
  758. // Custom headers and User Agent
  759. if dialCustomHeaders == nil {
  760. dialCustomHeaders = makeDialCustomHeaders(config, p)
  761. }
  762. if brokerDialParams.SelectedUserAgent {
  763. // Limitation: if config.CustomHeaders adds a User-Agent between
  764. // replays, it may be ignored due to replaying a selected User-Agent.
  765. dialCustomHeaders.Set("User-Agent", brokerDialParams.UserAgent)
  766. }
  767. // Fragmentor
  768. fragmentorConfig := fragmentor.NewUpstreamConfig(
  769. p, equivilentTunnelProtocol, brokerDialParams.FragmentorSeed)
  770. // Resolver
  771. var resolveIP func(ctx context.Context, hostname string) ([]net.IP, error)
  772. if net.ParseIP(brokerDialParams.FrontingDialAddress) == nil {
  773. resolver := config.GetResolver()
  774. if resolver == nil {
  775. return errors.TraceNew("missing resolver")
  776. }
  777. resolveIP = func(ctx context.Context, hostname string) ([]net.IP, error) {
  778. IPs, err := resolver.ResolveIP(
  779. ctx, networkID, brokerDialParams.ResolveParameters, hostname)
  780. return IPs, errors.Trace(err)
  781. }
  782. } else {
  783. resolveIP = func(ctx context.Context, hostname string) ([]net.IP, error) {
  784. return nil, errors.TraceNew("unexpected resolve")
  785. }
  786. }
  787. // DialConfig
  788. brokerDialParams.ResolvedIPAddress.Store("")
  789. brokerDialParams.dialConfig = &DialConfig{
  790. DiagnosticID: brokerDialParams.brokerSpec.BrokerPublicKey,
  791. CustomHeaders: dialCustomHeaders,
  792. BPFProgramInstructions: brokerDialParams.BPFProgramInstructions,
  793. DeviceBinder: config.deviceBinder,
  794. IPv6Synthesizer: config.IPv6Synthesizer,
  795. ResolveIP: resolveIP,
  796. TrustedCACertificatesFilename: config.TrustedCACertificatesFilename,
  797. FragmentorConfig: fragmentorConfig,
  798. ResolvedIPCallback: func(IPAddress string) {
  799. brokerDialParams.ResolvedIPAddress.Store(IPAddress)
  800. },
  801. }
  802. // MeekDialConfig
  803. //
  804. // The broker round trips use MeekModePlaintextRoundTrip without meek
  805. // cookies, so meek obfuscation is not configured. The in-proxy broker
  806. // session payloads have their own obfuscation layer.
  807. addPsiphonFrontingHeader := false
  808. if brokerDialParams.FrontingProviderID != "" {
  809. addPsiphonFrontingHeader = common.Contains(
  810. p.LabeledTunnelProtocols(
  811. parameters.AddFrontingProviderPsiphonFrontingHeader,
  812. brokerDialParams.FrontingProviderID),
  813. equivilentTunnelProtocol)
  814. }
  815. brokerDialParams.meekConfig = &MeekConfig{
  816. Mode: MeekModePlaintextRoundTrip,
  817. DiagnosticID: brokerDialParams.FrontingProviderID,
  818. Parameters: config.GetParameters(),
  819. DialAddress: brokerDialParams.DialAddress,
  820. TLSProfile: brokerDialParams.TLSProfile,
  821. NoDefaultTLSSessionID: brokerDialParams.NoDefaultTLSSessionID,
  822. RandomizedTLSProfileSeed: brokerDialParams.RandomizedTLSProfileSeed,
  823. SNIServerName: brokerDialParams.SNIServerName,
  824. AddPsiphonFrontingHeader: addPsiphonFrontingHeader,
  825. VerifyServerName: brokerDialParams.VerifyServerName,
  826. VerifyPins: brokerDialParams.VerifyPins,
  827. HostHeader: brokerDialParams.HostHeader,
  828. TransformedHostName: brokerDialParams.TransformedHostName,
  829. NetworkLatencyMultiplier: brokerDialParams.NetworkLatencyMultiplier,
  830. AdditionalHeaders: config.MeekAdditionalHeaders,
  831. }
  832. switch brokerDialParams.BrokerTransport {
  833. case protocol.FRONTING_TRANSPORT_HTTPS:
  834. brokerDialParams.meekConfig.UseHTTPS = true
  835. case protocol.FRONTING_TRANSPORT_QUIC:
  836. brokerDialParams.meekConfig.UseQUIC = true
  837. }
  838. return nil
  839. }
  840. // GetMetrics implements the common.MetricsSource interface and returns log
  841. // fields detailing the broker dial parameters.
  842. func (brokerDialParams *InproxyBrokerDialParameters) GetMetrics() common.LogFields {
  843. logFields := make(common.LogFields)
  844. logFields["inproxy_broker_transport"] = brokerDialParams.BrokerTransport
  845. isReplay := "0"
  846. if brokerDialParams.isReplay {
  847. isReplay = "1"
  848. }
  849. logFields["inproxy_broker_is_replay"] = isReplay
  850. // Note: as At the broker client transport is currently limited to domain
  851. // fronted HTTPS, the following related parameters are included
  852. // unconditionally.
  853. logFields["inproxy_broker_fronting_provider_id"] = brokerDialParams.FrontingProviderID
  854. logFields["inproxy_broker_dial_address"] = brokerDialParams.FrontingDialAddress
  855. resolvedIPAddress := brokerDialParams.ResolvedIPAddress.Load().(string)
  856. if resolvedIPAddress != "" {
  857. logFields["inproxy_broker_resolved_ip_address"] = resolvedIPAddress
  858. }
  859. if brokerDialParams.SNIServerName != "" {
  860. logFields["inproxy_broker_sni_server_name"] = brokerDialParams.SNIServerName
  861. }
  862. logFields["inproxy_broker_host_header"] = brokerDialParams.HostHeader
  863. transformedHostName := "0"
  864. if brokerDialParams.TransformedHostName {
  865. transformedHostName = "1"
  866. }
  867. logFields["inproxy_broker_transformed_host_name"] = transformedHostName
  868. if brokerDialParams.UserAgent != "" {
  869. logFields["inproxy_broker_user_agent"] = brokerDialParams.UserAgent
  870. }
  871. if brokerDialParams.BrokerTransport == protocol.FRONTING_TRANSPORT_HTTPS {
  872. if brokerDialParams.TLSProfile != "" {
  873. logFields["inproxy_broker_tls_profile"] = brokerDialParams.TLSProfile
  874. }
  875. logFields["inproxy_broker_tls_version"] = brokerDialParams.TLSVersion
  876. tlsFragmented := "0"
  877. if brokerDialParams.TLSFragmentClientHello {
  878. tlsFragmented = "1"
  879. }
  880. logFields["inproxy_broker_tls_fragmented"] = tlsFragmented
  881. }
  882. if brokerDialParams.BPFProgramName != "" {
  883. logFields["inproxy_broker_client_bpf"] = brokerDialParams.BPFProgramName
  884. }
  885. if brokerDialParams.ResolveParameters != nil {
  886. // See comment for dialParams.ResolveParameters handling in
  887. // getBaseAPIParameters.
  888. if brokerDialParams.ResolveParameters.PreresolvedIPAddress != "" {
  889. dialDomain, _, _ := net.SplitHostPort(brokerDialParams.DialAddress)
  890. if brokerDialParams.ResolveParameters.PreresolvedDomain == dialDomain {
  891. logFields["inproxy_broker_dns_preresolved"] = brokerDialParams.ResolveParameters.PreresolvedIPAddress
  892. }
  893. }
  894. if brokerDialParams.ResolveParameters.PreferAlternateDNSServer {
  895. logFields["inproxy_broker_dns_preferred"] = brokerDialParams.ResolveParameters.AlternateDNSServer
  896. }
  897. if brokerDialParams.ResolveParameters.ProtocolTransformName != "" {
  898. logFields["inproxy_broker_dns_transform"] = brokerDialParams.ResolveParameters.ProtocolTransformName
  899. }
  900. logFields["inproxy_broker_dns_attempt"] = strconv.Itoa(
  901. brokerDialParams.ResolveParameters.GetFirstAttemptWithAnswer())
  902. }
  903. // TODO: get fragmentor metrics, if any, from MeekConn.
  904. return logFields
  905. }
  906. // hashBrokerSpec hashes the broker spec. The hash is used to detect when
  907. // broker spec tactics have changed.
  908. func hashBrokerSpec(spec *parameters.InproxyBrokerSpec) []byte {
  909. var hash [8]byte
  910. binary.BigEndian.PutUint64(
  911. hash[:],
  912. uint64(xxhash.Sum64String(fmt.Sprintf("%+v", spec))))
  913. return hash[:]
  914. }
  915. // InproxyBrokerRoundTripper is a broker request round trip transport
  916. // implemented using MeekConn in MeekModePlaintextRoundTrip mode, utilizing
  917. // MeekConn's domain fronting capabilities and using persistent and
  918. // multiplexed connections, via HTTP/2, to support multiple concurrent
  919. // in-flight round trips.
  920. //
  921. // InproxyBrokerRoundTripper implements the inproxy.RoundTripper interface.
  922. type InproxyBrokerRoundTripper struct {
  923. brokerDialParams *InproxyBrokerDialParameters
  924. runCtx context.Context
  925. stopRunning context.CancelFunc
  926. dial int32
  927. dialCompleted chan struct{}
  928. dialErr error
  929. conn *MeekConn
  930. }
  931. // NewInproxyBrokerRoundTripper creates a new InproxyBrokerRoundTripper. The
  932. // initial DialMeek is defered until the first call to RoundTrip, so
  933. // NewInproxyBrokerRoundTripper does not perform any network operations.
  934. //
  935. // The input brokerDialParams dial parameter and config fields must not
  936. // modifed after NewInproxyBrokerRoundTripper is called.
  937. func NewInproxyBrokerRoundTripper(
  938. brokerDialParams *InproxyBrokerDialParameters) *InproxyBrokerRoundTripper {
  939. runCtx, stopRunning := context.WithCancel(context.Background())
  940. return &InproxyBrokerRoundTripper{
  941. brokerDialParams: brokerDialParams,
  942. runCtx: runCtx,
  943. stopRunning: stopRunning,
  944. dialCompleted: make(chan struct{}),
  945. }
  946. }
  947. // Close interrupts any in-flight request and closes the underlying
  948. // MeekConn.
  949. func (rt *InproxyBrokerRoundTripper) Close() error {
  950. // Interrupt any DialMeek or RoundTrip.
  951. rt.stopRunning()
  952. if atomic.CompareAndSwapInt32(&rt.dial, 0, 1) {
  953. // RoundTrip has not yet been called or has not yet kicked off
  954. // DialMeek, so there is no MeekConn to close. Prevent any future
  955. // DialMeek by signaling dialCompleted and fail any future round trip
  956. // attempt by setting dialErr.
  957. rt.dialErr = errors.TraceNew("closed")
  958. close(rt.dialCompleted)
  959. } else {
  960. // Await any ongoing DialMeek or RoundTrip (stopRunning should
  961. // interrupt either one quickly).
  962. <-rt.dialCompleted
  963. if rt.conn != nil {
  964. _ = rt.conn.Close()
  965. }
  966. }
  967. // As with MeekConn.Close, any Close errors from underlying conns are not
  968. // propagated.
  969. return nil
  970. }
  971. // RoundTrip transports a request to the broker endpoint and returns a
  972. // response.
  973. func (rt *InproxyBrokerRoundTripper) RoundTrip(
  974. ctx context.Context, requestPayload []byte) ([]byte, error) {
  975. // Cancel DialMeek or MeekConn.RoundTrip when:
  976. // - Close is called
  977. // - the input context is done
  978. ctx, cancelFunc := common.MergeContextCancel(ctx, rt.runCtx)
  979. defer cancelFunc()
  980. // The first RoundTrip caller will perform the DialMeek step, which
  981. // establishes the TLS trasport connection to the fronted endpoint.
  982. // Following callers will await that DialMeek or share an established
  983. // connection.
  984. //
  985. // To accomodate using custom utls fingerprints, with varying ALPNs, with
  986. // net/http, DialMeek completes a full TLS handshake before instantiating
  987. // the appropriate http.Transport or http2.Transport. Until that first
  988. // DialMeek completes, and unlike standard net/http round trips,
  989. // InproxyBrokerRoundTripper won't spawn distinct TLS persistent
  990. // connections for concurrent round trips. After DialMeek, concurrent
  991. // round trips over HTTP/2 connections may simply share the one TLS
  992. // connection, while concurrent round trips over HTTP connections may
  993. // spawn additional TLS persistent connections.
  994. //
  995. // There is no retry here is DialMeek fails, as higher levels will invoke
  996. // BrokerClientRoundTripperFailed on failure, clear any replay, select
  997. // new dial parameters, and retry.
  998. if atomic.CompareAndSwapInt32(&rt.dial, 0, 1) {
  999. // DialMeek hasn't been called yet.
  1000. conn, err := DialMeek(
  1001. ctx,
  1002. rt.brokerDialParams.meekConfig,
  1003. rt.brokerDialParams.dialConfig)
  1004. rt.conn = conn
  1005. rt.dialErr = err
  1006. close(rt.dialCompleted)
  1007. if err != nil {
  1008. return nil, errors.Trace(rt.dialErr)
  1009. }
  1010. } else {
  1011. // Await any ongoing DialMeek run by a concurrent RoundTrip caller.
  1012. select {
  1013. case <-rt.dialCompleted:
  1014. case <-ctx.Done():
  1015. return nil, errors.Trace(ctx.Err())
  1016. }
  1017. if rt.dialErr != nil {
  1018. return nil, errors.Trace(rt.dialErr)
  1019. }
  1020. }
  1021. // At this point, rt.conn is an established MeekConn.
  1022. // Note that the network address portion of the URL will be ignored by
  1023. // MeekConn in favor of the MeekDialConfig, while the path will be used.
  1024. url := fmt.Sprintf(
  1025. "https://%s/%s",
  1026. rt.brokerDialParams.DialAddress,
  1027. inproxy.BrokerEndPointName)
  1028. request, err := http.NewRequestWithContext(
  1029. ctx, "POST", url, bytes.NewBuffer(requestPayload))
  1030. if err != nil {
  1031. return nil, errors.Trace(err)
  1032. }
  1033. response, err := rt.conn.RoundTrip(request)
  1034. if err == nil {
  1035. defer response.Body.Close()
  1036. if response.StatusCode != http.StatusOK {
  1037. err = fmt.Errorf("unexpected response status code: %d", response.StatusCode)
  1038. }
  1039. }
  1040. if err != nil {
  1041. return nil, errors.Trace(err)
  1042. }
  1043. responsePayload, err := io.ReadAll(response.Body)
  1044. if err != nil {
  1045. return nil, errors.Trace(err)
  1046. }
  1047. return responsePayload, nil
  1048. }
  1049. // InproxyWebRTCDialInstance is the network state and dial parameters for a
  1050. // single WebRTC client or proxy connection.
  1051. //
  1052. // InproxyWebRTCDialInstance implements the inproxy.WebRTCDialCoordinator
  1053. // interface, which provides the WebRTC dial configuration and support to the
  1054. // in-proxy package.
  1055. type InproxyWebRTCDialInstance struct {
  1056. config *Config
  1057. networkID string
  1058. natStateManager *InproxyNATStateManager
  1059. stunDialParameters *InproxySTUNDialParameters
  1060. webRTCDialParameters *InproxyWebRTCDialParameters
  1061. discoverNAT bool
  1062. disableSTUN bool
  1063. disablePortMapping bool
  1064. disableInboundForMobleNetworks bool
  1065. disableIPv6ICECandidates bool
  1066. discoverNATTimeout time.Duration
  1067. webRTCAnswerTimeout time.Duration
  1068. awaitDataChannelTimeout time.Duration
  1069. proxyDestinationDialTimeout time.Duration
  1070. }
  1071. // NewInproxyWebRTCDialInstance creates a new InproxyWebRTCDialInstance.
  1072. //
  1073. // The caller provides STUN and WebRTC dial parameters that are either newly
  1074. // generated or replayed. Proxies may optionally pass in nil for either
  1075. // stunDialParameters or webRTCDialParameters, and new parameters will be
  1076. // generated.
  1077. func NewInproxyWebRTCDialInstance(
  1078. config *Config,
  1079. networkID string,
  1080. isProxy bool,
  1081. natStateManager *InproxyNATStateManager,
  1082. stunDialParameters *InproxySTUNDialParameters,
  1083. webRTCDialParameters *InproxyWebRTCDialParameters) (*InproxyWebRTCDialInstance, error) {
  1084. p := config.GetParameters().Get()
  1085. defer p.Close()
  1086. if isProxy && stunDialParameters == nil {
  1087. // Auto-generate STUN dial parameters. There's no replay in this case.
  1088. var err error
  1089. stunDialParameters, err = MakeInproxySTUNDialParameters(config, p, isProxy)
  1090. if err != nil {
  1091. return nil, errors.Trace(err)
  1092. }
  1093. }
  1094. if isProxy && webRTCDialParameters == nil {
  1095. // Auto-generate STUN dial parameters. There's no replay in this case.
  1096. var err error
  1097. webRTCDialParameters, err = MakeInproxyWebRTCDialParameters(p)
  1098. if err != nil {
  1099. return nil, errors.Trace(err)
  1100. }
  1101. }
  1102. var awaitDataChannelTimeout time.Duration
  1103. if isProxy {
  1104. awaitDataChannelTimeout = p.Duration(parameters.InproxyProxyWebRTCAwaitDataChannelTimeout)
  1105. } else {
  1106. awaitDataChannelTimeout = p.Duration(parameters.InproxyClientWebRTCAwaitDataChannelTimeout)
  1107. }
  1108. // Parameters such as disabling certain operations and operation timeouts
  1109. // are not replayed, but snapshots are stored in the
  1110. // InproxyWebRTCDialInstance for efficient lookup.
  1111. return &InproxyWebRTCDialInstance{
  1112. config: config,
  1113. networkID: networkID,
  1114. natStateManager: natStateManager,
  1115. stunDialParameters: stunDialParameters,
  1116. webRTCDialParameters: webRTCDialParameters,
  1117. discoverNAT: p.WeightedCoinFlip(parameters.InproxyClientDiscoverNATProbability),
  1118. disableSTUN: p.Bool(parameters.InproxyDisableSTUN),
  1119. disablePortMapping: p.Bool(parameters.InproxyDisablePortMapping),
  1120. disableInboundForMobleNetworks: p.Bool(parameters.InproxyDisableInboundForMobleNetworks),
  1121. disableIPv6ICECandidates: p.Bool(parameters.InproxyDisableIPv6ICECandidates),
  1122. discoverNATTimeout: p.Duration(parameters.InproxyDiscoverNATTimeout),
  1123. webRTCAnswerTimeout: p.Duration(parameters.InproxyWebRTCAnswerTimeout),
  1124. awaitDataChannelTimeout: awaitDataChannelTimeout,
  1125. proxyDestinationDialTimeout: p.Duration(parameters.InproxyProxyDestinationDialTimeout),
  1126. }, nil
  1127. }
  1128. // Implements the inproxy.WebRTCDialCoordinator interface.
  1129. func (w *InproxyWebRTCDialInstance) NetworkID() string {
  1130. return w.networkID
  1131. }
  1132. // Implements the inproxy.WebRTCDialCoordinator interface.
  1133. func (w *InproxyWebRTCDialInstance) NetworkType() inproxy.NetworkType {
  1134. return getInproxyNetworkType(GetNetworkType(w.networkID))
  1135. }
  1136. // Implements the inproxy.WebRTCDialCoordinator interface.
  1137. func (w *InproxyWebRTCDialInstance) ClientRootObfuscationSecret() inproxy.ObfuscationSecret {
  1138. return w.webRTCDialParameters.RootObfuscationSecret
  1139. }
  1140. // Implements the inproxy.WebRTCDialCoordinator interface.
  1141. func (w *InproxyWebRTCDialInstance) DoDTLSRandomization() bool {
  1142. return w.webRTCDialParameters.DoDTLSRandomization
  1143. }
  1144. // Implements the inproxy.WebRTCDialCoordinator interface.
  1145. func (w *InproxyWebRTCDialInstance) DataChannelTrafficShapingParameters() *inproxy.DataChannelTrafficShapingParameters {
  1146. return &w.webRTCDialParameters.DataChannelTrafficShapingParameters
  1147. }
  1148. // Implements the inproxy.WebRTCDialCoordinator interface.
  1149. func (w *InproxyWebRTCDialInstance) STUNServerAddress(RFC5780 bool) string {
  1150. if RFC5780 {
  1151. return w.stunDialParameters.STUNServerAddressRFC5780
  1152. } else {
  1153. return w.stunDialParameters.STUNServerAddress
  1154. }
  1155. }
  1156. // Implements the inproxy.WebRTCDialCoordinator interface.
  1157. func (w *InproxyWebRTCDialInstance) STUNServerAddressResolved(RFC5780 bool) string {
  1158. if RFC5780 {
  1159. return w.stunDialParameters.STUNServerAddressRFC5780
  1160. } else {
  1161. return w.stunDialParameters.STUNServerAddress
  1162. }
  1163. }
  1164. // Implements the inproxy.WebRTCDialCoordinator interface.
  1165. func (w *InproxyWebRTCDialInstance) STUNServerAddressSucceeded(RFC5780 bool, address string) {
  1166. // Currently, for client tunnel dials, STUN dial parameter replay is
  1167. // managed by DialParameters and DialParameters.InproxySTUNDialParameters
  1168. // are replayed only when the entire dial succeeds.
  1169. //
  1170. // Note that, for a client tunnel dial, even if the STUN step fails and
  1171. // there are no STUN ICE candidates, the subsequent WebRTC connection may
  1172. // still proceed and be successful. In this case, the failed STUN dial
  1173. // parameters may be replayed.
  1174. //
  1175. // For proxies, there is no STUN dial parameter replay.
  1176. //
  1177. // As a future enhancement, consider independent and shared replay of
  1178. // working STUN servers, similar to how broker client dial parameters are
  1179. // replayed independent of overall dials and proxy relays, and shared
  1180. // between local client and proxy instances.
  1181. // Verify/extend the resolver cache entry for any resolved domain after a
  1182. // success.
  1183. resolver := w.config.GetResolver()
  1184. if resolver != nil {
  1185. resolver.VerifyCacheExtension(address)
  1186. }
  1187. }
  1188. // Implements the inproxy.WebRTCDialCoordinator interface.
  1189. func (w *InproxyWebRTCDialInstance) STUNServerAddressFailed(RFC5780 bool, address string) {
  1190. // Currently there is no independent replay for STUN dial parameters. See
  1191. // comment in STUNServerAddressSucceeded.
  1192. }
  1193. // Implements the inproxy.WebRTCDialCoordinator interface.
  1194. func (w *InproxyWebRTCDialInstance) DiscoverNAT() bool {
  1195. return w.discoverNAT
  1196. }
  1197. // Implements the inproxy.WebRTCDialCoordinator interface.
  1198. func (w *InproxyWebRTCDialInstance) DisableSTUN() bool {
  1199. return w.disableSTUN
  1200. }
  1201. // Implements the inproxy.WebRTCDialCoordinator interface.
  1202. func (w *InproxyWebRTCDialInstance) DisablePortMapping() bool {
  1203. return w.disablePortMapping
  1204. }
  1205. // Implements the inproxy.WebRTCDialCoordinator interface.
  1206. func (w *InproxyWebRTCDialInstance) DisableInboundForMobleNetworks() bool {
  1207. return w.disableInboundForMobleNetworks
  1208. }
  1209. // Implements the inproxy.WebRTCDialCoordinator interface.
  1210. func (w *InproxyWebRTCDialInstance) DisableIPv6ICECandidates() bool {
  1211. return w.disableIPv6ICECandidates
  1212. }
  1213. // Implements the inproxy.WebRTCDialCoordinator interface.
  1214. func (w *InproxyWebRTCDialInstance) NATType() inproxy.NATType {
  1215. return w.natStateManager.getNATType(w.networkID)
  1216. }
  1217. // Implements the inproxy.WebRTCDialCoordinator interface.
  1218. func (w *InproxyWebRTCDialInstance) SetNATType(natType inproxy.NATType) {
  1219. w.natStateManager.setNATType(w.networkID, natType)
  1220. }
  1221. // Implements the inproxy.WebRTCDialCoordinator interface.
  1222. func (w *InproxyWebRTCDialInstance) PortMappingTypes() inproxy.PortMappingTypes {
  1223. return w.natStateManager.getPortMappingTypes(w.networkID)
  1224. }
  1225. // Implements the inproxy.WebRTCDialCoordinator interface.
  1226. func (w *InproxyWebRTCDialInstance) SetPortMappingTypes(portMappingTypes inproxy.PortMappingTypes) {
  1227. w.natStateManager.setPortMappingTypes(w.networkID, portMappingTypes)
  1228. }
  1229. // Implements the inproxy.WebRTCDialCoordinator interface.
  1230. func (w *InproxyWebRTCDialInstance) ResolveAddress(ctx context.Context, network, address string) (string, error) {
  1231. // Use the Psiphon resolver to resolve addresses.
  1232. r := w.config.GetResolver()
  1233. if r == nil {
  1234. return "", errors.TraceNew("missing resolver")
  1235. }
  1236. // Identify when the address to be resolved is one of the configured STUN
  1237. // servers, and, in those cases, use/replay any STUN dial parameters
  1238. // ResolveParameters; and record the resolved IP address for metrics.
  1239. //
  1240. // In the in-proxy proxy case, ResolveAddress is invoked for the upstream,
  1241. // 2nd hop dial as well as for STUN server addresses.
  1242. //
  1243. // Limitation: there's ResolveParameters, including no preresolved DNS
  1244. // tactics, for 2nd hop dials.
  1245. isSTUNServerAddress := address == w.stunDialParameters.STUNServerAddress
  1246. isSTUNServerAddressRFC5780 := address == w.stunDialParameters.STUNServerAddressRFC5780
  1247. var resolveParams *resolver.ResolveParameters
  1248. if isSTUNServerAddress || isSTUNServerAddressRFC5780 {
  1249. resolveParams = w.stunDialParameters.ResolveParameters
  1250. }
  1251. resolved, err := r.ResolveAddress(
  1252. ctx, w.networkID, resolveParams, network, address)
  1253. if err != nil {
  1254. return "", errors.Trace(err)
  1255. }
  1256. // Invoke the resolved IP callbacks only when the input is not the
  1257. // resolved IP address (this differs from the meek
  1258. // DialConfig.ResolvedIPCallback case).
  1259. if resolved != address {
  1260. if isSTUNServerAddress {
  1261. w.stunDialParameters.STUNServerResolvedIPAddress.Store(resolved)
  1262. } else if isSTUNServerAddressRFC5780 {
  1263. w.stunDialParameters.STUNServerRFC5780ResolvedIPAddress.Store(resolved)
  1264. }
  1265. }
  1266. return resolved, nil
  1267. }
  1268. // Implements the inproxy.WebRTCDialCoordinator interface.
  1269. func (w *InproxyWebRTCDialInstance) UDPListen(ctx context.Context) (net.PacketConn, error) {
  1270. // Create a new inproxyUDPConn for use as the in-proxy STUN and/ord WebRTC
  1271. // UDP socket.
  1272. conn, err := newInproxyUDPConn(ctx, w.config)
  1273. if err != nil {
  1274. return nil, errors.Trace(err)
  1275. }
  1276. return conn, nil
  1277. }
  1278. // Implements the inproxy.WebRTCDialCoordinator interface.
  1279. func (w *InproxyWebRTCDialInstance) UDPConn(
  1280. ctx context.Context, network, remoteAddress string) (net.PacketConn, error) {
  1281. // Create a new UDPConn bound to the specified remote address. This UDP
  1282. // conn is used, by the inproxy package, to determine the local address
  1283. // of the active interface the OS will select for the specified remote
  1284. // destination.
  1285. //
  1286. // Only IP address destinations are supported. ResolveIP is wired up only
  1287. // because NewUDPConn requires a non-nil resolver.
  1288. dialConfig := &DialConfig{
  1289. DeviceBinder: w.config.deviceBinder,
  1290. IPv6Synthesizer: w.config.IPv6Synthesizer,
  1291. ResolveIP: func(_ context.Context, hostname string) ([]net.IP, error) {
  1292. IP := net.ParseIP(hostname)
  1293. if IP == nil {
  1294. return nil, errors.TraceNew("not supported")
  1295. }
  1296. return []net.IP{IP}, nil
  1297. },
  1298. }
  1299. conn, _, err := NewUDPConn(ctx, network, true, "", remoteAddress, dialConfig)
  1300. if err != nil {
  1301. return nil, errors.Trace(err)
  1302. }
  1303. return conn, nil
  1304. }
  1305. // Implements the inproxy.WebRTCDialCoordinator interface.
  1306. func (w *InproxyWebRTCDialInstance) BindToDevice(fileDescriptor int) error {
  1307. // Use config.deviceBinder, with wired up logging, not
  1308. // config.DeviceBinder; other tunnel-core dials do this indirectly via
  1309. // psiphon.DialConfig.
  1310. _, err := w.config.deviceBinder.BindToDevice(fileDescriptor)
  1311. return errors.Trace(err)
  1312. }
  1313. // Implements the inproxy.WebRTCDialCoordinator interface.
  1314. func (w *InproxyWebRTCDialInstance) DiscoverNATTimeout() time.Duration {
  1315. return w.discoverNATTimeout
  1316. }
  1317. // Implements the inproxy.WebRTCDialCoordinator interface.
  1318. func (w *InproxyWebRTCDialInstance) WebRTCAnswerTimeout() time.Duration {
  1319. return w.webRTCAnswerTimeout
  1320. }
  1321. // Implements the inproxy.WebRTCDialCoordinator interface.
  1322. func (w *InproxyWebRTCDialInstance) WebRTCAwaitDataChannelTimeout() time.Duration {
  1323. return w.awaitDataChannelTimeout
  1324. }
  1325. // Implements the inproxy.WebRTCDialCoordinator interface.
  1326. func (w *InproxyWebRTCDialInstance) ProxyDestinationDialTimeout() time.Duration {
  1327. return w.proxyDestinationDialTimeout
  1328. }
  1329. // InproxySTUNDialParameters is a set of STUN dial parameters.
  1330. // InproxySTUNDialParameters is compatible with DialParameters JSON
  1331. // marshaling. For client in-proxy tunnel dials, DialParameters will manage
  1332. // STUN dial parameter selection and replay.
  1333. //
  1334. // When an instance of InproxySTUNDialParameters is unmarshaled from JSON,
  1335. // Prepare must be called to initialize the instance for use.
  1336. type InproxySTUNDialParameters struct {
  1337. ResolveParameters *resolver.ResolveParameters
  1338. STUNServerAddress string
  1339. STUNServerAddressRFC5780 string
  1340. STUNServerResolvedIPAddress atomic.Value `json:"-"`
  1341. STUNServerRFC5780ResolvedIPAddress atomic.Value `json:"-"`
  1342. }
  1343. // MakeInproxySTUNDialParameters generates new STUN dial parameters from the
  1344. // given tactics parameters.
  1345. func MakeInproxySTUNDialParameters(
  1346. config *Config,
  1347. p parameters.ParametersAccessor,
  1348. isProxy bool) (*InproxySTUNDialParameters, error) {
  1349. var stunServerAddresses, stunServerAddressesRFC5780 []string
  1350. if isProxy {
  1351. stunServerAddresses = p.Strings(
  1352. parameters.InproxyProxySTUNServerAddresses, parameters.InproxySTUNServerAddresses)
  1353. stunServerAddressesRFC5780 = p.Strings(
  1354. parameters.InproxyProxySTUNServerAddressesRFC5780, parameters.InproxySTUNServerAddressesRFC5780)
  1355. } else {
  1356. stunServerAddresses = p.Strings(
  1357. parameters.InproxyClientSTUNServerAddresses, parameters.InproxySTUNServerAddresses)
  1358. stunServerAddressesRFC5780 = p.Strings(
  1359. parameters.InproxyClientSTUNServerAddressesRFC5780, parameters.InproxySTUNServerAddressesRFC5780)
  1360. }
  1361. // Empty STUN server address lists are not an error condition. When used
  1362. // for WebRTC, the STUN ICE candidate gathering will be skipped but the
  1363. // WebRTC connection may still be established via other candidate types.
  1364. var stunServerAddress, stunServerAddressRFC5780 string
  1365. if len(stunServerAddresses) > 0 {
  1366. prng.Shuffle(
  1367. len(stunServerAddresses),
  1368. func(i, j int) {
  1369. stunServerAddresses[i], stunServerAddresses[j] =
  1370. stunServerAddresses[j], stunServerAddresses[i]
  1371. })
  1372. stunServerAddress = stunServerAddresses[0]
  1373. }
  1374. if len(stunServerAddressesRFC5780) > 0 {
  1375. prng.Shuffle(
  1376. len(stunServerAddressesRFC5780),
  1377. func(i, j int) {
  1378. stunServerAddressesRFC5780[i], stunServerAddressesRFC5780[j] =
  1379. stunServerAddressesRFC5780[j], stunServerAddressesRFC5780[i]
  1380. })
  1381. stunServerAddressRFC5780 = stunServerAddressesRFC5780[0]
  1382. }
  1383. // Create DNS resolver dial parameters to use when resolving STUN server
  1384. // domain addresses. Instantiate only when there is a domain to be
  1385. // resolved; when recording DNS fields, GetMetrics will assume that a nil
  1386. // InproxySTUNDialParameters.ResolveParameters implies no resolve was
  1387. // attempted.
  1388. var resolveParameters *resolver.ResolveParameters
  1389. if (stunServerAddress != "" && net.ParseIP(stunServerAddress) == nil) ||
  1390. (stunServerAddressRFC5780 != "" && net.ParseIP(stunServerAddressRFC5780) == nil) {
  1391. // No DNSResolverPreresolvedIPAddressCIDRs will be selected since no
  1392. // fronting provider ID is specified.
  1393. //
  1394. // It would be possible to overload the meaning of the fronting
  1395. // provider ID field by using a string derived from STUN server
  1396. // address as the key.
  1397. //
  1398. // However, preresolved STUN configuration can already be achieved
  1399. // with IP addresses in the STUNServerAddresses tactics parameters.
  1400. // This approach results in slightly different metrics log fields vs.
  1401. // preresolved.
  1402. var err error
  1403. resolveParameters, err = config.GetResolver().MakeResolveParameters(p, "", "")
  1404. if err != nil {
  1405. return nil, errors.Trace(err)
  1406. }
  1407. }
  1408. dialParams := &InproxySTUNDialParameters{
  1409. ResolveParameters: resolveParameters,
  1410. STUNServerAddress: stunServerAddress,
  1411. STUNServerAddressRFC5780: stunServerAddressRFC5780,
  1412. }
  1413. dialParams.Prepare()
  1414. return dialParams, nil
  1415. }
  1416. // Prepare initializes an InproxySTUNDialParameters for use. Prepare should be
  1417. // called for any InproxySTUNDialParameters instance unmarshaled from JSON.
  1418. func (dialParams *InproxySTUNDialParameters) Prepare() {
  1419. dialParams.STUNServerResolvedIPAddress.Store("")
  1420. dialParams.STUNServerRFC5780ResolvedIPAddress.Store("")
  1421. }
  1422. // IsValidClientReplay checks that the selected STUN servers remain configured
  1423. // STUN server candidates for in-proxy clients.
  1424. func (dialParams *InproxySTUNDialParameters) IsValidClientReplay(
  1425. p parameters.ParametersAccessor) bool {
  1426. return (dialParams.STUNServerAddress == "" ||
  1427. common.Contains(
  1428. p.Strings(parameters.InproxyClientSTUNServerAddresses),
  1429. dialParams.STUNServerAddress)) &&
  1430. (dialParams.STUNServerAddressRFC5780 == "" ||
  1431. common.Contains(
  1432. p.Strings(parameters.InproxyClientSTUNServerAddressesRFC5780),
  1433. dialParams.STUNServerAddressRFC5780))
  1434. }
  1435. // GetMetrics implements the common.MetricsSource interface and returns log
  1436. // fields detailing the STUN dial parameters.
  1437. func (dialParams *InproxySTUNDialParameters) GetMetrics() common.LogFields {
  1438. // There is no is_replay-type field added here; replay is handled at a
  1439. // higher level, and, for client in-proxy tunnel dials, is part of the
  1440. // main tunnel dial parameters.
  1441. logFields := make(common.LogFields)
  1442. logFields["inproxy_webrtc_stun_server"] = dialParams.STUNServerAddress
  1443. resolvedIPAddress := dialParams.STUNServerResolvedIPAddress.Load().(string)
  1444. if resolvedIPAddress != "" {
  1445. logFields["inproxy_webrtc_stun_server_resolved_ip_address"] = resolvedIPAddress
  1446. }
  1447. // TODO: log RFC5780 selection only if used?
  1448. logFields["inproxy_webrtc_stun_server_RFC5780"] = dialParams.STUNServerAddressRFC5780
  1449. resolvedIPAddress = dialParams.STUNServerRFC5780ResolvedIPAddress.Load().(string)
  1450. if resolvedIPAddress != "" {
  1451. logFields["inproxy_webrtc_stun_server_RFC5780_resolved_ip_address"] = resolvedIPAddress
  1452. }
  1453. if dialParams.ResolveParameters != nil {
  1454. // See comment in getBaseAPIParameters regarding
  1455. // dialParams.ResolveParameters handling. As noted in
  1456. // MakeInproxySTUNDialParameters, no preresolved parameters are set,
  1457. // so none are checked for logging.
  1458. //
  1459. // Limitation: the potential use of single ResolveParameters to
  1460. // resolve multiple, different STUN server domains can skew the
  1461. // meaning of GetFirstAttemptWithAnswer.
  1462. if dialParams.ResolveParameters.PreferAlternateDNSServer {
  1463. logFields["inproxy_webrtc_dns_preferred"] = dialParams.ResolveParameters.AlternateDNSServer
  1464. }
  1465. if dialParams.ResolveParameters.ProtocolTransformName != "" {
  1466. logFields["inproxy_webrtc_dns_transform"] = dialParams.ResolveParameters.ProtocolTransformName
  1467. }
  1468. logFields["inproxy_webrtc_dns_attempt"] = strconv.Itoa(
  1469. dialParams.ResolveParameters.GetFirstAttemptWithAnswer())
  1470. }
  1471. return logFields
  1472. }
  1473. // InproxyWebRTCDialParameters is a set of WebRTC obfuscation dial parameters.
  1474. // InproxyWebRTCDialParameters is compatible with DialParameters JSON
  1475. // marshaling. For client in-proxy tunnel dials, DialParameters will manage
  1476. // WebRTC dial parameter selection and replay.
  1477. type InproxyWebRTCDialParameters struct {
  1478. RootObfuscationSecret inproxy.ObfuscationSecret
  1479. DataChannelTrafficShapingParameters inproxy.DataChannelTrafficShapingParameters
  1480. DoDTLSRandomization bool
  1481. }
  1482. // MakeInproxyWebRTCDialParameters generates new InproxyWebRTCDialParameters.
  1483. func MakeInproxyWebRTCDialParameters(
  1484. p parameters.ParametersAccessor) (*InproxyWebRTCDialParameters, error) {
  1485. rootObfuscationSecret, err := inproxy.GenerateRootObfuscationSecret()
  1486. if err != nil {
  1487. return nil, errors.Trace(err)
  1488. }
  1489. var trafficShapingParameters parameters.InproxyDataChannelTrafficShapingParametersValue
  1490. if p.WeightedCoinFlip(parameters.InproxyDataChannelTrafficShapingProbability) {
  1491. trafficShapingParameters = p.InproxyDataChannelTrafficShapingParameters(
  1492. parameters.InproxyDataChannelTrafficShapingParameters)
  1493. }
  1494. doDTLSRandomization := p.WeightedCoinFlip(parameters.InproxyDTLSRandomizationProbability)
  1495. return &InproxyWebRTCDialParameters{
  1496. RootObfuscationSecret: rootObfuscationSecret,
  1497. DataChannelTrafficShapingParameters: inproxy.DataChannelTrafficShapingParameters(trafficShapingParameters),
  1498. DoDTLSRandomization: doDTLSRandomization,
  1499. }, nil
  1500. }
  1501. // GetMetrics implements the common.MetricsSource interface.
  1502. func (dialParams *InproxyWebRTCDialParameters) GetMetrics() common.LogFields {
  1503. // There is no is_replay-type field added here; replay is handled at a
  1504. // higher level, and, for client in-proxy tunnel dials, is part of the
  1505. // main tunnel dial parameters.
  1506. // Currently, all WebRTC metrics are delivered via
  1507. // inproxy.ClientConn/WebRTCConn GetMetrics.
  1508. return common.LogFields{}
  1509. }
  1510. // InproxyNATStateManager manages the NAT-related network topology state for
  1511. // the current network, caching the discovered network NAT type and supported
  1512. // port mapping types, if any.
  1513. type InproxyNATStateManager struct {
  1514. config *Config
  1515. mutex sync.Mutex
  1516. networkID string
  1517. natType inproxy.NATType
  1518. portMappingTypes inproxy.PortMappingTypes
  1519. }
  1520. // NewInproxyNATStateManager creates a new InproxyNATStateManager.
  1521. func NewInproxyNATStateManager(config *Config) *InproxyNATStateManager {
  1522. s := &InproxyNATStateManager{
  1523. config: config,
  1524. natType: inproxy.NATTypeUnknown,
  1525. portMappingTypes: inproxy.PortMappingTypes{},
  1526. }
  1527. s.reset()
  1528. return s
  1529. }
  1530. // TacticsApplied implements the TacticsAppliedReceiver interface, and is
  1531. // called when tactics have changed, which triggers a cached NAT state reset
  1532. // in order to apply potentially changed parameters.
  1533. func (s *InproxyNATStateManager) TacticsApplied() error {
  1534. s.reset()
  1535. return nil
  1536. }
  1537. func (s *InproxyNATStateManager) reset() {
  1538. // Assumes s.mutex lock is held.
  1539. networkID := s.config.GetNetworkID()
  1540. s.networkID = networkID
  1541. s.natType = inproxy.NATTypeUnknown
  1542. s.portMappingTypes = inproxy.PortMappingTypes{}
  1543. }
  1544. func (s *InproxyNATStateManager) getNATType(
  1545. networkID string) inproxy.NATType {
  1546. s.mutex.Lock()
  1547. defer s.mutex.Unlock()
  1548. if s.networkID != networkID {
  1549. return inproxy.NATTypeUnknown
  1550. }
  1551. return s.natType
  1552. }
  1553. func (s *InproxyNATStateManager) setNATType(
  1554. networkID string, natType inproxy.NATType) {
  1555. s.mutex.Lock()
  1556. defer s.mutex.Unlock()
  1557. if s.networkID != networkID {
  1558. return
  1559. }
  1560. s.natType = natType
  1561. }
  1562. func (s *InproxyNATStateManager) getPortMappingTypes(
  1563. networkID string) inproxy.PortMappingTypes {
  1564. s.mutex.Lock()
  1565. defer s.mutex.Unlock()
  1566. if s.networkID != networkID {
  1567. return inproxy.PortMappingTypes{}
  1568. }
  1569. return s.portMappingTypes
  1570. }
  1571. func (s *InproxyNATStateManager) setPortMappingTypes(
  1572. networkID string, portMappingTypes inproxy.PortMappingTypes) {
  1573. s.mutex.Lock()
  1574. defer s.mutex.Unlock()
  1575. if s.networkID != networkID {
  1576. return
  1577. }
  1578. s.portMappingTypes = portMappingTypes
  1579. }
  1580. // inproxyUDPConn is based on NewUDPConn and includes the write timeout
  1581. // workaround from common.WriteTimeoutUDPConn.
  1582. //
  1583. // inproxyUDPConn expands the NewUDPConn IPv6Synthesizer to support many
  1584. // destination addresses, as the inproxyUDPConn will be used to send/receive
  1585. // packets between many remote destination addresses.
  1586. //
  1587. // inproxyUDPConn implements the net.PacketConn interface.
  1588. type inproxyUDPConn struct {
  1589. udpConn *net.UDPConn
  1590. ipv6Synthesizer IPv6Synthesizer
  1591. synthesizerMutex sync.Mutex
  1592. ipv4ToIPv6 map[netip.Addr]net.IP
  1593. ipv6ToIPv4 map[netip.Addr]net.IP
  1594. }
  1595. func newInproxyUDPConn(ctx context.Context, config *Config) (net.PacketConn, error) {
  1596. listen := &net.ListenConfig{
  1597. Control: func(_, _ string, c syscall.RawConn) error {
  1598. var controlErr error
  1599. err := c.Control(func(fd uintptr) {
  1600. socketFD := int(fd)
  1601. setAdditionalSocketOptions(socketFD)
  1602. // Use config.deviceBinder, with wired up logging, not
  1603. // config.DeviceBinder; other tunnel-core dials do this
  1604. // indirectly via psiphon.DialConfig.
  1605. if config.deviceBinder != nil {
  1606. _, err := config.deviceBinder.BindToDevice(socketFD)
  1607. if err != nil {
  1608. controlErr = errors.Tracef("BindToDevice failed: %s", err)
  1609. return
  1610. }
  1611. }
  1612. })
  1613. if controlErr != nil {
  1614. return errors.Trace(controlErr)
  1615. }
  1616. return errors.Trace(err)
  1617. },
  1618. }
  1619. // Create an "unconnected" UDP socket for use with WriteTo and listening
  1620. // on all interfaces. See the limitation comment in NewUDPConn regarding
  1621. // its equivilent mode.
  1622. packetConn, err := listen.ListenPacket(ctx, "udp", "")
  1623. if err != nil {
  1624. return nil, errors.Trace(err)
  1625. }
  1626. var ok bool
  1627. udpConn, ok := packetConn.(*net.UDPConn)
  1628. if !ok {
  1629. return nil, errors.Tracef("unexpected conn type: %T", packetConn)
  1630. }
  1631. conn := &inproxyUDPConn{
  1632. udpConn: udpConn,
  1633. ipv6Synthesizer: config.IPv6Synthesizer,
  1634. }
  1635. if conn.ipv6Synthesizer != nil {
  1636. conn.ipv4ToIPv6 = make(map[netip.Addr]net.IP)
  1637. conn.ipv6ToIPv4 = make(map[netip.Addr]net.IP)
  1638. }
  1639. return conn, nil
  1640. }
  1641. func inproxyUDPAddrFromAddrPort(addrPort netip.AddrPort) *net.UDPAddr {
  1642. return &net.UDPAddr{
  1643. IP: addrPort.Addr().AsSlice(),
  1644. Port: int(addrPort.Port()),
  1645. }
  1646. }
  1647. func (conn *inproxyUDPConn) ReadFrom(p []byte) (int, net.Addr, error) {
  1648. // net.UDPConn.ReadFrom currently allocates a &UDPAddr{} per call, and so
  1649. // the &net.UDPAddr{} allocations done in the following synthesizer code
  1650. // path are no more than the standard code path.
  1651. //
  1652. // TODO: avoid all address allocations in both ReadFrom and WriteTo by:
  1653. //
  1654. // - changing ipvXToIPvY to map[netip.AddrPort]*net.UDPAddr
  1655. // - using a similar lookup for the non-synthesizer code path
  1656. //
  1657. // Such a scheme would work only if the caller is guaranteed to not mutate
  1658. // the returned net.Addr.
  1659. if conn.ipv6Synthesizer == nil {
  1660. // Do not wrap any I/O err returned by UDPConn
  1661. return conn.udpConn.ReadFrom(p)
  1662. }
  1663. n, addrPort, err := conn.udpConn.ReadFromUDPAddrPort(p)
  1664. // Reverse any synthesized address before returning err.
  1665. // Reverse the IPv6 synthesizer, returning the original IPv4 address
  1666. // as expected by the caller, including pion/webrtc. This logic
  1667. // assumes that no synthesized IPv6 address will conflict with any
  1668. // real IPv6 address.
  1669. var IP net.IP
  1670. ipAddr := addrPort.Addr()
  1671. if ipAddr.Is6() {
  1672. conn.synthesizerMutex.Lock()
  1673. IP, _ = conn.ipv6ToIPv4[ipAddr]
  1674. conn.synthesizerMutex.Unlock()
  1675. }
  1676. if IP == nil {
  1677. IP = ipAddr.AsSlice()
  1678. }
  1679. // Do not wrap any I/O err returned by UDPConn
  1680. return n, &net.UDPAddr{IP: IP, Port: int(addrPort.Port())}, err
  1681. }
  1682. func (conn *inproxyUDPConn) WriteTo(b []byte, addr net.Addr) (int, error) {
  1683. // See common.WriteTimeoutUDPConn.
  1684. err := conn.udpConn.SetWriteDeadline(
  1685. time.Now().Add(common.UDP_PACKET_WRITE_TIMEOUT))
  1686. if err != nil {
  1687. return 0, errors.Trace(err)
  1688. }
  1689. if conn.ipv6Synthesizer == nil {
  1690. // Do not wrap any I/O err returned by UDPConn
  1691. return conn.udpConn.WriteTo(b, addr)
  1692. }
  1693. // When configured, attempt to synthesize IPv6 addresses from an IPv4
  1694. // addresses for compatibility on DNS64/NAT64 networks.
  1695. //
  1696. // Store any synthesized addresses in a lookup table and reuse for
  1697. // subsequent writes to the same destination as well as reversing the
  1698. // conversion on reads.
  1699. //
  1700. // If synthesize fails, fall back to trying the original address.
  1701. // The netip.Addr type is used as the map key and the input address is
  1702. // assumed to be of the type *net.UDPAddr. This allows for more efficient
  1703. // lookup operations vs. a string key and parsing the input address via
  1704. // addr.String()/net.SplitHostPort().
  1705. udpAddr, ok := addr.(*net.UDPAddr)
  1706. if !ok {
  1707. return 0, errors.Tracef("unexpected addr type: %T", addr)
  1708. }
  1709. // Stack allocate to avoid an extra heap allocation per write.
  1710. var synthesizedAddr net.UDPAddr
  1711. if udpAddr.IP.To4() != nil {
  1712. ip4Addr, ok := netip.AddrFromSlice(udpAddr.IP)
  1713. if !ok {
  1714. return 0, errors.Tracef("invalid addr")
  1715. }
  1716. conn.synthesizerMutex.Lock()
  1717. synthesizedIP, ok := conn.ipv4ToIPv6[ip4Addr]
  1718. conn.synthesizerMutex.Unlock()
  1719. if ok {
  1720. synthesizedAddr = net.UDPAddr{IP: synthesizedIP, Port: udpAddr.Port}
  1721. } else {
  1722. synthesized := conn.ipv6Synthesizer.IPv6Synthesize(udpAddr.IP.String())
  1723. if synthesized != "" {
  1724. synthesizedIP := net.ParseIP(synthesized)
  1725. if synthesizedIP != nil {
  1726. conn.synthesizerMutex.Lock()
  1727. conn.ipv4ToIPv6[ip4Addr] = synthesizedIP
  1728. ipv6Addr, _ := netip.AddrFromSlice(synthesizedIP)
  1729. conn.ipv6ToIPv4[ipv6Addr] = udpAddr.IP
  1730. conn.synthesizerMutex.Unlock()
  1731. synthesizedAddr = net.UDPAddr{IP: synthesizedIP, Port: udpAddr.Port}
  1732. }
  1733. }
  1734. }
  1735. }
  1736. if synthesizedAddr.IP == nil {
  1737. // Do not wrap any I/O err returned by UDPConn
  1738. return conn.udpConn.WriteTo(b, addr)
  1739. }
  1740. return conn.udpConn.WriteTo(b, &synthesizedAddr)
  1741. }
  1742. func (conn *inproxyUDPConn) Close() error {
  1743. // Do not wrap any I/O err returned by UDPConn
  1744. return conn.udpConn.Close()
  1745. }
  1746. func (conn *inproxyUDPConn) LocalAddr() net.Addr {
  1747. // Do not wrap any I/O err returned by UDPConn
  1748. return conn.udpConn.LocalAddr()
  1749. }
  1750. func (conn *inproxyUDPConn) SetDeadline(t time.Time) error {
  1751. // Do not wrap any I/O err returned by UDPConn
  1752. return conn.udpConn.SetDeadline(t)
  1753. }
  1754. func (conn *inproxyUDPConn) SetReadDeadline(t time.Time) error {
  1755. // Do not wrap any I/O err returned by UDPConn
  1756. return conn.udpConn.SetReadDeadline(t)
  1757. }
  1758. func (conn *inproxyUDPConn) SetWriteDeadline(t time.Time) error {
  1759. // Do not wrap any I/O err returned by UDPConn
  1760. return conn.udpConn.SetWriteDeadline(t)
  1761. }
  1762. // getInproxyNetworkType converts a legacy string network type to an inproxy
  1763. // package type.
  1764. func getInproxyNetworkType(networkType string) inproxy.NetworkType {
  1765. // There is no VPN type conversion; clients and proxies will skip/fail
  1766. // in-proxy operations on non-Psiphon VPN networks.
  1767. switch networkType {
  1768. case "WIFI":
  1769. return inproxy.NetworkTypeWiFi
  1770. case "MOBILE":
  1771. return inproxy.NetworkTypeMobile
  1772. }
  1773. return inproxy.NetworkTypeUnknown
  1774. }