Просмотр исходного кода

Add custom User-Agent to web API and download requests

Rod Hynes 9 лет назад
Родитель
Сommit
45b31e8a1f
7 измененных файлов с 59 добавлено и 6 удалено
  1. 10 2
      psiphon/feedback.go
  2. 3 0
      psiphon/net.go
  3. 5 1
      psiphon/remoteServerList.go
  4. 17 2
      psiphon/serverApi.go
  5. 4 0
      psiphon/splitTunnel.go
  6. 5 1
      psiphon/upgradeDownload.go
  7. 15 0
      psiphon/utils.go

+ 10 - 2
psiphon/feedback.go

@@ -139,7 +139,12 @@ func SendFeedback(configJson, diagnosticsJson, b64EncodedPublicKey, uploadServer
 	}
 	}
 
 
 	for i := 0; i < FEEDBACK_UPLOAD_MAX_RETRIES; i++ {
 	for i := 0; i < FEEDBACK_UPLOAD_MAX_RETRIES; i++ {
-		err := uploadFeedback(untunneledDialConfig, secureFeedback, url, headerPieces)
+		err := uploadFeedback(
+			untunneledDialConfig,
+			secureFeedback,
+			url,
+			MakePsiphonUserAgent(config),
+			headerPieces)
 		if err != nil {
 		if err != nil {
 			NoticeAlert("failed to upload feedback: %s", err)
 			NoticeAlert("failed to upload feedback: %s", err)
 			time.Sleep(FEEDBACK_UPLOAD_RETRY_DELAY_SECONDS * time.Second)
 			time.Sleep(FEEDBACK_UPLOAD_RETRY_DELAY_SECONDS * time.Second)
@@ -151,7 +156,7 @@ func SendFeedback(configJson, diagnosticsJson, b64EncodedPublicKey, uploadServer
 }
 }
 
 
 // Attempt to upload feedback data to server.
 // Attempt to upload feedback data to server.
-func uploadFeedback(config *DialConfig, feedbackData []byte, url string, headerPieces []string) error {
+func uploadFeedback(config *DialConfig, feedbackData []byte, url, userAgent string, headerPieces []string) error {
 	client, parsedUrl, err := MakeUntunneledHttpsClient(
 	client, parsedUrl, err := MakeUntunneledHttpsClient(
 		config, nil, url, false, time.Duration(FEEDBACK_UPLOAD_TIMEOUT_SECONDS*time.Second))
 		config, nil, url, false, time.Duration(FEEDBACK_UPLOAD_TIMEOUT_SECONDS*time.Second))
 	if err != nil {
 	if err != nil {
@@ -162,6 +167,9 @@ func uploadFeedback(config *DialConfig, feedbackData []byte, url string, headerP
 	if err != nil {
 	if err != nil {
 		return common.ContextError(err)
 		return common.ContextError(err)
 	}
 	}
+
+	req.Header.Set("User-Agent", userAgent)
+
 	req.Header.Set(headerPieces[0], headerPieces[1])
 	req.Header.Set(headerPieces[0], headerPieces[1])
 
 
 	resp, err := client.Do(req)
 	resp, err := client.Do(req)

+ 3 - 0
psiphon/net.go

@@ -408,6 +408,7 @@ func MakeDownloadHttpClient(
 func ResumeDownload(
 func ResumeDownload(
 	httpClient *http.Client,
 	httpClient *http.Client,
 	requestUrl string,
 	requestUrl string,
+	userAgent string,
 	downloadFilename string,
 	downloadFilename string,
 	ifNoneMatchETag string) (int64, string, error) {
 	ifNoneMatchETag string) (int64, string, error) {
 
 
@@ -451,6 +452,8 @@ func ResumeDownload(
 		return 0, "", common.ContextError(err)
 		return 0, "", common.ContextError(err)
 	}
 	}
 
 
+	request.Header.Set("User-Agent", userAgent)
+
 	request.Header.Add("Range", fmt.Sprintf("bytes=%d-", fileInfo.Size()))
 	request.Header.Add("Range", fmt.Sprintf("bytes=%d-", fileInfo.Size()))
 
 
 	if partialETag != nil {
 	if partialETag != nil {

+ 5 - 1
psiphon/remoteServerList.go

@@ -334,7 +334,11 @@ func downloadRemoteServerListFile(
 	}
 	}
 
 
 	n, responseETag, err := ResumeDownload(
 	n, responseETag, err := ResumeDownload(
-		httpClient, requestURL, destinationFilename, lastETag)
+		httpClient,
+		requestURL,
+		MakePsiphonUserAgent(config),
+		destinationFilename,
+		lastETag)
 
 
 	NoticeRemoteServerListResourceDownloadedBytes(sourceURL, n)
 	NoticeRemoteServerListResourceDownloadedBytes(sourceURL, n)
 
 

+ 17 - 2
psiphon/serverApi.go

@@ -743,7 +743,14 @@ func (serverContext *ServerContext) DoClientVerificationRequest(
 func (serverContext *ServerContext) doGetRequest(
 func (serverContext *ServerContext) doGetRequest(
 	requestUrl string) (responseBody []byte, err error) {
 	requestUrl string) (responseBody []byte, err error) {
 
 
-	response, err := serverContext.psiphonHttpsClient.Get(requestUrl)
+	request, err := http.NewRequest("GET", requestUrl, nil)
+	if err != nil {
+		return nil, common.ContextError(err)
+	}
+
+	request.Header.Set("User-Agent", MakePsiphonUserAgent(serverContext.tunnel.config))
+
+	response, err := serverContext.psiphonHttpsClient.Do(request)
 	if err == nil && response.StatusCode != http.StatusOK {
 	if err == nil && response.StatusCode != http.StatusOK {
 		response.Body.Close()
 		response.Body.Close()
 		err = fmt.Errorf("HTTP GET request failed with response code: %d", response.StatusCode)
 		err = fmt.Errorf("HTTP GET request failed with response code: %d", response.StatusCode)
@@ -764,7 +771,15 @@ func (serverContext *ServerContext) doGetRequest(
 func (serverContext *ServerContext) doPostRequest(
 func (serverContext *ServerContext) doPostRequest(
 	requestUrl string, bodyType string, body io.Reader) (responseBody []byte, err error) {
 	requestUrl string, bodyType string, body io.Reader) (responseBody []byte, err error) {
 
 
-	response, err := serverContext.psiphonHttpsClient.Post(requestUrl, bodyType, body)
+	request, err := http.NewRequest("POST", requestUrl, body)
+	if err != nil {
+		return nil, common.ContextError(err)
+	}
+
+	request.Header.Set("User-Agent", MakePsiphonUserAgent(serverContext.tunnel.config))
+	request.Header.Set("Content-Type", bodyType)
+
+	response, err := serverContext.psiphonHttpsClient.Do(request)
 	if err == nil && response.StatusCode != http.StatusOK {
 	if err == nil && response.StatusCode != http.StatusOK {
 		response.Body.Close()
 		response.Body.Close()
 		err = fmt.Errorf("HTTP POST request failed with response code: %d", response.StatusCode)
 		err = fmt.Errorf("HTTP POST request failed with response code: %d", response.StatusCode)

+ 4 - 0
psiphon/splitTunnel.go

@@ -69,6 +69,7 @@ import (
 type SplitTunnelClassifier struct {
 type SplitTunnelClassifier struct {
 	mutex                    sync.RWMutex
 	mutex                    sync.RWMutex
 	fetchRoutesUrlFormat     string
 	fetchRoutesUrlFormat     string
+	userAgent                string
 	routesSignaturePublicKey string
 	routesSignaturePublicKey string
 	dnsServerAddress         string
 	dnsServerAddress         string
 	dnsTunneler              Tunneler
 	dnsTunneler              Tunneler
@@ -86,6 +87,7 @@ type classification struct {
 func NewSplitTunnelClassifier(config *Config, tunneler Tunneler) *SplitTunnelClassifier {
 func NewSplitTunnelClassifier(config *Config, tunneler Tunneler) *SplitTunnelClassifier {
 	return &SplitTunnelClassifier{
 	return &SplitTunnelClassifier{
 		fetchRoutesUrlFormat:     config.SplitTunnelRoutesUrlFormat,
 		fetchRoutesUrlFormat:     config.SplitTunnelRoutesUrlFormat,
+		userAgent:                MakePsiphonUserAgent(config),
 		routesSignaturePublicKey: config.SplitTunnelRoutesSignaturePublicKey,
 		routesSignaturePublicKey: config.SplitTunnelRoutesSignaturePublicKey,
 		dnsServerAddress:         config.SplitTunnelDnsServer,
 		dnsServerAddress:         config.SplitTunnelDnsServer,
 		dnsTunneler:              tunneler,
 		dnsTunneler:              tunneler,
@@ -221,6 +223,8 @@ func (classifier *SplitTunnelClassifier) getRoutes(tunnel *Tunnel) (routesData [
 		return nil, common.ContextError(err)
 		return nil, common.ContextError(err)
 	}
 	}
 
 
+	request.Header.Set("User-Agent", classifier.userAgent)
+
 	etag, err := GetSplitTunnelRoutesETag(tunnel.serverContext.clientRegion)
 	etag, err := GetSplitTunnelRoutesETag(tunnel.serverContext.clientRegion)
 	if err != nil {
 	if err != nil {
 		return nil, common.ContextError(err)
 		return nil, common.ContextError(err)

+ 5 - 1
psiphon/upgradeDownload.go

@@ -139,7 +139,11 @@ func DownloadUpgrade(
 		"%s.%s", config.UpgradeDownloadFilename, availableClientVersion)
 		"%s.%s", config.UpgradeDownloadFilename, availableClientVersion)
 
 
 	n, _, err := ResumeDownload(
 	n, _, err := ResumeDownload(
-		httpClient, requestUrl, downloadFilename, "")
+		httpClient,
+		requestUrl,
+		MakePsiphonUserAgent(config),
+		downloadFilename,
+		"")
 
 
 	NoticeClientUpgradeDownloadedBytes(n)
 	NoticeClientUpgradeDownloadedBytes(n)
 
 

+ 15 - 0
psiphon/utils.go

@@ -32,6 +32,21 @@ import (
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
 )
 )
 
 
+// MakePsiphonUserAgent constructs a User-Agent value to use for web API,
+// download, etc. requests. The User-Agent includes useful stats information.
+// This User-Agent is to be used only for HTTPS requests, where the header
+// cannot be seen by an adversary.
+func MakePsiphonUserAgent(config *Config) string {
+	userAgent := "psiphon-tunnel-core"
+	if config.ClientVersion != "" {
+		userAgent += fmt.Sprintf("/%s", config.ClientVersion)
+	}
+	if config.ClientPlatform != "" {
+		userAgent += fmt.Sprintf(" (%s)", config.ClientPlatform)
+	}
+	return userAgent
+}
+
 func DecodeCertificate(encodedCertificate string) (certificate *x509.Certificate, err error) {
 func DecodeCertificate(encodedCertificate string) (certificate *x509.Certificate, err error) {
 	derEncodedCertificate, err := base64.StdEncoding.DecodeString(encodedCertificate)
 	derEncodedCertificate, err := base64.StdEncoding.DecodeString(encodedCertificate)
 	if err != nil {
 	if err != nil {