花了一些时间终于把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上。
网友评论