mirokuratczyk 5 лет назад
Родитель
Сommit
0694b54ce9

+ 43 - 1
MobileLibrary/Android/PsiphonTunnel/PsiphonTunnel.java

@@ -66,6 +66,7 @@ import java.util.concurrent.atomic.AtomicReference;
 
 import psi.Psi;
 import psi.PsiphonProvider;
+import psi.PsiphonProviderNoticeHandler;
 
 public class PsiphonTunnel {
 
@@ -307,7 +308,8 @@ public class PsiphonTunnel {
             // Adds fields used in feedback upload, e.g. client platform.
             String psiphonConfig = buildPsiphonConfig(context, logger, feedbackConfigJson,
                     clientPlatformPrefix, clientPlatformSuffix, false, 0);
-            Psi.sendFeedback(psiphonConfig, diagnosticsJson, uploadPath);
+            PsiphonProviderNoticeHandlerShim noticeHandler = new PsiphonProviderNoticeHandlerShim(logger);
+            Psi.sendFeedback(psiphonConfig, diagnosticsJson, uploadPath, noticeHandler);
         } catch (java.lang.Exception e) {
             throw new Exception("Error sending feedback", e);
         }
@@ -493,6 +495,46 @@ public class PsiphonTunnel {
         }
     }
 
+    //----------------------------------------------------------------------------------------------
+    // PsiphonProviderNoticeHandler (Core support) interface implementation
+    //----------------------------------------------------------------------------------------------
+
+    // The PsiphonProviderNoticeHandler function is called from Go, and must be public to be
+    // accessible via the gobind mechanim. To avoid making internal implementation functions public,
+    // PsiphonProviderNoticeHandlerShim is used as a wrapper.
+
+    private static class PsiphonProviderNoticeHandlerShim implements PsiphonProviderNoticeHandler {
+
+        private HostLogger mLogger;
+
+        public PsiphonProviderNoticeHandlerShim(HostLogger logger) {
+            mLogger = logger;
+        }
+
+        @Override
+        public void notice(String noticeJSON) {
+
+            try {
+                JSONObject notice = new JSONObject(noticeJSON);
+
+                String noticeType = notice.getString("noticeType");
+                if (noticeType == null) {
+                    return;
+                }
+
+                JSONObject data = notice.getJSONObject("data");
+                if (data == null) {
+                    return;
+                }
+
+                String diagnosticMessage = noticeType + ": " + data.toString();
+                mLogger.onDiagnosticMessage(diagnosticMessage);
+            } catch (java.lang.Exception e) {
+                mLogger.onDiagnosticMessage("Error handling notice " + e.toString());
+            }
+        }
+    }
+
     private void notice(String noticeJSON) {
         handlePsiphonNotice(noticeJSON);
     }

+ 16 - 0
MobileLibrary/iOS/PsiphonTunnel/PsiphonTunnel.xcodeproj/project.pbxproj

@@ -42,6 +42,8 @@
 		66BDB0681DC26CCC0079384C /* SBJson4Writer.m in Sources */ = {isa = PBXBuildFile; fileRef = 66BDB0591DC26CCC0079384C /* SBJson4Writer.m */; };
 		CE3D1DA523906003009A4AF6 /* Backups.h in Headers */ = {isa = PBXBuildFile; fileRef = CE3D1DA323906003009A4AF6 /* Backups.h */; };
 		CE3D1DA623906003009A4AF6 /* Backups.m in Sources */ = {isa = PBXBuildFile; fileRef = CE3D1DA423906003009A4AF6 /* Backups.m */; };
+		CEDE547924EBF5980053566E /* PsiphonNoticeHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = CEDE547724EBF5980053566E /* PsiphonNoticeHandler.h */; };
+		CEDE547A24EBF5980053566E /* PsiphonNoticeHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = CEDE547824EBF5980053566E /* PsiphonNoticeHandler.m */; };
 		EFED7EBF1F587F6E0078980F /* libresolv.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = EFED7EBE1F587F6E0078980F /* libresolv.tbd */; };
 /* End PBXBuildFile section */
 
@@ -106,6 +108,8 @@
 		66BDB0591DC26CCC0079384C /* SBJson4Writer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SBJson4Writer.m; sourceTree = "<group>"; };
 		CE3D1DA323906003009A4AF6 /* Backups.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Backups.h; sourceTree = "<group>"; };
 		CE3D1DA423906003009A4AF6 /* Backups.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Backups.m; sourceTree = "<group>"; };
+		CEDE547724EBF5980053566E /* PsiphonNoticeHandler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = PsiphonNoticeHandler.h; path = ../PsiphonNoticeHandler.h; sourceTree = "<group>"; };
+		CEDE547824EBF5980053566E /* PsiphonNoticeHandler.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = PsiphonNoticeHandler.m; path = ../PsiphonNoticeHandler.m; sourceTree = "<group>"; };
 		EFED7EBE1F587F6E0078980F /* libresolv.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libresolv.tbd; path = usr/lib/libresolv.tbd; sourceTree = SDKROOT; };
 /* End PBXFileReference section */
 
