• 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 #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