inproxy.go 81 KB

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