diff --git a/README.md b/README.md index 2becdf5..7b8e059 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,19 @@ -# network proxy +## 免费开源Http、Https抓包工具,支持Windows、Mac、Android、IOS, 全平台系统 -## 免费开全Http、Https抓包工具,支持Windows、Mac、Android、IOS, 支持全平台系统 +**Mac首次打开会提示已损坏,需要到系统偏好设置-安全性与隐私-允许任何来源。** -> ios先没有上架AppStore, 后面在处理,目前是通过分发平台签名安装 +国内下载地址:https://gitee.com/wanghongenpin/network-proxy-flutter/releases/tag/0.0.1 + +> ios先没有上架AppStore, 后面在处理。 -- mac会提示已损坏,需要到系统偏好设置-安全性与隐私-允许任何来源 - Mac先点左上角关闭按钮退出,直接Command+q可能不会清理系统代理。如果退出上不了网,请检查系统代理设置,清除即可。 -- [ ] 接下来会完善功能体验,JSON格式化展示,URL解码,UI优化。 +- [ ] 接下来会完善功能体验,JSON格式化展示,URL解码,UI优化。 - [ ] IOS上架app Store。 - [ ] 后面桌面端和手机端整合,扫码连接啥的,不用手动配置Wifi代理,包括配置同步。 - [ ] 支持安卓微信小程序抓包,安卓分为系统证书和用户证书,下载的自签名根证书安装都是用户证书,微信不信任用户证书,不Root导致Https抓不了了, 目前市场上所有抓包软件抓不了微信的包,后面单独做个运行空间插件,动态反编译修改配置,信任用户证书来解决。 + + +image. image + + diff --git a/ios/ProxyPin/PacketTunnelProvider.swift b/ios/ProxyPin/PacketTunnelProvider.swift index 378c5d8..7fd3da7 100644 --- a/ios/ProxyPin/PacketTunnelProvider.swift +++ b/ios/ProxyPin/PacketTunnelProvider.swift @@ -18,9 +18,10 @@ class PacketTunnelProvider: NEPacketTunnelProvider { exit(EXIT_FAILURE) } let networkSettings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: "127.0.0.1") - + networkSettings.mtu = 1500 + NSLog(conf.debugDescription) //http代理 - let host = conf["proxtHost"] as! String + let host = conf["proxyHost"] as! String let proxyPort = conf["proxyPort"] as! Int let proxySettings = NEProxySettings() proxySettings.httpEnabled = true @@ -28,8 +29,9 @@ class PacketTunnelProvider: NEPacketTunnelProvider { proxySettings.httpsEnabled = true proxySettings.httpsServer = NEProxyServer(address: host, port: proxyPort) proxySettings.matchDomains = [""] + networkSettings.proxySettings = proxySettings - + setTunnelNetworkSettings(networkSettings) { error in guard error == nil else { @@ -43,23 +45,24 @@ class PacketTunnelProvider: NEPacketTunnelProvider { } NSLog("startTunnelend") } - + override func stopTunnel(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) { completionHandler() } - + override func handleAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)?) { // Add code here to handle the message. if let handler = completionHandler { + NSLog("handleAppMessage ", messageData.debugDescription) handler(messageData) } } - + override func sleep(completionHandler: @escaping () -> Void) { // Add code here to get ready to sleep. completionHandler() } - + override func wake() { // Add code here to wake up. } diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 3b21ed3..b5eed67 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 54; + objectVersion = 55; objects = { /* Begin PBXBuildFile section */ @@ -19,6 +19,8 @@ 9B0912222A54593A001108B7 /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9B0912212A54593A001108B7 /* NetworkExtension.framework */; }; 9B0912252A54593A001108B7 /* PacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B0912242A54593A001108B7 /* PacketTunnelProvider.swift */; }; 9B09122A2A54593A001108B7 /* ProxyPin.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 9B0912202A54593A001108B7 /* ProxyPin.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 9B70772D2A5718FB00F184A9 /* AudioManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B70772C2A5718FB00F184A9 /* AudioManager.swift */; }; + 9B7077362A5728B900F184A9 /* silence.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 9B7077352A5728B900F184A9 /* silence.mp3 */; }; B375908E625E0AED772FA2C0 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D37E307095F2B3E689A68827 /* Pods_RunnerTests.framework */; }; /* End PBXBuildFile section */ @@ -92,6 +94,8 @@ 9B0912242A54593A001108B7 /* PacketTunnelProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PacketTunnelProvider.swift; sourceTree = ""; }; 9B0912262A54593A001108B7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 9B0912272A54593A001108B7 /* ProxyPin.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = ProxyPin.entitlements; sourceTree = ""; }; + 9B70772C2A5718FB00F184A9 /* AudioManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioManager.swift; sourceTree = ""; }; + 9B7077352A5728B900F184A9 /* silence.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = silence.mp3; sourceTree = ""; }; D37E307095F2B3E689A68827 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; E328C7F89A365CDC0EAD15C6 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ @@ -192,6 +196,7 @@ 97C146F01CF9000F007C117D /* Runner */ = { isa = PBXGroup; children = ( + 9B7077352A5728B900F184A9 /* silence.mp3 */, 9B09121A2A5457B3001108B7 /* VpnManager.swift */, 9B0912192A545757001108B7 /* Runner.entitlements */, 97C146FA1CF9000F007C117D /* Main.storyboard */, @@ -202,6 +207,7 @@ 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, + 9B70772C2A5718FB00F184A9 /* AudioManager.swift */, ); path = Runner; sourceTree = ""; @@ -303,7 +309,7 @@ }; }; buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; - compatibilityVersion = "Xcode 9.3"; + compatibilityVersion = "Xcode 13.0"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( @@ -334,6 +340,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 9B7077362A5728B900F184A9 /* silence.mp3 in Resources */, 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, @@ -460,6 +467,7 @@ files = ( 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + 9B70772D2A5718FB00F184A9 /* AudioManager.swift in Sources */, 9B09121B2A5457B3001108B7 /* VpnManager.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -805,7 +813,7 @@ INFOPLIST_FILE = ProxyPin/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = ProxyPin; INFOPLIST_KEY_NSHumanReadableCopyright = ""; - IPHONEOS_DEPLOYMENT_TARGET = 16.2; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -843,7 +851,7 @@ INFOPLIST_FILE = ProxyPin/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = ProxyPin; INFOPLIST_KEY_NSHumanReadableCopyright = ""; - IPHONEOS_DEPLOYMENT_TARGET = 16.2; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -878,7 +886,7 @@ INFOPLIST_FILE = ProxyPin/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = ProxyPin; INFOPLIST_KEY_NSHumanReadableCopyright = ""; - IPHONEOS_DEPLOYMENT_TARGET = 16.2; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift index 17ddef1..329d614 100644 --- a/ios/Runner/AppDelegate.swift +++ b/ios/Runner/AppDelegate.swift @@ -29,15 +29,47 @@ import NetworkExtension } }) - - return super.application(application, didFinishLaunchingWithOptions: launchOptions) } + override func applicationWillTerminate(_ application: UIApplication) { + VpnManager.shared.disconnect() + } - override func applicationWillTerminate(_ application: UIApplication) { - VpnManager.shared.disconnect() - } + var timer: Timer? + var bgTask: UIBackgroundTaskIdentifier? + override func applicationDidEnterBackground(_ application: UIApplication) { +// timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(timerAction), userInfo: nil, repeats: true) +// RunLoop.current.add(timer!, forMode: RunLoop.Mode.common) +// bgTask = application.beginBackgroundTask(expirationHandler: nil) + } + + @objc func timerAction() { + print(UIApplication.shared.backgroundTimeRemaining ) + if UIApplication.shared.backgroundTimeRemaining < 60.0 { + let application = UIApplication.shared + bgTask = application.beginBackgroundTask(expirationHandler: nil) + } + } + + var backgroundUpdateTask: UIBackgroundTaskIdentifier = UIBackgroundTaskIdentifier(rawValue: 0) + func endBackgroundUpdateTask() { + AudioManager.shared.openBackgroundAudioAutoplay = false + UIApplication.shared.endBackgroundTask(self.backgroundUpdateTask) + self.backgroundUpdateTask = UIBackgroundTaskIdentifier.invalid + } + + override func applicationWillResignActive(_ application: UIApplication) { + AudioManager.shared.openBackgroundAudioAutoplay = true + self.backgroundUpdateTask = UIApplication.shared.beginBackgroundTask(expirationHandler: { + self.endBackgroundUpdateTask() + }) + } + override func applicationDidBecomeActive(_ application: UIApplication) { + self.endBackgroundUpdateTask() + + } + } diff --git a/ios/Runner/AudioManager.swift b/ios/Runner/AudioManager.swift new file mode 100644 index 0000000..042506d --- /dev/null +++ b/ios/Runner/AudioManager.swift @@ -0,0 +1,157 @@ +import Foundation +import AVFoundation +import UIKit +class AudioManager: NSObject { + + static let shared = AudioManager() + fileprivate let audioSession = AVAudioSession.sharedInstance() + fileprivate var backgroundAudioPlayer: AVAudioPlayer? + fileprivate var backgroundTimeLength = 0 + fileprivate var timer: Timer? + + static let audioName = "" + // 是否开启后台自动播放无声音乐 + var openBackgroundAudioAutoplay = false { + didSet { + if self.openBackgroundAudioAutoplay { + self.setupAudioSession() + self.setupBackgroundAudioPlayer() + } else { + if let player = self.backgroundAudioPlayer { + if player.isPlaying { + player.stop() + } + } + self.backgroundAudioPlayer = nil + try? self.audioSession.setActive(false, options: AVAudioSession.SetActiveOptions.notifyOthersOnDeactivation) + } + } + } + + override init() { + super.init() + self.setupListener() + } + deinit { + NotificationCenter.default.removeObserver(self) + } + private func setupAudioSession() { + do { + try self.audioSession.setCategory(AVAudioSession.Category.playback, options: AVAudioSession.CategoryOptions.mixWithOthers) + try self.audioSession.setActive(false) + } catch let error { + debugPrint("\(type(of:self)):\(error)") + } + } + private func setupBackgroundAudioPlayer() { + do { + self.backgroundAudioPlayer = try AVAudioPlayer(contentsOf: URL(fileURLWithPath: Bundle.main.path(forResource: "silence", ofType: "mp3")!)) + } catch let error { + debugPrint("\(type(of:self)):\(error)") + } + self.backgroundAudioPlayer?.numberOfLoops = -1 + self.backgroundAudioPlayer?.volume = 1 + self.backgroundAudioPlayer?.delegate = self + } + +//主要修改了这里 + private func setupListener() { + + NotificationCenter.default.addObserver(self, selector: #selector(didEnterBackground), name: UIApplication.didEnterBackgroundNotification, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(didBecomeActive), name: UIApplication.didBecomeActiveNotification, object: nil) + + NotificationCenter.default.addObserver(self, selector: #selector(audioSessionInterruption(notification:)), name: AVAudioSession.interruptionNotification, object: nil) + + + } + +} +// MARK: - 扩展 监听通知 +extension AudioManager { + /// 进入后台 播放无声音乐 + @objc public func didEnterBackground() { + self.setupTimer() + guard self.openBackgroundAudioAutoplay else {return} + + do { + try self.audioSession.setActive(true) + } catch let error { + debugPrint("\(type(of:self)):\(error))") + } + self.backgroundAudioPlayer?.prepareToPlay() + self.backgroundAudioPlayer?.play() + } + + /// 进入前台,暂停播放音乐 + @objc public func didBecomeActive() { + self.removeTimer() + self.hintBackgroundTimeLength() + self.backgroundTimeLength = 0 + guard self.openBackgroundAudioAutoplay else {return} + + self.backgroundAudioPlayer?.pause() + do { + try self.audioSession.setActive(false, options: AVAudioSession.SetActiveOptions.notifyOthersOnDeactivation) + } catch let error { + debugPrint("\(type(of:self)):\(error))") + } + + + } + + /// 音乐中断处理 + @objc fileprivate func audioSessionInterruption(notification: NSNotification) { + guard self.openBackgroundAudioAutoplay else {return} + guard let userinfo = notification.userInfo else {return} + guard let interruptionType: UInt = userinfo[AVAudioSessionInterruptionTypeKey] as! UInt? else {return} + if interruptionType == AVAudioSession.InterruptionType.began.rawValue { + // 中断开始,音乐被暂停 + debugPrint("\(type(of:self)): 中断开始 userinfo:\(userinfo)") + } else if interruptionType == AVAudioSession.InterruptionType.ended.rawValue { + // 中断结束,恢复播放 + debugPrint("\(type(of:self)): 中断结束 userinfo:\(userinfo)") + guard let player = self.backgroundAudioPlayer else {return} + if player.isPlaying == false { + debugPrint("\(type(of:self)): 音乐未播放,准备开始播放") + do { + try self.audioSession.setActive(true) + } catch let error { + debugPrint("\(type(of:self)):\(error)") + } + player.prepareToPlay() + player.play() + } else { + debugPrint("\(type(of:self)): 音乐正在播放") + } + } + } +} +// MARK: - 扩展 定时器任务 +extension AudioManager { + fileprivate func setupTimer() { + self.removeTimer() + self.timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(timerTask), userInfo: nil, repeats: true) + RunLoop.main.add(self.timer!, forMode: RunLoop.Mode.common) +// RunLoop.main.add(self.timer!, forMode: RunLoop.Mode.init(rawValue: "")) + } + fileprivate func removeTimer() { + self.timer?.invalidate() + self.timer = nil; + } + @objc func timerTask() { + self.backgroundTimeLength += 1 + } + fileprivate func hintBackgroundTimeLength() { + let message = "本次后台持续时间:\(self.backgroundTimeLength)s" + print(message) + } +} +// MARK: - 扩展 播放代理 +extension AudioManager: AVAudioPlayerDelegate { + func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) { + + } + func audioPlayerDecodeErrorDidOccur(_ player: AVAudioPlayer, error: Error?) { + debugPrint("\(type(of:self))" + error.debugDescription) + } +} diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index ccbe8f4..4702458 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -2,6 +2,10 @@ + BGTaskSchedulerPermittedIdentifiers + + $(PRODUCT_BUNDLE_IDENTIFIER) + CADisableMinimumFrameDurationOnPhone CFBundleDevelopmentRegion @@ -26,8 +30,18 @@ $(FLUTTER_BUILD_NUMBER) LSRequiresIPhoneOS + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + UIApplicationSupportsIndirectInputEvents + UIBackgroundModes + + audio + processing + UILaunchStoryboardName LaunchScreen UIMainStoryboardFile @@ -45,11 +59,6 @@ UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight - NSAppTransportSecurity - - NSAllowsArbitraryLoads - - UIViewControllerBasedStatusBarAppearance diff --git a/ios/Runner/Runner.entitlements b/ios/Runner/Runner.entitlements index 70e1d98..0e27801 100644 --- a/ios/Runner/Runner.entitlements +++ b/ios/Runner/Runner.entitlements @@ -10,7 +10,6 @@ allow-vpn - com.apple.security.application-groups group.com.proxy.pin diff --git a/ios/Runner/VpnManager.swift b/ios/Runner/VpnManager.swift index de32809..c8ab2db 100755 --- a/ios/Runner/VpnManager.swift +++ b/ios/Runner/VpnManager.swift @@ -14,7 +14,7 @@ enum VPNStatus { class VpnManager{ var activeVPN: NETunnelProviderManager?; - public var proxtHost: String = "127.0.0.01" + public var proxyHost: String = "127.0.0.01" public var proxyPort: Int = 8888 static let shared = VpnManager() @@ -94,7 +94,7 @@ extension VpnManager{ } var conf = [String:AnyObject]() - conf["proxtHost"] = self.proxtHost as AnyObject + conf["proxyHost"] = self.proxyHost as AnyObject conf["proxyPort"] = self.proxyPort as AnyObject let orignConf = manager.protocolConfiguration as! NETunnelProviderProtocol @@ -141,7 +141,7 @@ extension VpnManager{ extension VpnManager{ func connect(host: String?, port: Int?){ - self.proxtHost = host ?? self.proxtHost + self.proxyHost = host ?? self.proxyHost self.proxyPort = port ?? self.proxyPort self.loadAndCreatePrividerManager { (manager) in diff --git a/ios/Runner/silence.mp3 b/ios/Runner/silence.mp3 new file mode 100644 index 0000000..cf2063b Binary files /dev/null and b/ios/Runner/silence.mp3 differ diff --git a/lib/network/channel.dart b/lib/network/channel.dart index 6a16e95..8e9b2a9 100644 --- a/lib/network/channel.dart +++ b/lib/network/channel.dart @@ -124,7 +124,7 @@ class ChannelPipeline extends ChannelHandler { } if (data is HttpRequest) { data.hostAndPort = channel.getAttribute(AttributeKeys.host) ?? getHostAndPort(data); - if (data.headers.host() != null) { + if (data.headers.host() != null && data.headers.host()?.contains(":") == false) { data.hostAndPort?.host = data.headers.host()!; } diff --git a/lib/ui/desktop/toolbar/launch/launch.dart b/lib/ui/desktop/toolbar/launch/launch.dart index 80d2e0d..4694603 100644 --- a/lib/ui/desktop/toolbar/launch/launch.dart +++ b/lib/ui/desktop/toolbar/launch/launch.dart @@ -46,10 +46,9 @@ class _SocketLaunchState extends State with WindowListener, Widget @override void didChangeAppLifecycleState(AppLifecycleState state) { super.didChangeAppLifecycleState(state); - //页面即将关闭 if (state == AppLifecycleState.detached) { print('AppLifecycleState.detached'); - // widget.onStop?.call(); + widget.onStop?.call(); widget.proxyServer.stop(); started = false; } diff --git a/lib/ui/panel.dart b/lib/ui/panel.dart index 84953ef..df70722 100644 --- a/lib/ui/panel.dart +++ b/lib/ui/panel.dart @@ -107,11 +107,21 @@ class NetworkTabState extends State with SingleTickerProvi } Widget request() { - return message(widget.request.get(), "Request"); + return ListView(children: [ + Padding( + padding: const EdgeInsetsDirectional.only(start: 20, top: 20), + child: rowWidget("Path", widget.request.get()?.path.toString())), + ...message(widget.request.get(), "Request") + ]); } Widget response() { - return message(widget.response.get(), "Response"); + return ListView(children: [ + Padding( + padding: const EdgeInsetsDirectional.only(start: 20, top: 20), + child: rowWidget("StatusCode", widget.response.get()?.status.code.toString())), + ...message(widget.response.get(), "Response") + ]); } Widget cookies() { @@ -126,7 +136,7 @@ class NetworkTabState extends State with SingleTickerProvi ]); } - Widget message(HttpMessage? message, String type) { + List message(HttpMessage? message, String type) { var headers = []; message?.headers.forEach((name, values) { for (var v in values) { @@ -140,15 +150,14 @@ class NetworkTabState extends State with SingleTickerProvi Widget? bodyWidgets = message == null ? null : getBody(type, message); - return ListView(children: [ - ExpansionTile( - title: Text("$type Headers", style: const TextStyle(fontWeight: FontWeight.bold)), - initiallyExpanded: true, - shape: const Border(), - childrenPadding: const EdgeInsets.only(left: 20, bottom: 20), - children: headers), - bodyWidgets ?? const SizedBox() - ]); + Widget headerWidget = ExpansionTile( + title: Text("$type Headers", style: const TextStyle(fontWeight: FontWeight.bold)), + initiallyExpanded: true, + shape: const Border(), + childrenPadding: const EdgeInsets.only(left: 20, bottom: 20), + children: headers); + + return [headerWidget, bodyWidgets ?? const SizedBox()]; } Widget expansionTile(String title, List content) {