diff options
author | sr55 <[email protected]> | 2020-04-11 13:00:41 +0100 |
---|---|---|
committer | sr55 <[email protected]> | 2020-04-11 13:00:49 +0100 |
commit | a06bd83f2d1b1e9edc190fa09bdb7f0752783a7f (patch) | |
tree | 4e702f67080eb77e76879e75139414fb0637d4ab /win | |
parent | c3d62f1465fc9813d079bdf33f2e062a48ec8549 (diff) |
WinGui: Improvements to the Process Isolation Worker.
- Harden the worker process. Token is now required as a HTTP header for all actions.
- Added an option to portable.ini to completely disable this functioanlity. May be useful for some enterprise environents
- Few fixes
Diffstat (limited to 'win')
-rw-r--r-- | win/CS/HandBrake.Worker/HttpServer.cs | 46 | ||||
-rw-r--r-- | win/CS/HandBrake.Worker/Program.cs | 8 | ||||
-rw-r--r-- | win/CS/HandBrakeWPF/Converters/OptionTabConverter.cs | 5 | ||||
-rw-r--r-- | win/CS/HandBrakeWPF/Instance/RemoteInstance.cs | 20 | ||||
-rw-r--r-- | win/CS/HandBrakeWPF/Utilities/HttpRequestBase.cs | 54 | ||||
-rw-r--r-- | win/CS/HandBrakeWPF/Utilities/Portable.cs | 16 | ||||
-rw-r--r-- | win/CS/HandBrakeWPF/ViewModels/OptionsViewModel.cs | 8 | ||||
-rw-r--r-- | win/CS/HandBrakeWPF/Views/OptionsView.xaml | 22 | ||||
-rw-r--r-- | win/CS/HandBrakeWPF/portable.ini.template | 6 |
9 files changed, 146 insertions, 39 deletions
diff --git a/win/CS/HandBrake.Worker/HttpServer.cs b/win/CS/HandBrake.Worker/HttpServer.cs index 3c9a36b37..ef8811ab6 100644 --- a/win/CS/HandBrake.Worker/HttpServer.cs +++ b/win/CS/HandBrake.Worker/HttpServer.cs @@ -19,16 +19,20 @@ namespace HandBrake.Worker public class HttpServer { + private readonly string uiToken; + private readonly HttpListener httpListener = new HttpListener(); private readonly Dictionary<string, Func<HttpListenerRequest, string>> apiHandlers; - public HttpServer(Dictionary<string, Func<HttpListenerRequest, string>> apiCalls, int port) + public HttpServer(Dictionary<string, Func<HttpListenerRequest, string>> apiCalls, int port, string token) { if (!HttpListener.IsSupported) { throw new NotSupportedException("HttpListener not supported on this computer."); } + this.uiToken = token; + // Store the Handlers this.apiHandlers = new Dictionary<string, Func<HttpListenerRequest, string>>(apiCalls); @@ -65,15 +69,30 @@ namespace HandBrake.Worker try { string path = context.Request.RawUrl.TrimStart('/').TrimEnd('/'); + string token = context.Request.Headers.Get("token"); + if (!this.IsAuthenticated(token)) + { + string rstr = "Worker: Access Denied. The token provided in the HTTP header was not valid."; + Console.WriteLine(rstr); + byte[] buf = Encoding.UTF8.GetBytes(rstr); + context.Response.StatusCode = (int)HttpStatusCode.Unauthorized; + context.Response.ContentLength64 = buf.Length; + context.Response.OutputStream.Write(buf, 0, buf.Length); + return; + } + Debug.WriteLine("Handling call to: " + path); if (this.apiHandlers.TryGetValue(path, out var actionToPerform)) { string rstr = actionToPerform(context.Request); - byte[] buf = Encoding.UTF8.GetBytes(rstr); - context.Response.ContentLength64 = buf.Length; - context.Response.OutputStream.Write(buf, 0, buf.Length); + if (!string.IsNullOrEmpty(rstr)) + { + byte[] buf = Encoding.UTF8.GetBytes(rstr); + context.Response.ContentLength64 = buf.Length; + context.Response.OutputStream.Write(buf, 0, buf.Length); + } } else { @@ -85,7 +104,7 @@ namespace HandBrake.Worker } catch (Exception exc) { - Debug.WriteLine(exc); + Console.WriteLine("Worker: Listener Thread: " + exc); } finally { @@ -97,7 +116,7 @@ namespace HandBrake.Worker } catch (Exception exc) { - Debug.WriteLine(exc); + Console.WriteLine("Worker: " + exc); } }); } @@ -107,5 +126,20 @@ namespace HandBrake.Worker this.httpListener.Stop(); this.httpListener.Close(); } + + public bool IsAuthenticated(string token) + { + if (string.IsNullOrEmpty(token)) + { + return false; + } + + if (token != this.uiToken) + { + return false; + } + + return true; + } } }
\ No newline at end of file diff --git a/win/CS/HandBrake.Worker/Program.cs b/win/CS/HandBrake.Worker/Program.cs index f925ade38..a71192a41 100644 --- a/win/CS/HandBrake.Worker/Program.cs +++ b/win/CS/HandBrake.Worker/Program.cs @@ -25,6 +25,7 @@ namespace HandBrake.Worker public static void Main(string[] args) { int port = 8037; // Default Port; + string token = null; if (args.Length != 0) { @@ -38,6 +39,11 @@ namespace HandBrake.Worker port = parsedPort; } } + + if (argument.StartsWith("--token")) + { + token = argument.TrimStart("--token=".ToCharArray()); + } } } @@ -47,7 +53,7 @@ namespace HandBrake.Worker Console.WriteLine("Worker: Starting Web Server on port {0} ...", port); Dictionary<string, Func<HttpListenerRequest, string>> apiHandlers = RegisterApiHandlers(); - HttpServer webServer = new HttpServer(apiHandlers, port); + HttpServer webServer = new HttpServer(apiHandlers, port, token); webServer.Run(); Console.WriteLine("Worker: Server Started"); diff --git a/win/CS/HandBrakeWPF/Converters/OptionTabConverter.cs b/win/CS/HandBrakeWPF/Converters/OptionTabConverter.cs index 9ad7111ec..069918a71 100644 --- a/win/CS/HandBrakeWPF/Converters/OptionTabConverter.cs +++ b/win/CS/HandBrakeWPF/Converters/OptionTabConverter.cs @@ -22,11 +22,14 @@ namespace HandBrakeWPF.Converters public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { OptionsTab[] tabs = value as OptionsTab[]; - if (tabs != null && UwpDetect.IsUWP()) + if (tabs != null && (UwpDetect.IsUWP() || !Portable.IsUpdateCheckEnabled())) { return tabs.Where(s => s != OptionsTab.Updates).ToArray(); } + + + return value; } diff --git a/win/CS/HandBrakeWPF/Instance/RemoteInstance.cs b/win/CS/HandBrakeWPF/Instance/RemoteInstance.cs index 87af203c8..de0d50cc4 100644 --- a/win/CS/HandBrakeWPF/Instance/RemoteInstance.cs +++ b/win/CS/HandBrakeWPF/Instance/RemoteInstance.cs @@ -17,6 +17,7 @@ namespace HandBrakeWPF.Instance using System.Linq; using System.Net; using System.Net.NetworkInformation; + using System.Text; using System.Threading.Tasks; using System.Timers; using System.Windows.Media.Animation; @@ -48,9 +49,7 @@ namespace HandBrakeWPF.Instance public class RemoteInstance : HttpRequestBase, IEncodeInstance, IDisposable { private readonly HBConfiguration configuration; - private readonly ILog logService; - private readonly IUserSettingService userSettingService; private const double EncodePollIntervalMs = 500; @@ -94,7 +93,7 @@ namespace HandBrakeWPF.Instance EnableHardwareAcceleration = true, LogDirectory = DirectoryUtilities.GetLogDirectory(), LogVerbosity = this.userSettingService.GetUserSetting<int>(UserSettingConstants.Verbosity) - }; + }; initCommand.LogFile = Path.Combine(initCommand.LogDirectory, string.Format("activity_log.worker.{0}.txt", GeneralUtilities.ProcessId)); @@ -134,7 +133,6 @@ namespace HandBrakeWPF.Instance public void Dispose() { - this.client?.Dispose(); this.workerProcess?.Dispose(); } @@ -142,12 +140,15 @@ namespace HandBrakeWPF.Instance { if (this.workerProcess == null || this.workerProcess.HasExited) { + var plainTextBytes = Encoding.UTF8.GetBytes(Guid.NewGuid().ToString()); + this.base64Token = Convert.ToBase64String(plainTextBytes); + workerProcess = new Process { StartInfo = { FileName = "HandBrake.Worker.exe", - Arguments = string.Format(" --port={0}", port), + Arguments = string.Format(" --port={0} --token={1}", port, this.base64Token), UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardError = true, @@ -227,8 +228,6 @@ namespace HandBrakeWPF.Instance } response = await this.MakeHttpGetRequest("PollEncodeProgress"); - - this.retryCount = 0; // Reset } catch (Exception e) { @@ -241,6 +240,8 @@ namespace HandBrakeWPF.Instance return; } + this.retryCount = 0; // Reset + string statusJson = response.JsonResponse; JsonState state = JsonConvert.DeserializeObject<JsonState>(statusJson); @@ -278,6 +279,11 @@ namespace HandBrakeWPF.Instance private int GetOpenPort(int startPort) { + if (startPort == 0) + { + startPort = 8037; + } + int portStartIndex = startPort; IPGlobalProperties properties = IPGlobalProperties.GetIPGlobalProperties(); diff --git a/win/CS/HandBrakeWPF/Utilities/HttpRequestBase.cs b/win/CS/HandBrakeWPF/Utilities/HttpRequestBase.cs index cc2898b43..fd23565e3 100644 --- a/win/CS/HandBrakeWPF/Utilities/HttpRequestBase.cs +++ b/win/CS/HandBrakeWPF/Utilities/HttpRequestBase.cs @@ -10,6 +10,7 @@ namespace HandBrakeWPF.Utilities { using System; + using System.Net; using System.Net.Http; using System.Text; using System.Threading.Tasks; @@ -20,11 +21,14 @@ namespace HandBrakeWPF.Utilities public class HttpRequestBase { - protected readonly JsonSerializerSettings jsonNetSettings = new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }; - protected HttpClient client = new HttpClient(); protected string serverUrl; + protected int port; + protected string base64Token; + + private readonly JsonSerializerSettings jsonNetSettings = new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }; + public async Task<ServerResponse> MakeHttpJsonPostRequest(string urlPath, string json) { if (string.IsNullOrEmpty(json)) @@ -32,14 +36,26 @@ namespace HandBrakeWPF.Utilities throw new InvalidOperationException("No Post Values Found."); } - StringContent content = new StringContent(json, Encoding.UTF8, "application/json"); - HttpResponseMessage response = await client.PostAsync(this.serverUrl + urlPath, content); - if (response != null) + using (HttpClient client = new HttpClient()) { - string returnContent = await response.Content.ReadAsStringAsync(); - ServerResponse serverResponse = new ServerResponse(response.IsSuccessStatusCode, returnContent); + HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Post, this.serverUrl + urlPath); + if (!string.IsNullOrEmpty(this.base64Token)) + { + requestMessage.Headers.Add("token", this.base64Token); + } + + requestMessage.Content = new StringContent(json, Encoding.UTF8, "application/json"); - return serverResponse; + using (HttpResponseMessage response = await client.SendAsync(requestMessage)) + { + if (response != null) + { + string returnContent = await response.Content.ReadAsStringAsync(); + ServerResponse serverResponse = new ServerResponse(response.IsSuccessStatusCode, returnContent); + + return serverResponse; + } + } } return null; @@ -47,13 +63,25 @@ namespace HandBrakeWPF.Utilities public async Task<ServerResponse> MakeHttpGetRequest(string urlPath) { - HttpResponseMessage response = await client.GetAsync(this.serverUrl + urlPath); - if (response != null) + using (HttpClient client = new HttpClient()) { - string returnContent = await response.Content.ReadAsStringAsync(); - ServerResponse serverResponse = new ServerResponse(response.IsSuccessStatusCode, returnContent); + HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Get, this.serverUrl + urlPath); + if (!string.IsNullOrEmpty(this.base64Token)) + { + requestMessage.Headers.Add("token", this.base64Token); + } + + using (HttpResponseMessage response = await client.SendAsync(requestMessage)) + { + if (response != null) + { + string returnContent = await response.Content.ReadAsStringAsync(); + ServerResponse serverResponse = null; + serverResponse = response.StatusCode == HttpStatusCode.Unauthorized ? new ServerResponse(false, returnContent) : new ServerResponse(response.IsSuccessStatusCode, returnContent); - return serverResponse; + return serverResponse; + } + } } return null; diff --git a/win/CS/HandBrakeWPF/Utilities/Portable.cs b/win/CS/HandBrakeWPF/Utilities/Portable.cs index ecde42d82..578c3fe76 100644 --- a/win/CS/HandBrakeWPF/Utilities/Portable.cs +++ b/win/CS/HandBrakeWPF/Utilities/Portable.cs @@ -193,6 +193,22 @@ namespace HandBrakeWPF.Utilities return true; // Default to On. } + public static bool IsRemoteWorkerProcessEnabled() + { + if (keyPairs.ContainsKey("remote.worker.enabled")) + { + string enabled = keyPairs["remote.worker.enabled"]; + if (!string.IsNullOrEmpty(enabled) && enabled.Trim() == "true") + { + return true; + } + + return false; + } + + return true; // Default to On. + } + /// <summary> /// The get temp directory. /// </summary> diff --git a/win/CS/HandBrakeWPF/ViewModels/OptionsViewModel.cs b/win/CS/HandBrakeWPF/ViewModels/OptionsViewModel.cs index 6796141fd..0e92023dd 100644 --- a/win/CS/HandBrakeWPF/ViewModels/OptionsViewModel.cs +++ b/win/CS/HandBrakeWPF/ViewModels/OptionsViewModel.cs @@ -1354,6 +1354,14 @@ namespace HandBrakeWPF.ViewModels }
}
+ public bool IsRemoteWorkedAllowed
+ {
+ get
+ {
+ return Portable.IsRemoteWorkerProcessEnabled();
+ }
+ }
+
#region Public Methods
/// <summary>
diff --git a/win/CS/HandBrakeWPF/Views/OptionsView.xaml b/win/CS/HandBrakeWPF/Views/OptionsView.xaml index 0abb41364..606b940ac 100644 --- a/win/CS/HandBrakeWPF/Views/OptionsView.xaml +++ b/win/CS/HandBrakeWPF/Views/OptionsView.xaml @@ -113,7 +113,7 @@ <TextBlock Text="{x:Static Properties:Resources.Options_UserInterface}" FontSize="14" Margin="0,0,0,10"/>
<StackPanel Orientation="Horizontal" Margin="20,0,0,5">
- <TextBlock Text="{x:Static Properties:Resources.OptionsView_Language}" Margin="0,0,0,0" VerticalAlignment="Center"/>
+ <TextBlock Text="{x:Static Properties:Resources.OptionsView_Language}" Margin="0,0,0,0" VerticalAlignment="Center"/>
<ComboBox DisplayMemberPath="Name" ItemsSource="{Binding InterfaceLanguages}" SelectedItem="{Binding SelectedLanguage}" Margin="5,0,0,0" HorizontalAlignment="Left" Width="150" />
</StackPanel>
@@ -185,7 +185,7 @@ <Button Content="{x:Static Properties:Resources.Browse}" Margin="5,0,0,0" Grid.Column="2" Grid.Row="0" cal:Message.Attach="[Event Click] = [Action BrowseAutoNamePath]" HorizontalAlignment="Left" />
<TextBlock Text="{x:Static Properties:Resources.OptionsView_PathOptions}" Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2" FontStyle="Italic" FontSize="11" TextWrapping="Wrap" />
<CheckBox IsChecked="{Binding AlwaysUseDefaultPath}" Content="{x:Static Properties:Resources.OptionsView_AlwaysUseDefaultPath}" ToolTip="{x:Static Properties:ResourcesTooltips.OptionsView_AlwaysUseDefaultPath}" Grid.Row="2" Grid.Column="1" />
-
+
<TextBlock VerticalAlignment="Center" Text="{x:Static Properties:Resources.Options_Format}" Grid.Column="0" Grid.Row="4" Margin="0,5,0,0" />
<TextBox Name="autoNameFormat" Text="{Binding AutonameFormat, UpdateSourceTrigger=PropertyChanged}" Width="380" Grid.Column="1" Grid.Row="4" Margin="0,0,0,0" ToolTip="{x:Static Properties:Resources.Options_AdditionalFormatOptions}" HorizontalAlignment="Left" />
@@ -249,7 +249,7 @@ <CheckBox Content="{x:Static Properties:Resources.Options_PromptBeforeAction}" VerticalAlignment="Center" Margin="0,5,0,0" Grid.Row="2" Grid.Column="1"
IsChecked="{Binding WhenDonePerformActionImmediately}" />
</Grid>
-
+
<TextBlock Text="{x:Static Properties:Resources.Options_EncodeCompleted}" FontSize="14" Margin="0,10,0,10" />
<StackPanel Orientation="Vertical" Margin="20,0,0,0">
@@ -389,7 +389,7 @@ <TextBlock Text="{x:Static Properties:Resources.Options_PriorityLevel}" Width="250" VerticalAlignment="Center" />
<ComboBox Name="processPriorityLevel" ItemsSource="{Binding PriorityLevelOptions, Converter={StaticResource ProcessPriorityConverter}}" SelectedItem="{Binding SelectedPriority, Converter={StaticResource ProcessPriorityConverter}}" Width="120" />
</StackPanel>
- </StackPanel>
+ </StackPanel>
</StackPanel>
<StackPanel Orientation="Vertical" Margin="0,10,0,10">
@@ -400,13 +400,17 @@ </StackPanel>
</StackPanel>
- <TextBlock Text="Worker Processes" FontSize="14" Margin="0,10,0,10"/>
+ <StackPanel Orientation="Vertical" Visibility="{Binding IsRemoteWorkedAllowed, Converter={StaticResource boolToVisConverter}}">
- <CheckBox Content="{x:Static Properties:Resources.OptionsView_EnableWorkerProcesses}" IsChecked="{Binding RemoteServiceEnabled}" Margin="10,5,0,0" />
+ <TextBlock Text="Worker Processes" FontSize="14" Margin="0,10,0,10"/>
+
+ <CheckBox Content="{x:Static Properties:Resources.OptionsView_EnableWorkerProcesses}" IsChecked="{Binding RemoteServiceEnabled}" Margin="10,5,0,0" />
+
+ <StackPanel Orientation="Horizontal" Margin="10,10,0,0">
+ <TextBlock Text="{x:Static Properties:Resources.OptionsView_WorkerDefaultPort}" />
+ <TextBox Text="{Binding RemoteServicePort}" Width="100" />
+ </StackPanel>
- <StackPanel Orientation="Horizontal" Margin="10,10,0,0">
- <TextBlock Text="{x:Static Properties:Resources.OptionsView_WorkerDefaultPort}" />
- <TextBox Text="{Binding RemoteServicePort}" Width="100" />
</StackPanel>
diff --git a/win/CS/HandBrakeWPF/portable.ini.template b/win/CS/HandBrakeWPF/portable.ini.template index c667efece..21dae0bd5 100644 --- a/win/CS/HandBrakeWPF/portable.ini.template +++ b/win/CS/HandBrakeWPF/portable.ini.template @@ -7,7 +7,8 @@ # - tmp.dir => temporary files only. (i.e Preview images) # - update.check => true | false (enabled / disabled, default disabled for portable) # - hardware.enabled => true | false (Enables the hardware encoders such as QSV, NVENC or VCE) -# +# - remote.worker.enabled => true | false (Allows use of the remote worker process 'HandBrake.worker.exe' which uses a localhost http server. Setting to false will process jobs in-process.) +# # Set to 'cwd' to use the current applications directory. It will automatically create "storage" and "tmp" folders in this instance. # Leave blank to use the system "TMP" directory and the "AppData" user profile folder. ################################# @@ -15,4 +16,5 @@ storage.dir = cwd tmp.dir = cwd update.check = false -hardware.enabled = true
\ No newline at end of file +hardware.enabled = true +remote.worker.enabled = true
\ No newline at end of file |