Fix upstream CONNECT handshake
This commit is contained in:
416
Program.cs
416
Program.cs
@@ -6,6 +6,7 @@ using System.Linq;
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Runtime.InteropServices.ComTypes;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@@ -156,18 +157,17 @@ class Program
|
|||||||
await upstream.ConnectAsync(proxyUri.Host, proxyUri.Port);
|
await upstream.ConnectAsync(proxyUri.Host, proxyUri.Port);
|
||||||
|
|
||||||
var upstreamStream = upstream.GetStream();
|
var upstreamStream = upstream.GetStream();
|
||||||
|
var connectResp = await PerformUpstreamConnectHandshake(
|
||||||
|
upstreamStream,
|
||||||
|
host,
|
||||||
|
port,
|
||||||
|
proxyUri.Host,
|
||||||
|
connectionId);
|
||||||
|
|
||||||
string connectReq =
|
int statusCode = ParseStatusCode(connectResp?.Raw);
|
||||||
$"CONNECT {host}:{port} HTTP/1.1\r\nHost: {host}:{port}\r\n{UPSTREAM_PROXY_AUTH_HEADER}\r\n";
|
if (connectResp == null || statusCode != 200)
|
||||||
|
|
||||||
await upstreamStream.WriteAsync(Encoding.ASCII.GetBytes(connectReq));
|
|
||||||
|
|
||||||
var resp = await ReadHeaders(upstreamStream, $"conn:{connectionId} upstream-connect-response");
|
|
||||||
|
|
||||||
int statusCode = ParseStatusCode(resp?.Raw);
|
|
||||||
if (resp == null || statusCode != 200)
|
|
||||||
{
|
{
|
||||||
var status = resp?.Raw?.Split("\r\n", StringSplitOptions.None).FirstOrDefault() ?? "<no response>";
|
var status = connectResp?.Raw?.Split("\r\n", StringSplitOptions.None).FirstOrDefault() ?? "<no response>";
|
||||||
Log.Error($"[conn:{connectionId}] upstream CONNECT rejected ({statusCode}): {status}");
|
Log.Error($"[conn:{connectionId}] upstream CONNECT rejected ({statusCode}): {status}");
|
||||||
await clientStream.WriteAsync(Encoding.ASCII.GetBytes("HTTP/1.1 502 Bad Gateway\r\n\r\n"));
|
await clientStream.WriteAsync(Encoding.ASCII.GetBytes("HTTP/1.1 502 Bad Gateway\r\n\r\n"));
|
||||||
return;
|
return;
|
||||||
@@ -220,12 +220,10 @@ class Program
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var buffer = new byte[8192];
|
var rawBytes = await ReadHeaderBytes(stream);
|
||||||
int read = await stream.ReadAsync(buffer);
|
if (rawBytes == null || rawBytes.Length == 0)
|
||||||
|
return null;
|
||||||
if (read <= 0) return null;
|
string raw = Encoding.ASCII.GetString(rawBytes);
|
||||||
|
|
||||||
string raw = Encoding.ASCII.GetString(buffer, 0, read);
|
|
||||||
|
|
||||||
var lines = raw.Split("\r\n");
|
var lines = raw.Split("\r\n");
|
||||||
var first = lines[0].Split(' ', StringSplitOptions.RemoveEmptyEntries);
|
var first = lines[0].Split(' ', StringSplitOptions.RemoveEmptyEntries);
|
||||||
@@ -240,7 +238,7 @@ class Program
|
|||||||
Method = first[0],
|
Method = first[0],
|
||||||
Target = first[1],
|
Target = first[1],
|
||||||
Raw = raw,
|
Raw = raw,
|
||||||
RawBytes = buffer.Take(read).ToArray(),
|
RawBytes = rawBytes,
|
||||||
HeaderLength = GetHeaderLength(raw)
|
HeaderLength = GetHeaderLength(raw)
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -264,6 +262,41 @@ class Program
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async Task<byte[]> ReadHeaderBytes(Stream stream)
|
||||||
|
{
|
||||||
|
const int maxHeaderBytes = 64 * 1024;
|
||||||
|
var data = new List<byte>(4096);
|
||||||
|
var chunk = new byte[2048];
|
||||||
|
|
||||||
|
while (data.Count < maxHeaderBytes)
|
||||||
|
{
|
||||||
|
int read = await stream.ReadAsync(chunk, 0, chunk.Length);
|
||||||
|
if (read <= 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
data.AddRange(chunk.AsSpan(0, read).ToArray());
|
||||||
|
if (ContainsHeaderTerminator(data))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return data.Count == 0 ? null : data.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ContainsHeaderTerminator(List<byte> data)
|
||||||
|
{
|
||||||
|
if (data.Count < 4) return false;
|
||||||
|
int n = data.Count;
|
||||||
|
for (int i = Math.Max(0, n - 4096); i <= n - 4; i++)
|
||||||
|
{
|
||||||
|
if (data[i] == (byte)'\r'
|
||||||
|
&& data[i + 1] == (byte)'\n'
|
||||||
|
&& data[i + 2] == (byte)'\r'
|
||||||
|
&& data[i + 3] == (byte)'\n')
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// ================= STREAM =================
|
// ================= STREAM =================
|
||||||
|
|
||||||
static async Task Pump(Stream from, Stream to, string context)
|
static async Task Pump(Stream from, Stream to, string context)
|
||||||
@@ -402,6 +435,355 @@ class Program
|
|||||||
return $"{domain}\\{user}";
|
return $"{domain}\\{user}";
|
||||||
return user ?? string.Empty;
|
return user ?? string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async Task<HttpRequest> PerformUpstreamConnectHandshake(
|
||||||
|
NetworkStream upstreamStream,
|
||||||
|
string targetHost,
|
||||||
|
int targetPort,
|
||||||
|
string proxyHost,
|
||||||
|
long connectionId)
|
||||||
|
{
|
||||||
|
// 1) First CONNECT without auth (browser-like flow).
|
||||||
|
await SendConnectRequest(upstreamStream, targetHost, targetPort, null);
|
||||||
|
var resp = await ReadHeaders(upstreamStream, $"conn:{connectionId} upstream-connect-response-1");
|
||||||
|
int code = ParseStatusCode(resp?.Raw);
|
||||||
|
if (code == 200 || resp == null)
|
||||||
|
return resp;
|
||||||
|
|
||||||
|
var challenges = ParseProxyAuthenticateValues(resp.Raw);
|
||||||
|
if (code != 407 || challenges.Count == 0)
|
||||||
|
return resp;
|
||||||
|
|
||||||
|
// 2) Basic fallback.
|
||||||
|
if (!string.IsNullOrEmpty(UPSTREAM_PROXY_AUTH_HEADER))
|
||||||
|
{
|
||||||
|
await SendConnectRequest(upstreamStream, targetHost, targetPort, UPSTREAM_PROXY_AUTH_HEADER.TrimEnd('\r', '\n'));
|
||||||
|
var basicResp = await ReadHeaders(upstreamStream, $"conn:{connectionId} upstream-connect-response-basic");
|
||||||
|
if (ParseStatusCode(basicResp?.Raw) == 200)
|
||||||
|
return basicResp;
|
||||||
|
resp = basicResp ?? resp;
|
||||||
|
challenges = ParseProxyAuthenticateValues(resp.Raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3) Negotiate/NTLM over SSPI (as browsers do for AD proxies).
|
||||||
|
bool hasNegotiate = challenges.Any(c => c.StartsWith("Negotiate", StringComparison.OrdinalIgnoreCase));
|
||||||
|
bool hasNtlm = challenges.Any(c => c.StartsWith("NTLM", StringComparison.OrdinalIgnoreCase));
|
||||||
|
if (!hasNegotiate && !hasNtlm)
|
||||||
|
return resp;
|
||||||
|
|
||||||
|
string scheme = hasNegotiate ? "Negotiate" : "NTLM";
|
||||||
|
using var sspi = new SspiProxyAuthenticator(hasNegotiate ? "Negotiate" : "NTLM", proxyHost);
|
||||||
|
string inToken = ExtractSchemeToken(challenges, scheme);
|
||||||
|
|
||||||
|
for (int step = 0; step < 4; step++)
|
||||||
|
{
|
||||||
|
string outToken = sspi.NextToken(inToken);
|
||||||
|
if (string.IsNullOrEmpty(outToken))
|
||||||
|
break;
|
||||||
|
|
||||||
|
await SendConnectRequest(
|
||||||
|
upstreamStream,
|
||||||
|
targetHost,
|
||||||
|
targetPort,
|
||||||
|
$"Proxy-Authorization: {scheme} {outToken}");
|
||||||
|
|
||||||
|
var authResp = await ReadHeaders(upstreamStream, $"conn:{connectionId} upstream-connect-response-{scheme}-{step + 1}");
|
||||||
|
int authCode = ParseStatusCode(authResp?.Raw);
|
||||||
|
if (authCode == 200)
|
||||||
|
return authResp;
|
||||||
|
if (authCode != 407 || authResp == null)
|
||||||
|
return authResp ?? resp;
|
||||||
|
|
||||||
|
challenges = ParseProxyAuthenticateValues(authResp.Raw);
|
||||||
|
inToken = ExtractSchemeToken(challenges, scheme);
|
||||||
|
resp = authResp;
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async Task SendConnectRequest(NetworkStream stream, string host, int port, string proxyAuthHeader)
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
sb.Append($"CONNECT {host}:{port} HTTP/1.1\r\n");
|
||||||
|
sb.Append($"Host: {host}:{port}\r\n");
|
||||||
|
sb.Append("Proxy-Connection: Keep-Alive\r\n");
|
||||||
|
if (!string.IsNullOrWhiteSpace(proxyAuthHeader))
|
||||||
|
sb.Append(proxyAuthHeader).Append("\r\n");
|
||||||
|
sb.Append("\r\n");
|
||||||
|
await stream.WriteAsync(Encoding.ASCII.GetBytes(sb.ToString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
static List<string> ParseProxyAuthenticateValues(string rawHeaders)
|
||||||
|
{
|
||||||
|
var result = new List<string>();
|
||||||
|
if (string.IsNullOrWhiteSpace(rawHeaders))
|
||||||
|
return result;
|
||||||
|
|
||||||
|
foreach (var line in rawHeaders.Split("\r\n", StringSplitOptions.None))
|
||||||
|
{
|
||||||
|
if (line.StartsWith("Proxy-Authenticate:", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
var value = line.Substring("Proxy-Authenticate:".Length).Trim();
|
||||||
|
if (value.Length > 0)
|
||||||
|
result.Add(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static string ExtractSchemeToken(List<string> values, string scheme)
|
||||||
|
{
|
||||||
|
foreach (var v in values)
|
||||||
|
{
|
||||||
|
if (!v.StartsWith(scheme, StringComparison.OrdinalIgnoreCase))
|
||||||
|
continue;
|
||||||
|
var parts = v.Split(' ', 2, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
if (parts.Length == 2)
|
||||||
|
return parts[1].Trim();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class SspiProxyAuthenticator : IDisposable
|
||||||
|
{
|
||||||
|
const int SECPKG_CRED_OUTBOUND = 2;
|
||||||
|
const int SECURITY_NATIVE_DREP = 0x00000010;
|
||||||
|
const int ISC_REQ_CONNECTION = 0x00000800;
|
||||||
|
const int SECBUFFER_VERSION = 0;
|
||||||
|
const int SECBUFFER_TOKEN = 2;
|
||||||
|
const uint SEC_I_CONTINUE_NEEDED = 0x00090312;
|
||||||
|
const uint SEC_E_OK = 0x00000000;
|
||||||
|
|
||||||
|
SecHandle _cred;
|
||||||
|
SecHandle _ctx;
|
||||||
|
bool _credAcquired;
|
||||||
|
bool _ctxInitialized;
|
||||||
|
readonly string _package;
|
||||||
|
readonly string _targetSpn;
|
||||||
|
|
||||||
|
public SspiProxyAuthenticator(string package, string proxyHost)
|
||||||
|
{
|
||||||
|
_package = package;
|
||||||
|
_targetSpn = $"HTTP/{proxyHost}";
|
||||||
|
AcquireCredentials();
|
||||||
|
}
|
||||||
|
|
||||||
|
public string NextToken(string incomingBase64Token)
|
||||||
|
{
|
||||||
|
byte[] inBytes = null;
|
||||||
|
if (!string.IsNullOrWhiteSpace(incomingBase64Token))
|
||||||
|
{
|
||||||
|
try { inBytes = Convert.FromBase64String(incomingBase64Token); }
|
||||||
|
catch { inBytes = null; }
|
||||||
|
}
|
||||||
|
|
||||||
|
var outBuffer = new SecBuffer
|
||||||
|
{
|
||||||
|
cbBuffer = 0,
|
||||||
|
BufferType = SECBUFFER_TOKEN,
|
||||||
|
pvBuffer = IntPtr.Zero
|
||||||
|
};
|
||||||
|
var outDesc = new SecBufferDesc();
|
||||||
|
IntPtr outBufPtr = Marshal.AllocHGlobal(Marshal.SizeOf<SecBuffer>());
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Marshal.StructureToPtr(outBuffer, outBufPtr, false);
|
||||||
|
outDesc.ulVersion = SECBUFFER_VERSION;
|
||||||
|
outDesc.cBuffers = 1;
|
||||||
|
outDesc.pBuffers = outBufPtr;
|
||||||
|
|
||||||
|
uint attrs;
|
||||||
|
FILETIME expiry;
|
||||||
|
uint status;
|
||||||
|
|
||||||
|
IntPtr inDescPtr = IntPtr.Zero;
|
||||||
|
IntPtr inBufPtr = IntPtr.Zero;
|
||||||
|
IntPtr inTokenPtr = IntPtr.Zero;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (inBytes != null && inBytes.Length > 0)
|
||||||
|
{
|
||||||
|
inTokenPtr = Marshal.AllocHGlobal(inBytes.Length);
|
||||||
|
Marshal.Copy(inBytes, 0, inTokenPtr, inBytes.Length);
|
||||||
|
var inBuf = new SecBuffer
|
||||||
|
{
|
||||||
|
cbBuffer = inBytes.Length,
|
||||||
|
BufferType = SECBUFFER_TOKEN,
|
||||||
|
pvBuffer = inTokenPtr
|
||||||
|
};
|
||||||
|
inBufPtr = Marshal.AllocHGlobal(Marshal.SizeOf<SecBuffer>());
|
||||||
|
Marshal.StructureToPtr(inBuf, inBufPtr, false);
|
||||||
|
|
||||||
|
var inDesc = new SecBufferDesc
|
||||||
|
{
|
||||||
|
ulVersion = SECBUFFER_VERSION,
|
||||||
|
cBuffers = 1,
|
||||||
|
pBuffers = inBufPtr
|
||||||
|
};
|
||||||
|
inDescPtr = Marshal.AllocHGlobal(Marshal.SizeOf<SecBufferDesc>());
|
||||||
|
Marshal.StructureToPtr(inDesc, inDescPtr, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_ctxInitialized)
|
||||||
|
{
|
||||||
|
status = InitializeSecurityContext(
|
||||||
|
ref _cred,
|
||||||
|
ref _ctx,
|
||||||
|
_targetSpn,
|
||||||
|
ISC_REQ_CONNECTION,
|
||||||
|
0,
|
||||||
|
SECURITY_NATIVE_DREP,
|
||||||
|
inDescPtr,
|
||||||
|
0,
|
||||||
|
out _ctx,
|
||||||
|
out outDesc,
|
||||||
|
out attrs,
|
||||||
|
out expiry);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
status = InitializeSecurityContext(
|
||||||
|
ref _cred,
|
||||||
|
IntPtr.Zero,
|
||||||
|
_targetSpn,
|
||||||
|
ISC_REQ_CONNECTION,
|
||||||
|
0,
|
||||||
|
SECURITY_NATIVE_DREP,
|
||||||
|
inDescPtr,
|
||||||
|
0,
|
||||||
|
out _ctx,
|
||||||
|
out outDesc,
|
||||||
|
out attrs,
|
||||||
|
out expiry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (inTokenPtr != IntPtr.Zero) Marshal.FreeHGlobal(inTokenPtr);
|
||||||
|
if (inBufPtr != IntPtr.Zero) Marshal.FreeHGlobal(inBufPtr);
|
||||||
|
if (inDescPtr != IntPtr.Zero) Marshal.FreeHGlobal(inDescPtr);
|
||||||
|
}
|
||||||
|
|
||||||
|
_ctxInitialized = true;
|
||||||
|
if (status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var finalOut = Marshal.PtrToStructure<SecBuffer>(outBufPtr);
|
||||||
|
if (finalOut.cbBuffer <= 0 || finalOut.pvBuffer == IntPtr.Zero)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var token = new byte[finalOut.cbBuffer];
|
||||||
|
Marshal.Copy(finalOut.pvBuffer, token, 0, token.Length);
|
||||||
|
FreeContextBuffer(finalOut.pvBuffer);
|
||||||
|
return Convert.ToBase64String(token);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Marshal.FreeHGlobal(outBufPtr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AcquireCredentials()
|
||||||
|
{
|
||||||
|
FILETIME expiry;
|
||||||
|
uint status = AcquireCredentialsHandle(
|
||||||
|
null,
|
||||||
|
_package,
|
||||||
|
SECPKG_CRED_OUTBOUND,
|
||||||
|
IntPtr.Zero,
|
||||||
|
IntPtr.Zero,
|
||||||
|
IntPtr.Zero,
|
||||||
|
IntPtr.Zero,
|
||||||
|
out _cred,
|
||||||
|
out expiry);
|
||||||
|
_credAcquired = status == SEC_E_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_ctxInitialized)
|
||||||
|
DeleteSecurityContext(ref _ctx);
|
||||||
|
if (_credAcquired)
|
||||||
|
FreeCredentialsHandle(ref _cred);
|
||||||
|
}
|
||||||
|
|
||||||
|
[DllImport("secur32.dll", CharSet = CharSet.Unicode, SetLastError = false)]
|
||||||
|
static extern uint AcquireCredentialsHandle(
|
||||||
|
string pszPrincipal,
|
||||||
|
string pszPackage,
|
||||||
|
int fCredentialUse,
|
||||||
|
IntPtr pvLogonId,
|
||||||
|
IntPtr pAuthData,
|
||||||
|
IntPtr pGetKeyFn,
|
||||||
|
IntPtr pvGetKeyArgument,
|
||||||
|
out SecHandle phCredential,
|
||||||
|
out FILETIME ptsExpiry);
|
||||||
|
|
||||||
|
[DllImport("secur32.dll", CharSet = CharSet.Unicode, SetLastError = false)]
|
||||||
|
static extern uint InitializeSecurityContext(
|
||||||
|
ref SecHandle phCredential,
|
||||||
|
IntPtr phContext,
|
||||||
|
string pszTargetName,
|
||||||
|
int fContextReq,
|
||||||
|
int Reserved1,
|
||||||
|
int TargetDataRep,
|
||||||
|
IntPtr pInput,
|
||||||
|
int Reserved2,
|
||||||
|
out SecHandle phNewContext,
|
||||||
|
out SecBufferDesc pOutput,
|
||||||
|
out uint pfContextAttr,
|
||||||
|
out FILETIME ptsExpiry);
|
||||||
|
|
||||||
|
[DllImport("secur32.dll", CharSet = CharSet.Unicode, SetLastError = false)]
|
||||||
|
static extern uint InitializeSecurityContext(
|
||||||
|
ref SecHandle phCredential,
|
||||||
|
ref SecHandle phContext,
|
||||||
|
string pszTargetName,
|
||||||
|
int fContextReq,
|
||||||
|
int Reserved1,
|
||||||
|
int TargetDataRep,
|
||||||
|
IntPtr pInput,
|
||||||
|
int Reserved2,
|
||||||
|
out SecHandle phNewContext,
|
||||||
|
out SecBufferDesc pOutput,
|
||||||
|
out uint pfContextAttr,
|
||||||
|
out FILETIME ptsExpiry);
|
||||||
|
|
||||||
|
[DllImport("secur32.dll", SetLastError = false)]
|
||||||
|
static extern uint DeleteSecurityContext(ref SecHandle phContext);
|
||||||
|
|
||||||
|
[DllImport("secur32.dll", SetLastError = false)]
|
||||||
|
static extern uint FreeCredentialsHandle(ref SecHandle phCredential);
|
||||||
|
|
||||||
|
[DllImport("secur32.dll", SetLastError = false)]
|
||||||
|
static extern uint FreeContextBuffer(IntPtr pvContextBuffer);
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
struct SecHandle
|
||||||
|
{
|
||||||
|
public IntPtr dwLower;
|
||||||
|
public IntPtr dwUpper;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
struct SecBuffer
|
||||||
|
{
|
||||||
|
public int cbBuffer;
|
||||||
|
public int BufferType;
|
||||||
|
public IntPtr pvBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
struct SecBufferDesc
|
||||||
|
{
|
||||||
|
public int ulVersion;
|
||||||
|
public int cBuffers;
|
||||||
|
public IntPtr pBuffers;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ================= WINHTTP =================
|
// ================= WINHTTP =================
|
||||||
|
|||||||
2
run.bat
2
run.bat
@@ -12,12 +12,10 @@ if not exist "%EXE%" (
|
|||||||
set "UPSTREAM_PROXY=proxy.syktsu.ru:3128"
|
set "UPSTREAM_PROXY=proxy.syktsu.ru:3128"
|
||||||
set "UPSTREAM_USER=%USERNAME%"
|
set "UPSTREAM_USER=%USERNAME%"
|
||||||
set "UPSTREAM_PASS="
|
set "UPSTREAM_PASS="
|
||||||
set "UPSTREAM_ALLOW_EMPTY_PASSWORD=0"
|
|
||||||
|
|
||||||
echo [run] UPSTREAM_PROXY=%UPSTREAM_PROXY%
|
echo [run] UPSTREAM_PROXY=%UPSTREAM_PROXY%
|
||||||
echo [run] UPSTREAM_USER=%UPSTREAM_USER%
|
echo [run] UPSTREAM_USER=%UPSTREAM_USER%
|
||||||
echo [run] UPSTREAM_PASS is empty
|
echo [run] UPSTREAM_PASS is empty
|
||||||
echo [run] UPSTREAM_ALLOW_EMPTY_PASSWORD=%UPSTREAM_ALLOW_EMPTY_PASSWORD%
|
|
||||||
echo [run] Starting proxy...
|
echo [run] Starting proxy...
|
||||||
|
|
||||||
"%EXE%"
|
"%EXE%"
|
||||||
|
|||||||
Reference in New Issue
Block a user