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  * On Windows use 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
Java_sun_net_spi_DefaultProxySelector_init(JNIEnv * env,jclass clazz)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
56      * structures. 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 */
freeList(list_item * head)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  */
createProxyList(LPWSTR win_proxy,const WCHAR * pproto,list_item ** head)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
111      * separated by semicolons or whitespace:
112      *    ([<scheme>=][<scheme>"://"]<server>[":"<port>])
113      */
114     current_proxy = wcstok_s(win_proxy, separators, &context);
115     while (current_proxy != NULL) {
116         LPWSTR pport;
117         LPWSTR phost;
118         int portVal = 0;
119         wchar_t *next_proxy = NULL;
120         list_item *proxy = NULL;
121         wchar_t* pos = NULL;
122 
123         /* Filter based on the scheme, if there is one */
124         pos = wcschr(current_proxy, L'=');
125         if (pos) {
126           *pos = L'\0';
127           if (wcscmp(current_proxy, pproto) != 0) {
128               current_proxy = wcstok_s(NULL, separators, &context);
129               continue;
130           }
131           current_proxy = pos + 1;
132         }
133 
134         /* Let's check for a scheme and ignore it. */
135         if ((phost = wcsstr(current_proxy, L"://")) != NULL) {
136             phost += 3;
137         } else {
138             phost = current_proxy;
139         }
140 
141         /* Get the port */
142         pport = wcschr(phost, L':');
143         if (pport != NULL) {
144             *pport = 0;
145             pport++;
146             swscanf(pport, L"%d", &portVal);
147         }
148 
149         proxy = (list_item *)malloc(sizeof(list_item));
150         if (proxy != NULL) {
151             proxy->next = NULL;
152             proxy->port = portVal;
153             proxy->host = _wcsdup(phost);
154 
155             if (proxy->host != NULL) {
156                 if (*head == NULL) {
157                     *head = proxy; /* first elem */
158                 }
159                 if (current != NULL) {
160                     current->next = proxy;
161                 }
162                 current = proxy;
163                 nr_elems++;
164             } else {
165                 free(proxy); /* cleanup */
166             }
167         }
168         /* goto next proxy if available... */
169         current_proxy = wcstok_s(NULL, separators, &context);
170     }
171     return nr_elems;
172 }
173 
174 
175 
176 /*
177  * Class:     sun_net_spi_DefaultProxySelector
178  * Method:    getSystemProxies
179  * Signature: ([Ljava/lang/String;Ljava/lang/String;)[Ljava/net/Proxy;
180  */
181 JNIEXPORT jobjectArray JNICALL
Java_sun_net_spi_DefaultProxySelector_getSystemProxies(JNIEnv * env,jobject this,jstring proto,jstring host)182 Java_sun_net_spi_DefaultProxySelector_getSystemProxies(JNIEnv *env,
183                                                        jobject this,
184                                                        jstring proto,
185                                                        jstring host)
186 {
187     jobjectArray proxy_array = NULL;
188     jobject type_proxy = NULL;
189     LPCWSTR lpProto;
190     LPCWSTR lpHost;
191     list_item *head = NULL;
192 
193     BOOL                                   use_auto_proxy = FALSE;
194     WINHTTP_CURRENT_USER_IE_PROXY_CONFIG   ie_proxy_config;
195     WINHTTP_AUTOPROXY_OPTIONS              auto_proxy_options;
196     WINHTTP_PROXY_INFO                     proxy_info;
197     LPWSTR win_proxy = NULL;
198     LPWSTR win_bypass_proxy = NULL;
199 
200     memset(&ie_proxy_config, 0, sizeof(WINHTTP_CURRENT_USER_IE_PROXY_CONFIG));
201     memset(&auto_proxy_options, 0, sizeof(WINHTTP_AUTOPROXY_OPTIONS));
202     memset(&proxy_info, 0, sizeof(WINHTTP_PROXY_INFO));
203 
204     lpHost = (*env)->GetStringChars(env, host, NULL);
205     if (lpHost == NULL) {
206         if (!(*env)->ExceptionCheck(env))
207             JNU_ThrowOutOfMemoryError(env, NULL);
208         return NULL;
209     }
210 
211     lpProto = (*env)->GetStringChars(env, proto, NULL);
212     if (lpProto == NULL) {
213         (*env)->ReleaseStringChars(env, host, lpHost);
214         if (!(*env)->ExceptionCheck(env))
215             JNU_ThrowOutOfMemoryError(env, NULL);
216         return NULL;
217     }
218 
219     if (WinHttpGetIEProxyConfigForCurrentUser(&ie_proxy_config) == FALSE) {
220         /* cleanup and exit */
221         (*env)->ReleaseStringChars(env, host, lpHost);
222         (*env)->ReleaseStringChars(env, proto, lpProto);
223         return NULL;
224     }
225 
226     if (ie_proxy_config.fAutoDetect) {
227         /* Windows uses WPAD */
228         auto_proxy_options.dwAutoDetectFlags = WINHTTP_AUTO_DETECT_TYPE_DHCP |
229                                                WINHTTP_AUTO_DETECT_TYPE_DNS_A;
230         auto_proxy_options.dwFlags = WINHTTP_AUTOPROXY_AUTO_DETECT;
231         auto_proxy_options.fAutoLogonIfChallenged = TRUE;
232         use_auto_proxy = TRUE;
233     } else if (ie_proxy_config.lpszAutoConfigUrl != NULL) {
234         /* Windows uses PAC file */
235         auto_proxy_options.lpszAutoConfigUrl = ie_proxy_config.lpszAutoConfigUrl;
236         auto_proxy_options.dwFlags = WINHTTP_AUTOPROXY_CONFIG_URL;
237         use_auto_proxy = TRUE;
238     } else if (ie_proxy_config.lpszProxy != NULL) {
239         /* Windows uses manually entered proxy. */
240         use_auto_proxy = FALSE;
241         win_bypass_proxy = ie_proxy_config.lpszProxyBypass;
242         win_proxy = ie_proxy_config.lpszProxy;
243     }
244 
245     if (use_auto_proxy) {
246         WCHAR url[MAX_STR_LEN];
247         /* Create url for WinHttpGetProxyForUrl */
248         _snwprintf(url, sizeof(url) - 1, L"%s://%s", lpProto, lpHost);
249         /* Get proxy for URL from Windows */
250         use_auto_proxy = WinHttpGetProxyForUrl(session, &url[0], &auto_proxy_options, &proxy_info);
251         if (use_auto_proxy) {
252             win_proxy = proxy_info.lpszProxy;
253             win_bypass_proxy = proxy_info.lpszProxyBypass;
254         }
255     }
256 
257     /* Check the bypass entry. */
258     if (NULL != win_bypass_proxy) {
259         /*
260          * From MSDN:
261          * The proxy bypass list contains one or more server names separated by
262          * semicolons or whitespace. The proxy bypass list can also contain the
263          * string "<local>" to indicate that all local intranet sites are
264          * bypassed. Local intranet sites are considered to be all servers that
265          * do not contain a period in their name.
266          */
267         wchar_t *context = NULL;
268         LPWSTR s = wcstok_s(win_bypass_proxy, L"; ", &context);
269 
270         while (s != NULL) {
271             size_t maxlen = wcslen(s);
272             if (wcsncmp(s, lpHost, maxlen) == 0) {
273                 /*
274                  * The URL host name matches with one of the prefixes, use a
275                  * direct connection.
276                  */
277                 goto noproxy;
278             }
279             if (wcsncmp(s, L"<local>", maxlen) == 0) {
280                 /*
281                  * All local intranet sites are bypassed - Microsoft consider all
282                  * servers that do not contain a period in their name to be local.
283                  */
284                 if (wcschr(lpHost, '.') == NULL) {
285                     goto noproxy;
286                 }
287             }
288             s = wcstok_s(NULL, L"; ", &context);
289         }
290     }
291 
292     if (win_proxy != NULL) {
293         wchar_t *context = NULL;
294         int defport = 0;
295         int nr_elems = 0;
296 
297         /* Set the default port value & proxy type from protocol. */
298         if ((wcscmp(lpProto, L"http") == 0) ||
299             (wcscmp(lpProto, L"ftp") == 0))
300             defport = 80;
301         if (wcscmp(lpProto, L"https") == 0)
302             defport = 443;
303         if (wcscmp(lpProto, L"socks") == 0) {
304             defport = 1080;
305             type_proxy = (*env)->GetStaticObjectField(env, ptype_class, ptype_socksID);
306         } else {
307             type_proxy = (*env)->GetStaticObjectField(env, ptype_class, ptype_httpID);
308         }
309         if (type_proxy == NULL || (*env)->ExceptionCheck(env)) {
310             goto noproxy;
311         }
312 
313         nr_elems = createProxyList(win_proxy, lpProto, &head);
314         if (nr_elems != 0 && head != NULL) {
315             int index = 0;
316             proxy_array = (*env)->NewObjectArray(env, nr_elems, proxy_class, NULL);
317             if (proxy_array == NULL || (*env)->ExceptionCheck(env)) {
318                 goto noproxy;
319             }
320             while (head != NULL && index < nr_elems) {
321                 jstring jhost;
322                 jobject isa;
323                 jobject proxy;
324 
325                 if (head->host != NULL && proxy_array != NULL) {
326                     /* Let's create the appropriate Proxy object then. */
327                     if (head->port == 0) {
328                         head->port = defport;
329                     }
330                     jhost = (*env)->NewString(env, head->host, (jsize)wcslen(head->host));
331                     if (jhost == NULL || (*env)->ExceptionCheck(env)) {
332                         proxy_array = NULL;
333                     }
334                     isa = (*env)->CallStaticObjectMethod(env, isaddr_class,
335                                                          isaddr_createUnresolvedID, jhost,
336                                                          head->port);
337                     if (isa == NULL || (*env)->ExceptionCheck(env)) {
338                         proxy_array = NULL;
339                     }
340                     proxy = (*env)->NewObject(env, proxy_class, proxy_ctrID, type_proxy, isa);
341                     if (proxy == NULL || (*env)->ExceptionCheck(env)) {
342                         proxy_array = NULL;
343                     }
344                     (*env)->SetObjectArrayElement(env, proxy_array, index, proxy);
345                     if ((*env)->ExceptionCheck(env)) {
346                         proxy_array = NULL;
347                     }
348                     index++;
349                 }
350                 head = head->next;
351             }
352         }
353     }
354 
355 noproxy:
356     if (head != NULL) {
357         freeList(head);
358     }
359     if (proxy_info.lpszProxy != NULL)
360       GlobalFree(proxy_info.lpszProxy);
361     if (proxy_info.lpszProxyBypass != NULL)
362       GlobalFree(proxy_info.lpszProxyBypass);
363     if (ie_proxy_config.lpszAutoConfigUrl != NULL)
364       GlobalFree(ie_proxy_config.lpszAutoConfigUrl);
365     if (ie_proxy_config.lpszProxy != NULL)
366       GlobalFree(ie_proxy_config.lpszProxy);
367     if (ie_proxy_config.lpszProxyBypass != NULL)
368       GlobalFree(ie_proxy_config.lpszProxyBypass);
369     if (lpHost != NULL)
370       (*env)->ReleaseStringChars(env, host, lpHost);
371     if (lpProto != NULL)
372       (*env)->ReleaseStringChars(env, proto, lpProto);
373 
374     return proxy_array;
375 }
376