[ https://issues.apache.org/jira/browse/NETBEANS-96?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=16239514#comment-16239514 ]
ASF GitHub Bot commented on NETBEANS-96: ---------------------------------------- lbruun commented on a change in pull request #161: [NETBEANS-96] New PAC Script evaluation environment URL: https://github.com/apache/incubator-netbeans/pull/161#discussion_r148956417 ########## File path: core.network/src/org/netbeans/core/network/proxy/pac/PacUtils.java ########## @@ -0,0 +1,357 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.netbeans.core.network.proxy.pac; + +import java.math.BigInteger; +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.URI; +import java.net.UnknownHostException; +import java.util.Arrays; +import java.util.List; +import java.util.function.Function; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import org.netbeans.core.network.utils.IpAddressUtils; +import org.netbeans.core.network.utils.SimpleObjCache; + +/** + * Methods and constants useful in PAC script evaluation. + * + * @see org.netbeans.network.proxy.pac.datetime.PacUtilsDateTime + * + * @author lbruun + */ +public class PacUtils { + + /** + * Size of the cache used for precompiled GLOBs. + */ + public static final int PRECOMPILED_GLOB_CACHE_MAX_ITEMS = 10; + private static final SimpleObjCache<String, Pattern> PRECOMPILED_GLOB_CACHE + = new SimpleObjCache<>(PRECOMPILED_GLOB_CACHE_MAX_ITEMS); + + + + + /** + * Translate a GLOB pattern into a RegExp pattern. GLOB patterns originate + * from Unix hosts where they are primarily used for file pattern matching. + * In the original PAC specification from Netscape a GLOB pattern is + * referred to as a 'shell expression'. + * + * <p> + * This method supports all GLOB wildcards, such as + * <table border="0" style="order-collapse: separate;border-spacing: 50px 0;" summary=""> + * <tr align="left"><td>{@code *}</td><td>matches any number of any + * characters including none</td> + * <tr align="left"><td>{@code ?}</td><td>matches any single character</td> + * <tr align="left"><td>{@code [abc]}</td><td>matches one character given in + * the bracket</td> + * <tr align="left"><td>{@code [a-z]}</td><td>matches one character from the + * range given in the bracket</td> + * <tr align="left"><td>{@code [!abc]}</td><td>matches one character + * <i>not</i> given in the bracket</td> + * <tr align="left"><td>{@code [!a-z]}</td><td>matches one character + * <i>not</i> from the range given in the bracket</td> + * </table> + * + * <p> + * A small cache is used so that if a glob pattern has already been + * translated previously, the result from the cache will be returned. + * + * @param glob + * @return + */ + public static Pattern createRegexPatternFromGlob(String glob) { + + // First try the cache + Pattern pattern = PRECOMPILED_GLOB_CACHE.get(glob); + if (pattern != null) { + return pattern; + } + + StringBuilder out = new StringBuilder(); + out.append("^"); + for (int i = 0; i < glob.length(); ++i) { + final char c = glob.charAt(i); + switch (c) { + case '*': + out.append(".*?"); + break; + case '?': + out.append(".{1}"); + break; + case '.': + out.append("\\."); + break; + case '\\': + out.append("\\\\"); + break; + case '!': + if (i > 0 && glob.charAt(i - 1) == '[') { + out.append('^'); + } else { + out.append(c); + } + break; + default: + out.append(c); + } + } + out.append("$"); + pattern = Pattern.compile(out.toString()); + PRECOMPILED_GLOB_CACHE.put(glob, pattern); + return pattern; + } + + + + /** + * Converts list into semi-colon separated string where each element + * is represented by the result of the {@code fn} function. + * + * @param <T> + * @param list list of objects + * @param fn function which returns string + * @return string with elements separated by semi-colon + */ + public static <T> String toSemiColonList(List<T> list, Function<T,String> fn) { + return list.stream() + .map(i -> fn.apply(i)) + .collect(Collectors.joining(";")); + } + + /** + * Converts list into semi-colon separated string where each element + * is represented by the result of {@link Object#toString()}. + * + * @see #toSemiColonList(java.util.List, java.util.function.Function) + * @param <T> + * @param list list of objects + * @return string with elements separated by semi-colon + */ + public static <T> String toSemiColonList(List<T> list) { + return toSemiColonList(list, Object::toString); + } + + /** + * Converts a list of {@code InetAddress} into a semi-colon + * separated string. Each address is represented by the result + * of {@link InetAddress#getHostAddress()}. + * + * @see #toSemiColonList(java.util.List, java.util.function.Function) + * @param addresses + * @return semi-colon separated string of addresses in literal form + */ + public static String toSemiColonListInetAddress(InetAddress[] addresses) { + return toSemiColonList(Arrays.asList(addresses), InetAddress::getHostAddress); + } + + /** + * Converts an array of {@code InetAddress} into a semi-colon + * separated string. Each address is represented by the result + * of {@link InetAddress#getHostAddress()}. + * + * @see #toSemiColonList(java.util.List, java.util.function.Function) + * @param addresses + * @return semi-colon separated string of addresses in literal form + */ + public static String toSemiColonListInetAddress(List<InetAddress> addresses) { + return toSemiColonList(addresses, InetAddress::getHostAddress); + } + + + /** + * Checks if an IP address matches a given CIDR pattern. + * + * The pattern must use the format: + * <pre> + * ipLiteral/bitField + * </pre> + * + * Examples of valid patterns: + * <pre> + * 198.95.249.79/32 + * 198.95.0.0/16 + * 3ffe:8311:ffff/48 + * </pre> + * + * <p> + * For IPv6 the {@code ipLiteral} is allowed to be incomplete at the end. If + * not complete, then a suffix of "::" is appended to the literal, e.g. the + * method will translate {@code "3ffe:8311:ffff/48"} to + * {@code "3ffe:8311:ffff::/48"}. + * + * <p> + * A number of validation checks are carried out on the {@code ipPrefix} + * argument and {@code false} will be returned if these checks fails: + * <ul> + * <li>If {@code ipAddress} is IPv4 then {@code bitField} + * must be between 8 and 32. + * </li> + * <li>If {@code ipAddress} is IPv6 then {@code bitField} + * must be between 8 and 128. + * </li> + * <li>The {@code ipLiteral} value must be a valid IPv4 literal or IPv6 literal. + * </li> + * <li>The IP protocol type of {@code ipLiteral} value must match the IP protocol + * type of {@code ipAddress}. + * </li> + * </ul> + * + * <p> + * Note: The method was developed for the purpose of supporting the + * {@link PacHelperMethodsMicrosoft#isInNetEx(java.lang.String, java.lang.String) + * Microsoft isInNetEx()} extension to PAC scripting, but the method may + * have an appeal broader than this particular use case. + * + * <br><br> + * + * @param ipAddress address + * @param ipPrefix pattern + * @return true if the address, {@code ipAddress}, match the pattern, {@code ipPrefix}. + */ + public static boolean ipPrefixMatch(InetAddress ipAddress, String ipPrefix) { + if (ipPrefix.indexOf('/') == -1) { + return false; + } + String[] parts = ipPrefix.trim().split("\\/"); + + int bitField = 0; + try { + bitField = Integer.parseInt(parts[1].trim()); + // bitField < 8 doesn't make much sense in my book + if (!(bitField >= 8 && bitField <= 128)) { Review comment: Well, values below 8 become a bit academic, but you are are right. There's no technical problem in allowing it, so will do. ---------------------------------------------------------------- This is an automated message from the Apache Git Service. To respond to the message, please log on GitHub and use the URL above to go to the specific comment. For queries about this service, please contact Infrastructure at: us...@infra.apache.org > New PAC Script evaluator > ------------------------ > > Key: NETBEANS-96 > URL: https://issues.apache.org/jira/browse/NETBEANS-96 > Project: NetBeans > Issue Type: Improvement > Reporter: lbruun > Labels: pull-request-available > > The current [PAC script|https://en.wikipedia.org/wiki/Proxy_auto-config] > evaluator (in {{core.network}}) was developed pre-Nashorn and has a few > problems: > * It simply fails with Nashorn - but not with Rhino - if the downloaded > script uses {{isInNet()}}. This was reported in [Bug > 245116|https://netbeans.org/bugzilla/show_bug.cgi?id=245116]. It fails > silently in this case and defaults to no proxy. The user will never know the > reason - not even by looking in the message log - that there was an error. > * It doesn't implement two mandatory JavaScript helper methods, > {{dnsResolve()}} and {{myIpAddress()}}. This is a known issue. This causes > many PAC scripts to silently fail. > * It doesn't implement Microsoft's IPv6-aware additions to the PAC standard. > This is a problem in MS shops because they will have designed their PAC > script to be compatible with MS IE and MS Edge (which unsurprisingly support > these functions .. as do Chrome). > * It uses a small JavaScript helper, {{nsProxyAutoConfig.js}}, which uses a > license which is not compatible with Apache. This is described in NETBEANS-4. > * Isn't executing the downloaded PAC script in a sandboxed environment. (The > PAC script should be treated as hostile because the download may have been > spoofed. Browsers indeed treat the PAC script as hostile and so should > NetBeans). > Pull Request with a new implementation is on its way. -- This message was sent by Atlassian JIRA (v6.4.14#64029)