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 PyString_FromString(s);
25 
26     } else {
27         CFIndex len = CFStringGetLength(ref);
28         Boolean ok;
29         PyObject* result;
30         result = PyString_FromStringAndSize(NULL, len*4);
31 
32         ok = CFStringGetCString(ref,
33                         PyString_AS_STRING(result),
34                         PyString_GET_SIZE(result),
35                         kCFStringEncodingUTF8);
36         if (!ok) {
37             Py_DECREF(result);
38             return NULL;
39         } else {
40             _PyString_Resize(&result,
41                 strlen(PyString_AS_STRING(result)));
42         }
43         return result;
44     }
45 }
46 
47 
48 static PyObject*
get_proxy_settings(PyObject * mod)49 get_proxy_settings(PyObject* mod __attribute__((__unused__)))
50 {
51     CFDictionaryRef proxyDict = NULL;
52     CFNumberRef aNum = NULL;
53     CFArrayRef anArray = NULL;
54     PyObject* result = NULL;
55     PyObject* v;
56     int r;
57 
58     proxyDict = SCDynamicStoreCopyProxies(NULL);
59     if (!proxyDict) {
60         Py_INCREF(Py_None);
61         return Py_None;
62     }
63 
64     result = PyDict_New();
65     if (result == NULL) goto error;
66 
67     if (&kSCPropNetProxiesExcludeSimpleHostnames != NULL) {
68         aNum = CFDictionaryGetValue(proxyDict,
69             kSCPropNetProxiesExcludeSimpleHostnames);
70         if (aNum == NULL) {
71             v = PyBool_FromLong(0);
72         } else {
73             v = PyBool_FromLong(cfnum_to_int32(aNum));
74         }
75     }  else {
76         v = PyBool_FromLong(0);
77     }
78 
79     if (v == NULL) goto error;
80 
81     r = PyDict_SetItemString(result, "exclude_simple", v);
82     Py_DECREF(v); v = NULL;
83     if (r == -1) goto error;
84 
85     anArray = CFDictionaryGetValue(proxyDict,
86                     kSCPropNetProxiesExceptionsList);
87     if (anArray != NULL) {
88         CFIndex len = CFArrayGetCount(anArray);
89         CFIndex i;
90         v = PyTuple_New(len);
91         if (v == NULL) goto error;
92 
93         r = PyDict_SetItemString(result, "exceptions", v);
94         Py_DECREF(v);
95         if (r == -1) goto error;
96 
97         for (i = 0; i < len; i++) {
98             CFStringRef aString = NULL;
99 
100             aString = CFArrayGetValueAtIndex(anArray, i);
101             if (aString == NULL) {
102                 PyTuple_SetItem(v, i, Py_None);
103                 Py_INCREF(Py_None);
104             } else {
105                 PyObject* t = cfstring_to_pystring(aString);
106                 if (!t) {
107                     PyTuple_SetItem(v, i, Py_None);
108                     Py_INCREF(Py_None);
109                 } else {
110                     PyTuple_SetItem(v, i, t);
111                 }
112             }
113         }
114     }
115 
116     CFRelease(proxyDict);
117     return result;
118 
119 error:
120     if (proxyDict)  CFRelease(proxyDict);
121     Py_XDECREF(result);
122     return NULL;
123 }
124 
125 static int
set_proxy(PyObject * proxies,char * proto,CFDictionaryRef proxyDict,CFStringRef enabledKey,CFStringRef hostKey,CFStringRef portKey)126 set_proxy(PyObject* proxies, char* proto, CFDictionaryRef proxyDict,
127                 CFStringRef enabledKey,
128                 CFStringRef hostKey, CFStringRef portKey)
129 {
130     CFNumberRef aNum;
131 
132     aNum = CFDictionaryGetValue(proxyDict, enabledKey);
133     if (aNum && cfnum_to_int32(aNum)) {
134         CFStringRef hostString;
135 
136         hostString = CFDictionaryGetValue(proxyDict, hostKey);
137         aNum = CFDictionaryGetValue(proxyDict, portKey);
138 
139         if (hostString) {
140             int r;
141             PyObject* h = cfstring_to_pystring(hostString);
142             PyObject* v;
143             if (h) {
144                 if (aNum) {
145                     int32_t port = cfnum_to_int32(aNum);
146                     v = PyString_FromFormat("http://%s:%ld",
147                         PyString_AS_STRING(h),
148                         (long)port);
149                 } else {
150                     v = PyString_FromFormat("http://%s",
151                         PyString_AS_STRING(h));
152                 }
153                 Py_DECREF(h);
154                 if (!v) return -1;
155                 r = PyDict_SetItemString(proxies, proto,
156                     v);
157                 Py_DECREF(v);
158                 return r;
159             }
160         }
161 
162     }
163     return 0;
164 }
165 
166 
167 
168 static PyObject*
get_proxies(PyObject * mod)169 get_proxies(PyObject* mod __attribute__((__unused__)))
170 {
171     PyObject* result = NULL;
172     int r;
173     CFDictionaryRef proxyDict = NULL;
174 
175     proxyDict = SCDynamicStoreCopyProxies(NULL);
176     if (proxyDict == NULL) {
177         return PyDict_New();
178     }
179 
180     result = PyDict_New();
181     if (result == NULL) goto error;
182 
183     r = set_proxy(result, "http", proxyDict,
184         kSCPropNetProxiesHTTPEnable,
185         kSCPropNetProxiesHTTPProxy,
186         kSCPropNetProxiesHTTPPort);
187     if (r == -1) goto error;
188     r = set_proxy(result, "https", proxyDict,
189         kSCPropNetProxiesHTTPSEnable,
190         kSCPropNetProxiesHTTPSProxy,
191         kSCPropNetProxiesHTTPSPort);
192     if (r == -1) goto error;
193     r = set_proxy(result, "ftp", proxyDict,
194         kSCPropNetProxiesFTPEnable,
195         kSCPropNetProxiesFTPProxy,
196         kSCPropNetProxiesFTPPort);
197     if (r == -1) goto error;
198     r = set_proxy(result, "gopher", proxyDict,
199         kSCPropNetProxiesGopherEnable,
200         kSCPropNetProxiesGopherProxy,
201         kSCPropNetProxiesGopherPort);
202     if (r == -1) goto error;
203 
204     CFRelease(proxyDict);
205     return result;
206 error:
207     if (proxyDict)  CFRelease(proxyDict);
208     Py_XDECREF(result);
209     return NULL;
210 }
211 
212 static PyMethodDef mod_methods[] = {
213     {
214         "_get_proxy_settings",
215         (PyCFunction)get_proxy_settings,
216         METH_NOARGS,
217         NULL,
218     },
219     {
220         "_get_proxies",
221         (PyCFunction)get_proxies,
222         METH_NOARGS,
223         NULL,
224     },
225     { 0, 0, 0, 0 }
226 };
227 
init_scproxy(void)228 void init_scproxy(void)
229 {
230     (void)Py_InitModule4("_scproxy", mod_methods, NULL, NULL, PYTHON_API_VERSION);
231 }
232