311 lines
12 KiB
C
Executable File
311 lines
12 KiB
C
Executable File
/*
|
|
* Copyright (C) 2010-2015 Mamadou DIOP.
|
|
*
|
|
* This file is part of Open Source Doubango Framework.
|
|
*
|
|
* DOUBANGO is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* DOUBANGO is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with DOUBANGO.
|
|
*
|
|
*/
|
|
#include "tnet_proxydetect.h"
|
|
#include "tnet_utils.h"
|
|
|
|
#include "tsk_string.h"
|
|
#include "tsk_memory.h"
|
|
#include "tsk_debug.h"
|
|
|
|
/* "tnet_proxyinfo_t" object definition */
|
|
static tsk_object_t* _tnet_proxyinfo_ctor(tsk_object_t * self, va_list * app)
|
|
{
|
|
tnet_proxyinfo_t *info = (tnet_proxyinfo_t *)self;
|
|
if (info) {
|
|
|
|
}
|
|
return self;
|
|
}
|
|
static tsk_object_t* _tnet_proxyinfo_dtor(tsk_object_t * self)
|
|
{
|
|
tnet_proxyinfo_t *info = (tnet_proxyinfo_t *)self;
|
|
if (info) {
|
|
TSK_FREE(info->autoconfig_url);
|
|
TSK_FREE(info->bypass_list);
|
|
TSK_FREE(info->hostname);
|
|
TSK_FREE(info->username);
|
|
TSK_FREE(info->password);
|
|
}
|
|
|
|
return self;
|
|
}
|
|
static const tsk_object_def_t tnet_proxyinfo_def_s = {
|
|
sizeof(tnet_proxyinfo_t),
|
|
_tnet_proxyinfo_ctor,
|
|
_tnet_proxyinfo_dtor,
|
|
tsk_null,
|
|
};
|
|
const tsk_object_def_t *tnet_proxyinfo_def_t = &tnet_proxyinfo_def_s;
|
|
|
|
tnet_proxyinfo_t* tnet_proxyinfo_create()
|
|
{
|
|
tnet_proxyinfo_t* info = tsk_object_new(tnet_proxyinfo_def_t);
|
|
if (!info) {
|
|
TSK_DEBUG_ERROR("Failed to creatr 'tnet_proxyinfo_t' instance");
|
|
return info;
|
|
}
|
|
|
|
return info;
|
|
}
|
|
|
|
|
|
|
|
/******** Windows ************/
|
|
#if TNET_UNDER_WINDOWS && !TNET_UNDER_WINDOWS_RT && !TNET_UNDER_WINDOWS_CE
|
|
# include <Windows.h>
|
|
# include <winhttp.h>
|
|
|
|
tnet_proxyinfo_t* tnet_proxydetect_get_info(const char* url, tnet_socket_type_t socket_type, tsk_bool_t long_operation)
|
|
{
|
|
TSK_DEBUG_WARN("Proxy detection not supported on your OS");
|
|
return tsk_null;
|
|
}
|
|
|
|
/******** iOS and OSX ************/
|
|
#elif TNET_UNDER_APPLE
|
|
|
|
#import <CFNetwork/CFNetwork.h>
|
|
|
|
#define CFSafeRelease(pp_cf) if (pp_cf && *pp_cf) CFRelease(*pp_cf), *pp_cf = NULL;
|
|
#if !defined (TNET_AUTODETECT_RUNLOOP_MODE)
|
|
# define TNET_AUTODETECT_RUNLOOP_MODE "org.doubango.proxydetect.auto"
|
|
#endif
|
|
|
|
typedef struct appl_proxyinfo_xs {
|
|
CFStringRef host;
|
|
int port;
|
|
CFStringRef username;
|
|
CFStringRef password;
|
|
CFStringRef type;
|
|
}
|
|
appl_proxyinfo_xt;
|
|
|
|
static tnet_proxy_type_t _appl_proxy_type_convert(CFStringRef cfProxyType);
|
|
static void _appl_proxyinfo_release(appl_proxyinfo_xt * info);
|
|
static tsk_bool_t _appl_proxyinfo_is_valid(const appl_proxyinfo_xt * info);
|
|
static void _ProxyAutoConfigurationResultCallback(void *client, CFArrayRef proxyList, CFErrorRef error);
|
|
static void _appl_find_best_proxy(CFURLRef cfTargetURL, CFArrayRef _cfProxies, appl_proxyinfo_xt *_proxyInfo);
|
|
|
|
tnet_proxyinfo_t* tnet_proxydetect_get_info(const char* url, tnet_socket_type_t socket_type, tsk_bool_t long_operation)
|
|
{
|
|
tnet_proxyinfo_t* info = tsk_null;
|
|
CFStringRef cfUrl = CFStringCreateWithCString(CFAllocatorGetDefault(), url, kCFStringEncodingUTF8);
|
|
CFURLRef cfTargetUrl = CFURLCreateWithString(CFAllocatorGetDefault(), cfUrl, NULL);
|
|
CFDictionaryRef cfProxySettings = NULL;
|
|
CFArrayRef cfProxies = NULL;
|
|
appl_proxyinfo_xt _info = { 0 };
|
|
if (!cfTargetUrl) {
|
|
TSK_DEBUG_ERROR("Failed to create CFURLRef from %s", url);
|
|
goto resolve_done;
|
|
}
|
|
cfProxySettings = CFNetworkCopySystemProxySettings();
|
|
if (!cfProxySettings) {
|
|
TSK_DEBUG_ERROR("CFNetworkCopySystemProxySettings returned nil");
|
|
goto resolve_done;
|
|
}
|
|
|
|
cfProxies = CFNetworkCopyProxiesForURL(cfTargetUrl, cfProxySettings);
|
|
if (!cfProxies) {
|
|
TSK_DEBUG_ERROR("CFNetworkCopyProxiesForURL returned 0-array");
|
|
goto resolve_done;
|
|
}
|
|
// find best proxy
|
|
_appl_find_best_proxy(cfTargetUrl, cfProxies, &_info);
|
|
|
|
|
|
resolve_done:
|
|
if (cfUrl) {
|
|
CFRelease(cfUrl);
|
|
}
|
|
if (cfTargetUrl) {
|
|
CFRelease(cfTargetUrl);
|
|
}
|
|
if (cfProxySettings) {
|
|
CFRelease(cfProxySettings);
|
|
}
|
|
if (cfProxies) {
|
|
CFRelease(cfProxies);
|
|
}
|
|
|
|
if (_appl_proxyinfo_is_valid(&_info)) {
|
|
info = tnet_proxyinfo_create();
|
|
if (info) {
|
|
info->type = _appl_proxy_type_convert(_info.type);
|
|
info->socket_type = socket_type;
|
|
info->port = _info.port;
|
|
info->hostname = tsk_strdup(CFStringGetCStringPtr(_info.host, kCFStringEncodingUTF8));
|
|
info->username = tsk_strdup(CFStringGetCStringPtr(_info.username, kCFStringEncodingUTF8));
|
|
info->password = tsk_strdup(CFStringGetCStringPtr(_info.password, kCFStringEncodingUTF8));
|
|
}
|
|
}
|
|
_appl_proxyinfo_release(&_info);
|
|
|
|
return info;
|
|
}
|
|
|
|
static tsk_bool_t _appl_proxyinfo_is_valid(const appl_proxyinfo_xt * info)
|
|
{
|
|
if (info) {
|
|
return info->port
|
|
&& info->type && !CFEqual(info->type, kCFProxyTypeNone)
|
|
&& info->host && CFStringGetLength(info->host) > 0 && CFStringCompare(info->host, CFSTR("127.0.0.1"), 0) != kCFCompareEqualTo;
|
|
}
|
|
return tsk_false;
|
|
}
|
|
|
|
static tnet_proxy_type_t _appl_proxy_type_convert(CFStringRef cfProxyType)
|
|
{
|
|
if (CFEqual(cfProxyType, kCFProxyTypeHTTP) || CFEqual(cfProxyType, kCFProxyTypeHTTPS)) {
|
|
// kCFProxyTypeHTTPS: means the destination url is "https://" not the proxy connection type is HTTPS
|
|
return tnet_proxy_type_http;
|
|
}
|
|
else if(CFEqual(cfProxyType,kCFProxyTypeSOCKS)) {
|
|
return tnet_proxy_type_socks5;
|
|
}
|
|
else {
|
|
return tnet_proxy_type_none;
|
|
}
|
|
}
|
|
|
|
static void _appl_proxyinfo_release(appl_proxyinfo_xt * info)
|
|
{
|
|
if (info) {
|
|
CFSafeRelease(&info->host);
|
|
CFSafeRelease(&info->username);
|
|
CFSafeRelease(&info->password);
|
|
CFSafeRelease(&info->type);
|
|
info->port = 0;
|
|
}
|
|
}
|
|
|
|
static void _ProxyAutoConfigurationResultCallback(void *client, CFArrayRef proxyList, CFErrorRef error)
|
|
{
|
|
CFTypeRef* cfResult = (CFTypeRef*)client;
|
|
if (error != NULL) {
|
|
*cfResult = CFRetain(error);
|
|
}
|
|
else {
|
|
*cfResult = CFRetain(proxyList);
|
|
}
|
|
CFRunLoopStop(CFRunLoopGetCurrent());
|
|
}
|
|
|
|
static void _appl_find_best_proxy(CFURLRef cfTargetURL, CFArrayRef _cfProxies, appl_proxyinfo_xt *_proxyInfo)
|
|
{
|
|
CFDictionaryRef cfProxy;
|
|
CFIndex index = 0;
|
|
CFIndex count = CFArrayGetCount(_cfProxies);
|
|
|
|
while (index < count && (cfProxy = CFArrayGetValueAtIndex(_cfProxies, index++)) && !_appl_proxyinfo_is_valid(_proxyInfo)) {
|
|
_appl_proxyinfo_release(_proxyInfo);
|
|
CFStringRef cfProxyType = CFDictionaryGetValue(cfProxy, (const void*)kCFProxyTypeKey);
|
|
if (!cfProxyType) {
|
|
continue;
|
|
}
|
|
|
|
TSK_DEBUG_INFO("Found at %li proxy type = %s", (index - 1), CFStringGetCStringPtr(cfProxyType, kCFStringEncodingUTF8));
|
|
if (CFEqual(cfProxyType, kCFProxyTypeNone)) {
|
|
continue;
|
|
}
|
|
else if (CFEqual(cfProxyType, kCFProxyTypeHTTP) || CFEqual(cfProxyType, kCFProxyTypeHTTPS) || CFEqual(cfProxyType, kCFProxyTypeSOCKS)) {
|
|
// "kCFProxyTypeHTTPS" means the url is "https://" not the connection should be TLS
|
|
CFStringRef cfHostName = (CFStringRef)CFDictionaryGetValue(cfProxy, (const void*)kCFProxyHostNameKey);
|
|
if (cfHostName) {
|
|
CFNumberRef cfPortNumber = (CFNumberRef)CFDictionaryGetValue(cfProxy, (const void*)kCFProxyPortNumberKey);
|
|
if (cfPortNumber) {
|
|
int port = 0;
|
|
if (!CFNumberGetValue(cfPortNumber, kCFNumberSInt32Type, &port)) {
|
|
continue;
|
|
}
|
|
_proxyInfo->port = port;
|
|
_proxyInfo->host = CFStringCreateCopy(CFAllocatorGetDefault(), cfHostName);
|
|
_proxyInfo->type = CFStringCreateCopy(CFAllocatorGetDefault(), cfProxyType);
|
|
CFStringRef cfStringName = (CFStringRef)CFDictionaryGetValue(cfProxy, (const void*)kCFProxyUsernameKey);
|
|
if (cfStringName) {
|
|
_proxyInfo->username = CFStringCreateCopy(CFAllocatorGetDefault(), cfStringName);
|
|
}
|
|
CFStringRef cfPassword = (CFStringRef)CFDictionaryGetValue(cfProxy, (const void*)kCFProxyPasswordKey);
|
|
if (cfPassword) {
|
|
_proxyInfo->password = CFStringCreateCopy(CFAllocatorGetDefault(), cfPassword);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (CFEqual(cfProxyType, kCFProxyTypeAutoConfigurationURL)) {
|
|
CFURLRef cfPACUrl = (CFURLRef)CFDictionaryGetValue(cfProxy, (const void*)kCFProxyAutoConfigurationURLKey);
|
|
if (cfPACUrl) {
|
|
TSK_DEBUG_INFO("Found at %li PACUrl = %s", (index - 1), CFStringGetCStringPtr(CFURLGetString(cfPACUrl), kCFStringEncodingUTF8));
|
|
CFTypeRef cfResult = NULL;
|
|
CFStreamClientContext context = { 0, &cfResult, NULL, NULL, NULL };
|
|
CFRunLoopSourceRef cfrunLoop = CFNetworkExecuteProxyAutoConfigurationURL(cfPACUrl,
|
|
cfTargetURL,
|
|
_ProxyAutoConfigurationResultCallback,
|
|
&context);
|
|
if (!cfrunLoop) {
|
|
TSK_DEBUG_ERROR("CFNetworkExecuteProxyAutoConfigurationURL(%li, %s) failed", (index - 1), CFStringGetCStringPtr(CFURLGetString(cfPACUrl), kCFStringEncodingUTF8));
|
|
continue;
|
|
}
|
|
static const CFStringRef kPrivateRunloopMode = CFSTR(TNET_AUTODETECT_RUNLOOP_MODE);
|
|
CFRunLoopAddSource(CFRunLoopGetCurrent(), cfrunLoop, kPrivateRunloopMode);
|
|
CFRunLoopRunInMode(kPrivateRunloopMode, DBL_MAX, false);
|
|
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), cfrunLoop, kPrivateRunloopMode);
|
|
if (cfResult == NULL) {
|
|
TSK_DEBUG_INFO("Result from ProxyAutoConfigurationResultCallback is nil");
|
|
continue;
|
|
}
|
|
if (CFGetTypeID(cfResult) == CFErrorGetTypeID()) {
|
|
CFStringRef cfErrorDescription = CFErrorCopyDescription ((CFErrorRef)cfResult);
|
|
TSK_DEBUG_ERROR("Result from ProxyAutoConfigurationResultCallback is error: %s", CFStringGetCStringPtr(cfErrorDescription, kCFStringEncodingUTF8));
|
|
CFRelease(cfErrorDescription);
|
|
}
|
|
else if (CFGetTypeID(cfResult) == CFArrayGetTypeID()) {
|
|
TSK_DEBUG_INFO("Result from ProxyAutoConfigurationResultCallback is array");
|
|
_appl_find_best_proxy(cfTargetURL, (CFArrayRef)cfResult, _proxyInfo);
|
|
}
|
|
CFRelease(cfResult);
|
|
}
|
|
else {
|
|
TSK_DEBUG_INFO("PACUrl at %li is nil", (index - 1));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/******** Not supported ************/
|
|
#else
|
|
|
|
tnet_proxyinfo_t* tnet_proxydetect_get_info(const char* url, tnet_socket_type_t socket_type, tsk_bool_t long_operation)
|
|
{
|
|
TSK_DEBUG_WARN("Proxy detection not supported on your OS");
|
|
return tsk_null;
|
|
}
|
|
|
|
#endif /* END-OF-CONDITIONAL-OS-IMPL */
|
|
|
|
|
|
tsk_bool_t tnet_proxyinfo_is_valid(const tnet_proxyinfo_t* self)
|
|
{
|
|
if (self) {
|
|
return self->port && self->type != tnet_proxy_type_none && !tsk_strnullORempty(self->hostname);
|
|
}
|
|
return tsk_false;
|
|
}
|
|
|