关于NEKit使用的一些心得

作者: 秦砖 | 来源:发表于2017-07-16 23:27 被阅读4015次

    花了一些时间终于把NEKit跑通了,ShadowSocks服务器也连上了,Google/Facebook/Twitter等墙外世界拥入眼中。甚至今天在简书上还无意中知道Pornhub这个东东,我去,忽然感觉被墙起来的世界也不错啊!废话不多说,具体说下跑NEKit中遇到的一些坑。

    目标与参考资料

    使用NEKit框架搭建ShadowSocks在iOS平台上的客户端,具体搭建流程参考了这篇文章。虽然很感谢这位作者的辛勤劳作,但还是要吐槽下:文章作者的思维跳跃幅度过大,且对一些基础性的东西末作更为透彻的说明,导致该文章参考难度过大,很容易跳进坑中而不知。

    应用创建

    首先创建一个普通的应用,主要功能就是打开关闭VPN,其UI界面大家自己随意,没什么可说的。

    为实现全局VPN,我们还需要为应用添加一个Extension,
    最新版本的xcode移除了该Extension的创建模板,所以我们需要下载并安装该模板

    然后为VPN应用添加一个target,target选择xcode里新出现的Package Tunnrl Provider,设定Extension名称、选择开发语言为swift并继续,Extension就生成完毕了。创建生成的Extension有且仅有一个代码文件,是后面我们配置ShadowSocks代理的主体文件。

    Extension生成模板

    权限配置

    权限是指VPN应用所需要的NeworkExtension使用权限。网上很多文章包括上面说的Demo都说该权限需要向Apple申请,且Apple审核的也比较严格,还给出了填写申请的地址。这是要强调的是:现在不用这么麻烦了,权限不需要申请了,直接在xcode里打开并使用就可以了。

    在应用的两个target的Capabilities选项栏中,打开Personal VPN与Network Extensions两项。注意:是要在应用与Extension这两个target中都打开这两个开关。然后我们就可以在工程文件中看到新生成的两个entitlements文件了。

    创建VPN并保存

    上面那个demo里说的比较杂乱,通读一遍下来,可能完全get不到文章要表达的意识,或者get的比较模糊而不得要领。其实很简单:创建vpn管理类并保存VPN配置,加载配置后可以打开关闭VPN。

    -(void)loadVpn
    {
        [NETunnelProviderManager loadAllFromPreferencesWithCompletionHandler:^(NSArray<NETunnelProviderManager *> * _Nullable managers, NSError * _Nullable error) {
            if (error == nil && [managers count] > 0) {
                self.manager = [managers firstObject];
                if ([managers count] > 1) {
                    for (NETunnelProviderManager* manager in managers) {
                        [manager removeFromPreferencesWithCompletionHandler:^(NSError * _Nullable error) {
                            if (error == nil) {
                                NSLog(@"remove dumplicate VPN config successful!");
                            }else{
                                NSLog(@"remove dumplicate VPN config failed with %@", error);
                            }
                        }];
                    }
                }
            }
            if (self.manager == nil) {
                [self createVPN];
                [self saveVPN];
            }
        }];
    }
    
    -(void)createVPN
    {
        self.manager = [[NETunnelProviderManager alloc] init];
        NETunnelProviderProtocol* config = [[NETunnelProviderProtocol alloc] init];
        config.serverAddress = @"DeepSS";
        NSString* rule = [self ruleConfig];
        config.providerConfiguration = @{@"ss_address":@"*.*.*.*", @"ss_port":@17238,@"ss_method":@"CHACHA20",@"ss_password":@"******",@"ymal_conf":rule};
        self.manager.protocolConfiguration = config;
        self.manager.localizedDescription = @"WXQ";
        self.manager.enabled = YES;
    }
    
    -(void)saveVPN
    {
        [self.manager saveToPreferencesWithCompletionHandler:^(NSError * _Nullable error) {
            if (error == nil) {
                //注意这里保存配置成功后,一定要再次load,否则会导致后面StartVPN出异常
                [self.manager loadFromPreferencesWithCompletionHandler:^(NSError * _Nullable error) {
                    if (error == nil) {
                        NSLog(@"save vpn success");
                    }
                }];
            }
        }];
    }
    

    在手机上运行后,应用就会在设置的vpn页中添加一项(该VPN页只有存在两项以上配置时才可进入查看,只有一项时不能进入,只可开关),参考如下图:


    保存Extension

    开关并监听VPN状态

    开关VPN只需简单的调用NetworkExtension为我们提供的接口即可,NetworkExtension人在前面添加的Extension中将数据的控制权移交给我们,在Extension中我们可以决定手机上不同的数据使用不同的处理手段,进而达到翻墙的目的。

    可以通过NEVPNStatusDidChangeNotification通知来获取当前VPN的状态。

    -(instancetype)init
    {
        if (self = [super init]) {
            [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(vpnStatusChanged:) name:NEVPNStatusDidChangeNotification object:nil];
            [self loadVpn];
        }
        return self;
    }
    
    -(void)startVPN
    {
        NSError* error = nil;
        [self.manager.connection startVPNTunnelWithOptions:nil andReturnError:&error];
        if (error == nil) {
            NSLog(@"Start VPN success");
        }else{
            NSLog(@"%@", error);
        }
    }
    
    -(void)stopVPN
    {
        [self.manager.connection stopVPNTunnel];
    }
    
    -(void)vpnStatusChanged:(id)sender
    {
        switch (self.manager.connection.status) {
            case NEVPNStatusReasserting:
                NSLog(@"********************ReConnecting******************");
                break;
            case NEVPNStatusConnecting:
                NSLog(@"********************Connecting******************");
                break;
            case NEVPNStatusConnected:
                NSLog(@"********************Connected******************");
                break;
            case NEVPNStatusDisconnecting:
                NSLog(@"********************Disconnectiong******************");
                break;
            case NEVPNStatusDisconnected:
                NSLog(@"********************Disconnected******************");
                break;
            case NEVPNStatusInvalid:
                NSLog(@"********************Invalid******************");
                break;
            default:
                break;
        }
    }
    
    

    ShadowSocks相关

    应该有同学注意到了,直到现在我们都没有用到NEKit!是的,没错,NEKit只在Extension中才会使用到。这里因为到网络相关知识了解不多,就将上面demo中Extension代码原样的copy过来了。主要内容是根据域名配置文件定义不同域名使用不同的代理模式:被墙的域名要使用NEkit提供的ShadowsocksAdapterFactory模式、没被墙的国内的域名使用DirectAdapterFactory模式等,大家参考下Demo中的代码。

    Extension是不能调试的,上面的demo说可以用attach的方式来调试Extension代码,但我没有验证成功过。所以最好的调试方法是加LOG,LOG当然也不会在xcode的输出日志中出现,需要我们在window-Devices里查看机器的全量日志,是不是有点做Android开发的感觉!

    NEKit

    还没对该开源方案的具体原理深入研究过,只说下使用上的几个点。NEKit使用Carthage而不是cocoaPods,整个项目中还使用了另外九个开源swift方案,在编译时要将这九个项目的framework库拷贝到应用发布包里,所以需要在Build Phase中添加下图中的拷贝脚本:


    拷贝脚本

    后记

    对swift不是很熟,Extension的开发语言第一选择是OC的,但后来发现NEKit提供的OC接口竟然无法完成ShadowSocks的代理开发。然后尝试对NEKit进行定制使其暴露足够的API给OC侧使用,又发现以目前的Swift水平无法完成这样的工作。只得放弃Extension使用OC开发的想法。幸运的是,系统支持应用使用OC,Extension使用Swift这种混合式编程。后续会将整理后的Demo发布到Github上。

    相关文章

      网友评论

      • 喝酸奶舔下盖:NEKitRule.conf里面怎么写才能不让访问youtube,google...类似的。什么规则,大佬指导下
        上个月:@Gentleman_W 这一样也算是一种办法吧
        喝酸奶舔下盖:@Aze_ 找了很久也找到相关的。这些墙外的资源默认走代理,我把一些不想访问的资源走的直连,然后就访问不了了。这样能满足需求,但是感觉不是特别科学。😭
        上个月:问下这个需求搞定了么
      • 小月_c665:现在 更新xcode 更新到9.4.1 你的Nekit 还能运行么?能不能给个demo
        b99fc8b4f30b:https://www.jianshu.com/p/5ed93a8a1449#comment-24048235这里有
        b99fc8b4f30b:@Aze_ https://www.jianshu.com/p/5ed93a8a1449#comment-24048235 这里有
        上个月:同求
      • LEOHAI:博主 您好 您这种方式 是 openconnect 吗
      • 时光浅影:想知道demo在哪
      • ab891fb8e7f3:楼主,你用NEKit可以上架appStore吗
        秦砖:@天天好心情_73bf 国内vpn好像都上不了了吧
      • Har_vey:哥们儿 能不能问下你一个问题, 现在rabbitVpn已经成功链接了VPN, 因为我们项目使用的是OC,现在把swift netkit框架导入了工程,新建了PacktTunnel 里面的PacketTunnelProvider文件是swift rabbitVPN的源文件,但是我现在没法链接,请问下是哪里出错了呢 Hub connection error Error Domain=NSCocoaErrorDomain Code=4097 这是报的错误 遇到过没有呢
      • miwer:NEProviderTargetTemplates.pkg 这个你还有吗? 能不能发我下呢? 971513280@qq.com 万分感谢
      • moxacist:楼主 这个是默认没配置过的域名是走代理吗 我想没配置过的默认不走代理该咋设置
      • 化身孤岛的鲸_ca35:导入nekit 库报错 MMDB-iOS 编译不过去
      • 瞬间看见永远:WhatsApp无法上网你解决了?
      • Nasser:楼主可以整理一个demo吗? 我在调试这个 RabittDemo的时候, 任何网络都是求是网络链接, 调试的时候, 只是修改了ss的配置。
      • Sherlock_chan:楼主大神,请问应用使用oc ,extension 使用swift 这么实现的,求助啊
        Sherlock_chan:楼主能给个你的工程的链接么
        秦砖:@Sherlock_chan 应用可以自动识别出extension的,我这个工程就是这样的啊
      • 海外党一枚:请教楼主个问题,今天也是按照楼主提到的那篇文章来做的,RabittDemo,在startTunnel中let ssAdapterFactory = ShadowsocksAdapterFactory(serverHost: ss_adder, serverPort: ss_port, protocolObfuscaterFactory:ShadowsocksAdapter.ProtocolObfuscater.OriginProtocolObfuscater.Factory(), cryptorFactory: ShadowsocksAdapter.CryptoStreamProcessor.Factory(password: password, algorithm: algorithm), streamObfuscaterFactory: ShadowsocksAdapter.StreamObfuscater.OriginStreamObfuscater.Factory()),密码参数应该没错,能发送数据到ss服务器,但是格式错误,纠结了,楼主调通的demo是这么用的吗?求指点啊
        上个月:@俺们就是土生土长的人 请问一下你的demo是OC的么
        海外党一枚:自己动手丰衣足食
      • 脚步太快该怎么停歇:楼主您好,方便提供一下Demo看下嘛?我的联系方式是2904499024@qq.com,非常感谢
      • MarkJoyful:你好我想问下你那个rule是什么,规则是怎么制定的
        MarkJoyful:@秦砖 好的 谢谢
        秦砖:@MarkJoyful 使用NEKIT自带的rule文件,没做改动,github上有很多类似的rule文件,你可以参考下。
      • 灵界小灵:大神能详细说下L2TP中config.providerConfiguration的配置吗,现在有l2tp VPN服务器的账号密码,地址和共享秘钥,不知道怎么配置才能连接成功.跪求帮助.
        妳笇what:你配置成功了么?我目前也遇到要使用l2pt类型开发一个vpn项目
      • 独风孤月:在target里面有network extension选项吗?是不是更新不一致了,我只profile里面勾选过,到本地xcode里面就没有这个选项了。
      • Bruce_XHG:能加一下楼主的联系方式吗 有些细节想请教
      • 走一朝:楼主 求帮助啊
        走一朝:@秦砖 谢谢 我去看看
        秦砖:https://github.com/wxq491216/NEKit.git 你到这里取下代码看看编译OK不
        秦砖:@走一朝 现在不方便,晚上我给你给git库
      • 走一朝:你好 有没有遇到 ld: framework not found CommonCrypto的错误 代码上传git了吗
      • LeeCen:学习了

      本文标题:关于NEKit使用的一些心得

      本文链接:https://www.haomeiwen.com/subject/laoohxtx.html