1 /* 2 * Copyright (c) 2004, 2017, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 #include <windows.h> 27 #include <Winhttp.h> 28 29 #include "jni.h" 30 #include "jni_util.h" 31 #include "jvm.h" 32 33 #include "proxy_util.h" 34 35 #include "sun_net_spi_DefaultProxySelector.h" 36 37 /** 38 * These functions are used by the sun.net.spi.DefaultProxySelector class 39 * to access some platform specific settings. 40 * This is the Windows code using WinHTTP functions to get the system settings. 41 */ 42 43 /* Keep one static session for all requests. */ 44 static HINTERNET session = NULL; 45 46 /* 47 * Class: sun_net_spi_DefaultProxySelector 48 * Method: init 49 * Signature: ()Z 50 */ 51 JNIEXPORT jboolean JNICALL 52 Java_sun_net_spi_DefaultProxySelector_init(JNIEnv *env, jclass clazz) { 53 54 /* 55 * Get one WinHTTP session handle to initialize the WinHTTP internal data structures 56 * We keep and use only this one for the whole life time. 57 */ 58 session = WinHttpOpen(L"Only used internal", /* we need no real agent string here */ 59 WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, 60 WINHTTP_NO_PROXY_NAME, 61 WINHTTP_NO_PROXY_BYPASS, 62 0); 63 if (session == NULL) { 64 return JNI_FALSE; 65 } 66 67 if (!initJavaClass(env)) { 68 return JNI_FALSE; 69 } 70 71 return JNI_TRUE; 72 } 73 74 75 #define MAX_STR_LEN 1024 76 77 /* A linked list element for a proxy */ 78 typedef struct list_item { 79 wchar_t *host; 80 int port; 81 struct list_item *next; 82 } list_item; 83 84 /* Free the linked list */ 85 static void freeList(list_item *head) { 86 list_item *next = NULL; 87 list_item *current = head; 88 while (current != NULL) { 89 next = current->next; 90 free(current->host); 91 free(current); 92 current = next; 93 } 94 } 95 96 97 /* 98 * Creates a linked list of list_item elements that has to be freed later on. 99 * Returns the size of the array as int. 100 */ 101 static int createProxyList(LPWSTR win_proxy, const WCHAR *pproto, list_item **head) { 102 static const wchar_t separators[] = L"\t\r\n ;"; 103 list_item *current = NULL; 104 int nr_elems = 0; 105 wchar_t *context = NULL; 106 wchar_t *current_proxy = NULL; 107 BOOL error = FALSE; 108 109 /* 110 * The proxy server list contains one or more of the following strings separated by semicolons or whitespace. 111 * ([<scheme>=][<scheme>"://"]<server>[":"<port>]) 112 */ 113 current_proxy = wcstok_s(win_proxy, separators, &context); 114 while (current_proxy != NULL) { 115 LPWSTR pport; 116 LPWSTR phost; 117 int portVal = 0; 118 wchar_t *next_proxy = NULL; 119 list_item *proxy = NULL; 120 wchar_t* pos = NULL; 121 122 /* Filter based on the scheme, if there is one */ 123 pos = wcschr(current_proxy, L'='); 124 if (pos) { 125 *pos = L'\0'; 126 if (wcscmp(current_proxy, pproto) != 0) { 127 current_proxy = wcstok_s(NULL, separators, &context); 128 continue; 129 } 130 current_proxy = pos + 1; 131 } 132 133 /* Let's check for a scheme and ignore it. */ 134 if ((phost = wcsstr(current_proxy, L"://")) != NULL) { 135 phost += 3; 136 } else { 137 phost = current_proxy; 138 } 139 140 /* Get the port */ 141 pport = wcschr(phost, L':'); 142 if (pport != NULL) { 143 *pport = 0; 144 pport++; 145 swscanf(pport, L"%d", &portVal); 146 } 147 148 proxy = (list_item *)malloc(sizeof(list_item)); 149 if (proxy != NULL) { 150 proxy->next = NULL; 151 proxy->port = portVal; 152 proxy->host = _wcsdup(phost); 153 154 if (proxy->host != NULL) { 155 if (*head == NULL) { 156 *head = proxy; /* first elem */ 157 } 158 if (current != NULL) { 159 current->next = proxy; 160 } 161 current = proxy; 162 nr_elems++; 163 } else { 164 free(proxy); /* cleanup */ 165 } 166 } 167 /* goto next proxy if available... */ 168 current_proxy = wcstok_s(NULL, separators, &context); 169 } 170 return nr_elems; 171 } 172 173 174 175 /* 176 * Class: sun_net_spi_DefaultProxySelector 177 * Method: getSystemProxies 178 * Signature: ([Ljava/lang/String;Ljava/lang/String;)[Ljava/net/Proxy; 179 */ 180 JNIEXPORT jobjectArray JNICALL 181 Java_sun_net_spi_DefaultProxySelector_getSystemProxies(JNIEnv *env, 182 jobject this, 183 jstring proto, 184 jstring host) 185 { 186 jobjectArray proxy_array = NULL; 187 jobject type_proxy = NULL; 188 LPCWSTR lpProto; 189 LPCWSTR lpHost; 190 list_item *head = NULL; 191 192 BOOL use_auto_proxy = FALSE; 193 WINHTTP_CURRENT_USER_IE_PROXY_CONFIG ie_proxy_config; 194 WINHTTP_AUTOPROXY_OPTIONS auto_proxy_options; 195 WINHTTP_PROXY_INFO proxy_info; 196 LPWSTR win_proxy = NULL; 197 LPWSTR win_bypass_proxy = NULL; 198 199 memset(&ie_proxy_config, 0, sizeof(WINHTTP_CURRENT_USER_IE_PROXY_CONFIG)); 200 memset(&auto_proxy_options, 0, sizeof(WINHTTP_AUTOPROXY_OPTIONS)); 201 memset(&proxy_info, 0, sizeof(WINHTTP_PROXY_INFO)); 202 203 lpHost = (*env)->GetStringChars(env, host, NULL); 204 if (lpHost == NULL) { 205 if (!(*env)->ExceptionCheck(env)) 206 JNU_ThrowOutOfMemoryError(env, NULL); 207 return NULL; 208 } 209 210 lpProto = (*env)->GetStringChars(env, proto, NULL); 211 if (lpProto == NULL) { 212 (*env)->ReleaseStringChars(env, host, lpHost); 213 if (!(*env)->ExceptionCheck(env)) 214 JNU_ThrowOutOfMemoryError(env, NULL); 215 return NULL; 216 } 217 218 if (WinHttpGetIEProxyConfigForCurrentUser(&ie_proxy_config) == FALSE) { 219 /* cleanup and exit */ 220 (*env)->ReleaseStringChars(env, host, lpHost); 221 (*env)->ReleaseStringChars(env, proto, lpProto); 222 return NULL; 223 } 224 225 if (ie_proxy_config.fAutoDetect) { 226 /* Windows uses WPAD */ 227 auto_proxy_options.dwAutoDetectFlags = WINHTTP_AUTO_DETECT_TYPE_DHCP | 228 WINHTTP_AUTO_DETECT_TYPE_DNS_A; 229 auto_proxy_options.dwFlags = WINHTTP_AUTOPROXY_AUTO_DETECT; 230 auto_proxy_options.fAutoLogonIfChallenged = TRUE; 231 use_auto_proxy = TRUE; 232 } else if (ie_proxy_config.lpszAutoConfigUrl != NULL) { 233 /* Windows uses PAC file */ 234 auto_proxy_options.lpszAutoConfigUrl = ie_proxy_config.lpszAutoConfigUrl; 235 auto_proxy_options.dwFlags = WINHTTP_AUTOPROXY_CONFIG_URL; 236 use_auto_proxy = TRUE; 237 } else if (ie_proxy_config.lpszProxy != NULL) { 238 /* Windows uses manually entered proxy. */ 239 use_auto_proxy = FALSE; 240 win_bypass_proxy = ie_proxy_config.lpszProxyBypass; 241 win_proxy = ie_proxy_config.lpszProxy; 242 } 243 244 if (use_auto_proxy) { 245 WCHAR url[MAX_STR_LEN]; 246 /* Create url for WinHttpGetProxyForUrl */ 247 _snwprintf(url, sizeof(url) - 1, L"%s://%s", lpProto, lpHost); 248 /* Get proxy for URL from Windows */ 249 use_auto_proxy = WinHttpGetProxyForUrl(session, &url[0], &auto_proxy_options, &proxy_info); 250 if (use_auto_proxy) { 251 win_proxy = proxy_info.lpszProxy; 252 win_bypass_proxy = proxy_info.lpszProxyBypass; 253 } 254 } 255 256 /* Check the bypass entry. */ 257 if (NULL != win_bypass_proxy) { 258 /* 259 * From MSDN: 260 * The proxy bypass list contains one or more server names separated by semicolons or 261 * whitespace. The proxy bypass list can also contain the string "<local>" to indicate 262 * that all local intranet sites are bypassed. Local intranet sites are considered to 263 * be all servers that do not contain a period in their name. 264 */ 265 wchar_t *context = NULL; 266 LPWSTR s = wcstok_s(win_bypass_proxy, L"; ", &context); 267 268 while (s != NULL) { 269 size_t maxlen = wcslen(s); 270 if (wcsncmp(s, lpHost, maxlen) == 0) { 271 /* 272 * The URL host name matches with one of the prefixes, we have to use a direct 273 * connection. 274 */ 275 goto noproxy; 276 } 277 if (wcsncmp(s, L"<local>", maxlen) == 0) { 278 /* 279 * All local intranet sites are bypassed - Microsoft considers all servers that 280 * do not contain a period in their name to be local. 281 */ 282 if (wcschr(lpHost, '.') == NULL) { 283 goto noproxy; 284 } 285 } 286 s = wcstok_s(NULL, L"; ", &context); 287 } 288 } 289 290 if (win_proxy != NULL) { 291 wchar_t *context = NULL; 292 int defport = 0; 293 int nr_elems = 0; 294 295 /** 296 * Set default port value & proxy type from protocol. 297 */ 298 if ((wcscmp(lpProto, L"http") == 0) || 299 (wcscmp(lpProto, L"ftp") == 0) || 300 (wcscmp(lpProto, L"gopher") == 0)) 301 defport = 80; 302 if (wcscmp(lpProto, L"https") == 0) 303 defport = 443; 304 if (wcscmp(lpProto, L"socks") == 0) { 305 defport = 1080; 306 type_proxy = (*env)->GetStaticObjectField(env, ptype_class, ptype_socksID); 307 } else { 308 type_proxy = (*env)->GetStaticObjectField(env, ptype_class, ptype_httpID); 309 } 310 if (type_proxy == NULL || (*env)->ExceptionCheck(env)) { 311 goto noproxy; 312 } 313 314 nr_elems = createProxyList(win_proxy, lpProto, &head); 315 if (nr_elems != 0 && head != NULL) { 316 int index = 0; 317 proxy_array = (*env)->NewObjectArray(env, nr_elems, proxy_class, NULL); 318 if (proxy_array == NULL || (*env)->ExceptionCheck(env)) { 319 goto noproxy; 320 } 321 while (head != NULL && index < nr_elems) { 322 jstring jhost; 323 jobject isa; 324 jobject proxy; 325 326 if (head->host != NULL && proxy_array != NULL) { 327 /* Let's create the appropriate Proxy object then. */ 328 if (head->port == 0) { 329 head->port = defport; 330 } 331 jhost = (*env)->NewString(env, head->host, (jsize)wcslen(head->host)); 332 if (jhost == NULL || (*env)->ExceptionCheck(env)) { 333 proxy_array = NULL; 334 } 335 isa = (*env)->CallStaticObjectMethod(env, isaddr_class, 336 isaddr_createUnresolvedID, jhost, 337 head->port); 338 if (isa == NULL || (*env)->ExceptionCheck(env)) { 339 proxy_array = NULL; 340 } 341 proxy = (*env)->NewObject(env, proxy_class, proxy_ctrID, type_proxy, isa); 342 if (proxy == NULL || (*env)->ExceptionCheck(env)) { 343 proxy_array = NULL; 344 } 345 (*env)->SetObjectArrayElement(env, proxy_array, index, proxy); 346 if ((*env)->ExceptionCheck(env)) { 347 proxy_array = NULL; 348 } 349 index++; 350 } 351 head = head->next; 352 } 353 } 354 } 355 356 noproxy: 357 if (head != NULL) { 358 freeList(head); 359 } 360 if (proxy_info.lpszProxy != NULL) 361 GlobalFree(proxy_info.lpszProxy); 362 if (proxy_info.lpszProxyBypass != NULL) 363 GlobalFree(proxy_info.lpszProxyBypass); 364 if (ie_proxy_config.lpszAutoConfigUrl != NULL) 365 GlobalFree(ie_proxy_config.lpszAutoConfigUrl); 366 if (ie_proxy_config.lpszProxy != NULL) 367 GlobalFree(ie_proxy_config.lpszProxy); 368 if (ie_proxy_config.lpszProxyBypass != NULL) 369 GlobalFree(ie_proxy_config.lpszProxyBypass); 370 if (lpHost != NULL) 371 (*env)->ReleaseStringChars(env, host, lpHost); 372 if (lpProto != NULL) 373 (*env)->ReleaseStringChars(env, proto, lpProto); 374 375 return proxy_array; 376 }