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