diff options
author | Kenneth Skovhede <kenneth@hexad.dk> | 2017-07-04 00:23:39 +0300 |
---|---|---|
committer | Kenneth Skovhede <kenneth@hexad.dk> | 2017-07-04 00:23:39 +0300 |
commit | 36cccd9776c7a554008a94c01e941c9bcb136185 (patch) | |
tree | 4bc59c9dd8cee153eeda76a8325484d3e51c4542 /Tools | |
parent | 9394e7e3b7f6d72019a2fb647f1668505faa5fdf (diff) | |
parent | 396ea160dbb969dc8fbadd666fa45775939eb119 (diff) |
Merge branch 'feature/synology_ngix_proxy'
Diffstat (limited to 'Tools')
-rw-r--r-- | Tools/CGIProxyHandler/CGIProxyHandler.csproj | 33 | ||||
-rw-r--r-- | Tools/CGIProxyHandler/CGIProxyHandler.sln | 17 | ||||
-rw-r--r-- | Tools/CGIProxyHandler/Program.cs | 427 | ||||
-rw-r--r-- | Tools/CGIProxyHandler/Properties/AssemblyInfo.cs | 27 |
4 files changed, 0 insertions, 504 deletions
diff --git a/Tools/CGIProxyHandler/CGIProxyHandler.csproj b/Tools/CGIProxyHandler/CGIProxyHandler.csproj deleted file mode 100644 index 1939f37ec..000000000 --- a/Tools/CGIProxyHandler/CGIProxyHandler.csproj +++ /dev/null @@ -1,33 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <PropertyGroup> - <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> - <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> - <ProjectGuid>{EA4505A7-3BDE-4057-BD90-94808D649570}</ProjectGuid> - <OutputType>Exe</OutputType> - <Prefer32Bit>False</Prefer32Bit> - <RootNamespace>CGIProxyHandler</RootNamespace> - <AssemblyName>CGIProxyHandler</AssemblyName> - <TargetFrameworkVersion>v4.5</TargetFrameworkVersion> - </PropertyGroup> - <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> - <Optimize>false</Optimize> - <OutputPath>bin\Debug</OutputPath> - <WarningLevel>4</WarningLevel> - <DebugSymbols>true</DebugSymbols> - <DefineConstants>DEBUG</DefineConstants> - </PropertyGroup> - <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> - <Optimize>true</Optimize> - <OutputPath>bin\Release</OutputPath> - <WarningLevel>4</WarningLevel> - </PropertyGroup> - <ItemGroup> - <Reference Include="System" /> - </ItemGroup> - <ItemGroup> - <Compile Include="Program.cs" /> - <Compile Include="Properties\AssemblyInfo.cs" /> - </ItemGroup> - <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> -</Project>
\ No newline at end of file diff --git a/Tools/CGIProxyHandler/CGIProxyHandler.sln b/Tools/CGIProxyHandler/CGIProxyHandler.sln deleted file mode 100644 index dec62370f..000000000 --- a/Tools/CGIProxyHandler/CGIProxyHandler.sln +++ /dev/null @@ -1,17 +0,0 @@ -
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 2012
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CGIProxyHandler", "CGIProxyHandler.csproj", "{EA4505A7-3BDE-4057-BD90-94808D649570}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Any CPU = Debug|Any CPU
- Release|Any CPU = Release|Any CPU
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {EA4505A7-3BDE-4057-BD90-94808D649570}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {EA4505A7-3BDE-4057-BD90-94808D649570}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {EA4505A7-3BDE-4057-BD90-94808D649570}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {EA4505A7-3BDE-4057-BD90-94808D649570}.Release|Any CPU.Build.0 = Release|Any CPU
- EndGlobalSection
-EndGlobal
diff --git a/Tools/CGIProxyHandler/Program.cs b/Tools/CGIProxyHandler/Program.cs deleted file mode 100644 index dc423699b..000000000 --- a/Tools/CGIProxyHandler/Program.cs +++ /dev/null @@ -1,427 +0,0 @@ -using System; -using System.Diagnostics; -using System.Threading.Tasks; -using System.Text.RegularExpressions; -using System.Linq; -using System.IO; -using System.Collections.Generic; -using System.Threading; -using System.Runtime.InteropServices; - -namespace CGIProxyHandler -{ - class MainClass - { - private class LogHandler - { - private object m_lock = new object(); - private StreamWriter m_stream; - private bool m_debug; - - public LogHandler(StreamWriter writer, bool debug) - { - m_stream = writer; - m_debug = debug; - } - - public void WriteMessage(string msg) - { - if (m_stream != null) - lock(m_lock) - m_stream.WriteLine(msg); - } - - public void WriteDebugMessage(string msg) - { - if (m_stream != null && m_debug) - lock(m_lock) - m_stream.WriteLine(msg); - } - - } - - /// <summary> - /// Runs an external command - /// </summary> - /// <returns>The stdout data.</returns> - /// <param name="command">The executable</param> - /// <param name="args">The executable and the arguments.</param> - /// <param name="shell">If set to <c>true</c> use the shell context for execution.</param> - /// <param name="exitcode">Set the value to check for a particular exitcode.</param> - private static async Task<string> ShellExec(string command, string args = null, bool shell = false, int exitcode = -1, Dictionary<string, string> env = null) - { - var psi = new ProcessStartInfo() { - FileName = command, - Arguments = shell ? null : args, - UseShellExecute = false, - RedirectStandardInput = shell, - RedirectStandardOutput = true - }; - - if (env != null) - foreach (var pk in env) - psi.EnvironmentVariables[pk.Key] = pk.Value; - - using (var p = Process.Start(psi)) - { - if (shell && args != null) - await p.StandardInput.WriteLineAsync(args); - - var res = await p.StandardOutput.ReadToEndAsync(); - - p.WaitForExit((int)TimeSpan.FromSeconds(5).TotalMilliseconds); - - if (p.ExitCode != exitcode && exitcode != -1) - throw new Exception(string.Format("Exit code was: {0}, stdout: {1}", p.ExitCode, res)); - return res; - } - } - - private static string GetEnvArg(string key, string @default = null) - { - var res = Environment.GetEnvironmentVariable(key); - return string.IsNullOrWhiteSpace(res) ? @default : res.Trim(); - } - - private const string CRLF = "\r\n"; - private static readonly byte[] ERROR_MESSAGE = System.Text.Encoding.ASCII.GetBytes("Status: 500 Server error" + CRLF + CRLF); - - public static void Main(string[] args) - { - var debug = GetEnvArg("PROXY_DEBUG", "0") == "1"; - var logfile = GetEnvArg("PROXY_LOGFILE", "/var/log/duplicat-proxy.log"); - - using (var logout = new StreamWriter(File.Open(logfile, System.IO.FileMode.Append, FileAccess.Write, FileShare.ReadWrite))) - using(var stdout = Console.OpenStandardOutput()) - { - if (logout != null) - logout.AutoFlush = true; - - - var logger = new LogHandler(logout, debug); - - try - { - //stdout.WriteTimeout = (int)ACTIVITY_TIMEOUT.TotalMilliseconds; - logger.WriteDebugMessage("Started!"); - logger.WriteDebugMessage(string.Format("Processing request for target url: {0}", GetEnvArg("HTTP_X_PROXY_PATH"))); - - logger.WriteDebugMessage(string.Format("Redirects: {0},{1},{2}", Console.IsInputRedirected, Console.IsOutputRedirected, Console.IsErrorRedirected)); - - if (args == null) - args = new string[0]; - - foreach(var a in args) - logger.WriteDebugMessage(string.Format("arg: {0}", a)); - - Run(args, logger, stdout).Wait(); - } - catch (Exception ex) - { - var rex = ex; - if (rex is AggregateException && (rex as AggregateException).Flatten().InnerExceptions.Count == 1) - rex = (rex as AggregateException).Flatten().InnerExceptions.First(); - - if (debug) - logger.WriteMessage(string.Format("Failed: {0}", rex)); - else - logger.WriteMessage(string.Format("Failed: {0}", rex.Message)); - - try - { - stdout.Write(ERROR_MESSAGE, 0, ERROR_MESSAGE.Length); - } - catch (Exception ex2) - { - logger.WriteDebugMessage(string.Format("Failed to set error status: {0}", ex2)); - } - } - } - } - - - private static async Task Run(string[] args, LogHandler logger, Stream stdout) - { - var login_cgi = GetEnvArg("SYNO_LOGIN_CGI", "/usr/syno/synoman/webman/login.cgi"); - var auth_cgi = GetEnvArg("SYNO_AUTHENTICATE_CGI", "/usr/syno/synoman/webman/modules/authenticate.cgi"); - var admin_only = !(GetEnvArg("SYNO_ALL_USERS", "0") == "1"); - var auto_xsrf = GetEnvArg("SYNO_AUTO_XSRF", "1") == "1"; - var skip_auth = GetEnvArg("SYNO_SKIP_AUTH", "0") == "1"; - var query_string = GetEnvArg("QUERY_STRING", ""); - var proxy_host = GetEnvArg("PROXY_HOST", "localhost"); - var proxy_port = GetEnvArg("PROXY_PORT", "8200"); - - var xsrftoken = GetEnvArg("HTTP_X_SYNO_TOKEN"); - if (string.IsNullOrWhiteSpace(xsrftoken) && !string.IsNullOrWhiteSpace(query_string)) - { - // Avoid loading a library just for parsing the token - var tkre = new Regex(@"SynoToken=(<?token>[^&+])"); - var m = tkre.Match(query_string); - if (m.Success) - xsrftoken = m.Groups["token"].Value; - } - - if (!skip_auth) - { - if (string.IsNullOrWhiteSpace(xsrftoken) && auto_xsrf) - { - var authre = new Regex(@"""SynoToken""\s?\:\s?""(?<token>[^""]+)"""); - var resp = await ShellExec(login_cgi); - - logger.WriteDebugMessage(string.Format("xsrf response is: {0}", resp)); - - var m = authre.Match(resp); - if (m.Success) - xsrftoken = m.Groups["token"].Value; - else - throw new Exception("Unable to get XSRF token"); - } - - var tmpenv = new Dictionary<string, string>(); - tmpenv["QUERY_STRING"] = "SynoToken=" + xsrftoken; - - var username = GetEnvArg("SYNO_USERNAME"); - - if (string.IsNullOrWhiteSpace(username)) - { - username = await ShellExec(auth_cgi, shell: false, exitcode: 0, env: tmpenv); - logger.WriteDebugMessage(string.Format("Username: {0}", username)); - } - - if (string.IsNullOrWhiteSpace(username)) - throw new Exception("Not logged in"); - - username = username.Trim(); - - if (admin_only) - { - var groups = GetEnvArg("SYNO_GROUP_IDS"); - - if (string.IsNullOrWhiteSpace(groups)) - groups = await ShellExec("id", "-G '" + username.Trim().Replace("'", "\\'") + "'", exitcode: 0) ?? ""; - if (!groups.Split(new char[] { ' ' }).Contains("101")) - throw new Exception(string.Format("User {0} is not an admin", username)); - - logger.WriteDebugMessage("User is admin"); - } - } - - var path = GetEnvArg("HTTP_X_PROXY_PATH"); - if (string.IsNullOrWhiteSpace(path)) - { - var xpre = new Regex(@"x-proxy-path=(<?url>[^&+])"); - var m = xpre.Match(query_string); - if (m.Success) - path = Uri.UnescapeDataString(m.Groups["url"].Value); - } - - logger.WriteDebugMessage(string.Format("Path is {0} and query string is {1}", path, query_string)); - - if (string.IsNullOrWhiteSpace(path) || !path.StartsWith("/")) - throw new Exception("Invalid path requested"); - - if (!string.IsNullOrWhiteSpace(query_string)) - path += (query_string.StartsWith("?") ? "" : "?") + Uri.EscapeUriString(query_string); - - int port; - if (!int.TryParse(proxy_port, out port)) - port = 8200; - - logger.WriteDebugMessage(string.Format("About to connect to {0}:{1}", proxy_host, port)); - - using (var client = new System.Net.Sockets.TcpClient()) - { - logger.WriteDebugMessage(string.Format("Connecting to {0}:{1}", proxy_host, port)); - client.Connect(proxy_host, port); - logger.WriteDebugMessage("Connected"); - - using (var ns = client.GetStream()) - { - logger.WriteDebugMessage("Opened TCP stream"); - - using (var sw = new StreamWriter(ns)) - { - logger.WriteDebugMessage("Created StreamWriter"); - - //await ForwardRequest(sw, path, logout); - //await ForwardResponse(ns, stdout, logout); - - await Task.WhenAll( - ForwardRequest(sw, path, logger), - ForwardResponse(ns, stdout, logger) - ); - - logger.WriteDebugMessage("Done processing"); - } - } - } - } - - private static readonly byte[] STATUS_PREFIX = System.Text.Encoding.ASCII.GetBytes("Status: "); - private static readonly int HTTP_HEAD_LEN = "HTTP/1.1 ".Length; - - - private static async Task ForwardResponse(Stream source, Stream target, LogHandler logger) - { - var buf = new byte[8 * 1024]; - int r = 0; - int offset = 0; - var lastmatch = 0; - var status = false; - long contentlength = -1; - var canceltoken = new CancellationTokenSource(); - - logger.WriteDebugMessage("Forward response"); - - while ((r = await source.ReadAsync(buf, offset, buf.Length - offset, canceltoken.Token)) != 0) - { - logger.WriteDebugMessage(string.Format("Read {0} bytes", r)); - - offset += r; - var ix = Array.IndexOf(buf, (byte)13, 0, offset); - - while (ix >= 0 && ix < offset - 1) - { - if (buf[ix + 1] == 10) - { - if (!status) - { - status = true; - logger.WriteDebugMessage("Writing: Status: " + System.Text.Encoding.ASCII.GetString(buf, lastmatch + HTTP_HEAD_LEN, ix - lastmatch - HTTP_HEAD_LEN)); - - await target.WriteAsync(STATUS_PREFIX, 0, STATUS_PREFIX.Length, canceltoken.Token); - await target.WriteAsync(buf, lastmatch + HTTP_HEAD_LEN, (ix - lastmatch - HTTP_HEAD_LEN) + 2, canceltoken.Token); - - logger.WriteDebugMessage("Wrote status line"); - } - else - { - // Blank line and we are done - if (ix - lastmatch == 0) - { - logger.WriteDebugMessage(string.Format("Completed header, writing remaining {0} bytes", offset - lastmatch)); - - await target.WriteAsync(buf, lastmatch, offset - lastmatch, canceltoken.Token); - - // Adjust remaining data length - if (contentlength > 0) - contentlength -= offset - lastmatch - 2; - - - logger.WriteDebugMessage(string.Format("Body has remaining {0} bytes", contentlength)); - - while(contentlength > 0) - { - r = await source.ReadAsync(buf, 0, (int)Math.Min(buf.Length, contentlength), canceltoken.Token); - if (r == 0) - break; - - contentlength -= r; - - - await target.WriteAsync(buf, 0, r, canceltoken.Token); - - logger.WriteDebugMessage(string.Format("Body has remaining {0} bytes", contentlength)); - } - - await target.FlushAsync(canceltoken.Token); - - //await logout.WriteDebugMessageAsync(string.Format("Last body chunck: {0}", System.Text.Encoding.ASCII.GetString(buf, 0, r))); - logger.WriteDebugMessage(string.Format("Completed response forward")); - - target.Close(); - - return; - } - else - { - - var header = System.Text.Encoding.ASCII.GetString(buf, lastmatch, ix - lastmatch) ?? string.Empty; - if (header.StartsWith("Content-Length: ", StringComparison.OrdinalIgnoreCase)) - if (!long.TryParse(header.Substring("Content-Length: ".Length), out contentlength)) - contentlength = -1; - - logger.WriteDebugMessage("Writing: " + header); - - await target.WriteAsync(buf, lastmatch, (ix - lastmatch) + 2, canceltoken.Token); - } - - } - - lastmatch = ix + 2; - } - - //await logger.WriteDebugMessageAsync(string.Format("Buf stats: {0},{1},{2},{3}", buf.Length, ix, offset, lastmatch)); - - ix = Array.IndexOf(buf, (byte)13, ix + 1, offset - ix - 1); - } - } - - } - - private static async Task ForwardRequest(StreamWriter sw, string path, LogHandler logger) - { - var canceltoken = new CancellationTokenSource(); - var env = Environment.GetEnvironmentVariables(); - - /*foreach (var k in env.Keys) - logger.WriteDebugMessage(string.Format("{0}: {1}", k, env[k]));*/ - - await sw.WriteAsync(string.Format("{0} {1} HTTP/1.1{2}", GetEnvArg("REQUEST_METHOD", "").Trim(), path, CRLF)); - - logger.WriteDebugMessage("Wrote request header line"); - - foreach (var key in env.Keys.Cast<string>().Where<string>(x => x.StartsWith("HTTP_"))) - await sw.WriteAsync(string.Format("{0}: {1}{2}", key.Substring("HTTP_".Length).Replace("_", "-"), env[key], CRLF)); - - if (!string.IsNullOrWhiteSpace(GetEnvArg("CONTENT_TYPE"))) - await sw.WriteAsync(string.Format("{0}: {1}{2}", "Content-Type", GetEnvArg("CONTENT_TYPE"), CRLF)); - if (!string.IsNullOrWhiteSpace(GetEnvArg("CONTENT_LENGTH"))) - await sw.WriteAsync(string.Format("{0}: {1}{2}", "Content-Length", GetEnvArg("CONTENT_LENGTH"), CRLF)); - - await sw.WriteAsync(string.Format("{0}: {1}{2}", "Connection", "close", CRLF)); - - await sw.WriteAsync(CRLF); - await sw.FlushAsync(); - - logger.WriteDebugMessage("Wrote all header lines"); - - if (new string[] { "POST", "PUT", "PATCH" }.Contains(GetEnvArg("REQUEST_METHOD", "").Trim().ToUpper())) - { - logger.WriteDebugMessage(string.Format("Copying StdIn")); - - using(var stdin = Console.OpenStandardInput()) - { - logger.WriteDebugMessage("Opened StdIn"); - - long reqsize; - if (!long.TryParse(GetEnvArg("CONTENT_LENGTH"), out reqsize)) - reqsize = long.MaxValue; - - var buf = new byte[4 * 1024 * 1024]; - var r = 0; - while(reqsize > 0) - { - logger.WriteDebugMessage(string.Format("Remaining {0} bytes from stdin", reqsize)); - - r = await stdin.ReadAsync(buf, 0, buf.Length, canceltoken.Token); - logger.WriteDebugMessage(string.Format("Got {0} bytes from stdin", r)); - - if (r == 0) - break; - - reqsize -= r; - await sw.BaseStream.WriteAsync(buf, 0, r, canceltoken.Token); - } - } - - logger.WriteDebugMessage("Copy stdin done"); - - } - - logger.WriteDebugMessage("Completed writing request"); - - } - - } -} diff --git a/Tools/CGIProxyHandler/Properties/AssemblyInfo.cs b/Tools/CGIProxyHandler/Properties/AssemblyInfo.cs deleted file mode 100644 index 4cb3fd3f2..000000000 --- a/Tools/CGIProxyHandler/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; - -// Information about this assembly is defined by the following attributes. -// Change them to the values specific to your project. - -[assembly: AssemblyTitle("CGIProxyHandler")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("")] -[assembly: AssemblyCopyright("kenneth")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". -// The form "{Major}.{Minor}.*" will automatically update the build and revision, -// and "{Major}.{Minor}.{Build}.*" will update just the revision. - -[assembly: AssemblyVersion("2.0.0.7")] - -// The following attributes are used to specify the signing key for the assembly, -// if desired. See the Mono documentation for more information about signing. - -//[assembly: AssemblyDelaySign(false)] -//[assembly: AssemblyKeyFile("")] - |