1 /*
2 * Helper method for urllib to fetch the proxy configuration settings
3 * using the SystemConfiguration framework.
4 */
5 #include <Python.h>
6 #include <SystemConfiguration/SystemConfiguration.h>
7
8 static int32_t
cfnum_to_int32(CFNumberRef num)9 cfnum_to_int32(CFNumberRef num)
10 {
11 int32_t result;
12
13 CFNumberGetValue(num, kCFNumberSInt32Type, &result);
14 return result;
15 }
16
17 static PyObject*
cfstring_to_pystring(CFStringRef ref)18 cfstring_to_pystring(CFStringRef ref)
19 {
20 const char* s;
21
22 s = CFStringGetCStringPtr(ref, kCFStringEncodingUTF8);
23 if (s) {
24 return PyUnicode_DecodeUTF8(
25 s, strlen(s), NULL);
26
27 } else {
28 CFIndex len = CFStringGetLength(ref);
29 Boolean ok;
30 PyObject* result;
31 char* buf;
32
33 buf = PyMem_Malloc(len*4);
34 if (buf == NULL) {
35 PyErr_NoMemory();
36 return NULL;
37 }
38
39 ok = CFStringGetCString(ref,
40 buf, len * 4,
41 kCFStringEncodingUTF8);
42 if (!ok) {
43 PyMem_Free(buf);
44 return NULL;
45 } else {
46 result = PyUnicode_DecodeUTF8(
47 buf, strlen(buf), NULL);
48 PyMem_Free(buf);
49 }
50 return result;
51 }
52 }
53
54
55 static PyObject*
get_proxy_settings(PyObject * Py_UNUSED (mod),PyObject * Py_UNUSED (ignored))56 get_proxy_settings(PyObject* Py_UNUSED(mod), PyObject *Py_UNUSED(ignored))
57 {
58 CFDictionaryRef proxyDict = NULL;
59 CFNumberRef aNum = NULL;
60 CFArrayRef anArray = NULL;
61 PyObject* result = NULL;
62 PyObject* v;
63 int r;
64
65 Py_BEGIN_ALLOW_THREADS
66 proxyDict = SCDynamicStoreCopyProxies(NULL);
67 Py_END_ALLOW_THREADS
68
69 if (!proxyDict) {
70 Py_RETURN_NONE;
71 }
72
73 result = PyDict_New();
74 if (result == NULL) goto error;
75
76 aNum = CFDictionaryGetValue(proxyDict,
77 kSCPropNetProxiesExcludeSimpleHostnames);
78 if (aNum == NULL) {
79 v = PyBool_FromLong(0);
80 } else {
81 v = PyBool_FromLong(cfnum_to_int32(aNum));
82 }
83
84 if (v == NULL) goto error;
85
86 r = PyDict_SetItemString(result, "exclude_simple", v);
87 Py_DECREF(v); v = NULL;
88 if (r == -1) goto error;
89
90 anArray = CFDictionaryGetValue(proxyDict,
91 kSCPropNetProxiesExceptionsList);
92 if (anArray != NULL) {
93 CFIndex len = CFArrayGetCount(anArray);
94 CFIndex i;
95 v = PyTuple_New(len);
96 if (v == NULL) goto error;
97
98 r = PyDict_SetItemString(result, "exceptions", v);
99 Py_DECREF(v);
100 if (r == -1) goto error;
101
102 for (i = 0; i < len; i++) {
103 CFStringRef aString = NULL;
104
105 aString = CFArrayGetValueAtIndex(anArray, i);
106 if (aString == NULL) {
107 PyTuple_SetItem(v, i, Py_None);
108 Py_INCREF(Py_None);
109 } else {
110 PyObject* t = cfstring_to_pystring(aString);
111 if (!t) {
112 PyTuple_SetItem(v, i, Py_None);
113 Py_INCREF(Py_None);
114 } else {
115 PyTuple_SetItem(v, i, t);
116 }
117 }
118 }
119 }
120
121 CFRelease(proxyDict);
122 return result;
123
124 error:
125 if (proxyDict) CFRelease(proxyDict);
126 Py_XDECREF(result);
127 return NULL;
128 }
129
130 static int
set_proxy(PyObject * proxies,const char * proto,CFDictionaryRef proxyDict,CFStringRef enabledKey,CFStringRef hostKey,CFStringRef portKey)131 set_proxy(PyObject* proxies, const char* proto, CFDictionaryRef proxyDict,
132 CFStringRef enabledKey,
133 CFStringRef hostKey, CFStringRef portKey)
134 {
135 CFNumberRef aNum;
136
137 aNum = CFDictionaryGetValue(proxyDict, enabledKey);
138 if (aNum && cfnum_to_int32(aNum)) {
139 CFStringRef hostString;
140
141 hostString = CFDictionaryGetValue(proxyDict, hostKey);
142 aNum = CFDictionaryGetValue(proxyDict, portKey);
143
144 if (hostString) {
145 int r;
146 PyObject* h = cfstring_to_pystring(hostString);
147 PyObject* v;
148 if (h) {
149 if (aNum) {
150 int32_t port = cfnum_to_int32(aNum);
151 v = PyUnicode_FromFormat("http://%U:%ld",
152 h, (long)port);
153 } else {
154 v = PyUnicode_FromFormat("http://%U", h);
155 }
156 Py_DECREF(h);
157 if (!v) return -1;
158 r = PyDict_SetItemString(proxies, proto,
159 v);
160 Py_DECREF(v);
161 return r;
162 }
163 }
164
165 }
166 return 0;
167 }
168
169
170
171 static PyObject*
get_proxies(PyObject * Py_UNUSED (mod),PyObject * Py_UNUSED (ignored))172 get_proxies(PyObject* Py_UNUSED(mod), PyObject *Py_UNUSED(ignored))
173 {
174 PyObject* result = NULL;
175 int r;
176 CFDictionaryRef proxyDict = NULL;
177
178 Py_BEGIN_ALLOW_THREADS
179 proxyDict = SCDynamicStoreCopyProxies(NULL);
180 Py_END_ALLOW_THREADS
181
182 if (proxyDict == NULL) {
183 return PyDict_New();
184 }
185
186 result = PyDict_New();
187 if (result == NULL) goto error;
188
189 r = set_proxy(result, "http", proxyDict,
190 kSCPropNetProxiesHTTPEnable,
191 kSCPropNetProxiesHTTPProxy,
192 kSCPropNetProxiesHTTPPort);
193 if (r == -1) goto error;
194 r = set_proxy(result, "https", proxyDict,
195 kSCPropNetProxiesHTTPSEnable,
196 kSCPropNetProxiesHTTPSProxy,
197 kSCPropNetProxiesHTTPSPort);
198 if (r == -1) goto error;
199 r = set_proxy(result, "ftp", proxyDict,
200 kSCPropNetProxiesFTPEnable,
201 kSCPropNetProxiesFTPProxy,
202 kSCPropNetProxiesFTPPort);
203 if (r == -1) goto error;
204 r = set_proxy(result, "gopher", proxyDict,
205 kSCPropNetProxiesGopherEnable,
206 kSCPropNetProxiesGopherProxy,
207 kSCPropNetProxiesGopherPort);
208 if (r == -1) goto error;
209
210 CFRelease(proxyDict);
211 return result;
212 error:
213 if (proxyDict) CFRelease(proxyDict);
214 Py_XDECREF(result);
215 return NULL;
216 }
217
218 static PyMethodDef mod_methods[] = {
219 {
220 "_get_proxy_settings",
221 get_proxy_settings,
222 METH_NOARGS,
223 NULL,
224 },
225 {
226 "_get_proxies",
227 get_proxies,
228 METH_NOARGS,
229 NULL,
230 },
231 { 0, 0, 0, 0 }
232 };
233
234 static PyModuleDef_Slot _scproxy_slots[] = {
235 {0, NULL}
236 };
237
238 static struct PyModuleDef _scproxy_module = {
239 PyModuleDef_HEAD_INIT,
240 .m_name = "_scproxy",
241 .m_size = 0,
242 .m_methods = mod_methods,
243 .m_slots = _scproxy_slots,
244 };
245
246 #ifdef __cplusplus
247 extern "C" {
248 #endif
249
250 PyMODINIT_FUNC
PyInit__scproxy(void)251 PyInit__scproxy(void)
252 {
253 return PyModuleDef_Init(&_scproxy_module);
254 }
255
256 #ifdef __cplusplus
257 }
258 #endif
259