• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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