2016-06-10 03:59:27 +08:00
|
|
|
|
//
|
|
|
|
|
// ProxyConfHelper.m
|
|
|
|
|
// ShadowsocksX-NG
|
|
|
|
|
//
|
|
|
|
|
// Created by 邱宇舟 on 16/6/10.
|
|
|
|
|
// Copyright © 2016年 qiuyuzhou. All rights reserved.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
#import "ProxyConfHelper.h"
|
|
|
|
|
#import "proxy_conf_helper_version.h"
|
|
|
|
|
|
|
|
|
|
#define kShadowsocksHelper @"/Library/Application Support/ShadowsocksX-NG/proxy_conf_helper"
|
|
|
|
|
|
|
|
|
|
@implementation ProxyConfHelper
|
|
|
|
|
|
2016-08-24 11:52:40 +08:00
|
|
|
|
GCDWebServer *webServer =nil;
|
2016-06-10 03:59:27 +08:00
|
|
|
|
|
|
|
|
|
+ (BOOL)isVersionOk {
|
|
|
|
|
NSTask *task;
|
|
|
|
|
task = [[NSTask alloc] init];
|
|
|
|
|
[task setLaunchPath:kShadowsocksHelper];
|
|
|
|
|
|
|
|
|
|
NSArray *args;
|
|
|
|
|
args = [NSArray arrayWithObjects:@"-v", nil];
|
|
|
|
|
[task setArguments: args];
|
|
|
|
|
|
|
|
|
|
NSPipe *pipe;
|
|
|
|
|
pipe = [NSPipe pipe];
|
|
|
|
|
[task setStandardOutput:pipe];
|
|
|
|
|
|
|
|
|
|
NSFileHandle *fd;
|
|
|
|
|
fd = [pipe fileHandleForReading];
|
|
|
|
|
|
|
|
|
|
[task launch];
|
|
|
|
|
|
|
|
|
|
NSData *data;
|
|
|
|
|
data = [fd readDataToEndOfFile];
|
|
|
|
|
|
|
|
|
|
NSString *str;
|
|
|
|
|
str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
|
|
|
|
|
|
|
|
|
if (![str isEqualToString:kProxyConfHelperVersion]) {
|
|
|
|
|
return NO;
|
|
|
|
|
}
|
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
+ (void)install {
|
|
|
|
|
NSFileManager *fileManager = [NSFileManager defaultManager];
|
|
|
|
|
if (![fileManager fileExistsAtPath:kShadowsocksHelper] || ![self isVersionOk]) {
|
|
|
|
|
NSString *helperPath = [NSString stringWithFormat:@"%@/%@", [[NSBundle mainBundle] resourcePath], @"install_helper.sh"];
|
|
|
|
|
NSLog(@"run install script: %@", helperPath);
|
|
|
|
|
NSDictionary *error;
|
2017-09-13 09:59:48 +08:00
|
|
|
|
NSString *script = [NSString stringWithFormat:@"do shell script \"/bin/bash \\\"%@\\\"\" with administrator privileges", helperPath];
|
2016-06-10 03:59:27 +08:00
|
|
|
|
NSAppleScript *appleScript = [[NSAppleScript new] initWithSource:script];
|
|
|
|
|
if ([appleScript executeAndReturnError:&error]) {
|
|
|
|
|
NSLog(@"installation success");
|
|
|
|
|
} else {
|
2017-09-13 09:59:48 +08:00
|
|
|
|
NSLog(@"installation failure: %@", error);
|
2016-06-10 03:59:27 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
+ (void)callHelper:(NSArray*) arguments {
|
|
|
|
|
NSTask *task;
|
|
|
|
|
task = [[NSTask alloc] init];
|
|
|
|
|
[task setLaunchPath:kShadowsocksHelper];
|
|
|
|
|
|
|
|
|
|
// this log is very important
|
|
|
|
|
NSLog(@"run shadowsocks helper: %@", kShadowsocksHelper);
|
|
|
|
|
[task setArguments:arguments];
|
|
|
|
|
|
|
|
|
|
NSPipe *stdoutpipe;
|
|
|
|
|
stdoutpipe = [NSPipe pipe];
|
|
|
|
|
[task setStandardOutput:stdoutpipe];
|
|
|
|
|
|
|
|
|
|
NSPipe *stderrpipe;
|
|
|
|
|
stderrpipe = [NSPipe pipe];
|
|
|
|
|
[task setStandardError:stderrpipe];
|
|
|
|
|
|
|
|
|
|
NSFileHandle *file;
|
|
|
|
|
file = [stdoutpipe fileHandleForReading];
|
|
|
|
|
|
|
|
|
|
[task launch];
|
|
|
|
|
|
|
|
|
|
NSData *data;
|
|
|
|
|
data = [file readDataToEndOfFile];
|
|
|
|
|
|
|
|
|
|
NSString *string;
|
|
|
|
|
string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
|
|
|
|
if (string.length > 0) {
|
|
|
|
|
NSLog(@"%@", string);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
file = [stderrpipe fileHandleForReading];
|
|
|
|
|
data = [file readDataToEndOfFile];
|
|
|
|
|
string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
|
|
|
|
if (string.length > 0) {
|
|
|
|
|
NSLog(@"%@", string);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-06-30 02:33:37 +08:00
|
|
|
|
+ (void)addArguments4ManualSpecifyNetworkServices:(NSMutableArray*) args {
|
|
|
|
|
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
|
|
|
|
|
|
|
|
|
|
if (![defaults boolForKey:@"AutoConfigureNetworkServices"]) {
|
|
|
|
|
NSArray* serviceKeys = [defaults arrayForKey:@"Proxy4NetworkServices"];
|
|
|
|
|
if (serviceKeys) {
|
|
|
|
|
for (NSString* key in serviceKeys) {
|
|
|
|
|
[args addObject:@"--network-service"];
|
|
|
|
|
[args addObject:key];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-30 15:03:02 +08:00
|
|
|
|
+ (void)addArguments4ManualSpecifyProxyExceptions:(NSMutableArray*) args {
|
|
|
|
|
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
|
|
|
|
|
|
|
|
|
|
NSString* rawExceptions = [defaults stringForKey:@"ProxyExceptions"];
|
|
|
|
|
if (rawExceptions) {
|
|
|
|
|
NSCharacterSet* whites = [NSCharacterSet whitespaceAndNewlineCharacterSet];
|
2017-11-26 12:46:00 +08:00
|
|
|
|
NSMutableCharacterSet* seps = [NSMutableCharacterSet characterSetWithCharactersInString:@",、"];
|
|
|
|
|
[seps formUnionWithCharacterSet:whites];
|
|
|
|
|
|
2017-10-30 15:03:02 +08:00
|
|
|
|
NSArray* exceptions = [rawExceptions componentsSeparatedByCharactersInSet:seps];
|
2017-11-26 12:46:00 +08:00
|
|
|
|
for (NSString* domainOrHost in exceptions) {
|
2017-10-30 15:03:02 +08:00
|
|
|
|
if ([domainOrHost length] > 0) {
|
|
|
|
|
[args addObject:@"-x"];
|
|
|
|
|
[args addObject:domainOrHost];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-10-19 00:24:45 +08:00
|
|
|
|
+ (NSString*)getPACFilePath {
|
|
|
|
|
return [NSString stringWithFormat:@"%@/%@", NSHomeDirectory(), @".ShadowsocksX-NG/gfwlist.js"];
|
|
|
|
|
}
|
|
|
|
|
|
2016-10-18 23:35:17 +08:00
|
|
|
|
+ (void)enablePACProxy {
|
2016-08-23 17:08:21 +08:00
|
|
|
|
//start server here and then using the string next line
|
|
|
|
|
//next two lines can open gcdwebserver and work around pac file
|
2016-10-19 00:24:45 +08:00
|
|
|
|
NSString* PACFilePath = [self getPACFilePath];
|
2016-10-18 23:35:17 +08:00
|
|
|
|
[self startPACServer: PACFilePath];
|
|
|
|
|
|
|
|
|
|
NSURL* url = [NSURL URLWithString: [self getHttpPACUrl]];
|
|
|
|
|
|
2016-06-30 02:33:37 +08:00
|
|
|
|
NSMutableArray* args = [@[@"--mode", @"auto", @"--pac-url", [url absoluteString]]mutableCopy];
|
|
|
|
|
|
|
|
|
|
[self addArguments4ManualSpecifyNetworkServices:args];
|
2017-10-30 15:03:02 +08:00
|
|
|
|
[self addArguments4ManualSpecifyProxyExceptions:args];
|
2016-06-30 02:33:37 +08:00
|
|
|
|
[self callHelper:args];
|
2016-06-10 03:59:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
+ (void)enableGlobalProxy {
|
|
|
|
|
NSUInteger port = [[NSUserDefaults standardUserDefaults]integerForKey:@"LocalSocks5.ListenPort"];
|
2016-06-30 02:33:37 +08:00
|
|
|
|
|
|
|
|
|
NSMutableArray* args = [@[@"--mode", @"global", @"--port"
|
|
|
|
|
, [NSString stringWithFormat:@"%lu", (unsigned long)port]]mutableCopy];
|
|
|
|
|
|
2016-10-21 23:36:05 +08:00
|
|
|
|
// Because issue #106 https://github.com/shadowsocks/ShadowsocksX-NG/issues/106
|
|
|
|
|
// Comment below out.
|
2016-10-26 19:25:35 +08:00
|
|
|
|
// if ([[NSUserDefaults standardUserDefaults] boolForKey:@"LocalHTTPOn"] && [[NSUserDefaults standardUserDefaults] boolForKey:@"LocalHTTP.FollowGlobal"]) {
|
2016-10-21 23:36:05 +08:00
|
|
|
|
// NSUInteger privoxyPort = [[NSUserDefaults standardUserDefaults]integerForKey:@"LocalHTTP.ListenPort"];
|
|
|
|
|
//
|
|
|
|
|
// [args addObject:@"--privoxy-port"];
|
|
|
|
|
// [args addObject:[NSString stringWithFormat:@"%lu", (unsigned long)privoxyPort]];
|
|
|
|
|
// }
|
2016-10-09 14:31:13 +08:00
|
|
|
|
|
2016-06-30 02:33:37 +08:00
|
|
|
|
[self addArguments4ManualSpecifyNetworkServices:args];
|
2017-10-30 15:03:02 +08:00
|
|
|
|
[self addArguments4ManualSpecifyProxyExceptions:args];
|
2016-06-30 02:33:37 +08:00
|
|
|
|
[self callHelper:args];
|
2016-08-24 11:52:40 +08:00
|
|
|
|
[self stopPACServer];
|
2016-06-10 03:59:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
2016-10-18 23:35:17 +08:00
|
|
|
|
+ (void)disableProxy {
|
|
|
|
|
// 带上所有参数是为了判断是否原有代理设置是否由ssx-ng设置的。如果是用户手工设置的其他配置,则不进行清空。
|
|
|
|
|
NSURL* url = [NSURL URLWithString: [self getHttpPACUrl]];
|
2016-08-04 00:41:00 +08:00
|
|
|
|
NSUInteger port = [[NSUserDefaults standardUserDefaults]integerForKey:@"LocalSocks5.ListenPort"];
|
|
|
|
|
|
|
|
|
|
NSMutableArray* args = [@[@"--mode", @"off"
|
|
|
|
|
, @"--port", [NSString stringWithFormat:@"%lu", (unsigned long)port]
|
|
|
|
|
, @"--pac-url", [url absoluteString]
|
|
|
|
|
]mutableCopy];
|
2016-06-30 02:33:37 +08:00
|
|
|
|
[self addArguments4ManualSpecifyNetworkServices:args];
|
2017-10-30 15:03:02 +08:00
|
|
|
|
[self addArguments4ManualSpecifyProxyExceptions:args];
|
2016-06-30 02:33:37 +08:00
|
|
|
|
[self callHelper:args];
|
2016-08-24 11:52:40 +08:00
|
|
|
|
[self stopPACServer];
|
2016-06-10 03:59:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
2016-10-18 23:35:17 +08:00
|
|
|
|
+ (NSString*)getHttpPACUrl {
|
|
|
|
|
NSString * routerPath = @"/proxy.pac";
|
|
|
|
|
|
|
|
|
|
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
|
|
|
|
|
|
|
|
|
NSString * address = @"127.0.0.1";
|
|
|
|
|
int port = (short)[defaults integerForKey:@"PacServer.ListenPort"];
|
|
|
|
|
|
|
|
|
|
return [NSString stringWithFormat:@"%@%@:%d%@",@"http://",address,port,routerPath];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
+ (void)startPACServer:(NSString*) PACFilePath {
|
2016-10-19 00:24:45 +08:00
|
|
|
|
[self stopPACServer];
|
2016-10-18 23:35:17 +08:00
|
|
|
|
|
2016-08-24 11:52:40 +08:00
|
|
|
|
NSString * routerPath = @"/proxy.pac";
|
2016-10-18 23:35:17 +08:00
|
|
|
|
|
2016-10-19 00:24:45 +08:00
|
|
|
|
NSData* originalPACData = [NSData dataWithContentsOfFile:PACFilePath];
|
|
|
|
|
|
2016-08-24 11:52:40 +08:00
|
|
|
|
webServer = [[GCDWebServer alloc] init];
|
2016-10-18 23:35:17 +08:00
|
|
|
|
[webServer addHandlerForMethod:@"GET"
|
|
|
|
|
path:routerPath
|
|
|
|
|
requestClass:[GCDWebServerRequest class]
|
|
|
|
|
processBlock:^GCDWebServerResponse *(GCDWebServerRequest *request)
|
|
|
|
|
{
|
|
|
|
|
GCDWebServerDataResponse* resp = [GCDWebServerDataResponse responseWithData:originalPACData
|
|
|
|
|
contentType:@"application/x-ns-proxy-autoconfig"];
|
|
|
|
|
return resp;
|
2016-08-23 17:08:21 +08:00
|
|
|
|
}
|
|
|
|
|
];
|
2016-10-18 23:35:17 +08:00
|
|
|
|
|
2016-08-25 09:19:47 +08:00
|
|
|
|
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
2016-10-18 23:35:17 +08:00
|
|
|
|
|
2016-08-25 09:19:47 +08:00
|
|
|
|
int port = (short)[defaults integerForKey:@"PacServer.ListenPort"];
|
2016-10-18 23:35:17 +08:00
|
|
|
|
|
2016-10-02 01:24:38 +08:00
|
|
|
|
[webServer startWithOptions:@{@"BindToLocalhost":@YES, @"Port":@(port)} error:nil];
|
2016-08-23 17:08:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
+ (void)stopPACServer {
|
|
|
|
|
//原版似乎没有处理这个,本来设计计划如果切换到全局模式或者手动模式就关掉webserver 似乎没有这个必要了?
|
2016-08-25 09:19:47 +08:00
|
|
|
|
if ([webServer isRunning]) {
|
|
|
|
|
[webServer stop];
|
|
|
|
|
}
|
2016-08-23 17:08:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
2016-10-19 00:24:45 +08:00
|
|
|
|
+ (void)startMonitorPAC {
|
|
|
|
|
NSString* PACFilePath = [self getPACFilePath];
|
2017-01-11 10:43:37 +08:00
|
|
|
|
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
|
|
|
|
|
int fileId = open([PACFilePath UTF8String], O_EVTONLY);
|
|
|
|
|
__block dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fileId,
|
|
|
|
|
DISPATCH_VNODE_DELETE | DISPATCH_VNODE_WRITE | DISPATCH_VNODE_EXTEND | DISPATCH_VNODE_ATTRIB | DISPATCH_VNODE_LINK | DISPATCH_VNODE_RENAME | DISPATCH_VNODE_REVOKE,
|
|
|
|
|
queue);
|
|
|
|
|
dispatch_source_set_event_handler(source, ^
|
|
|
|
|
{
|
|
|
|
|
unsigned long flags = dispatch_source_get_data(source);
|
|
|
|
|
if(flags & DISPATCH_VNODE_DELETE)
|
|
|
|
|
{
|
|
|
|
|
dispatch_source_cancel(source);
|
|
|
|
|
} else {
|
|
|
|
|
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
|
|
|
|
|
if ([defaults boolForKey:@"ShadowsocksOn"]) {
|
|
|
|
|
if ([[defaults stringForKey:@"ShadowsocksRunningMode"] isEqualToString:@"auto"]) {
|
|
|
|
|
[ProxyConfHelper disableProxy];
|
|
|
|
|
[ProxyConfHelper enablePACProxy];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
dispatch_source_set_cancel_handler(source, ^(void)
|
|
|
|
|
{
|
|
|
|
|
close(fileId);
|
|
|
|
|
});
|
|
|
|
|
dispatch_resume(source);
|
2016-10-19 00:24:45 +08:00
|
|
|
|
}
|
|
|
|
|
|
2016-06-10 03:59:27 +08:00
|
|
|
|
@end
|