1 /*******************************************************************************
2  * libproxy - A library for proxy configuration
3  * Copyright (C) 2006 Nathaniel McCallum <nathaniel@natemccallum.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
18  ******************************************************************************/
19 
20 #include <sstream>
21 
22 #include "../extension_config.hpp"
23 using namespace libproxy;
24 
25 #include <SystemConfiguration/SystemConfiguration.h>
26 
27 class str : public string {
28 public:
str(CFStringRef s)29 	str(CFStringRef s) : string() {
30 		if (!s) return;
31 		const char* tmp = CFStringGetCStringPtr(s, CFStringGetFastestEncoding(s));
32 		*this += tmp ? tmp : "";
33 	}
34 
str(CFArrayRef a)35 	str(CFArrayRef a) : string() {
36 		if (!a) return;
37 		for (CFIndex i=0 ; i < CFArrayGetCount(a) ; i++) {
38 			CFStringRef s = (CFStringRef) CFArrayGetValueAtIndex(a, i);
39 			*this += str(s);
40 			if (i+1 < CFArrayGetCount(a))
41 				*this += ",";
42 		}
43 	}
44 };
45 
46 template <class T>
getobj(CFDictionaryRef settings,string key)47 static T getobj(CFDictionaryRef settings, string key) {
48 	if (!settings) return NULL;
49 	CFStringRef k = CFStringCreateWithCString(NULL, key.c_str(), kCFStringEncodingMacRoman);
50 	if (!k) return NULL;
51 	T retval = (T) CFDictionaryGetValue(settings, k);
52 	CFRelease(k);
53 	return retval;
54 }
55 
getint(CFDictionaryRef settings,string key,int64_t & answer)56 static bool getint(CFDictionaryRef settings, string key, int64_t& answer) {
57 	CFNumberRef n = getobj<CFNumberRef>(settings, key);
58 	if (!n) return false;
59 	if (!CFNumberGetValue(n, kCFNumberSInt64Type, &answer))
60 		return false;
61 	return true;
62 }
63 
getbool(CFDictionaryRef settings,string key,bool dflt=false)64 static bool getbool(CFDictionaryRef settings, string key, bool dflt=false) {
65 	int64_t i;
66 	if (!getint(settings, key, i)) return dflt;
67 	return i != 0;
68 }
69 
protocol_url(CFDictionaryRef settings,string protocol,string & config)70 static bool protocol_url(CFDictionaryRef settings, string protocol, string& config) {
71 	int64_t port;
72 	string  host;
73 
74 	// Check ProtocolEnabled
75 	if (!getbool(settings, protocol + "Enable"))
76 		return false;
77 
78 	// Get ProtocolPort
79 	if (!getint(settings, protocol + "Port", port))
80 		return false;
81 
82 	// Get ProtocolProxy
83 	if ((host = str(getobj<CFStringRef>(settings, protocol + "Proxy"))) == "")
84 		return false;
85 
86 	stringstream ss;
87 	if (protocol == "HTTP" || protocol == "HTTPS" || protocol == "FTP" || protocol == "Gopher")
88 		ss << "http://";
89 	else if (protocol == "RTSP")
90 		ss << "rtsp://";
91 	else if (protocol == "SOCKS")
92 		ss << "socks://";
93 	else
94 		return false;
95 	ss << host;
96 	ss << ":";
97 	ss << port;
98 
99 	config = ss.str();
100 	return true;
101 }
102 
toupper(string str)103 static string toupper(string str) {
104 	string tmp;
105 	for (unsigned int i=0 ; str.c_str()[i] ; i++)
106 		tmp += toupper(str.c_str()[i]);
107 	return tmp;
108 }
109 
capitalize(string str)110 static string capitalize(string str) {
111 	char c = toupper(str.c_str()[0]);
112 	return string(&c, 1) + str.substr(1);
113 }
114 
115 class macosx_config_extension : public config_extension {
116 public:
get_config(const url & the_url)117 	vector<url> get_config(const url &the_url) {
118 		string tmp;
119 		CFDictionaryRef proxies = SCDynamicStoreCopyProxies(NULL);
120 		vector<url> response;
121 
122 		if (!proxies) throw runtime_error("Unable to fetch proxy configuration");
123 
124 		// wpad://
125 		if (getbool(proxies, "ProxyAutoDiscoveryEnable")) {
126 			CFRelease(proxies);
127 			response.push_back(url("wpad://"));
128 		}
129 
130 		// pac+http://...
131 		else if (getbool(proxies, "ProxyAutoConfigEnable") &&
132 		    (tmp = str(getobj<CFStringRef>(proxies, "ProxyAutoConfigURLString"))) != "" &&
133         	    url::is_valid(tmp)) {
134 			CFRelease(proxies);
135 			response.push_back(url(string("pac+") + tmp));
136 		}
137 
138 		// http:// or socks:// (TODO: gopher:// and rtsp:// ???)
139 		else if ((protocol_url(proxies, toupper(the_url.get_scheme()), tmp)    && url::is_valid(tmp)) ||
140 		    (protocol_url(proxies, capitalize(the_url.get_scheme()), tmp) && url::is_valid(tmp)) ||
141 		    (protocol_url(proxies, toupper("http"), tmp)              && url::is_valid(tmp)) ||
142 	            (protocol_url(proxies, toupper("socks"), tmp)             && url::is_valid(tmp))) {
143 			CFRelease(proxies);
144 			response.push_back(url(tmp));
145 		}
146 		else {
147 			// direct://
148 			CFRelease(proxies);
149 			response.push_back(url("direct://"));
150 		}
151 
152 		return response;
153 	}
154 
get_ignore(const url &)155 	string get_ignore(const url&) {
156 		// Get config dict
157 		CFDictionaryRef proxies = SCDynamicStoreCopyProxies(NULL);
158 		if (!proxies) return "";
159 
160 		// Get ignores
161 		string tmp = str(getobj<CFArrayRef>(proxies, "ExceptionsList"));
162 		if (getbool(proxies, "ExcludeSimpleHostnames"))
163 			tmp += (tmp == "" ? string("") : string(",")) + "<local>";
164 
165 		CFRelease(proxies);
166 		return tmp;
167 	}
168 };
169 
170 MM_MODULE_INIT_EZ(macosx_config_extension, true, NULL, NULL);
171 
172