net.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524
  1. /*
  2. * Copyright (c) 2015, 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. "context"
  22. "crypto/tls"
  23. "crypto/x509"
  24. "errors"
  25. "fmt"
  26. "io"
  27. "io/ioutil"
  28. "net"
  29. "net/http"
  30. "os"
  31. "sync"
  32. "time"
  33. "github.com/Psiphon-Labs/dns"
  34. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
  35. )
  36. const DNS_PORT = 53
  37. // DialConfig contains parameters to determine the behavior
  38. // of a Psiphon dialer (TCPDial, UDPDial, MeekDial, etc.)
  39. type DialConfig struct {
  40. // UpstreamProxyURL specifies a proxy to connect through.
  41. // E.g., "http://proxyhost:8080"
  42. // "socks5://user:password@proxyhost:1080"
  43. // "socks4a://proxyhost:1080"
  44. // "http://NTDOMAIN\NTUser:password@proxyhost:3375"
  45. //
  46. // Certain tunnel protocols require HTTP CONNECT support
  47. // when a HTTP proxy is specified. If CONNECT is not
  48. // supported, those protocols will not connect.
  49. //
  50. // UpstreamProxyURL is not used by UDPDial.
  51. UpstreamProxyURL string
  52. // CustomHeaders is a set of additional arbitrary HTTP headers that are
  53. // added to all plaintext HTTP requests and requests made through an HTTP
  54. // upstream proxy when specified by UpstreamProxyURL.
  55. CustomHeaders http.Header
  56. // BindToDevice parameters are used to exclude connections and
  57. // associated DNS requests from VPN routing.
  58. // When DeviceBinder is set, any underlying socket is
  59. // submitted to the device binding servicebefore connecting.
  60. // The service should bind the socket to a device so that it doesn't route
  61. // through a VPN interface. This service is also used to bind UDP sockets used
  62. // for DNS requests, in which case DnsServerGetter is used to get the
  63. // current active untunneled network DNS server.
  64. DeviceBinder DeviceBinder
  65. DnsServerGetter DnsServerGetter
  66. IPv6Synthesizer IPv6Synthesizer
  67. // TrustedCACertificatesFilename specifies a file containing trusted
  68. // CA certs. See Config.TrustedCACertificatesFilename.
  69. TrustedCACertificatesFilename string
  70. // ResolvedIPCallback, when set, is called with the IP address that was
  71. // dialed. This is either the specified IP address in the dial address,
  72. // or the resolved IP address in the case where the dial address is a
  73. // domain name.
  74. // The callback may be invoked by a concurrent goroutine.
  75. ResolvedIPCallback func(string)
  76. }
  77. // NetworkConnectivityChecker defines the interface to the external
  78. // HasNetworkConnectivity provider, which call into the host application to
  79. // check for network connectivity.
  80. type NetworkConnectivityChecker interface {
  81. // TODO: change to bool return value once gobind supports that type
  82. HasNetworkConnectivity() int
  83. }
  84. // DeviceBinder defines the interface to the external BindToDevice provider
  85. // which calls into the host application to bind sockets to specific devices.
  86. // This is used for VPN routing exclusion.
  87. // The string return value should report device information for diagnostics.
  88. type DeviceBinder interface {
  89. BindToDevice(fileDescriptor int) (string, error)
  90. }
  91. // DnsServerGetter defines the interface to the external GetDnsServer provider
  92. // which calls into the host application to discover the native network DNS
  93. // server settings.
  94. type DnsServerGetter interface {
  95. GetPrimaryDnsServer() string
  96. GetSecondaryDnsServer() string
  97. }
  98. // IPv6Synthesizer defines the interface to the external IPv6Synthesize
  99. // provider which calls into the host application to synthesize IPv6 addresses
  100. // from IPv4 ones. This is used to correctly lookup IPs on DNS64/NAT64
  101. // networks.
  102. type IPv6Synthesizer interface {
  103. IPv6Synthesize(IPv4Addr string) string
  104. }
  105. // NetworkIDGetter defines the interface to the external GetNetworkID
  106. // provider, which returns an identifier for the host's current active
  107. // network.
  108. //
  109. // The identifier is a string that should indicate the network type and
  110. // identity; for example "WIFI-<BSSID>" or "MOBILE-<MCC/MNC>". As this network
  111. // ID is personally identifying, it is only used locally in the client to
  112. // determine network context and is not sent to the Psiphon server. The
  113. // identifer will be logged in diagnostics messages; in this case only the
  114. // substring before the first "-" is logged, so all PII must appear after the
  115. // first "-".
  116. //
  117. // NetworkIDGetter.GetNetworkID should always return an identifier value, as
  118. // logic that uses GetNetworkID, including tactics, is intended to proceed
  119. // regardless of whether an accurate network identifier can be obtained. By
  120. // convention, the provider should return "UNKNOWN" when an accurate network
  121. // identifier cannot be obtained. Best-effort is acceptable: e.g., return just
  122. // "WIFI" when only the type of the network but no details can be determined.
  123. type NetworkIDGetter interface {
  124. GetNetworkID() string
  125. }
  126. // Dialer is a custom network dialer.
  127. type Dialer func(context.Context, string, string) (net.Conn, error)
  128. // LocalProxyRelay sends to remoteConn bytes received from localConn,
  129. // and sends to localConn bytes received from remoteConn.
  130. func LocalProxyRelay(proxyType string, localConn, remoteConn net.Conn) {
  131. copyWaitGroup := new(sync.WaitGroup)
  132. copyWaitGroup.Add(1)
  133. go func() {
  134. defer copyWaitGroup.Done()
  135. _, err := io.Copy(localConn, remoteConn)
  136. if err != nil {
  137. err = fmt.Errorf("Relay failed: %s", common.ContextError(err))
  138. NoticeLocalProxyError(proxyType, err)
  139. }
  140. }()
  141. _, err := io.Copy(remoteConn, localConn)
  142. if err != nil {
  143. err = fmt.Errorf("Relay failed: %s", common.ContextError(err))
  144. NoticeLocalProxyError(proxyType, err)
  145. }
  146. copyWaitGroup.Wait()
  147. }
  148. // WaitForNetworkConnectivity uses a NetworkConnectivityChecker to
  149. // periodically check for network connectivity. It returns true if
  150. // no NetworkConnectivityChecker is provided (waiting is disabled)
  151. // or when NetworkConnectivityChecker.HasNetworkConnectivity()
  152. // indicates connectivity. It waits and polls the checker once a second.
  153. // When the context is done, false is returned immediately.
  154. func WaitForNetworkConnectivity(
  155. ctx context.Context, connectivityChecker NetworkConnectivityChecker) bool {
  156. if connectivityChecker == nil || 1 == connectivityChecker.HasNetworkConnectivity() {
  157. return true
  158. }
  159. NoticeInfo("waiting for network connectivity")
  160. ticker := time.NewTicker(1 * time.Second)
  161. defer ticker.Stop()
  162. for {
  163. if 1 == connectivityChecker.HasNetworkConnectivity() {
  164. return true
  165. }
  166. select {
  167. case <-ticker.C:
  168. // Check HasNetworkConnectivity again
  169. case <-ctx.Done():
  170. return false
  171. }
  172. }
  173. }
  174. // ResolveIP uses a custom dns stack to make a DNS query over the
  175. // given TCP or UDP conn. This is used, e.g., when we need to ensure
  176. // that a DNS connection bypasses a VPN interface (BindToDevice) or
  177. // when we need to ensure that a DNS connection is tunneled.
  178. // Caller must set timeouts or interruptibility as required for conn.
  179. func ResolveIP(host string, conn net.Conn) (addrs []net.IP, ttls []time.Duration, err error) {
  180. // Send the DNS query
  181. dnsConn := &dns.Conn{Conn: conn}
  182. defer dnsConn.Close()
  183. query := new(dns.Msg)
  184. query.SetQuestion(dns.Fqdn(host), dns.TypeA)
  185. query.RecursionDesired = true
  186. dnsConn.WriteMsg(query)
  187. // Process the response
  188. response, err := dnsConn.ReadMsg()
  189. if err != nil {
  190. return nil, nil, common.ContextError(err)
  191. }
  192. addrs = make([]net.IP, 0)
  193. ttls = make([]time.Duration, 0)
  194. for _, answer := range response.Answer {
  195. if a, ok := answer.(*dns.A); ok {
  196. addrs = append(addrs, a.A)
  197. ttl := time.Duration(a.Hdr.Ttl) * time.Second
  198. ttls = append(ttls, ttl)
  199. }
  200. }
  201. return addrs, ttls, nil
  202. }
  203. // MakeUntunneledHTTPClient returns a net/http.Client which is configured to
  204. // use custom dialing features -- including BindToDevice, etc. If
  205. // verifyLegacyCertificate is not nil, it's used for certificate verification.
  206. // The context is applied to underlying TCP dials. The caller is responsible
  207. // for applying the context to requests made with the returned http.Client.
  208. func MakeUntunneledHTTPClient(
  209. ctx context.Context,
  210. config *Config,
  211. untunneledDialConfig *DialConfig,
  212. verifyLegacyCertificate *x509.Certificate,
  213. skipVerify bool) (*http.Client, error) {
  214. dialer := NewTCPDialer(untunneledDialConfig)
  215. tlsDialer := NewCustomTLSDialer(
  216. // Note: when verifyLegacyCertificate is not nil, some
  217. // of the other CustomTLSConfig is overridden.
  218. &CustomTLSConfig{
  219. ClientParameters: config.clientParameters,
  220. Dial: dialer,
  221. VerifyLegacyCertificate: verifyLegacyCertificate,
  222. UseDialAddrSNI: true,
  223. SNIServerName: "",
  224. SkipVerify: skipVerify,
  225. TrustedCACertificatesFilename: untunneledDialConfig.TrustedCACertificatesFilename,
  226. })
  227. transport := &http.Transport{
  228. Dial: func(network, addr string) (net.Conn, error) {
  229. return dialer(ctx, network, addr)
  230. },
  231. DialTLS: func(network, addr string) (net.Conn, error) {
  232. return tlsDialer(ctx, network, addr)
  233. },
  234. }
  235. httpClient := &http.Client{
  236. Transport: transport,
  237. }
  238. return httpClient, nil
  239. }
  240. // MakeTunneledHTTPClient returns a net/http.Client which is
  241. // configured to use custom dialing features including tunneled
  242. // dialing and, optionally, UseTrustedCACertificatesForStockTLS.
  243. // This http.Client uses stock TLS for HTTPS.
  244. func MakeTunneledHTTPClient(
  245. config *Config,
  246. tunnel *Tunnel,
  247. skipVerify bool) (*http.Client, error) {
  248. // Note: there is no dial context since SSH port forward dials cannot
  249. // be interrupted directly. Closing the tunnel will interrupt the dials.
  250. tunneledDialer := func(_, addr string) (conn net.Conn, err error) {
  251. return tunnel.sshClient.Dial("tcp", addr)
  252. }
  253. transport := &http.Transport{
  254. Dial: tunneledDialer,
  255. }
  256. if skipVerify {
  257. transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
  258. } else if config.TrustedCACertificatesFilename != "" {
  259. rootCAs := x509.NewCertPool()
  260. certData, err := ioutil.ReadFile(config.TrustedCACertificatesFilename)
  261. if err != nil {
  262. return nil, common.ContextError(err)
  263. }
  264. rootCAs.AppendCertsFromPEM(certData)
  265. transport.TLSClientConfig = &tls.Config{RootCAs: rootCAs}
  266. }
  267. return &http.Client{
  268. Transport: transport,
  269. }, nil
  270. }
  271. // MakeDownloadHTTPClient is a helper that sets up a http.Client
  272. // for use either untunneled or through a tunnel.
  273. func MakeDownloadHTTPClient(
  274. ctx context.Context,
  275. config *Config,
  276. tunnel *Tunnel,
  277. untunneledDialConfig *DialConfig,
  278. skipVerify bool) (*http.Client, error) {
  279. var httpClient *http.Client
  280. var err error
  281. if tunnel != nil {
  282. httpClient, err = MakeTunneledHTTPClient(
  283. config, tunnel, skipVerify)
  284. if err != nil {
  285. return nil, common.ContextError(err)
  286. }
  287. } else {
  288. httpClient, err = MakeUntunneledHTTPClient(
  289. ctx, config, untunneledDialConfig, nil, skipVerify)
  290. if err != nil {
  291. return nil, common.ContextError(err)
  292. }
  293. }
  294. return httpClient, nil
  295. }
  296. // ResumeDownload is a reusable helper that downloads requestUrl via the
  297. // httpClient, storing the result in downloadFilename when the download is
  298. // complete. Intermediate, partial downloads state is stored in
  299. // downloadFilename.part and downloadFilename.part.etag.
  300. // Any existing downloadFilename file will be overwritten.
  301. //
  302. // In the case where the remote object has changed while a partial download
  303. // is to be resumed, the partial state is reset and resumeDownload fails.
  304. // The caller must restart the download.
  305. //
  306. // When ifNoneMatchETag is specified, no download is made if the remote
  307. // object has the same ETag. ifNoneMatchETag has an effect only when no
  308. // partial download is in progress.
  309. //
  310. func ResumeDownload(
  311. ctx context.Context,
  312. httpClient *http.Client,
  313. downloadURL string,
  314. userAgent string,
  315. downloadFilename string,
  316. ifNoneMatchETag string) (int64, string, error) {
  317. partialFilename := fmt.Sprintf("%s.part", downloadFilename)
  318. partialETagFilename := fmt.Sprintf("%s.part.etag", downloadFilename)
  319. file, err := os.OpenFile(partialFilename, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600)
  320. if err != nil {
  321. return 0, "", common.ContextError(err)
  322. }
  323. defer file.Close()
  324. fileInfo, err := file.Stat()
  325. if err != nil {
  326. return 0, "", common.ContextError(err)
  327. }
  328. // A partial download should have an ETag which is to be sent with the
  329. // Range request to ensure that the source object is the same as the
  330. // one that is partially downloaded.
  331. var partialETag []byte
  332. if fileInfo.Size() > 0 {
  333. partialETag, err = ioutil.ReadFile(partialETagFilename)
  334. // When the ETag can't be loaded, delete the partial download. To keep the
  335. // code simple, there is no immediate, inline retry here, on the assumption
  336. // that the controller's upgradeDownloader will shortly call DownloadUpgrade
  337. // again.
  338. if err != nil {
  339. // On Windows, file must be closed before it can be deleted
  340. file.Close()
  341. tempErr := os.Remove(partialFilename)
  342. if tempErr != nil && !os.IsNotExist(tempErr) {
  343. NoticeAlert("reset partial download failed: %s", tempErr)
  344. }
  345. tempErr = os.Remove(partialETagFilename)
  346. if tempErr != nil && !os.IsNotExist(tempErr) {
  347. NoticeAlert("reset partial download ETag failed: %s", tempErr)
  348. }
  349. return 0, "", common.ContextError(
  350. fmt.Errorf("failed to load partial download ETag: %s", err))
  351. }
  352. }
  353. request, err := http.NewRequest("GET", downloadURL, nil)
  354. if err != nil {
  355. return 0, "", common.ContextError(err)
  356. }
  357. request = request.WithContext(ctx)
  358. request.Header.Set("User-Agent", userAgent)
  359. request.Header.Add("Range", fmt.Sprintf("bytes=%d-", fileInfo.Size()))
  360. if partialETag != nil {
  361. // Note: not using If-Range, since not all host servers support it.
  362. // Using If-Match means we need to check for status code 412 and reset
  363. // when the ETag has changed since the last partial download.
  364. request.Header.Add("If-Match", string(partialETag))
  365. } else if ifNoneMatchETag != "" {
  366. // Can't specify both If-Match and If-None-Match. Behavior is undefined.
  367. // https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.26
  368. // So for downloaders that store an ETag and wish to use that to prevent
  369. // redundant downloads, that ETag is sent as If-None-Match in the case
  370. // where a partial download is not in progress. When a partial download
  371. // is in progress, the partial ETag is sent as If-Match: either that's
  372. // a version that was never fully received, or it's no longer current in
  373. // which case the response will be StatusPreconditionFailed, the partial
  374. // download will be discarded, and then the next retry will use
  375. // If-None-Match.
  376. // Note: in this case, fileInfo.Size() == 0
  377. request.Header.Add("If-None-Match", ifNoneMatchETag)
  378. }
  379. response, err := httpClient.Do(request)
  380. // The resumeable download may ask for bytes past the resource range
  381. // since it doesn't store the "completed download" state. In this case,
  382. // the HTTP server returns 416. Otherwise, we expect 206. We may also
  383. // receive 412 on ETag mismatch.
  384. if err == nil &&
  385. (response.StatusCode != http.StatusPartialContent &&
  386. // Certain http servers return 200 OK where we expect 206, so accept that.
  387. response.StatusCode != http.StatusOK &&
  388. response.StatusCode != http.StatusRequestedRangeNotSatisfiable &&
  389. response.StatusCode != http.StatusPreconditionFailed &&
  390. response.StatusCode != http.StatusNotModified) {
  391. response.Body.Close()
  392. err = fmt.Errorf("unexpected response status code: %d", response.StatusCode)
  393. }
  394. if err != nil {
  395. return 0, "", common.ContextError(err)
  396. }
  397. defer response.Body.Close()
  398. responseETag := response.Header.Get("ETag")
  399. if response.StatusCode == http.StatusPreconditionFailed {
  400. // When the ETag no longer matches, delete the partial download. As above,
  401. // simply failing and relying on the caller's retry schedule.
  402. os.Remove(partialFilename)
  403. os.Remove(partialETagFilename)
  404. return 0, "", common.ContextError(errors.New("partial download ETag mismatch"))
  405. } else if response.StatusCode == http.StatusNotModified {
  406. // This status code is possible in the "If-None-Match" case. Don't leave
  407. // any partial download in progress. Caller should check that responseETag
  408. // matches ifNoneMatchETag.
  409. os.Remove(partialFilename)
  410. os.Remove(partialETagFilename)
  411. return 0, responseETag, nil
  412. }
  413. // Not making failure to write ETag file fatal, in case the entire download
  414. // succeeds in this one request.
  415. ioutil.WriteFile(partialETagFilename, []byte(responseETag), 0600)
  416. // A partial download occurs when this copy is interrupted. The io.Copy
  417. // will fail, leaving a partial download in place (.part and .part.etag).
  418. n, err := io.Copy(NewSyncFileWriter(file), response.Body)
  419. // From this point, n bytes are indicated as downloaded, even if there is
  420. // an error; the caller may use this to report partial download progress.
  421. if err != nil {
  422. return n, "", common.ContextError(err)
  423. }
  424. // Ensure the file is flushed to disk. The deferred close
  425. // will be a noop when this succeeds.
  426. err = file.Close()
  427. if err != nil {
  428. return n, "", common.ContextError(err)
  429. }
  430. // Remove if exists, to enable rename
  431. os.Remove(downloadFilename)
  432. err = os.Rename(partialFilename, downloadFilename)
  433. if err != nil {
  434. return n, "", common.ContextError(err)
  435. }
  436. os.Remove(partialETagFilename)
  437. return n, responseETag, nil
  438. }