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 8static int32_t 9cfnum_to_int32(CFNumberRef num) 10{ 11 int32_t result; 12 13 CFNumberGetValue(num, kCFNumberSInt32Type, &result); 14 return result; 15} 16 17static PyObject* 18cfstring_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 55static PyObject* 56get_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 124error: 125 if (proxyDict) CFRelease(proxyDict); 126 Py_XDECREF(result); 127 return NULL; 128} 129 130static int 131set_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 171static PyObject* 172get_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; 212error: 213 if (proxyDict) CFRelease(proxyDict); 214 Py_XDECREF(result); 215 return NULL; 216} 217 218static 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 234static PyModuleDef_Slot _scproxy_slots[] = { 235 {0, NULL} 236}; 237 238static 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 247extern "C" { 248#endif 249 250PyMODINIT_FUNC 251PyInit__scproxy(void) 252{ 253 return PyModuleDef_Init(&_scproxy_module); 254} 255 256#ifdef __cplusplus 257} 258#endif 259