Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions omega-pac/src/profiles.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,23 @@ module.exports = exports =
port: port
}

normalizeProxyHost: (host) ->
return host unless typeof host == 'string'
return host unless /[^\x00-\x7f]/.test(host)
try
normalized = new URL('http://' + host).hostname
return normalized if normalized
catch _
return host
host

pacResult: (proxy) ->
if proxy
host = exports.normalizeProxyHost(proxy.host)
if checkNeedFixForSocks5(proxy)
"SOCKS5 #{proxy.host}:#{proxy.port}; SOCKS #{proxy.host}:#{proxy.port}"
"SOCKS5 #{host}:#{proxy.port}; SOCKS #{host}:#{proxy.port}"
else
"#{exports.pacProtocols[proxy.scheme]} #{proxy.host}:#{proxy.port}"
"#{exports.pacProtocols[proxy.scheme]} #{host}:#{proxy.port}"
else
'DIRECT'

Expand Down
4 changes: 4 additions & 0 deletions omega-pac/test/profiles.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ describe 'Profiles', ->
it 'should return a valid PAC result for a proxy', ->
proxy = {scheme: "http", host: "127.0.0.1", port: 8888}
Profiles.pacResult(proxy).should.equal("PROXY 127.0.0.1:8888")
it 'should normalize IDN proxy hosts to ASCII', ->
proxy = {scheme: "https", host: "\u30b7.\u30b7", port: 443}
Profiles.pacResult(proxy).should.equal("HTTPS xn--xck.xn--xck:443")
proxy.host.should.equal("\u30b7.\u30b7")
it 'should return special compatible result for SOCKS5', ->
proxy = {scheme: "socks5", host: "127.0.0.1", port: 8888}
compatibleResult = "SOCKS5 127.0.0.1:8888; SOCKS 127.0.0.1:8888"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ FindProxyForURL = (function () {
if (proxy && !state.useLegacyStringReturn) {
var proxyInfo = {
type: proxy.scheme,
host: proxy.host,
host: OmegaPac.Profiles.normalizeProxyHost(proxy.host),
port: proxy.port,
};
if (proxyInfo.type === 'socks5') {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ module.exports = class ProxyAuth
)
@listening = true

_keyForProxy: (proxy) -> "#{proxy.host.toLowerCase()}:#{proxy.port}"
_keyForProxy: (proxy) ->
host = OmegaPac.Profiles.normalizeProxyHost(proxy.host)
"#{host.toLowerCase()}:#{proxy.port}"
setProxies: (profiles) ->
@_proxies = {}
@_fallbacks = []
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
OmegaTarget = require('omega-target')
OmegaPac = OmegaTarget.OmegaPac
# The browser only accepts native promises as onRequest return values.
# DO NOT USE Bluebird Promises here!
NativePromise = Promise ? null
Expand Down Expand Up @@ -96,15 +97,15 @@ class FirefoxProxyImpl extends ProxyImpl
proxyInfo: (proxy, auth) ->
proxyInfo =
type: proxy.scheme
host: proxy.host
host: OmegaPac.Profiles.normalizeProxyHost(proxy.host)
port: proxy.port
if proxyInfo.type == 'socks5'
# MOZ: SOCKS5 proxies should be specified as "type": "socks".
# https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/proxy/ProxyInfo
# See MDN WebExtensions proxy.ProxyInfo.
proxyInfo.type = 'socks'
if auth
# Username & password here are only available for SOCKS5.
# https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/proxy/ProxyInfo
# See MDN WebExtensions proxy.ProxyInfo.
# HTTP proxy auth must be handled via webRequest.onAuthRequired.
proxyInfo.username = auth.username
proxyInfo.password = auth.password
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
OmegaTarget = require('omega-target')
OmegaPac = OmegaTarget.OmegaPac
# The browser only accepts native promises as onRequest return values.
# DO NOT USE Bluebird Promises here!
NativePromise = Promise ? null
Expand Down Expand Up @@ -60,15 +61,15 @@ class ListenerProxyImpl extends ProxyImpl
proxyInfo: (proxy, auth) ->
proxyInfo =
type: proxy.scheme
host: proxy.host
host: OmegaPac.Profiles.normalizeProxyHost(proxy.host)
port: proxy.port
if proxyInfo.type == 'socks5'
# MOZ: SOCKS5 proxies should be specified as "type": "socks".
# https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/proxy/ProxyInfo
# See MDN WebExtensions proxy.ProxyInfo.
proxyInfo.type = 'socks'
if auth
# Username & password here are only available for SOCKS5.
# https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/proxy/ProxyInfo
# See MDN WebExtensions proxy.ProxyInfo.
# HTTP proxy auth must be handled via webRequest.onAuthRequired.
proxyInfo.username = auth.username
proxyInfo.password = auth.password
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,21 +81,22 @@ class SettingsProxyImpl extends ProxyImpl
protocols = ['proxyForHttp', 'proxyForHttps', 'proxyForFtp']
protocolProxySet = false
for protocol in protocols when profile[protocol]?
rules[protocol] = profile[protocol]
rules[protocol] = @_normalizeProxyHost(profile[protocol])
protocolProxySet = true

if profile.fallbackProxy
fallbackProxy = @_normalizeProxyHost(profile.fallbackProxy)
if profile.fallbackProxy.scheme == 'http'
# Chromium does not allow HTTP proxies in 'fallbackProxy'.
if not protocolProxySet
# Use 'singleProxy' if no proxy is configured for other protocols.
rules['singleProxy'] = profile.fallbackProxy
rules['singleProxy'] = fallbackProxy
else
# Try to set the proxies of all possible protocols.
for protocol in protocols
rules[protocol] ?= JSON.parse(JSON.stringify(profile.fallbackProxy))
rules[protocol] ?= JSON.parse(JSON.stringify(fallbackProxy))
else
rules['fallbackProxy'] = profile.fallbackProxy
rules['fallbackProxy'] = fallbackProxy
else if not protocolProxySet
config['mode'] = 'direct'

Expand All @@ -105,6 +106,12 @@ class SettingsProxyImpl extends ProxyImpl
bypassList.push(@_formatBypassItem(condition))
config['rules'] = rules
return config
_normalizeProxyHost: (proxy) ->
host = OmegaPac.Profiles.normalizeProxyHost(proxy.host)
return proxy if host == proxy.host
proxy = JSON.parse(JSON.stringify(proxy))
proxy.host = host
proxy
_formatBypassItem: (condition) ->
str = OmegaPac.Conditions.str(condition)
i = str.indexOf(' ')
Expand Down
57 changes: 57 additions & 0 deletions omega-target-chromium-extension/test/proxy_hosts.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
chai = require 'chai'
should = chai.should()

describe 'Proxy host normalization', ->
SettingsProxyImpl =
require '../src/module/proxy/proxy_impl_settings'
ListenerProxyImpl =
require '../src/module/proxy/proxy_impl_listener'
FirefoxProxyImpl =
require '../src/module/proxy/proxy_impl_firefox'
ProxyAuth = require '../src/module/proxy/proxy_auth'

idnHost = '\u30b7.\u30b7'
idnHostAscii = 'xn--xck.xn--xck'
idnFallbackHost = 'b\u00fccher.example'
idnFallbackHostAscii = 'xn--bcher-kva.example'

it 'should normalize fixed server proxy hosts without mutating profiles', ->
log =
error: -> null
impl = new SettingsProxyImpl(log)
profile =
profileType: 'FixedProfile'
bypassList: []
proxyForHttp:
scheme: 'http'
host: idnHost
port: 8080
fallbackProxy:
scheme: 'socks5'
host: idnFallbackHost
port: 1080

config = impl._fixedProfileConfig(profile)

config.rules.proxyForHttp.host.should.equal(idnHostAscii)
config.rules.fallbackProxy.host.should.equal(idnFallbackHostAscii)
profile.proxyForHttp.host.should.equal(idnHost)
profile.fallbackProxy.host.should.equal(idnFallbackHost)

it 'should normalize listener proxy info hosts', ->
proxy =
scheme: 'https'
host: idnHost
port: 8443

ListenerProxyImpl::proxyInfo(proxy)[0].host.should.equal(idnHostAscii)
FirefoxProxyImpl::proxyInfo(proxy)[0].host.should.equal(idnHostAscii)

it 'should normalize proxy auth lookup keys', ->
log =
error: -> null
log: -> null
auth = new ProxyAuth(log)
key = auth._keyForProxy(host: idnHost, port: 3128)

key.should.equal(idnHostAscii + ':3128')