// // RaylinkProxy.m // raylink_sdk_flutter // // Created by Sim Tsai on 2022/12/14. // #import "RaylinkProxy.h" #import "TyphoonMultiSocks.h" #import "TyphoonServer.h" #include "net_detection.h" #include "string.h" @interface RayLinkConnectionInfo : NSObject @property (nonatomic, strong) NSValue *pHandle; @property (nonatomic, copy) NSString *sdnId; @end @implementation RayLinkConnectionInfo @end @implementation RaylinkProxy { NSMutableDictionary *_connectInfos; std::string _last_root_info; } + (instancetype)sharedManager { static RaylinkProxy *_sharedManager = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _sharedManager = [RaylinkProxy new]; }); return _sharedManager; } - (void)initServer:(NSString * _Nonnull)logPath rootSdnInfo:(NSData * _Nonnull)rootSdnInfo { struct InitServerPrama param = { 0 }; param.listenAddr = "127.0.0.1"; param.listenPort = 32004; param.minSpeed = 5*1024/8; param.maxSpeed = 200*1024/8; param.logPath = (char *)[logPath cStringUsingEncoding:NSUTF8StringEncoding]; param.licensePath = (char *)[logPath cStringUsingEncoding:NSUTF8StringEncoding]; param.caPath = "."; param.licenseType = TYPHOON_APP_ID_PROXYS; param.license = ""; param.MaxSegmentSize = 1200; param.bSdnOn = true; param.rootsData = (void *)rootSdnInfo.bytes; param.rootsDataLen = rootSdnInfo.length; param.FecPercent = 1000; param.bCompress = true; stTyphoonServerProxyConnectionStatusCb cb1 = {OnTcpConnected, OnTcpDisConnected}; TyphoonServerProxy_SetConnectionStatusCb(cb1); if (TyphoonProxy_InitServer(¶m) == 0) { // 初始化成功后记录信息,下次使用相同信息初始化时(如websocket断线重连)不再重新初始化 NSLog(@"TyphoonProxy_InitServer ok"); _last_root_info = std::string((char *)rootSdnInfo.bytes, rootSdnInfo.length); } } - (void)initProxy:(NSString *)logPath rootSdnInfo:(NSData *)rootSdnInfo { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _connectInfos = @{}.mutableCopy; [self initServer:logPath rootSdnInfo:rootSdnInfo];; NSLog(@"TyphoonMultiProxy_InitClient rootSdnInfoSize %ld", rootSdnInfo.length); int result = TyphoonMultiProxy_InitClient(0, 0, (char *)[logPath cStringUsingEncoding:NSUTF8StringEncoding], "", (void *)rootSdnInfo.bytes, rootSdnInfo.length); if (result == 0) { NSLog(@"TyphoonMultiProxy_InitClient ok"); } struct TyphoonMultiProxyConnectionStatusCb cb2; cb2.pfnOnConnected = OnProxyConnected_cb; cb2.pfnOnDisConnected = OnProxyDisconnected_cb; cb2.pfnOnSDNRouteChange = OnSDNRouteChange; TyphoonMultiProxy_SetConnectionStatusCb(cb2); }); TyphoonMultiProxy_SetUserData((uint64_t)(__bridge void *)(self)); if (!_last_root_info.empty()) { if (_last_root_info.compare(std::string((char *)rootSdnInfo.bytes, rootSdnInfo.length)) != 0) { if (TyphoonProxy_RestartSdnRoot((void *)rootSdnInfo.bytes, rootSdnInfo.length) == 0) { _last_root_info = std::string((char *)rootSdnInfo.bytes, rootSdnInfo.length); } } } } - (BOOL)isSdnConnected { return TyphoonProxy_CheckSdnIsOnline() == 0; } - (BOOL)createNewConnection:(NSString *)sdnId { char ipBuff[64]; short sdnPort = 0; TyphoonMultiProxy_SDNId2IpPort((char *)[sdnId cStringUsingEncoding:NSUTF8StringEncoding], ipBuff, &sdnPort); HTyphoonHandle pHandle = TyphoonMultiProxy_CreateNewTyphoonConn(ipBuff, sdnPort, 1200, 0, 0, 1/*fec*/, true); if (!pHandle) { return NO; } RayLinkConnectionInfo *connectInfo = [[RayLinkConnectionInfo alloc] init]; connectInfo.pHandle = [NSValue valueWithPointer:pHandle]; connectInfo.sdnId = sdnId; @synchronized (self) { _connectInfos[sdnId] = connectInfo; } NSLog(@"TyphoonMultiProxy_CreateNewTyphoonConn ok"); return YES; } - (BOOL)addSdnId:(NSString *)sdnId ip:(NSString *)ip allowPort:(NSUInteger)port { RayLinkConnectionInfo *connectInfo = _connectInfos[sdnId]; if (connectInfo) { NSLog(@"TyphoonMultiProxy_AddHttpFirewallAllowAddress: %d", (int)port); TyphoonMultiProxy_AddHttpFirewallAllowAddress(connectInfo.pHandle.pointerValue, (char *)[ip cStringUsingEncoding:NSUTF8StringEncoding], (int)port); return YES; } return NO; } - (NSString *)getSdnId { uint64_t nodeId = TyphoonProxy_GetNodeId(); char sdnBuf[64] = {0}; std::snprintf(sdnBuf, 64, "%llx", nodeId); return [NSString stringWithCString:sdnBuf encoding:NSUTF8StringEncoding]; } - (RayLinkConnectionInfo *)getConnectInfoByHandle:(HTyphoonHandle) pyHandle { @synchronized (self) { for (RayLinkConnectionInfo *value in _connectInfos.allValues) { RayLinkConnectionInfo *connectInfo = value; if (connectInfo.pHandle.pointerValue == pyHandle) { return connectInfo; } } } return NULL; } - (void)onProxyConnected:(NSString *)sdnId status:(int)status { [self.delegate onProxyConnected:sdnId status:status]; } void OnProxyConnected_cb(HTyphoonHandle pyHandle, int status) { RaylinkProxy* proxy = (__bridge RaylinkProxy *)(void *)TyphoonMultiProxy_GetUserData(); RayLinkConnectionInfo *connectInfo = [proxy getConnectInfoByHandle:pyHandle]; if (connectInfo != NULL) { dispatch_async(dispatch_get_main_queue(), ^{ [proxy onProxyConnected:connectInfo.sdnId status:status]; }); } if (status == 0) { NSLog(@"pyHandle = 0x%x OnProxyConnected ok", pyHandle); OnSDNRouteChange(pyHandle, 0); } else { NSLog(@"pyHandle = 0x%x OnProxyConnected %d", pyHandle, status); } } - (NSUInteger)createHttpService:(NSString *)sdnId { RayLinkConnectionInfo *info = _connectInfos[sdnId]; if (info) { if (TyphoonMultiProxy_CheckHttpPartUsable((HTyphoonHandle)info.pHandle.pointerValue) == 1) { return TyphoonMultiProxy_GetHttpPort((HTyphoonHandle)info.pHandle.pointerValue); } int port = TyphoonMultiProxy_GetHttpPort((HTyphoonHandle)info.pHandle.pointerValue); if (port > 0) { TyphoonMultiProxy_CloseHttpService((HTyphoonHandle)info.pHandle.pointerValue); } else { port = 0; } port = TyphoonMultiProxy_CreateHttpService(info.pHandle.pointerValue, "127.0.0.1", 0, 0, 1, 1); TyphoonMultiProxy_SetHttpServiceFecPercent(info.pHandle.pointerValue, 1000); TyphoonMultiProxy_SetConnRecvSpeed(info.pHandle.pointerValue, 20*1024/8); TyphoonMultiProxy_SetMaxSendSpeed(info.pHandle.pointerValue, 20*1024/8); TyphoonMultiProxy_SetMinSendSpeed(info.pHandle.pointerValue, 5*1024/8); return port; } return 0; } - (void)closeHttpService:(NSString *)sdnId { RayLinkConnectionInfo *info = _connectInfos[sdnId]; if (info) { NSLog(@"closeHttpService"); TyphoonMultiProxy_CloseHttpService(info.pHandle.pointerValue); } } - (void)closeConnection:(NSString *)sdnId { RayLinkConnectionInfo *info = _connectInfos[sdnId]; if (info) { NSLog(@"closeHttpService"); TyphoonMultiProxy_CloseConnect(info.pHandle.pointerValue); } } void OnProxyDisconnected_cb(HTyphoonHandle pyHandle) { NSLog(@"pyHandle = 0x%x OnProxyDisconnected_cb", pyHandle); RaylinkProxy* proxy = (__bridge RaylinkProxy *)(void *)TyphoonMultiProxy_GetUserData(); RayLinkConnectionInfo *info = [proxy getConnectInfoByHandle:pyHandle]; if (info) { if ([proxy.delegate respondsToSelector:@selector(OnProxyDisconnected_cb:)]) { [proxy.delegate OnProxyDisconnected_cb:info.sdnId]; } } } void OnSDNRouteChange(HTyphoonHandle pyHandle, int type) { RaylinkProxy* proxy = (__bridge RaylinkProxy *)(void *)TyphoonMultiProxy_GetUserData(); RayLinkConnectionInfo *info = [proxy getConnectInfoByHandle:pyHandle]; if (info) { if ([proxy.delegate respondsToSelector:@selector(sdnRouteChange:type:)]) { [proxy.delegate sdnRouteChange:info.sdnId type:type]; } } NSLog(@"pyHandle = 0x%x OnSDNRouteChange = %d", pyHandle, type); } void OnTcpConnected(void *pHandle, const char *userData, int userDataLen) { NSLog(@"OnTcpConnected"); } void OnTcpDisConnected(void *pHandle) { NSLog(@"OnTcpDisConnected"); } - (NSArray *)get_nat_type:(NSString *)stun_server port:(int)port { std::string natIpPort; NatBehavior natBehavior = ServiceNatDetection::get_nat_behavior(natIpPort, [stun_server cStringUsingEncoding:NSUTF8StringEncoding], port); NatFiltering filterKind = ServiceNatDetection::get_nat_filtering(natIpPort, [stun_server cStringUsingEncoding:NSUTF8StringEncoding], port); NSString *natIp = [NSString stringWithCString:natIpPort.c_str() encoding:NSUTF8StringEncoding]; int type = ServiceNatDetection::get_nat_type(natBehavior, filterKind); return @[@(type), natIp]; } - (int)get_nat_behavior:(NSString *)stun_server port:(int)port { std::string natIpPort; NatBehavior natBehavior = ServiceNatDetection::get_nat_behavior(natIpPort, [stun_server cStringUsingEncoding:NSUTF8StringEncoding], port); return natBehavior; } @end