@@ -190,6 +194,7 @@
 				CE3D1D9E239056E4009A4AF6 /* Files */,
 				66BAD3321E525FBC00CD06DE /* JailbreakCheck */,
 				66BDB04A1DC26CCC0079384C /* json-framework */,
+				CEDE547B24EBF5A00053566E /* Psiphon */,
 				662659241DD270E900872F6C /* Reachability */,
 				66BDB0231DA6BFCC0079384C /* PsiphonTunnel.h */,
 				66BDB0431DA6C7DD0079384C /* PsiphonTunnel.m */,
@@ -247,6 +252,15 @@
 			path = Files;
 			sourceTree = "<group>";
 		};
+		CEDE547B24EBF5A00053566E /* Psiphon */ = {
+			isa = PBXGroup;
+			children = (
+				CEDE547724EBF5980053566E /* PsiphonNoticeHandler.h */,
+				CEDE547824EBF5980053566E /* PsiphonNoticeHandler.m */,
+			);
+			path = Psiphon;
+			sourceTree = "<group>";
+		};
 		EFED7EBD1F587F6E0078980F /* Frameworks */ = {
 			isa = PBXGroup;
 			children = (
@@ -275,6 +289,7 @@
 				6685BDCA1E2E882800F0E414 /* Psi.h in Headers */,
 				66BDB0651DC26CCC0079384C /* SBJson4StreamWriterState.h in Headers */,
 				66BDB05B1DC26CCC0079384C /* SBJson4Parser.h in Headers */,
+				CEDE547924EBF5980053566E /* PsiphonNoticeHandler.h in Headers */,
 				6685BDCD1E2E88A200F0E414 /* Psi-meta.h in Headers */,
 				66BDB05A1DC26CCC0079384C /* SBJson4.h in Headers */,
 				66BDB0611DC26CCC0079384C /* SBJson4StreamTokeniser.h in Headers */,
@@ -406,6 +421,7 @@
 			files = (
 				66BDB05E1DC26CCC0079384C /* SBJson4StreamParser.m in Sources */,
 				66BDB0641DC26CCC0079384C /* SBJson4StreamWriter.m in Sources */,
+				CEDE547A24EBF5980053566E /* PsiphonNoticeHandler.m in Sources */,
 				66BDB0661DC26CCC0079384C /* SBJson4StreamWriterState.m in Sources */,
 				66BDB05C1DC26CCC0079384C /* SBJson4Parser.m in Sources */,
 				4E89F7FE1E2ED3CE00005F4C /* LookupIPv6.c in Sources */,

+ 35 - 0
MobileLibrary/iOS/PsiphonTunnel/PsiphonTunnel/PsiphonNoticeHandler.h

@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2016, Psiphon Inc.
+ * All rights reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#import <Foundation/Foundation.h>
+#import "Psi-meta.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+/// PsiphonNoticeHandler passes along notice events to the configured logger.
+/// @note This indirection is required because gomobile does not support Objective-C blocks.
+@interface PsiphonNoticeHandler : NSObject <GoPsiPsiphonProviderNoticeHandler>
+
+/// Initialize the notice handler with a given logger.
+/// @param logger Logger which will receive notices.
+- (id)initWithLogger:(void (^__nonnull)(NSString *_Nonnull))logger;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 42 - 0
MobileLibrary/iOS/PsiphonTunnel/PsiphonTunnel/PsiphonNoticeHandler.m

@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2016, Psiphon Inc.
+ * All rights reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#import "PsiphonNoticeHandler.h"
+
+@implementation PsiphonNoticeHandler {
+    void (^logger) (NSString *_Nonnull);
+}
+
+- (id)initWithLogger:(void (^__nonnull)(NSString *_Nonnull))logger {
+    self = [super init];
+    if (self != nil) {
+        self->logger = logger;
+    }
+    return self;
+}
+
+#pragma mark - GoPsiPsiphonProviderNoticeHandler implementation
+
+- (void)notice:(NSString *)noticeJSON {
+    if (self->logger != nil) {
+        self->logger(noticeJSON);
+    }
+}
+
+@end

+ 54 - 1
MobileLibrary/iOS/PsiphonTunnel/PsiphonTunnel/PsiphonTunnel.m

@@ -25,6 +25,7 @@
 #import <SystemConfiguration/CaptiveNetwork.h>
 #import "LookupIPv6.h"
 #import "Psi-meta.h"
+#import "PsiphonNoticeHandler.h"
 #import "PsiphonTunnel.h"
 #import "Backups.h"
 #import "json-framework/SBJson4.h"
@@ -501,7 +502,56 @@ typedef NS_ERROR_ENUM(PsiphonTunnelErrorDomain, PsiphonTunnelErrorCode) {
         return;
     }
 
-    GoPsiSendFeedback(psiphonConfig, feedbackJson, uploadPath, &err);
+    // Convert notice to a diagnostic message and then log it.
+    void (^logNotice)(NSString * _Nonnull) = ^void(NSString * _Nonnull noticeJSON) {
+
+        if (logger != nil && [logger respondsToSelector:@selector(onDiagnosticMessage:withTimestamp:)]) {
+
+            __block NSDictionary *notice = nil;
+            id block = ^(id obj, BOOL *ignored) {
+                if (ignored == nil || *ignored == YES) {
+                    return;
+                }
+                notice = (NSDictionary *)obj;
+            };
+
+            id eh = ^(NSError *err) {
+                notice = nil;
+                logMessage([NSString stringWithFormat: @"Notice JSON parse failed: %@", err.description]);
+            };
+
+            id parser = [SBJson4Parser parserWithBlock:block allowMultiRoot:NO unwrapRootArray:NO errorHandler:eh];
+            [parser parse:[noticeJSON dataUsingEncoding:NSUTF8StringEncoding]];
+
+            if (notice == nil) {
+                return;
+            }
+
+            NSString *noticeType = notice[@"noticeType"];
+            if (noticeType == nil) {
+                logMessage(@"Notice missing noticeType");
+                return;
+            }
+
+            NSDictionary *data = notice[@"data"];
+            if (data == nil) {
+                return;
+            }
+
+            NSString *dataStr = [[[SBJson4Writer alloc] init] stringWithObject:data];
+            NSString *timestampStr = notice[@"timestamp"];
+            if (timestampStr == nil) {
+                return;
+            }
+
+            NSString *diagnosticMessage = [NSString stringWithFormat:@"%@: %@", noticeType, dataStr];
+            [logger onDiagnosticMessage:diagnosticMessage withTimestamp:timestampStr];
+        }
+    };
+
+    PsiphonNoticeHandler *noticeHandler = [[PsiphonNoticeHandler alloc] initWithLogger:logNotice];
+
+    GoPsiSendFeedback(psiphonConfig, feedbackJson, uploadPath, noticeHandler, &err);
 
     if (err != nil) {
         *outError = [NSError errorWithDomain:PsiphonTunnelErrorDomain
@@ -1215,6 +1265,9 @@ typedef NS_ERROR_ENUM(PsiphonTunnelErrorDomain, PsiphonTunnelErrorCode) {
         
         NSString *dataStr = [[[SBJson4Writer alloc] init] stringWithObject:data];
         NSString *timestampStr = notice[@"timestamp"];
+        if (timestampStr == nil) {
+            return;
+        }
 
         NSString *diagnosticMessage = [NSString stringWithFormat:@"%@: %@", noticeType, dataStr];
         [self postDiagnosticMessage:diagnosticMessage withTimestamp:timestampStr];

+ 16 - 2
MobileLibrary/psi/psi.go

@@ -40,8 +40,12 @@ import (
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/tun"
 )
 
-type PsiphonProvider interface {
+type PsiphonProviderNoticeHandler interface {
 	Notice(noticeJSON string)
+}
+
+type PsiphonProvider interface {
+	PsiphonProviderNoticeHandler
 	HasNetworkConnectivity() int
 	BindToDevice(fileDescriptor int) (string, error)
 	IPv6Synthesize(IPv4Addr string) string
@@ -291,7 +295,17 @@ func ImportExchangePayload(payload string) bool {
 }
 
 // Encrypt and upload feedback.
-func SendFeedback(configJson, diagnosticsJson, uploadPath string) error {
+func SendFeedback(
+	configJson,
+	diagnosticsJson,
+	uploadPath string,
+	noticeHandler PsiphonProviderNoticeHandler) error {
+
+	psiphon.SetNoticeWriter(psiphon.NewNoticeReceiver(
+		func(notice []byte) {
+			noticeHandler.Notice(string(notice))
+		}))
+
 	return psiphon.SendFeedback(configJson, diagnosticsJson, uploadPath)
 }
 

+ 1 - 0
psiphon/feedback.go

@@ -193,6 +193,7 @@ func SendFeedback(configJson, diagnosticsJson, uploadPath string) error {
 		err = uploadFeedback(client, request)
 		cancelFunc()
 		if err != nil {
+			NoticeWarning("uploadFeedback failed: %s", errors.Trace(err))
 			// Do not sleep after the last attempt
 			if i+1 < feedbackUploadMaxRetries {
 				time.Sleep(