diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..62aefca2 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,1246 @@ +// Avoid looking for .editorconfig files in parent directories +root=true + +[*] + +insert_final_newline = true +indent_style = space +indent_size = 4 +tab_width = 4 +end_of_line = crlf + +[*.cs] + +#### Sonar rules #### + +# S101: Types should be named in PascalCase +# https://rules.sonarsource.com/csharp/RSPEC-101 +# +# TODO: Remove this when code has been updated! +dotnet_diagnostic.S101.severity = none + +# S112: General exceptions should never be thrown +# https://rules.sonarsource.com/csharp/RSPEC-112 +# +# This is a duplicate of CA2201 and MA0012. +dotnet_diagnostic.S112.severity = none + +# S907: Remove use of 'goto' +# https://rules.sonarsource.com/csharp/RSPEC-907 +# +# Limited use of 'goto' is accepted when performance is critical. +dotnet_diagnostic.S907.severity = none + +# S1066: Collapsible "if" statements should be merged +# https://rules.sonarsource.com/csharp/RSPEC-1066 +# +dotnet_diagnostic.S1066.severity = none + +# S1075: URIs should not be hardcoded +# https://rules.sonarsource.com/csharp/RSPEC-1075 +# +# The rule reports false positives for XML namespaces. +dotnet_diagnostic.S1075.severity = none + +# S1104: Fields should not have public accessibility +# https://rules.sonarsource.com/csharp/RSPEC-1104 +# +# This is a duplicate of SA1401 and CA1051. +dotnet_diagnostic.S1104.severity = none + +# S1125: Boolean literals should not be redundant +# https://rules.sonarsource.com/csharp/RSPEC-1125 +# +# This is a duplicate of MA0073. +dotnet_diagnostic.S1125.severity = none + +# S1135: Track uses of "TODO" tags +# +# This is a duplicate of MA0026. +dotnet_diagnostic.S1135.severity = none + +# S1168: Empty arrays and collections should be returned instead of null +# https://rules.sonarsource.com/csharp/RSPEC-1168 +# +# We sometimes return null to avoid allocating an empty List. +dotnet_diagnostic.S1168.severity = none + +# S1172: Unused method parameters should be removed +# https://rules.sonarsource.com/csharp/RSPEC-1172 +# +# This is a duplicate of IDE0060. +dotnet_diagnostic.S1172.severity = none + +# S1481: Unused local variables should be removed +# https://rules.sonarsource.com/csharp/RSPEC-1481 +# +# This is a duplicate of IDE0059. +dotnet_diagnostic.S1481.severity = none + +# S2259: Null pointers should not be dereferenced +# https://rules.sonarsource.com/csharp/RSPEC-2259 +# +# The analysis is not precise enough, leading to false positives. +dotnet_diagnostic.S2259.severity = none + +# S2445: Blocks should be synchronized on read-only fields +# https://rules.sonarsource.com/csharp/RSPEC-2445 +# +# This is a (partial) duplicate of MA0064. +dotnet_diagnostic.S2445.severity = none + +# S2551: Shared resources should not be used for locking +# https://rules.sonarsource.com/csharp/RSPEC-2551 +# +# This is a duplicate of CA2002, and partial duplicate of MA0064. +dotnet_diagnostic.S2551.severity = none + +# S2583: Conditionally executed code should be reachable +# https://rules.sonarsource.com/csharp/RSPEC-2583 +# +# This rule produces false errors in, for example, for loops. +#dotnet_diagnostic.S2583.severity = none + +# S2699: Tests should include assertions +# https://rules.sonarsource.com/csharp/RSPEC-2699 +# +# Sometimes you want a test in which you invoke a method and just want to verify that it does not throw. +# For example: +# [TestMethod] +# public void InvokeDisposeWithoutNotifyObjectShouldNotThrow() +# { +# _timer.Dispose(); +# } +dotnet_diagnostic.S2699.severity = none + +# S2933: Fields that are only assigned in the constructor should be "readonly" +# https://rules.sonarsource.com/csharp/RSPEC-2933 +# +# This is a duplicate of IDE0044, but IDE0044 is not reported when targeting .NET Framework 4.8. +dotnet_diagnostic.S2933.severity = none + +# S2971: "IEnumerable" LINQs should be simplified +# https://rules.sonarsource.com/csharp/RSPEC-2971 +# +# This is a duplicate of MA0020. +dotnet_diagnostic.S2971.severity = none + +# S3218: Inner class members should not shadow outer class "static" or type members +# https://rules.sonarsource.com/csharp/RSPEC-3218 +# +# This is rather harmless. +dotnet_diagnostic.S3218.severity = none + +# S3267: Loops should be simplified with "LINQ" expressions +# https://rules.sonarsource.com/csharp/RSPEC-3267 +# +# LINQ is the root of all evil :p +dotnet_diagnostic.S3267.severity = none + +# S3376: Attribute, EventArgs, and Exception type names should end with the type being extended +# https://rules.sonarsource.com/csharp/RSPEC-3376 +# +# This is a partial duplicate of MA0058. If we enable the Sonar in all repositories, we should +# consider enabling S3376 in favor of MA0058. +dotnet_diagnostic.S3376.severity = none + +# S3871: Exception types should be "public" +# https://rules.sonarsource.com/csharp/RSPEC-3871 +# +# This is a duplicate of CA1064. +dotnet_diagnostic.S3871.severity = none + +# S3925: "ISerializable" should be implemented correctly +# https://rules.sonarsource.com/csharp/RSPEC-3925 +# +# This is a duplicate of CA2229. +dotnet_diagnostic.S3925.severity = none + +# S3928: Parameter names used into ArgumentException constructors should match an existing one +# https://rules.sonarsource.com/csharp/RSPEC-3928 +# +# This is a duplicate of MA0015. +dotnet_diagnostic.S3928.severity = none + +# S3998: Threads should not lock on objects with weak identity +# https://rules.sonarsource.com/csharp/RSPEC-3998 +# +# This is a duplicate of CA2002, and partial duplicate of MA0064. +dotnet_diagnostic.S3998.severity = none + +# S4456: Parameter validation in yielding methods should be wrapped +# https://rules.sonarsource.com/csharp/RSPEC-4456 +# +# This is a duplicate of MA0050. +dotnet_diagnostic.S4456.severity = none + +# S4487: Unread "private" fields should be removed +# https://rules.sonarsource.com/csharp/RSPEC-4487 +# +# This is a duplicate of IDE0052. +dotnet_diagnostic.S4487.severity = none + +# S4581: "new Guid()" should not be used +# https://rules.sonarsource.com/csharp/RSPEC-4581 +# +# This is a partial duplicate of MA0067, and we do not want to report the use of 'default' for a Guid as error. +dotnet_diagnostic.S4581.severity = none + +#### StyleCop rules #### + +# SA1003: Symbols must be spaced correctly +# +# When enabled, a diagnostic is produced when there's a space after a cast. +# For example: +# var x = (int) z; +dotnet_diagnostic.SA1003.severity = none + +# SA1008: Opening parenthesis should not be preceded by a space +# +# When enabled, a diagnostic is produce when a cast precedes braces. +# For example: +# (long) (a * b) +dotnet_diagnostic.SA1008.severity = none + +# SA1009: Closing parenthesis should not be followed by a space +# +# When enabled, a diagnostic is produced when there's a space after a cast. +# For example: +# var x = (int) z; +dotnet_diagnostic.SA1009.severity = none + +# SA1101: Prefix local calls with this +dotnet_diagnostic.SA1101.severity = none + +# SA1116: Split parameters must start on line after declaration +# +# When enabled, a diagnostic is produced when the first parameter is on the same line as the method or constructor. +# For example: +# arrayBuilder.Add(new StatisticsCallInfo(callsByType.Key, +# callsForType.Count); +dotnet_diagnostic.SA1116.severity = none + +# SA1200: Using directives must be placed correctly +# +# This is already verified by the .NET compiler platform analyzers (csharp_using_directive_placement option and IDE0065 rule). +dotnet_diagnostic.SA1200.severity = none + +# SA1201: Elements must appear in the correct order +dotnet_diagnostic.SA1201.severity = none + +# SA1206: Modifiers are not ordered +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1206.md +# +# This is a duplicate of IDE0036, except that it cannot be configured and expects the required modifier to be before the +# accessibility modifier. +dotnet_diagnostic.SA1206.severity = none + +# SA1309: Field names must not begin with underscore +dotnet_diagnostic.SA1309.severity = none + +# SA1405: Debug.Assert should provide message text +# +# To be discussed if we want to enable this. +dotnet_diagnostic.SA1405.severity = none + +# SA1413: Use trailing comma in multi-line initializers +dotnet_diagnostic.SA1413.severity = none + +# SA1503: Braces should not be omitted +# +# This is a duplicate of IDE0011. +dotnet_diagnostic.SA1503.severity = none + +# SA1516: Elements must be separated by blank line +# +# When enabled, a diagnostic is produced for properties with both a get and set accessor. +# For example: +# public bool EnableStatistics +# { +# get +# { +# return _enableStatistics; +# } +# set +# { +# _enableStatistics = value; +# } +# } +dotnet_diagnostic.SA1516.severity = none + +# SA1520: Use braces consistently +# +# Since we always require braces (configured via csharp_prefer_braces and reported as IDE0011), it does not make sense to check if braces +# are used consistently. +dotnet_diagnostic.SA1520.severity = none + +# SA1633: File must have header +# +# We do not use file headers. +dotnet_diagnostic.SA1633.severity = none + +# SA1648: must be used with inheriting class +# +# This rule is disabled by default, hence we need to explicitly enable it. +dotnet_diagnostic.SA1648.severity = error + +# SX1101: Do not prefix local members with 'this.' +# +# This rule is disabled by default, hence we need to explicitly enable it. +dotnet_diagnostic.SX1101.severity = error + +# SX1309: Field names must begin with underscore +# +# This rule is disabled by default, hence we need to explicitly enable it. +dotnet_diagnostic.SX1309.severity = error + +# SX1309S: Static field names must begin with underscore +# +# This rule is disabled by default, hence we need to explicitly enable it. +dotnet_diagnostic.SX1309S.severity = error + +#### Meziantou.Analyzer rules #### + +# MA0002: Use an overload that has a IEqualityComparer or IComparer parameter +# +# In .NET (Core) there have been quite some optimizations for EqualityComparer.Default (eg. https://github.com/dotnet/coreclr/pull/14125) +# and Comparer.Default (eg. https://github.com/dotnet/runtime/pull/48160). +# +# We'll have to verify impact on performance before we decide to use specific comparers (eg. StringComparer.InvariantCultureIgnoreCase). +dotnet_diagnostic.MA0002.severity = none + +# MA0006: Use string.Equals instead of Equals operator +# +# We almost always want ordinal comparison, and using the explicit overload adds a little overhead +# and is more chatty. +dotnet_diagnostic.MA0006.severity = none + +# MA0007: Add a comma after the last value +# +# We do not add a comma after the last value in multi-line initializers. +# For example: +# public enum Sex +# { +# Male = 1, +# Female = 2 // No comma here +# } +# +# Note: +# This is a duplicate of SA1413. +dotnet_diagnostic.MA0007.severity = none + +# MA0009: Add regex evaluation timeout +# +# We do not see a need guard our regex's against a DOS attack. +dotnet_diagnostic.MA0009.severity = none + +# MA0011: IFormatProvider is missing +# +# Also report diagnostic in ToString(...) methods +MA0011.exclude_tostring_methods = false + +# MA0012: Do not raise reserved exception type +# +# This is a duplicate of CA2201. +dotnet_diagnostic.MA0012.severity = none + +# MA0014: Do not raise System.ApplicationException type +# +# This is a duplicate of CA2201. +dotnet_diagnostic.MA0014.severity = none + +# MA0016: Prefer returning collection abstraction instead of implementation +# +# This is a duplicate of CA1002. +dotnet_diagnostic.MA0016.severity = none + +# MA0018: Do not declare static members on generic types +# +# This is a duplicate of CA1000. +dotnet_diagnostic.MA0018.severity = none + +# MA0021: Use StringComparer.GetHashCode instead of string.GetHashCode +# +# No strong need for this, and may negatively affect performance. +dotnet_diagnostic.MA0021.severity = none + +# MA0031: Optimize Enumerable.Count() usage +# https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0031.md +# +# The proposed code is less readable. +# +# For example: +# +# the following code fragment: +# enumerable.Count() > 10; +# +# would become: +# enumerable.Skip(10).Any(); +dotnet_diagnostic.MA0031.severity = none + +# MA0036: Make class static +# +# This is a partial duplicate of CA1052. +dotnet_diagnostic.MA0036.severity = none + +# MA0038: Make method static +# +# This is a partial duplicate of, and deprecated in favor of, CA1822. +dotnet_diagnostic.MA0038.severity = none + +# MA0041: Make property static +# +# This is a partial duplicate of, and deprecated in favor of, CA1822. +dotnet_diagnostic.MA0041.severity = none + +# MA0048: File name must match type name +# +# This is a duplicate of SA1649. +dotnet_diagnostic.MA0048.severity = none + +# MA0049: Type name should not match containing namespace +# +# This is a duplicate of CA1724 +dotnet_diagnostic.MA0049.severity = none + +# MA0051: Method is too long +# +# We do not want to limit the number of lines or statements per method. +dotnet_diagnostic.MA0051.severity = none + +# MA0053: Make class sealed +# +# Also report diagnostic for public types. +MA0053.public_class_should_be_sealed = true + +# MA0053: Make class sealed +# +# Also report diagnostic for types that derive from System.Exception. +MA0053.exceptions_should_be_sealed = true + +# MA0053: Make class sealed +# +# Also report diagnostic for types that define (new) virtual members. +MA0053.class_with_virtual_member_shoud_be_sealed = true + +# MA0112: Use 'Count > 0' instead of 'Any()' +# +# This rule is disabled by default, hence we need to explicitly enable it. +dotnet_diagnostic.MA0112.severity = error + +#### .NET Compiler Platform code quality rules #### + +# CA1002: Do not expose generic lists +# +# For performance reasons - to avoid interface dispatch - we expose generic lists +# instead of a base class or interface. +dotnet_diagnostic.CA1002.severity = none + +# CA1008: Enums should have zero value +# +# TODO: To be discussed. Having a zero value offers a performance advantage. +dotnet_diagnostic.CA1008.severity = none + +# CA1014: Mark assemblies with CLSCompliantAttribute +# +# This rule is disabled by default, hence we need to explicitly enable it. +# +# Even when enabled, this diagnostic does not appear to be reported for assemblies without CLSCompliantAttribute. +# We reported this issue as https://github.com/dotnet/roslyn-analyzers/issues/6563. +dotnet_diagnostic.CA1014.severity = error + +# CA1051: Do not declare visible instance fields +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1051 +# +# This is a duplicate of S1104 and SA1401. +dotnet_diagnostic.CA1051.severity = none + +# CA1052: Static holder types should be Static or NotInheritable +# +# By default, this diagnostic is only reported for public types. +dotnet_code_quality.CA1052.api_surface = all + +# CA1303: Do not pass literals as localized parameters +# +# We don't care about localization. +dotnet_diagnostic.CA1303.severity = none + +# CA1305: Specify IFormatProvider +# +# This is a an equivalent of MA0011, except that it does not report a diagnostic for the use of +# DateTime.TryParse(string s, out DateTime result). +# +# Submitted https://github.com/dotnet/roslyn-analyzers/issues/6096 to fix CA1305. +dotnet_diagnostic.CA1305.severity = none + +# CA1510: Use ArgumentNullException throw helper +# +# This is only available in .NET 6.0 and higher. We'd need to use conditional compilation to only +# use these throw helper when targeting a framework that supports it. +dotnet_diagnostic.CA1510.severity = none + +# CA1725: Parameter names should match base declaration +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1725 +# +# This is a duplicate of S927, but contains at least one bug: +# https://github.com/dotnet/roslyn-analyzers/issues/6461 +# +# Since we do not enable any of the Sonar rules by default, we'll leave CA1725 enabled. +dotnet_diagnostic.CA1725.severity = error + +# CA1819: Properties should not return arrays +# +# Arrays offer better performance than collections. +dotnet_diagnostic.CA1819.severity = none + +# CA1828: Mark members as static +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1822 +# +# Documentation does not mention which API surface(s) this rule runs on, so we explictly configure it. +dotnet_code_quality.CA1828.api_surface = all + +# CA1852: Seal internal types +# +# Similar to MA0053, but does not support public types and types that define (new) virtual members. +dotnet_diagnostic.CA1852.severity = none + +# CA1859: Change return type for improved performance +# +# By default, this diagnostic is only reported for private members. +dotnet_code_quality.CA1859.api_surface = all + +# CA2208: Instantiate argument exceptions correctly +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca2208 +# +# This is similar to, but less powerful than, MA0015. +dotnet_diagnostic.CA2208.severity = none + +#### Roslyn IDE analyser rules #### + +# IDE0032: Use auto-implemented property +# +# For performance reasons, we do not always want to enforce the use of +# auto-implemented properties. +dotnet_diagnostic.IDE0032.severity = suggestion + +# IDE0045: Use conditional expression for assignment +# +# This does not always result in cleaner/clearer code. +dotnet_diagnostic.IDE0045.severity = none + +# IDE0046: Use conditional expression for return +# +# Using a conditional expression is not always a clear win for readability. +# +# Configured using 'dotnet_style_prefer_conditional_expression_over_return' +dotnet_diagnostic.IDE0046.severity = suggestion + +# IDE0047: Remove unnecessary parentheses +# +# Removing "unnecessary" parentheses is not always a clear win for readability. +dotnet_diagnostic.IDE0047.severity = suggestion + +# IDE0055: Fix formatting +# +# When enabled, diagnostics are reported for indented object initializers. +# For example: +# _content = new Person +# { +# Name = "\u13AAlarm" +# }; +# +# There are no settings to configure this correctly, unless https://github.com/dotnet/roslyn/issues/63256 (or similar) is ever implemented. +dotnet_diagnostic.IDE0055.severity = none + +# IDE0130: Namespace does not match folder structure +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0130 +# +# TODO: Remove when https://github.com/sshnet/SSH.NET/issues/1129 is fixed +dotnet_diagnostic.IDE0130.severity = none + +# IDE0270: Null check can be simplified +# +# var inputPath = originalDossierPathList.Find(x => x.id == updatedPath.id); +# if (inputPath is null) +# { +# throw new PcsException($"Path id ({updatedPath.id}) unknown in PCS for dossier id {dossierFromTs.dossier.id}", updatedPath.id); +# } +# +# We do not want to modify the code using a null coalescing operator: +# +# var inputPath = originalDossierPathList.Find(x => x.id == updatedPath.id) ?? throw new PcsException($"Path id ({updatedPath.id}) unknown in PCS for dossier id {dossierFromTs.dossier.id}", updatedPath.id); +dotnet_diagnostic.IDE0270.severity = none + +#### .NET Compiler Platform code style rules #### + +### Language rules ### + +## Modifier preferences + +dotnet_style_require_accessibility_modifiers = true +dotnet_style_readonly_field = true +csharp_prefer_static_local_function = true + +## Parentheses preferences + +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity +dotnet_style_parentheses_in_other_operators = never_if_unnecessary + +# Expression-level preferences + +dotnet_style_object_initializer = true +csharp_style_inlined_variable_declaration = true +dotnet_style_collection_initializer = true +dotnet_style_prefer_auto_properties = true +dotnet_style_explicit_tuple_names = true +csharp_prefer_simple_default_expression = true +dotnet_style_prefer_inferred_tuple_names = true +dotnet_style_prefer_inferred_anonymous_type_member_names = true +csharp_style_prefer_local_over_anonymous_function = true +csharp_style_deconstructed_variable_declaration = false +dotnet_style_prefer_conditional_expression_over_assignment = true +dotnet_style_prefer_conditional_expression_over_return = true +dotnet_style_prefer_compound_assignment = true +csharp_style_prefer_index_operator = false +csharp_style_prefer_range_operator = false +dotnet_style_prefer_simplified_interpolation = false +dotnet_style_prefer_simplified_boolean_expressions = true +csharp_style_implicit_object_creation_when_type_is_apparent = false +csharp_style_prefer_tuple_swap = false + +# Namespace declaration preferences + +csharp_style_namespace_declarations = block_scoped + +# Null-checking preferences + +csharp_style_throw_expression = false +dotnet_style_coalesce_expression = true +dotnet_style_null_propagation = true +dotnet_style_prefer_is_null_check_over_reference_equality_method = true +csharp_style_prefer_null_check_over_type_check = true +csharp_style_conditional_delegate_call = true + +# 'var' preferences + +csharp_style_var_for_built_in_types = true +csharp_style_var_when_type_is_apparent = true +csharp_style_var_elsewhere = true + +# Expression-bodies members + +csharp_style_expression_bodied_methods = false +csharp_style_expression_bodied_constructors = false +csharp_style_expression_bodied_operators = false +csharp_style_expression_bodied_properties = false +csharp_style_expression_bodied_indexers = false +csharp_style_expression_bodied_accessors = false +csharp_style_expression_bodied_lambdas = false +csharp_style_expression_bodied_local_functions = false + +# Pattern matching preferences + +csharp_style_pattern_matching_over_as_with_null_check = true +csharp_style_pattern_matching_over_is_with_cast_check = true +csharp_style_prefer_switch_expression = false +csharp_style_prefer_pattern_matching = true +csharp_style_prefer_not_pattern = true +csharp_style_prefer_extended_property_pattern = true + +# Code block preferences + +csharp_prefer_braces = true +csharp_prefer_simple_using_statement = false + +# Using directive preferences + +csharp_using_directive_placement = outside_namespace + +# Namespace naming preferences + +dotnet_style_namespace_match_folder = true + +# Undocumented preferences + +csharp_style_prefer_method_group_conversion = false +csharp_style_prefer_top_level_statements = false + +### Formatting rules ### + +## .NET formatting options ## + +# Using directive options + +dotnet_sort_system_directives_first = true +dotnet_separate_import_directive_groups = true + +## C# formatting options ## + +# New-line options + +# TNIS-13005: Enabling this setting breaks Resharper indentation for lambdas +#csharp_new_line_before_open_brace = accessors, anonymous_methods, anonymous_types, control_blocks, events, indexers, lambdas, local_functions, methods, object_collection_array_initializers, properties, types +csharp_new_line_before_else = true +csharp_new_line_before_catch = true +csharp_new_line_before_finally = true +# Enabling this setting breaks Resharper formatting for an enum field reference that is +# deeply nested in an object initializer. +# +# For an example, see TDataExchangeGeneralEnricher_CernInfrastructureObstruction. +#csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_between_query_expression_clauses = true + +# Indentation options + +csharp_indent_case_contents = true +csharp_indent_switch_labels = true +csharp_indent_labels = one_less_than_current +csharp_indent_block_contents = true +# TNIS-13005: Enabling this setting breaks Resharper indentation for lambdas +#csharp_indent_braces = false +# TNIS-13005: Enabling this setting breaks Resharper indentation for lambdas +#csharp_indent_case_contents_when_block = true + +# Spacing options + +csharp_space_after_cast = true +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_between_parentheses = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_around_binary_operators = before_and_after +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_declaration_name_and_open_parenthesis = false +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_after_comma = true +csharp_space_before_comma = false +csharp_space_after_dot = false +csharp_space_before_dot = false +csharp_space_after_semicolon_in_for_statement = true +csharp_space_before_semicolon_in_for_statement = false +csharp_space_around_declaration_statements = false +csharp_space_before_open_square_brackets = false +csharp_space_between_empty_square_brackets = false +csharp_space_between_square_brackets = false + +# Wrap options + +csharp_preserve_single_line_statements = false +csharp_preserve_single_line_blocks = true + +### Naming styles ### + +# Naming rules + +dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i + +dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.private_fields_camel_case_begins_with_underscore.symbols = private_fields +dotnet_naming_rule.private_fields_camel_case_begins_with_underscore.style = camel_case_begins_with_underscore +dotnet_naming_rule.private_fields_camel_case_begins_with_underscore.severity = error + +dotnet_naming_rule.private_static_fields_camel_case_begins_with_underscore.symbols = private_static_fields +dotnet_naming_rule.private_static_fields_camel_case_begins_with_underscore.style = camel_case_begins_with_underscore +dotnet_naming_rule.private_static_fields_camel_case_begins_with_underscore.severity = error + +dotnet_naming_rule.private_static_readonly_fields_pascal_case.symbols = private_static_readonly_fields +dotnet_naming_rule.private_static_readonly_fields_pascal_case.style = pascal_case +dotnet_naming_rule.private_static_readonly_fields_pascal_case.severity = error + +dotnet_naming_rule.private_const_fields_pascal_case.symbols = private_const_fields +dotnet_naming_rule.private_const_fields_pascal_case.style = pascal_case +dotnet_naming_rule.private_const_fields_pascal_case.severity = error + +# Symbol specifications + +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = * +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.types.applicable_accessibilities = * +dotnet_naming_symbols.types.required_modifiers = + +dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method +dotnet_naming_symbols.non_field_members.applicable_accessibilities = * +dotnet_naming_symbols.non_field_members.required_modifiers = + +dotnet_naming_symbols.private_fields.applicable_kinds = field +dotnet_naming_symbols.private_fields.applicable_accessibilities = private +dotnet_naming_symbols.private_fields.required_modifiers = + +dotnet_naming_symbols.private_static_fields.applicable_kinds = field +dotnet_naming_symbols.private_static_fields.applicable_accessibilities = private +dotnet_naming_symbols.private_static_fields.required_modifiers = static + +dotnet_naming_symbols.private_static_readonly_fields.applicable_kinds = field +dotnet_naming_symbols.private_static_readonly_fields.applicable_accessibilities = private +dotnet_naming_symbols.private_static_readonly_fields.required_modifiers = static, readonly + +dotnet_naming_symbols.private_const_fields.applicable_kinds = field +dotnet_naming_symbols.private_const_fields.applicable_accessibilities = private +dotnet_naming_symbols.private_const_fields.required_modifiers = const + +# Naming styles + +dotnet_naming_style.begins_with_i.required_prefix = I +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.capitalization = pascal_case + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case + +dotnet_naming_style.camel_case_begins_with_underscore.required_prefix = _ +dotnet_naming_style.camel_case_begins_with_underscore.required_suffix = +dotnet_naming_style.camel_case_begins_with_underscore.word_separator = +dotnet_naming_style.camel_case_begins_with_underscore.capitalization = camel_case + +#### .NET Compiler Platform general options #### + +# Change the default rule severity for all analyzer rules that are enabled by default +dotnet_analyzer_diagnostic.severity = error + +#### .NET Compiler Platform code refactoring rules #### + +dotnet_style_operator_placement_when_wrapping = end_of_line + +#### ReSharper code style for C# #### + +## Blank Lines + +resharper_csharp_blank_lines_around_region = 1 +resharper_csharp_blank_lines_inside_region = 1 +resharper_csharp_blank_lines_before_single_line_comment = 1 +resharper_csharp_keep_blank_lines_in_declarations = 1 +resharper_csharp_remove_blank_lines_near_braces_in_declarations = true +resharper_csharp_blank_lines_after_start_comment = 1 +resharper_csharp_blank_lines_between_using_groups = 1 +resharper_csharp_blank_lines_after_using_list = 1 +resharper_csharp_blank_lines_around_namespace = 1 +resharper_csharp_blank_lines_inside_namespace = 0 +resharper_csharp_blank_lines_after_file_scoped_namespace_directive = 1 +resharper_csharp_blank_lines_around_type = 1 +resharper_csharp_blank_lines_around_single_line_type = 1 +resharper_csharp_blank_lines_inside_type = 0 +resharper_csharp_blank_lines_around_field = 0 +resharper_csharp_blank_lines_around_single_line_field = 0 +resharper_csharp_blank_lines_around_property = 1 +resharper_csharp_blank_lines_around_single_line_property = 1 +resharper_csharp_blank_lines_around_auto_property = 1 +resharper_csharp_blank_lines_around_single_line_auto_property = 1 +resharper_csharp_blank_lines_around_accessor = 0 +resharper_csharp_blank_lines_around_single_line_accessor = 0 +resharper_csharp_blank_lines_around_invocable = 1 +resharper_csharp_blank_lines_around_single_line_invocable = 1 +resharper_csharp_keep_blank_lines_in_code = 1 +resharper_csharp_remove_blank_lines_near_braces_in_code = true +resharper_csharp_blank_lines_around_local_method = 1 +resharper_csharp_blank_lines_around_single_line_local_method = 1 +resharper_csharp_blank_lines_before_control_transfer_statements = 0 +resharper_csharp_blank_lines_after_control_transfer_statements = 0 +resharper_csharp_blank_lines_before_block_statements = 0 +resharper_csharp_blank_lines_after_block_statements = 1 +resharper_csharp_blank_lines_before_multiline_statements = 0 +resharper_csharp_blank_lines_after_multiline_statements = 0 +resharper_csharp_blank_lines_around_block_case_section = 0 +resharper_csharp_blank_lines_around_multiline_case_section = 0 +resharper_csharp_blank_lines_before_case = 0 +resharper_csharp_blank_lines_after_case = 0 + +## Braces Layout + +resharper_csharp_type_declaration_braces = next_line +resharper_csharp_indent_inside_namespace = true +resharper_csharp_invocable_declaration_braces = next_line +resharper_csharp_anonymous_method_declaration_braces = next_line_shifted_2 +resharper_csharp_accessor_owner_declaration_braces = next_line +resharper_csharp_accessor_declaration_braces = next_line +resharper_csharp_case_block_braces = next_line_shifted_2 +resharper_csharp_initializer_braces = next_line_shifted_2 +resharper_csharp_use_continuous_indent_inside_initializer_braces = true +resharper_csharp_other_braces = next_line +resharper_csharp_allow_comment_after_lbrace = false +resharper_csharp_empty_block_style = multiline + +## Syntax Style + +# 'var' usage in declarations + +resharper_csharp_for_built_in_types = use_var +resharper_csharp_for_simple_types = use_var +resharper_csharp_for_other_types = use_var + +# Instance members qualification + +resharper_csharp_instance_members_qualify_members = none +resharper_csharp_instance_members_qualify_declared_in = base_class + +# Static members qualification + +resharper_csharp_static_members_qualify_with = declared_type +resharper_csharp_static_members_qualify_members = none + +# Built-in types + +resharper_csharp_builtin_type_reference_style = use_keyword +resharper_csharp_builtin_type_reference_for_member_access_style = use_keyword + +# Reference qualification and 'using' directives + +resharper_csharp_prefer_qualified_reference = false +resharper_csharp_add_imports_to_deepest_scope = false +resharper_csharp_qualified_using_at_nested_scope = false +resharper_csharp_allow_alias = true +resharper_csharp_can_use_global_alias = true + +# Modifiers + +resharper_csharp_default_private_modifier = explicit +resharper_csharp_default_internal_modifier = explicit +resharper_csharp_modifiers_order = public private protected internal file static extern new virtual abstract sealed override readonly unsafe required volatile async + +# Braces + +resharper_csharp_braces_for_ifelse = required +resharper_csharp_braces_for_for = required +resharper_csharp_braces_for_foreach = required +resharper_csharp_braces_for_while = required +resharper_csharp_braces_for_dowhile = required +resharper_csharp_braces_for_using = required +resharper_csharp_braces_for_lock = required +resharper_csharp_braces_for_fixed = required +resharper_csharp_braces_redundant = false + +# Code body + +resharper_csharp_method_or_operator_body = block_body +resharper_csharp_local_function_body = block_body +resharper_csharp_constructor_or_destructor_body = block_body +resharper_csharp_accessor_owner_body = accessors_with_block_body +resharper_csharp_namespace_body = block_scoped +resharper_csharp_use_heuristics_for_body_style = false + +# Trailing comma + +resharper_csharp_trailing_comma_in_multiline_lists = false +resharper_csharp_trailing_comma_in_singleline_lists = false + +# Object creation + +resharper_csharp_object_creation_when_type_evident = explicitly_typed +resharper_csharp_object_creation_when_type_not_evident = explicitly_typed + +# Default value + +resharper_csharp_default_value_when_type_evident = default_literal +resharper_csharp_default_value_when_type_not_evident = default_literal + +## Tabs, Indents, Alignment + +# Nested statements + +resharper_csharp_indent_nested_usings_stmt = false +resharper_csharp_indent_nested_fixed_stmt = false +resharper_csharp_indent_nested_lock_stmt = false +resharper_csharp_indent_nested_for_stmt = true +resharper_csharp_indent_nested_foreach_stmt = true +resharper_csharp_indent_nested_while_stmt = true + +# Parenthesis + +resharper_csharp_use_continuous_indent_inside_parens = true +resharper_csharp_indent_method_decl_pars = outside_and_inside +resharper_csharp_indent_invocation_pars = outside_and_inside +resharper_csharp_indent_statement_pars = outside_and_inside +resharper_csharp_indent_typeparam_angles = outside_and_inside +resharper_csharp_indent_typearg_angles = outside_and_inside +resharper_csharp_indent_pars = outside_and_inside + +# Preprocessor directives + +resharper_csharp_indent_preprocessor_if = no_indent +resharper_csharp_indent_preprocessor_region = usual_indent +resharper_csharp_indent_preprocessor_other = no_indent + +# Other indents + +resharper_indent_switch_labels = true +resharper_csharp_outdent_statement_labels = true +resharper_csharp_indent_type_constraints = true +resharper_csharp_stick_comment = false +resharper_csharp_place_comments_at_first_column = false +resharper_csharp_use_indent_from_previous_element = true +resharper_csharp_indent_braces_inside_statement_conditions = true + +# Align multiline constructs + +resharper_csharp_alignment_tab_fill_style = use_spaces +resharper_csharp_allow_far_alignment = true +resharper_csharp_align_multiline_parameter = true +resharper_csharp_align_multiline_extends_list = true +resharper_csharp_align_linq_query = true +resharper_csharp_align_multiline_binary_expressions_chain = true +resharper_csharp_outdent_binary_ops = false +resharper_csharp_align_multiline_calls_chain = true +resharper_csharp_outdent_dots = false +resharper_csharp_align_multiline_array_and_object_initializer = false +resharper_csharp_align_multiline_switch_expression = false +resharper_csharp_align_multiline_property_pattern = false +resharper_csharp_align_multiline_list_pattern = false +resharper_csharp_align_multiline_binary_patterns = false +resharper_csharp_outdent_binary_pattern_ops = false +resharper_csharp_indent_anonymous_method_block = true +resharper_csharp_align_first_arg_by_paren = false +resharper_csharp_align_multiline_argument = true +resharper_csharp_align_tuple_components = true +resharper_csharp_align_multiline_expression = true +resharper_csharp_align_multiline_statement_conditions = true +resharper_csharp_align_multiline_for_stmt = true +resharper_csharp_align_multiple_declaration = true +resharper_csharp_align_multline_type_parameter_list = true +resharper_csharp_align_multline_type_parameter_constrains = true +resharper_csharp_outdent_commas = false + +## Line Breaks + +# General + +resharper_csharp_keep_user_linebreaks = true +resharper_csharp_max_line_length = 140 +resharper_csharp_wrap_before_comma = false +resharper_csharp_wrap_before_eq = false +resharper_csharp_special_else_if_treatment = true +resharper_csharp_insert_final_newline = true + +# Arrangement of attributes + +resharper_csharp_keep_existing_attribute_arrangement = false +resharper_csharp_place_type_attribute_on_same_line = false +resharper_csharp_place_method_attribute_on_same_line = false +resharper_csharp_place_accessorholder_attribute_on_same_line = false +resharper_csharp_place_accessor_attribute_on_same_line = false +resharper_csharp_place_field_attribute_on_same_line = false +resharper_csharp_place_record_field_attribute_on_same_line = true + +# Arrangement of method signatures + +resharper_csharp_place_constructor_initializer_on_same_line = false +resharper_csharp_place_expr_method_on_single_line = true +resharper_csharp_place_expr_property_on_single_line = true +resharper_csharp_place_expr_accessor_on_single_line = true + +# Arrangement of type parameters, constraints, and base types + +resharper_csharp_place_type_constraints_on_same_line = false +resharper_csharp_wrap_before_first_type_parameter_constraint = true + +# Arrangement of declaration blocks + +resharper_csharp_place_abstract_accessorholder_on_single_line = true + +# Arrangement of statements + +resharper_new_line_before_else = true +resharper_new_line_before_while = true +resharper_new_line_before_catch = true +resharper_new_line_before_finally = true +resharper_wrap_for_stmt_header_style = chop_if_long +resharper_wrap_multiple_declaration_style = chop_always + +## Spaces + +# Preserve existing formatting + +resharper_csharp_extra_spaces = remove_all + +# Before parentheses in statements + +resharper_csharp_space_before_if_parentheses = true +resharper_csharp_space_before_while_parentheses = true +resharper_csharp_space_before_catch_parentheses = true +resharper_csharp_space_before_switch_parentheses = true +resharper_csharp_space_before_for_parentheses = true +resharper_csharp_space_before_foreach_parentheses = true +resharper_csharp_space_before_using_parentheses = true +resharper_csharp_space_before_lock_parentheses = true +resharper_csharp_space_before_fixed_parentheses = true + +# Before other parentheses + +resharper_csharp_space_before_method_call_parentheses = false +resharper_csharp_space_before_empty_method_call_parentheses = false +resharper_csharp_space_before_method_parentheses = false +resharper_csharp_space_before_empty_method_parentheses = false +resharper_csharp_space_before_typeof_parentheses = false +resharper_csharp_space_before_default_parentheses = false +resharper_csharp_space_before_checked_parentheses = false +resharper_csharp_space_before_sizeof_parentheses = false +resharper_csharp_space_before_nameof_parentheses = false +resharper_csharp_space_before_new_parentheses = false +resharper_csharp_space_between_keyword_and_expression = true +resharper_csharp_space_between_keyword_and_type = false + +# Within parentheses in statements + +resharper_csharp_space_within_if_parentheses = false +resharper_csharp_space_within_while_parentheses = false +resharper_csharp_space_within_catch_parentheses = false +resharper_csharp_space_within_switch_parentheses = false +resharper_csharp_space_within_for_parentheses = false +resharper_csharp_space_within_foreach_parentheses = false +resharper_csharp_space_within_using_parentheses = false +resharper_csharp_space_within_lock_parentheses = false +resharper_csharp_space_within_fixed_parentheses = false + +# Within other parentheses + +resharper_csharp_space_within_parentheses = false +resharper_csharp_space_between_typecast_parentheses = false +resharper_csharp_space_between_method_declaration_parameter_list_parentheses = false +resharper_csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +resharper_csharp_space_between_method_call_parameter_list_parentheses = false +resharper_csharp_space_between_method_call_empty_parameter_list_parentheses = false +resharper_csharp_space_within_typeof_parentheses = false +resharper_csharp_space_within_default_parentheses = false +resharper_csharp_space_within_checked_parentheses = false +resharper_csharp_space_within_sizeof_parentheses = false +resharper_csharp_space_within_nameof_parentheses = false +resharper_csharp_space_within_new_parentheses = false + +# Around array brackets + +resharper_csharp_space_before_array_access_brackets = false +resharper_csharp_space_before_open_square_brackets = false +resharper_csharp_space_before_array_rank_brackets = false +resharper_csharp_space_within_array_access_brackets = false +resharper_csharp_space_between_square_brackets = false +resharper_csharp_space_within_array_rank_brackets = false +resharper_csharp_space_within_array_rank_empty_brackets = false +resharper_csharp_space_between_empty_square_bracket = false + +# Around angle brackets + +resharper_csharp_space_before_type_parameter_angle = false +resharper_csharp_space_before_type_argument_angle = false +resharper_csharp_space_within_type_parameter_angles = false +resharper_csharp_space_within_type_argument_angles = false + +### ReSharper code style for XMLDOC ### + +## Tabs and indents + +resharper_xmldoc_indent_style = space +# ReSharper currently ignores this setting. See https://youtrack.jetbrains.com/issue/RSRP-465678/XMLDOC-indent-settings-ignored. +resharper_xmldoc_indent_size = 2 +resharper_xmldoc_tab_width = 2 +resharper_xmldoc_alignment_tab_fill_style = use_spaces +resharper_xmldoc_allow_far_alignment = true + +## Line wrapping + +resharper_xmldoc_max_line_length = 140 +resharper_xmldoc_wrap_tags_and_pi = false + +## Processing instructions + +resharper_xmldoc_spaces_around_eq_in_pi_attribute = false +resharper_xmldoc_space_after_last_pi_attribute = false +resharper_xmldoc_pi_attribute_style = on_single_line +resharper_xmldoc_pi_attributes_indent = align_by_first_attribute +resharper_xmldoc_blank_line_after_pi = false + +## Inside of tag header + +resharper_xmldoc_spaces_around_eq_in_attribute = false +resharper_xmldoc_space_after_last_attribute = false +resharper_xmldoc_space_before_self_closing = false +resharper_xmldoc_attribute_style = do_not_touch +resharper_xmldoc_attribute_indent = align_by_first_attribute + +## Tag content + +resharper_xmldoc_keep_user_linebreaks = true +resharper_xmldoc_linebreaks_inside_tags_for_multiline_elements = true +resharper_xmldoc_linebreaks_inside_tags_for_elements_with_child_elements = false +resharper_xmldoc_spaces_inside_tags = false +resharper_xmldoc_wrap_text = false +resharper_xmldoc_wrap_around_elements = false +# ReSharper currently ignores the 'resharper_xmldoc_indent_size' setting. Once https://youtrack.jetbrains.com/issue/RSRP-465678/XMLDOC-indent-settings-ignored +# is fixed, we should change the value of this setting to 'one_indent'. +resharper_xmldoc_indent_child_elements = zero_indent +resharper_xmldoc_indent_text = zero_indent + +## Around tags + +resharper_xmldoc_max_blank_lines_between_tags = 1 +resharper_xmldoc_linebreak_before_multiline_elements = true +resharper_xmldoc_linebreak_before_singleline_elements = false + +[*.{xml,xsd,csproj,targets,proj,props,runsettings,config}] + +#### ReSharper code style for XML #### + +## Tabs and indents + +resharper_xml_indent_style = space +resharper_xml_indent_size = 4 +resharper_xml_tab_width = 4 +resharper_xml_alignment_tab_fill_style = use_spaces +resharper_xml_allow_far_alignment = true + +## Line wrapping + +resharper_xml_wrap_tags_and_pi = false + +## Processing instructions + +resharper_xml_spaces_around_eq_in_pi_attribute = false +resharper_xml_space_after_last_pi_attribute = false +resharper_xml_pi_attribute_style = on_single_line +resharper_xml_pi_attributes_indent = align_by_first_attribute +resharper_xml_blank_line_after_pi = false + +## Inside of tag header + +resharper_xml_spaces_around_eq_in_attribute = false +resharper_xml_space_after_last_attribute = false +resharper_xml_space_before_self_closing = true +resharper_xml_attribute_style = do_not_touch +resharper_xml_attribute_indent = align_by_first_attribute + +## Tag content + +resharper_xml_keep_user_linebreaks = true +resharper_xml_linebreaks_inside_tags_for_multiline_elements = false +resharper_xml_linebreaks_inside_tags_for_elements_with_child_elements = false +resharper_xml_linebreaks_inside_tags_for_elements_longer_than = false +resharper_xml_spaces_inside_tags = false +resharper_xml_wrap_text = false +resharper_xml_wrap_around_elements = false +resharper_xml_indent_child_elements = one_indent +resharper_xml_indent_text = zero_indent +resharper_xml_max_blank_lines_between_tags = 1 +resharper_xml_linebreak_before_multiline_elements = false +resharper_xml_linebreak_before_singleline_elements = false + +## Other + +resharper_xml_insert_final_newline = true diff --git a/.gitignore b/.gitignore index 7d6bdbf6..37793444 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,6 @@ project.lock.json # Build outputs build/target/ + +# Benchmark results +BenchmarkDotNet.Artifacts/ diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 00000000..31f99465 --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1 @@ +* @drieseng @WojciechNagorski diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 00000000..0baf38f4 --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,44 @@ + + + + + + true + $(MSBuildThisFileDirectory)src\Renci.SshNet.snk + true + latest + 9999 + true + false + + + + + false + preview-All + true + + + + + + + + + + + + diff --git a/LICENSE b/LICENSE index d13cc4b2..f2aef4f3 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,7 @@ The MIT License (MIT) +Copyright (c) Renci, Oleg Kapeljushnik, Gert Driesen and contributors + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights diff --git a/README.md b/README.md index b44ae97b..9ab4978f 100644 --- a/README.md +++ b/README.md @@ -80,7 +80,7 @@ the missing test once you figure things out. 🤓 * RSA in OpenSSL PEM and ssh.com format * DSA in OpenSSL PEM and ssh.com format * ECDSA 256/384/521 in OpenSSL PEM format -* ED25519 in OpenSSH key format +* ECDSA 256/384/521, ED25519 and RSA in OpenSSH key format Private keys can be encrypted using one of the following cipher methods: * DES-EDE3-CBC @@ -97,6 +97,8 @@ Private keys can be encrypted using one of the following cipher methods: * ecdsa-sha2-nistp256 * ecdsa-sha2-nistp384 * ecdsa-sha2-nistp521 +* rsa-sha2-512 +* rsa-sha2-256 * ssh-rsa * ssh-dss @@ -116,15 +118,9 @@ Private keys can be encrypted using one of the following cipher methods: ## Framework Support **SSH.NET** supports the following target frameworks: -* .NET Framework 3.5 -* .NET Framework 4.0 (and higher) -* .NET Standard 1.3 -* .NET Standard 2.0 -* Silverlight 4 -* Silverlight 5 -* Windows Phone 7.1 -* Windows Phone 8.0 -* Universal Windows Platform 10 +* .NETFramework 4.6.2 (and higher) +* .NET Standard 2.0 and 2.1 +* .NET 6 (and higher) ## Usage @@ -149,45 +145,18 @@ using (var client = new SftpClient(connectionInfo)) Establish a SSH connection using user name and password, and reject the connection if the fingerprint of the server does not match the expected fingerprint: ```cs -byte[] expectedFingerPrint = new byte[] { - 0x66, 0x31, 0xaf, 0x00, 0x54, 0xb9, 0x87, 0x31, - 0xff, 0x58, 0x1c, 0x31, 0xb1, 0xa2, 0x4c, 0x6b - }; +string expectedFingerPrint = "LKOy5LvmtEe17S4lyxVXqvs7uPMy+yF79MQpHeCs/Qo"; using (var client = new SshClient("sftp.foo.com", "guest", "pwd")) { client.HostKeyReceived += (sender, e) => { - if (expectedFingerPrint.Length == e.FingerPrint.Length) - { - for (var i = 0; i < expectedFingerPrint.Length; i++) - { - if (expectedFingerPrint[i] != e.FingerPrint[i]) - { - e.CanTrust = false; - break; - } - } - } - else - { - e.CanTrust = false; - } + e.CanTrust = expectedFingerPrint.Equals(e.FingerPrintSHA256); }; client.Connect(); } ``` -## Building SSH.NET - -Software | net35 | net40 | netstandard1.3 | netstandard2.0 | sl4 | sl5 | wp71 | wp8 | uap10.0 | ---------------------------------- | :---: | :---: | :------------: | :------------: | :-: | :-: | :--: | :-: | :-----: | -Windows Phone SDK 8.0 | | | | | x | x | x | x | -Visual Studio 2012 Update 5 | x | x | | | x | x | x | x | -Visual Studio 2015 Update 3 | x | x | | | | x | | x | x -Visual Studio 2017 | x | x | x | x | | | | | -Visual Studio 2019 | x | x | x | x | | | | | - ## Supporting SSH.NET Do you or your company rely on **SSH.NET** in your projects? If you want to encourage us to keep on going and show us that you appreciate our work, please consider becoming a [sponsor](https://github.com/sponsors/sshnet) through GitHub Sponsors. diff --git a/appveyor.yml b/appveyor.yml index c5bb4a18..c9bb5168 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,14 +1,14 @@ -os: Visual Studio 2019 +os: Visual Studio 2022 before_build: - - nuget restore src\Renci.SshNet.VS2019.sln + - nuget restore src\Renci.SshNet.sln build: - project: src\Renci.SshNet.VS2019.sln + project: src\Renci.SshNet.sln verbosity: minimal test_script: - cmd: >- - vstest.console /logger:Appveyor src\Renci.SshNet.Tests\bin\Debug\net40\Renci.SshNet.Tests.dll /TestCaseFilter:"TestCategory!=integration&TestCategory!=LongRunning" - - vstest.console /logger:Appveyor src\Renci.SshNet.Tests\bin\Debug\net35\Renci.SshNet.Tests.dll /TestCaseFilter:"TestCategory!=integration&TestCategory!=LongRunning" \ No newline at end of file + vstest.console /logger:Appveyor src\Renci.SshNet.Tests\bin\Debug\net462\Renci.SshNet.Tests.dll /TestCaseFilter:"TestCategory!=integration" --blame + + vstest.console /logger:Appveyor src\Renci.SshNet.Tests\bin\Debug\net7.0\Renci.SshNet.Tests.dll /TestCaseFilter:"TestCategory!=integration" --blame diff --git a/build/build.proj b/build/build.proj index 40ef012b..97b406cf 100644 --- a/build/build.proj +++ b/build/build.proj @@ -8,86 +8,41 @@ MSBuildTasks 1.5.0.214 - + - - $(MSBuildThisFileDirectory)..\src\Renci.SshNet.VS2012.sln - 14.0 - 14.0 - - - $(MSBuildThisFileDirectory)..\src\Renci.SshNet.VS2015.sln - 14.0 - 14.0 - - - - - - $(MSBuildThisFileDirectory)..\src\Renci.SshNet.VS2019.sln - 16.0 + + $(MSBuildThisFileDirectory)..\src\Renci.SshNet.sln + 17.0 - - Renci.SshNet.WindowsPhone\bin\$(Configuration) - wp71 - - - Renci.SshNet.WindowsPhone8\bin\$(Configuration) - wp8 - - - Renci.SshNet.Silverlight\bin\$(Configuration) - sl4 - - - Renci.SshNet.Silverlight5\bin\$(Configuration) - sl5 - - - Renci.SshNet.UAP10\bin\$(Configuration) - uap10 - - - - - - Renci.SshNet\bin\$(Configuration)\net35 - net35 - - - Renci.SshNet\bin\$(Configuration)\net40 - net40 - - - Renci.SshNet\bin\$(Configuration)\netstandard1.3 - netstandard1.3 + + Renci.SshNet\bin\$(Configuration)\net462 + net462 Renci.SshNet\bin\$(Configuration)\netstandard2.0 netstandard2.0 + + Renci.SshNet\bin\$(Configuration)\netstandard2.1 + netstandard2.1 + + + Renci.SshNet\bin\$(Configuration)\net6.0 + net6.0 + + + Renci.SshNet\bin\$(Configuration)\net7.0 + net7.0 + - - - - - + - - - - - Configuration=Release;VisualStudioVersion=%(VisualStudioVersionClassic.VisualStudioVersion) - - - - @@ -99,26 +54,11 @@ - - - - - - - - - - - Configuration=Release;VisualStudioVersion=%(VisualStudioVersionClassic.VisualStudioVersion) - - - - - + @@ -131,12 +71,7 @@ - - - - - - + @@ -153,16 +88,6 @@ - - - - - - - - - - diff --git a/build/nuget/SSH.NET.nuspec b/build/nuget/SSH.NET.nuspec index 03830583..55ce42e2 100644 --- a/build/nuget/SSH.NET.nuspec +++ b/build/nuget/SSH.NET.nuspec @@ -6,7 +6,7 @@ SSH.NET Renci olegkap,drieseng - https://github.com/sshnet/SSH.NET/blob/master/LICENSE + MIT https://github.com/sshnet/SSH.NET/ false SSH.NET is a Secure Shell (SSH) library for .NET, optimized for parallelism and with broad framework support. @@ -16,38 +16,19 @@ en-US ssh scp sftp - - - - - - - - - - - - - + - - + + - + - + - - - - - - - \ No newline at end of file diff --git a/build/sandcastle/SSH.NET.shfbproj b/build/sandcastle/SSH.NET.shfbproj index 29fff93e..15d8dc2f 100644 --- a/build/sandcastle/SSH.NET.shfbproj +++ b/build/sandcastle/SSH.NET.shfbproj @@ -1,21 +1,22 @@  - + + v4.6.2 + Debug AnyCPU + 2.0 {f7266fb1-f50a-4a5b-b35a-5ea8ebdc1be9} - 2015.6.5.0 + 2017.9.26.0 Documentation Documentation Documentation - .NET Framework 4.0 + .NET Framework 4.6.2 ..\target\help SshNet.Help en-US @@ -24,25 +25,15 @@ C# Blank False - VS2010 + VS2013 False Guid - SSH.NET Client Library Documenation + SSH.NET Client Library Documentation AboveNamespaces - - - - - {@HelpFormatOutputPaths} - - - - - - + - - + + Summary, Parameter, Returns, AutoDocumentCtors, TypeParameter, AutoDocumentDispose OnlyWarningsAndErrors @@ -54,7 +45,7 @@ True + the build. The others are optional common platform types that may appear. --> @@ -73,4 +64,4 @@ - \ No newline at end of file + diff --git a/images/logo/png/SS-NET-1280x640.png b/images/logo/png/SS-NET-1280x640.png new file mode 100644 index 00000000..d2e5bfd4 Binary files /dev/null and b/images/logo/png/SS-NET-1280x640.png differ diff --git a/src/Renci.SshNet.Tests/Data/Key.ECDSA.Encrypted.txt b/src/Data/Key.ECDSA.Encrypted.txt similarity index 100% rename from src/Renci.SshNet.Tests/Data/Key.ECDSA.Encrypted.txt rename to src/Data/Key.ECDSA.Encrypted.txt diff --git a/src/Renci.SshNet.Tests/Data/Key.ECDSA.txt b/src/Data/Key.ECDSA.txt similarity index 100% rename from src/Renci.SshNet.Tests/Data/Key.ECDSA.txt rename to src/Data/Key.ECDSA.txt diff --git a/src/Renci.SshNet.Tests/Data/Key.ECDSA384.Encrypted.txt b/src/Data/Key.ECDSA384.Encrypted.txt similarity index 100% rename from src/Renci.SshNet.Tests/Data/Key.ECDSA384.Encrypted.txt rename to src/Data/Key.ECDSA384.Encrypted.txt diff --git a/src/Renci.SshNet.Tests/Data/Key.ECDSA384.txt b/src/Data/Key.ECDSA384.txt similarity index 100% rename from src/Renci.SshNet.Tests/Data/Key.ECDSA384.txt rename to src/Data/Key.ECDSA384.txt diff --git a/src/Renci.SshNet.Tests/Data/Key.ECDSA521.Encrypted.txt b/src/Data/Key.ECDSA521.Encrypted.txt similarity index 100% rename from src/Renci.SshNet.Tests/Data/Key.ECDSA521.Encrypted.txt rename to src/Data/Key.ECDSA521.Encrypted.txt diff --git a/src/Renci.SshNet.Tests/Data/Key.ECDSA521.txt b/src/Data/Key.ECDSA521.txt similarity index 100% rename from src/Renci.SshNet.Tests/Data/Key.ECDSA521.txt rename to src/Data/Key.ECDSA521.txt diff --git a/src/Data/Key.OPENSSH.ECDSA.Encrypted.txt b/src/Data/Key.OPENSSH.ECDSA.Encrypted.txt new file mode 100644 index 00000000..ab76db4a --- /dev/null +++ b/src/Data/Key.OPENSSH.ECDSA.Encrypted.txt @@ -0,0 +1,9 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABCPSPglZ3 +w/7DmCJxYohONLAAAAEAAAAAEAAABoAAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlz +dHAyNTYAAABBBK6YY2NwwLDSMnJTD+a4OfitCDuG/MnY/AstPgh54xMrZF6Qr0U1H6kRMK +Y6JJsj31CI97qDYrnTA00Sx5Jy6ywAAACwq4qisorVCP6yvrmf/fcPacX4+FVEmrHNn3fW +TiYsat7oKoItqTiDaHkIloSX93ue3fzcKXpGPR/qnpu4SezkhL9Uk6ntiwO4coB/kbEnjk +IFY6ZK0HENRXkdIuDG9qmoB0wjVPJ6L9e5RWZwiCPvNI2O60bpKOUs+tUSah1W7eTWy5Ss +ttdTgmwqS84c5+uitK1DJh2jsDqfdGm7h1XpDJsRmIEXxTVu/EdtD0hZ/x4= +-----END OPENSSH PRIVATE KEY----- \ No newline at end of file diff --git a/src/Data/Key.OPENSSH.ECDSA.txt b/src/Data/Key.OPENSSH.ECDSA.txt new file mode 100644 index 00000000..94f16bcb --- /dev/null +++ b/src/Data/Key.OPENSSH.ECDSA.txt @@ -0,0 +1,9 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS +1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQSP3ZTb37LFvSmKweu03A5s/cwcw3+3 +jL1LqQK6D929xY1J2J6S91LXOBpBfz4l+8Ng7sWhu9P/hF/wmB2QRygrAAAAqMq583bKuf +N2AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBI/dlNvfssW9KYrB +67TcDmz9zBzDf7eMvUupAroP3b3FjUnYnpL3Utc4GkF/PiX7w2DuxaG70/+EX/CYHZBHKC +sAAAAhALVqID3K/N7IazKNbhrg09r7rLLtjy81RLV+VDxloQnxAAAAC3NyaW5rZXNATkVP +AQIDBA== +-----END OPENSSH PRIVATE KEY----- \ No newline at end of file diff --git a/src/Data/Key.OPENSSH.ECDSA384.Encrypted.txt b/src/Data/Key.OPENSSH.ECDSA384.Encrypted.txt new file mode 100644 index 00000000..50065450 --- /dev/null +++ b/src/Data/Key.OPENSSH.ECDSA384.Encrypted.txt @@ -0,0 +1,11 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABAMZphcrt +UKJMlSabtzt2GdAAAAEAAAAAEAAACIAAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlz +dHAzODQAAABhBFL5NEL9uRhgkF2q+8m58EvtZq4mDGgcVEzafPRuNIn1018m9KuqNpOQ6d ++435n+MRYThe4MUdijSIDuopX2i14Z35oKZ9x2LsV+RxQczjmbnoWZdvgcvdOo6jiJdY7X +JwAAAOBvXOaTq8vPRy7y5BBzr26QAYouJfGprYOqpywiIAZaICu0FJ8EXmmen6310CTG6Z +CZ4VhC5MWCWRYTaOnPNn8FvGqo2bxEqWZmyZfVvv1Z35MtSAZEfwgfXaOZKJ/lPKsRndg5 +okpqNU1aG2u+4J7eZ7QyCD/1RCCEL5wwVcrDeuMkTDPpnJc1NEGz8HbfcZ5xZavrz6Wa9t +tX7pFICqK9IIeOGMJ2WRXR6sQGyag+jNn9KmsIya7hkNJVeZeY2GKAk2s/0vxfYx9RFD55 +ewB34oHyTdxAQT3L+FZT6XfRHw== +-----END OPENSSH PRIVATE KEY----- \ No newline at end of file diff --git a/src/Data/Key.OPENSSH.ECDSA384.txt b/src/Data/Key.OPENSSH.ECDSA384.txt new file mode 100644 index 00000000..e5e6e06a --- /dev/null +++ b/src/Data/Key.OPENSSH.ECDSA384.txt @@ -0,0 +1,10 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAiAAAABNlY2RzYS +1zaGEyLW5pc3RwMzg0AAAACG5pc3RwMzg0AAAAYQRTP1DMXoHgW+RX+S/NxUEElou1cmLD +6CEiR+zpzaGG6mzl6LhUY+Z3f2M3u4u7tcM8TgB/jiHbnI9TaeN5QK4HX1D9DXkH5RhfnL +frm3kCTNoCFKd0Wa/QAvKrlNKiRi8AAADYlABjHJQAYxwAAAATZWNkc2Etc2hhMi1uaXN0 +cDM4NAAAAAhuaXN0cDM4NAAAAGEEUz9QzF6B4FvkV/kvzcVBBJaLtXJiw+ghIkfs6c2hhu +ps5ei4VGPmd39jN7uLu7XDPE4Af44h25yPU2njeUCuB19Q/Q15B+UYX5y365t5AkzaAhSn +dFmv0ALyq5TSokYvAAAAMAXLUgK32yWzUrpeLzpdFB2/eiNnkxQlu5OneTPukKcZYclfgo +jv0YHK5LCvAtF8lwAAAAtzcmlua2VzQE5FTwECAwQF +-----END OPENSSH PRIVATE KEY----- \ No newline at end of file diff --git a/src/Data/Key.OPENSSH.ECDSA521.Encrypted.txt b/src/Data/Key.OPENSSH.ECDSA521.Encrypted.txt new file mode 100644 index 00000000..da831cf9 --- /dev/null +++ b/src/Data/Key.OPENSSH.ECDSA521.Encrypted.txt @@ -0,0 +1,12 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABBWjIyzbM +MQ3UPE8BiQ0n4LAAAAEAAAAAEAAACsAAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlz +dHA1MjEAAACFBAH9BVM6bRhbELtgdMGsin5lM42R2EWoT+6Akakl5rQy2tHHLIYGLEfaqI ++0iUo2V6MxEf9w0hVz6SEsF+yDgyrYPQCIieaB1oBvIl+PZmL1XsuAXs2uMRsNJb4myGU/ +DiekxqzIPa0LMrBZ4xmErcn5Gazkw1EA0B3HoaW5wj+geI/efQAAARDi+GGTYH1T+5Dd8N +EVCiL+J7fm8uP8yAcvQNh3JBYIf1g/GZ0hJDuA47fcTzXEfTGZLGWdgaE8cxIUICpjBoak +EpNS1HyhqYZAt2J8o/14t2GbXczJfoQLOIQl2S1zXQ9shof12odu9DGcBhSAz9hswlndBE +d99uCz/ymzwQ0i2Pp+urUXo7+YXB6HMh9YTMeGQAiDJFO3NPDqDczfUECtTUkQMhy8r06m +hAp/oZ6K1KBbZzdc0xyqDePKAqqyHnN4FD7Wfv11SWoOhlUcEVg2ZvNj/O+CsoWzMpN+dt +DPKZHmH/kegWKBsdtAC9f5Hg3b2oQAK0pKghms1+/J9iilnIMwv80CPzGdv0YAG9Vx5w== +-----END OPENSSH PRIVATE KEY----- diff --git a/src/Data/Key.OPENSSH.ECDSA521.txt b/src/Data/Key.OPENSSH.ECDSA521.txt new file mode 100644 index 00000000..0ad09eb2 --- /dev/null +++ b/src/Data/Key.OPENSSH.ECDSA521.txt @@ -0,0 +1,12 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAArAAAABNlY2RzYS +1zaGEyLW5pc3RwNTIxAAAACG5pc3RwNTIxAAAAhQQAa7p4WVga+08qu160BrdzKyRNJMQC +eGhFluNdq73/uaW3qlqoEiaNtc5uB7HHyjCWQTmfzrSRLRZ7YBwUancwyh4Aq6gBgGsXVz +wNvY3kxRDlGrSfMmsHlXz41dgw9wm1fBcf97niexRQ/xGlhkyf7IYlQ/s5BpXF2lS9l0H5 +hBippRgAAAEI/9prf//aa38AAAATZWNkc2Etc2hhMi1uaXN0cDUyMQAAAAhuaXN0cDUyMQ +AAAIUEAGu6eFlYGvtPKrtetAa3cyskTSTEAnhoRZbjXau9/7mlt6paqBImjbXObgexx8ow +lkE5n860kS0We2AcFGp3MMoeAKuoAYBrF1c8Db2N5MUQ5Rq0nzJrB5V8+NXYMPcJtXwXH/ +e54nsUUP8RpYZMn+yGJUP7OQaVxdpUvZdB+YQYqaUYAAAAQXwQnI20tNxwLKHPMDmumblD +b0sBqW5Y9248L//x4sWFrkjk6k1LcZno9KLqz8+tIFMJ5sji+axRoUZCXb3cIPzPAAAAC3 +NyaW5rZXNATkVP +-----END OPENSSH PRIVATE KEY----- \ No newline at end of file diff --git a/src/Renci.SshNet.Tests/Data/Key.OPENSSH.ED25519.Encrypted.txt b/src/Data/Key.OPENSSH.ED25519.Encrypted.txt similarity index 100% rename from src/Renci.SshNet.Tests/Data/Key.OPENSSH.ED25519.Encrypted.txt rename to src/Data/Key.OPENSSH.ED25519.Encrypted.txt diff --git a/src/Renci.SshNet.Tests/Data/Key.OPENSSH.ED25519.txt b/src/Data/Key.OPENSSH.ED25519.txt similarity index 100% rename from src/Renci.SshNet.Tests/Data/Key.OPENSSH.ED25519.txt rename to src/Data/Key.OPENSSH.ED25519.txt diff --git a/src/Data/Key.OPENSSH.RSA.Encrypted.txt b/src/Data/Key.OPENSSH.RSA.Encrypted.txt new file mode 100644 index 00000000..b9eed58b --- /dev/null +++ b/src/Data/Key.OPENSSH.RSA.Encrypted.txt @@ -0,0 +1,28 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABCoWhCSaG +psKT80oPIOAlqJAAAAEAAAAAEAAAEXAAAAB3NzaC1yc2EAAAADAQABAAABAQCxOfnmxC1g +mmX18LCBG/X73BoCXQEBEAJz3V9w0FMTgUBebK3fkfOMLNzWn5aMR608wQHOEPYhHffCJf +dJR6lUWLHZz5+EZRfM9oHpysMtToYQoGtb9xM7D5J3lnWZgSea2R7xSeqpClN5nQUMGu8y +2d2S3g9o1vrTdeu71u09QFOx2AXBPUmCjpCuBlNyYEEQMfRMtQ6PDbPdvLM1uylbQqB+/6 +jMsCyEFvlLit9GcZ7ItKQN+jsZNmP+f7ytVXLsZTjPLd5mWWrf6T1T1Xt9DBoLXnMrmDiC +f/EXiTYonIO6B0FHvNUCrDzZ7rxIebqePLes1Q2yoUqmC8g+cww3AAADwNRNGSnB7cRpgU +BxdyC0ofj0hUONXjmoT+OGPph9lgZMUnzcon9Z1bpsJuMoRXL14Cdbds7YPmw1YB94Uc+S +8QexLJG0wGel2yvzJhU+QFsLeVRS4tayERFXGCoVpu7RunEYy+hvaiX5CD+luEkiarfj9I +N8+9QUMhDYkELwWBV4rde18Vr8m1P1FuFgqikY0TfSKUGCkvjl4FvDxrxqsewaEkkzwRTI +PhOFCCM5jBPWE+uWVcwKoidvAqcNbmwIzDNZGwXtrAvYYzZa62C/MNLHuFU1weuJiM8sYa +6iKrk681BrrpGcSEZEXd41CFY3BWlIDTozrWn03xFlIpeLG2YMPcuYqFhR/41BJfa+fW5B +Ei0SuUx2xjdRiamqpPku4H6ulkjl0KlFCr976Y2V1JZMQh7bd0huubmf4P4poBk6ZgGpSf +snhcv1HjCVkvfA2yhOcXogzK2HOZgDS5sdSb/kUGURdjlj6ccSzc3OYaHAy9gZXj8Q58pA +4KrXTDlCJ9BTR8PIND54j6gMKu5ijX0TP9nJf/hG9GXx+Xss8T3xdPxdNBapPCcuxGZGJN +H+KFqrpmZYHm0evqFPS7BCUp2VvID6SMgrTYiH0IIbMHLStfdNchtn3EudMbW9vRhxg3Do +npT7Px2JYp87PNoHg2eOx0yGy9r81n2+Wi7SpGCWD8MFfxqd4JIQ8+zjrIRAA1q53uuSUh +m/hlmJWEjQWmcBw5bKrOU0CfGGoT3o6HWYRQ9d5+kKeoS+dOINxxf80G5b7vOrE3PbFxT3 +W8zwRd90Msr3LXgPaN0V4RJeBX38e0EvVbArL2MgSs/BC5aID0N0Sqiu+13AqqNYxj6RH2 +FA7FN+BBa16fvdi5h5kNnZUrQUKOAImjEE494O8uGKQImviGqB5PY6DJqHPTtn7RSwFx9E +rR7nbAZPTucIN/OIfURxTedhROk0PXjWnwpjuz+UpaMRWqgWTv3bLOuqorqMLibAFLRQEQ +6pR0wbmTpTfEW1jNmAohxB4N14YdSfhThzkCAgpQW6UCLc83y3EDzQFi5862a+2ixULKhK +220tZRk2GU7OFAPRpgQ/sxptGqZbNdOV80wk1MgykoFkoptRkm7bfJcdLHZnP7E6yU0ssP +rCbQlfD0/dD2QE/7HqxHsipNNuEagULjK6WUYXkpx1Siq2vecjZw8dNp7EBh+KlujEm+Dr +R7KFdFCw8DUwrzXwfMIogeRVbW8H0/fQEqsX5oPLTEOnNBjzf8pHush7CCrprbo0ZK3xFp +Vr3LUCoA== +-----END OPENSSH PRIVATE KEY----- \ No newline at end of file diff --git a/src/Data/Key.OPENSSH.RSA.txt b/src/Data/Key.OPENSSH.RSA.txt new file mode 100644 index 00000000..893001eb --- /dev/null +++ b/src/Data/Key.OPENSSH.RSA.txt @@ -0,0 +1,27 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn +NhAAAAAwEAAQAAAQEA7W7Oigi7Hj1msa2l8HimLPzagVmE/CseMKCRxPMTtWTvoZdMt7hf +kthWA7h20a7oSSeH2t7FqeOpVNGFYvRV3BVWsKZJMRcdJiTCZAjH38rYk90XvZ3mKrKN/3 +fcQsh4OiPnWqT6HSqg14oiV8UPtUwnE65skWSxvxt+ELlVpCqG5vE2O/GNDKQE7FzYt6vQ +ihMFSABBqjvau0mjX002KYiCMr6vgl8XYkDA4n5+JyQSQxznnfrL93ZoaqujRP2bT5UhXg +bzr8HFwuqQGvURECZTTI8Biv/6tOIn9x2hEaN6vxDLtXc99E2d6NW6cdriwaGFJ4jgVWDE +5mU006XdPQAAA8jzN6zf8zes3wAAAAdzc2gtcnNhAAABAQDtbs6KCLsePWaxraXweKYs/N +qBWYT8Kx4woJHE8xO1ZO+hl0y3uF+S2FYDuHbRruhJJ4fa3sWp46lU0YVi9FXcFVawpkkx +Fx0mJMJkCMffytiT3Re9neYqso3/d9xCyHg6I+dapPodKqDXiiJXxQ+1TCcTrmyRZLG/G3 +4QuVWkKobm8TY78Y0MpATsXNi3q9CKEwVIAEGqO9q7SaNfTTYpiIIyvq+CXxdiQMDifn4n +JBJDHOed+sv3dmhqq6NE/ZtPlSFeBvOvwcXC6pAa9REQJlNMjwGK//q04if3HaERo3q/EM +u1dz30TZ3o1bpx2uLBoYUniOBVYMTmZTTTpd09AAAAAwEAAQAAAQEA6Xgq+gppzOt9nrts +z5Ajf1tHlSesn7XaYuCRVgPb3mOZSuEW3BUdTa0Sr2fk1nzSBpUrfqnN3idyK2g3bD1sbB +RDgUKR+AaNcCN3TpxfxgyVeJhQLvEkEdovzQRUfwrXRfxmE3jkRGfVbvxylrG8p35xcmXy +del46r2i8dj8gIY3tKp0RMvZ4ZlNbhWHPd5OxyHWL9e3gbOSyIyjQgTKZezhzErS/X/KJ/ +XYqzyBAqNqZ2Rg1kKiHRlHS6KEI2tyFwYfh+Rb6L9xch1SqOtQhTWirmxS25dpGD2jgalX +eyiw8PmuqTiWCqUmUMx6MdF3tFsirr54K4QA9kqMeaRLtQAAAIEAsUQT0Uhq7l0ugTd4Y9 +89bH6eW0fol21/m7B5zkJQepNadUPTs188uvv4inW8n2O3RCanXWHZfCJ1AsR/MEEW2C6Q +DtvqKXHbzfWQlCYSVxB17CjURKa8fNaIAk98zgmNNwO53NBleyrUhPgvm3xt7ACgpzXY5R +wNJL8/a0WOmgwAAACBAP508Op6wWPAwn1JfBZuqQtjcfnJeN4NkYQBdybn0vVu5UdyqSQL +a8hlAzROhA+qJvrlsZgM9h8CyLTyuim8likZHocwO13zBTdVaQ8c2lJvf2uXTIXNZHseS7 +ITkfBiO1hSB4z8RDkOr35mGfdbyJIFAwFZF4Xs8WnQF+vHEadrAAAAgQDu329eCwVFf9g0 +zNHfZu31p0WtErcsRv57fq+UoPtov8nxUF71oOWe5KSGnGtMICI31kBtPhUbvfOmuqNrgJ +BjgjbPQmi0xSAE5L3QuEKRNjlaE3/WadKBwzhJDtauuYk1ifkrdAVp67XyQ5puyuGgVaQB +NPbrxA9g1IbyeL4/9wAAAAtzcmlua2VzQE5FTwECAwQFBg== +-----END OPENSSH PRIVATE KEY----- \ No newline at end of file diff --git a/src/Renci.SshNet.Tests/Data/Key.RSA.Encrypted.Aes.128.CBC.12345.txt b/src/Data/Key.RSA.Encrypted.Aes.128.CBC.12345.txt similarity index 100% rename from src/Renci.SshNet.Tests/Data/Key.RSA.Encrypted.Aes.128.CBC.12345.txt rename to src/Data/Key.RSA.Encrypted.Aes.128.CBC.12345.txt diff --git a/src/Renci.SshNet.Tests/Data/Key.RSA.Encrypted.Aes.192.CBC.12345.txt b/src/Data/Key.RSA.Encrypted.Aes.192.CBC.12345.txt similarity index 100% rename from src/Renci.SshNet.Tests/Data/Key.RSA.Encrypted.Aes.192.CBC.12345.txt rename to src/Data/Key.RSA.Encrypted.Aes.192.CBC.12345.txt diff --git a/src/Renci.SshNet.Tests/Data/Key.RSA.Encrypted.Aes.256.CBC.12345.txt b/src/Data/Key.RSA.Encrypted.Aes.256.CBC.12345.txt similarity index 100% rename from src/Renci.SshNet.Tests/Data/Key.RSA.Encrypted.Aes.256.CBC.12345.txt rename to src/Data/Key.RSA.Encrypted.Aes.256.CBC.12345.txt diff --git a/src/Renci.SshNet.Tests/Data/Key.RSA.Encrypted.Des.CBC.12345.txt b/src/Data/Key.RSA.Encrypted.Des.CBC.12345.txt similarity index 100% rename from src/Renci.SshNet.Tests/Data/Key.RSA.Encrypted.Des.CBC.12345.txt rename to src/Data/Key.RSA.Encrypted.Des.CBC.12345.txt diff --git a/src/Renci.SshNet.Tests/Data/Key.RSA.Encrypted.Des.Ede3.CBC.12345.txt b/src/Data/Key.RSA.Encrypted.Des.Ede3.CBC.12345.txt similarity index 100% rename from src/Renci.SshNet.Tests/Data/Key.RSA.Encrypted.Des.Ede3.CBC.12345.txt rename to src/Data/Key.RSA.Encrypted.Des.Ede3.CBC.12345.txt diff --git a/src/Renci.SshNet.Tests/Data/Key.RSA.Encrypted.Des.Ede3.CFB.1234567890.txt b/src/Data/Key.RSA.Encrypted.Des.Ede3.CFB.1234567890.txt similarity index 100% rename from src/Renci.SshNet.Tests/Data/Key.RSA.Encrypted.Des.Ede3.CFB.1234567890.txt rename to src/Data/Key.RSA.Encrypted.Des.Ede3.CFB.1234567890.txt diff --git a/src/Renci.SshNet.Tests/Data/Key.RSA.txt b/src/Data/Key.RSA.txt similarity index 100% rename from src/Renci.SshNet.Tests/Data/Key.RSA.txt rename to src/Data/Key.RSA.txt diff --git a/src/Renci.SshNet.Tests/Data/Key.SSH2.DSA.Encrypted.Des.CBC.12345.txt b/src/Data/Key.SSH2.DSA.Encrypted.Des.CBC.12345.txt similarity index 100% rename from src/Renci.SshNet.Tests/Data/Key.SSH2.DSA.Encrypted.Des.CBC.12345.txt rename to src/Data/Key.SSH2.DSA.Encrypted.Des.CBC.12345.txt diff --git a/src/Renci.SshNet.Tests/Data/Key.SSH2.DSA.txt b/src/Data/Key.SSH2.DSA.txt similarity index 100% rename from src/Renci.SshNet.Tests/Data/Key.SSH2.DSA.txt rename to src/Data/Key.SSH2.DSA.txt diff --git a/src/Renci.SshNet.Tests/Data/Key.SSH2.RSA.Encrypted.Des.CBC.12345.txt b/src/Data/Key.SSH2.RSA.Encrypted.Des.CBC.12345.txt similarity index 100% rename from src/Renci.SshNet.Tests/Data/Key.SSH2.RSA.Encrypted.Des.CBC.12345.txt rename to src/Data/Key.SSH2.RSA.Encrypted.Des.CBC.12345.txt diff --git a/src/Renci.SshNet.Tests/Data/Key.SSH2.RSA.txt b/src/Data/Key.SSH2.RSA.txt similarity index 100% rename from src/Renci.SshNet.Tests/Data/Key.SSH2.RSA.txt rename to src/Data/Key.SSH2.RSA.txt diff --git a/src/Renci.SshNet.Benchmarks/Common/HostKeyEventArgsBenchmarks.cs b/src/Renci.SshNet.Benchmarks/Common/HostKeyEventArgsBenchmarks.cs new file mode 100644 index 00000000..54900d04 --- /dev/null +++ b/src/Renci.SshNet.Benchmarks/Common/HostKeyEventArgsBenchmarks.cs @@ -0,0 +1,66 @@ +using BenchmarkDotNet.Attributes; + +using Renci.SshNet.Benchmarks.Security.Cryptography.Ciphers; +using Renci.SshNet.Common; +using Renci.SshNet.Security; + +namespace Renci.SshNet.Benchmarks.Common +{ + [MemoryDiagnoser] + [ShortRunJob] + public class HostKeyEventArgsBenchmarks + { + private readonly KeyHostAlgorithm _keyHostAlgorithm; + + public HostKeyEventArgsBenchmarks() + { + _keyHostAlgorithm = GetKeyHostAlgorithm(); + } + private static KeyHostAlgorithm GetKeyHostAlgorithm() + { + using (var s = typeof(RsaCipherBenchmarks).Assembly.GetManifestResourceStream("Renci.SshNet.Benchmarks.Data.Key.RSA.txt")) + { + var privateKey = new PrivateKeyFile(s); + return (KeyHostAlgorithm) privateKey.HostKeyAlgorithms.First(); + } + } + + [Benchmark()] + public HostKeyEventArgs Constructor() + { + return new HostKeyEventArgs(_keyHostAlgorithm); + } + + [Benchmark()] + public (string, string) CalculateFingerPrintSHA256AndMD5() + { + var test = new HostKeyEventArgs(_keyHostAlgorithm); + + return (test.FingerPrintSHA256, test.FingerPrintMD5); + } + + [Benchmark()] + public string CalculateFingerPrintSHA256() + { + var test = new HostKeyEventArgs(_keyHostAlgorithm); + + return test.FingerPrintSHA256; + } + + [Benchmark()] + public byte[] CalculateFingerPrint() + { + var test = new HostKeyEventArgs(_keyHostAlgorithm); + + return test.FingerPrint; + } + + [Benchmark()] + public string CalculateFingerPrintMD5() + { + var test = new HostKeyEventArgs(_keyHostAlgorithm); + + return test.FingerPrintSHA256; + } + } +} diff --git a/src/Renci.SshNet.Benchmarks/Program.cs b/src/Renci.SshNet.Benchmarks/Program.cs new file mode 100644 index 00000000..3249baa9 --- /dev/null +++ b/src/Renci.SshNet.Benchmarks/Program.cs @@ -0,0 +1,27 @@ +using BenchmarkDotNet.Running; + +namespace Renci.SshNet.Benchmarks +{ + class Program + { + static void Main(string[] args) + { + // Usage examples: + // 1. Run all benchmarks: + // dotnet run -c Release -- --filter * + // 2. List all benchmarks: + // dotnet run -c Release -- --list flat + // 3. Run a subset of benchmarks based on a filter (of a benchmark method's fully-qualified name, + // e.g. "Renci.SshNet.Benchmarks.Security.Cryptography.Ciphers.AesCipherBenchmarks.Encrypt_CBC"): + // dotnet run -c Release -- --filter *Ciphers* + // 4. Run benchmarks and include memory usage statistics in the output: + // dotnet run -c Release -- filter *Rsa* --memory + // 3. Print help: + // dotnet run -c Release -- --help + + // See also https://benchmarkdotnet.org/articles/guides/console-args.html + + _ = BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args); + } + } +} diff --git a/src/Renci.SshNet.Benchmarks/Renci.SshNet.Benchmarks.csproj b/src/Renci.SshNet.Benchmarks/Renci.SshNet.Benchmarks.csproj new file mode 100644 index 00000000..daf3f509 --- /dev/null +++ b/src/Renci.SshNet.Benchmarks/Renci.SshNet.Benchmarks.csproj @@ -0,0 +1,31 @@ + + + + Exe + net7.0 + enable + enable + + $(NoWarn);CS1591 + + + + + + + + + + + + + + + diff --git a/src/Renci.SshNet.Benchmarks/Security/Cryptography/Ciphers/AesCipherBenchmarks.cs b/src/Renci.SshNet.Benchmarks/Security/Cryptography/Ciphers/AesCipherBenchmarks.cs new file mode 100644 index 00000000..ff414cc4 --- /dev/null +++ b/src/Renci.SshNet.Benchmarks/Security/Cryptography/Ciphers/AesCipherBenchmarks.cs @@ -0,0 +1,38 @@ +using BenchmarkDotNet.Attributes; +using Renci.SshNet.Security.Cryptography.Ciphers; +using Renci.SshNet.Security.Cryptography.Ciphers.Modes; + +namespace Renci.SshNet.Benchmarks.Security.Cryptography.Ciphers +{ + [MemoryDiagnoser] + public class AesCipherBenchmarks + { + private readonly byte[] _key; + private readonly byte[] _iv; + private readonly byte[] _data; + + public AesCipherBenchmarks() + { + _key = new byte[32]; + _iv = new byte[16]; + _data = new byte[256]; + + Random random = new(Seed: 12345); + random.NextBytes(_key); + random.NextBytes(_iv); + random.NextBytes(_data); + } + + [Benchmark] + public byte[] Encrypt_CBC() + { + return new AesCipher(_key, new CbcCipherMode(_iv), null).Encrypt(_data); + } + + [Benchmark] + public byte[] Decrypt_CBC() + { + return new AesCipher(_key, new CbcCipherMode(_iv), null).Decrypt(_data); + } + } +} diff --git a/src/Renci.SshNet.Benchmarks/Security/Cryptography/Ciphers/RsaCipherBenchmarks.cs b/src/Renci.SshNet.Benchmarks/Security/Cryptography/Ciphers/RsaCipherBenchmarks.cs new file mode 100644 index 00000000..13c08edb --- /dev/null +++ b/src/Renci.SshNet.Benchmarks/Security/Cryptography/Ciphers/RsaCipherBenchmarks.cs @@ -0,0 +1,49 @@ +using BenchmarkDotNet.Attributes; + +using Renci.SshNet.Security; +using Renci.SshNet.Security.Cryptography.Ciphers; + +namespace Renci.SshNet.Benchmarks.Security.Cryptography.Ciphers +{ + [MemoryDiagnoser] + public class RsaCipherBenchmarks + { + private readonly RsaKey _privateKey; + private readonly RsaKey _publicKey; + private readonly byte[] _data; + + public RsaCipherBenchmarks() + { + _data = new byte[128]; + + Random random = new(Seed: 12345); + random.NextBytes(_data); + + using (var s = typeof(RsaCipherBenchmarks).Assembly.GetManifestResourceStream("Renci.SshNet.Benchmarks.Data.Key.RSA.txt")) + { + + _privateKey = (RsaKey)new PrivateKeyFile(s).Key; + + // The implementations of RsaCipher.Encrypt/Decrypt differ based on whether the supplied RsaKey has private key information + // or only public. So we extract out the public key information to a separate variable. + _publicKey = new RsaKey() + { + Public = _privateKey.Public + }; + } + } + + [Benchmark] + public byte[] Encrypt() + { + return new RsaCipher(_publicKey).Encrypt(_data); + } + + // RSA Decrypt does not work + // [Benchmark] + // public byte[] Decrypt() + // { + // return new RsaCipher(_privateKey).Decrypt(_data); + // } + } +} diff --git a/src/Renci.SshNet.Benchmarks/Security/Cryptography/ED25519DigitalSignatureBenchmarks.cs b/src/Renci.SshNet.Benchmarks/Security/Cryptography/ED25519DigitalSignatureBenchmarks.cs new file mode 100644 index 00000000..44b2da8a --- /dev/null +++ b/src/Renci.SshNet.Benchmarks/Security/Cryptography/ED25519DigitalSignatureBenchmarks.cs @@ -0,0 +1,41 @@ +using BenchmarkDotNet.Attributes; + +using Renci.SshNet.Security; +using Renci.SshNet.Security.Cryptography; + +namespace Renci.SshNet.Benchmarks.Security.Cryptography +{ + [MemoryDiagnoser] + public class ED25519DigitalSignatureBenchmarks + { + private readonly ED25519Key _key; + private readonly byte[] _data; + private readonly byte[] _signature; + + public ED25519DigitalSignatureBenchmarks() + { + _data = new byte[128]; + + Random random = new(Seed: 12345); + random.NextBytes(_data); + + using (var s = typeof(ED25519DigitalSignatureBenchmarks).Assembly.GetManifestResourceStream("Renci.SshNet.Benchmarks.Data.Key.OPENSSH.ED25519.txt")) + { + _key = (ED25519Key) new PrivateKeyFile(s).Key; + } + _signature = new ED25519DigitalSignature(_key).Sign(_data); + } + + [Benchmark] + public byte[] Sign() + { + return new ED25519DigitalSignature(_key).Sign(_data); + } + + [Benchmark] + public bool Verify() + { + return new ED25519DigitalSignature(_key).Verify(_data, _signature); + } + } +} diff --git a/src/Renci.SshNet.IntegrationTests/.editorconfig b/src/Renci.SshNet.IntegrationTests/.editorconfig new file mode 100644 index 00000000..b94e2911 --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/.editorconfig @@ -0,0 +1,32 @@ +[*.cs] + +#### SYSLIB diagnostics #### + +# SYSLIB1045: Use 'GeneratedRegexAttribute' to generate the regular expression implementation at compile-time +# +# TODO: Remove this when https://github.com/sshnet/SSH.NET/issues/1131 is implemented. +dotnet_diagnostic.SYSLIB1045.severity = none + +### StyleCop Analyzers rules ### + +#### .NET Compiler Platform analysers rules #### + +# IDE0007: Use var instead of explicit type +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0007 +dotnet_diagnostic.IDE0007.severity = suggestion + +# IDE0028: Use collection initializers +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0028 +dotnet_diagnostic.IDE0028.severity = suggestion + +# IDE0058: Remove unnecessary expression value +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0058 +dotnet_diagnostic.IDE0058.severity = suggestion + +# IDE0059: Remove unnecessary value assignment +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0059 +dotnet_diagnostic.IDE0059.severity = suggestion + +# IDE0230: Use UTF-8 string literal +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0230 +dotnet_diagnostic.IDE0230.severity = suggestion diff --git a/src/Renci.SshNet.IntegrationTests/.gitignore b/src/Renci.SshNet.IntegrationTests/.gitignore new file mode 100644 index 00000000..0819dad7 --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/.gitignore @@ -0,0 +1 @@ +TestResults/ \ No newline at end of file diff --git a/src/Renci.SshNet.IntegrationTests/App.config b/src/Renci.SshNet.IntegrationTests/App.config new file mode 100644 index 00000000..c9794e84 --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/App.config @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Renci.SshNet.IntegrationTests/AuthenticationMethodFactory.cs b/src/Renci.SshNet.IntegrationTests/AuthenticationMethodFactory.cs new file mode 100644 index 00000000..638b285d --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/AuthenticationMethodFactory.cs @@ -0,0 +1,84 @@ +namespace Renci.SshNet.IntegrationTests +{ + public class AuthenticationMethodFactory + { + public PasswordAuthenticationMethod CreatePowerUserPasswordAuthenticationMethod() + { + var user = Users.Admin; + return new PasswordAuthenticationMethod(user.UserName, user.Password); + } + + public PrivateKeyAuthenticationMethod CreateRegularUserPrivateKeyAuthenticationMethod() + { + var privateKeyFile = GetPrivateKey("Renci.SshNet.IntegrationTests.resources.client.id_rsa"); + return new PrivateKeyAuthenticationMethod(Users.Regular.UserName, privateKeyFile); + } + + public PrivateKeyAuthenticationMethod CreateRegularUserMultiplePrivateKeyAuthenticationMethod() + { + var privateKeyFile1 = GetPrivateKey("Renci.SshNet.IntegrationTests.resources.client.id_rsa"); + var privateKeyFile2 = GetPrivateKey("Renci.SshNet.IntegrationTests.resources.client.id_rsa"); + return new PrivateKeyAuthenticationMethod(Users.Regular.UserName, privateKeyFile1, privateKeyFile2); + } + + public PrivateKeyAuthenticationMethod CreateRegularUserPrivateKeyWithPassPhraseAuthenticationMethod() + { + var privateKeyFile = GetPrivateKey("Renci.SshNet.IntegrationTests.resources.client.id_rsa_with_pass", "tester"); + return new PrivateKeyAuthenticationMethod(Users.Regular.UserName, privateKeyFile); + } + + public PrivateKeyAuthenticationMethod CreateRegularUserPrivateKeyWithEmptyPassPhraseAuthenticationMethod() + { + var privateKeyFile = GetPrivateKey("Renci.SshNet.IntegrationTests.resources.client.id_rsa_with_pass", null); + return new PrivateKeyAuthenticationMethod(Users.Regular.UserName, privateKeyFile); + } + + public PrivateKeyAuthenticationMethod CreateRegularUserPrivateKeyAuthenticationMethodWithBadKey() + { + var privateKeyFile = GetPrivateKey("Renci.SshNet.IntegrationTests.resources.client.id_noaccess.rsa"); + return new PrivateKeyAuthenticationMethod(Users.Regular.UserName, privateKeyFile); + } + + public PasswordAuthenticationMethod CreateRegulatUserPasswordAuthenticationMethod() + { + return new PasswordAuthenticationMethod(Users.Regular.UserName, Users.Regular.Password); + } + + public PasswordAuthenticationMethod CreateRegularUserPasswordAuthenticationMethodWithBadPassword() + { + return new PasswordAuthenticationMethod(Users.Regular.UserName, "xxx"); + } + + public KeyboardInteractiveAuthenticationMethod CreateRegularUserKeyboardInteractiveAuthenticationMethod() + { + var keyboardInteractive = new KeyboardInteractiveAuthenticationMethod(Users.Regular.UserName); + keyboardInteractive.AuthenticationPrompt += (sender, args) => + { + foreach (var authenticationPrompt in args.Prompts) + { + authenticationPrompt.Response = Users.Regular.Password; + } + }; + return keyboardInteractive; + } + + private PrivateKeyFile GetPrivateKey(string resourceName, string passPhrase = null) + { + using (var stream = GetResourceStream(resourceName)) + { + return new PrivateKeyFile(stream, passPhrase); + } + } + + private Stream GetResourceStream(string resourceName) + { + var type = GetType(); + var resourceStream = type.Assembly.GetManifestResourceStream(resourceName); + if (resourceStream == null) + { + throw new ArgumentException($"Resource '{resourceName}' not found in assembly '{type.Assembly.FullName}'.", nameof(resourceName)); + } + return resourceStream; + } + } +} diff --git a/src/Renci.SshNet.IntegrationTests/AuthenticationTests.cs b/src/Renci.SshNet.IntegrationTests/AuthenticationTests.cs new file mode 100644 index 00000000..c3968e94 --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/AuthenticationTests.cs @@ -0,0 +1,427 @@ +using Renci.SshNet.Common; +using Renci.SshNet.IntegrationTests.Common; + +namespace Renci.SshNet.IntegrationTests +{ + [TestClass] + public class AuthenticationTests : IntegrationTestBase + { + private AuthenticationMethodFactory _authenticationMethodFactory; + private IConnectionInfoFactory _connectionInfoFactory; + private IConnectionInfoFactory _adminConnectionInfoFactory; + private RemoteSshdConfig _remoteSshdConfig; + + [TestInitialize] + public void SetUp() + { + _authenticationMethodFactory = new AuthenticationMethodFactory(); + _connectionInfoFactory = new LinuxVMConnectionFactory(SshServerHostName, SshServerPort, _authenticationMethodFactory); + _adminConnectionInfoFactory = new LinuxAdminConnectionFactory(SshServerHostName, SshServerPort); + _remoteSshdConfig = new RemoteSshd(_adminConnectionInfoFactory).OpenConfig(); + } + + [TestCleanup] + public void TearDown() + { + _remoteSshdConfig?.Reset(); + + using (var client = new SshClient(_adminConnectionInfoFactory.Create())) + { + client.Connect(); + + // Reset the password back to the "regular" password. + using (var cmd = client.RunCommand($"echo \"{Users.Regular.Password}\n{Users.Regular.Password}\" | sudo passwd " + Users.Regular.UserName)) + { + Assert.AreEqual(0, cmd.ExitStatus, cmd.Error); + } + + // Remove password expiration + using (var cmd = client.RunCommand($"sudo chage --expiredate -1 " + Users.Regular.UserName)) + { + Assert.AreEqual(0, cmd.ExitStatus, cmd.Error); + } + } + } + + [TestMethod] + public void Multifactor_PublicKey() + { + _remoteSshdConfig.WithAuthenticationMethods(Users.Regular.UserName, "publickey") + .Update() + .Restart(); + + var connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegularUserPrivateKeyAuthenticationMethod()); + using (var client = new SftpClient(connectionInfo)) + { + client.Connect(); + } + } + + [TestMethod] + [TestCategory("Authentication")] + public void Multifactor_PublicKey_Connect_Then_Reconnect() + { + _remoteSshdConfig.WithAuthenticationMethods(Users.Regular.UserName, "publickey") + .Update() + .Restart(); + + var connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegularUserPrivateKeyAuthenticationMethod()); + using (var client = new SftpClient(connectionInfo)) + { + client.Connect(); + client.Disconnect(); + client.Connect(); + client.Disconnect(); + } + } + + [TestMethod] + public void Multifactor_PublicKeyWithPassPhrase() + { + _remoteSshdConfig.WithAuthenticationMethods(Users.Regular.UserName, "publickey") + .Update() + .Restart(); + + var connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegularUserPrivateKeyWithPassPhraseAuthenticationMethod()); + using (var client = new SftpClient(connectionInfo)) + { + client.Connect(); + } + } + + [TestMethod] + [ExpectedException(typeof(SshPassPhraseNullOrEmptyException))] + public void Multifactor_PublicKeyWithEmptyPassPhrase() + { + _remoteSshdConfig.WithAuthenticationMethods(Users.Regular.UserName, "publickey") + .Update() + .Restart(); + + var connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegularUserPrivateKeyWithEmptyPassPhraseAuthenticationMethod()); + using (var client = new SftpClient(connectionInfo)) + { + client.Connect(); + } + } + + [TestMethod] + public void Multifactor_PublicKey_MultiplePrivateKey() + { + _remoteSshdConfig.WithAuthenticationMethods(Users.Regular.UserName, "publickey") + .Update() + .Restart(); + + var connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegularUserMultiplePrivateKeyAuthenticationMethod()); + using (var client = new SftpClient(connectionInfo)) + { + client.Connect(); + } + } + + [TestMethod] + public void Multifactor_PublicKey_MultipleAuthenticationMethod() + { + _remoteSshdConfig.WithAuthenticationMethods(Users.Regular.UserName, "publickey") + .Update() + .Restart(); + + var connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegularUserPrivateKeyAuthenticationMethod(), + _authenticationMethodFactory.CreateRegularUserPrivateKeyAuthenticationMethod()); + using (var client = new SftpClient(connectionInfo)) + { + client.Connect(); + } + } + + [TestMethod] + public void Multifactor_KeyboardInteractiveAndPublicKey() + { + _remoteSshdConfig.WithAuthenticationMethods(Users.Regular.UserName, "keyboard-interactive,publickey") + .WithChallengeResponseAuthentication(true) + .WithKeyboardInteractiveAuthentication(true) + .WithUsePAM(true) + .Update() + .Restart(); + + var connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegularUserPasswordAuthenticationMethodWithBadPassword(), + _authenticationMethodFactory.CreateRegularUserKeyboardInteractiveAuthenticationMethod(), + _authenticationMethodFactory.CreateRegularUserPrivateKeyAuthenticationMethod()); + using (var client = new SftpClient(connectionInfo)) + { + client.Connect(); + } + } + + [TestMethod] + public void Multifactor_Password_ExceedsPartialSuccessLimit() + { + // configure server to require more successfull authentications from a given method than our partial + // success limit (5) allows + _remoteSshdConfig.WithAuthenticationMethods(Users.Regular.UserName, "password,password,password,password,password,password") + .Update() + .Restart(); + + var connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegulatUserPasswordAuthenticationMethod()); + using (var client = new SftpClient(connectionInfo)) + { + try + { + client.Connect(); + Assert.Fail(); + } + catch (SshAuthenticationException ex) + { + Assert.IsNull(ex.InnerException); + Assert.AreEqual("Reached authentication attempt limit for method (password).", ex.Message); + } + } + } + + [TestMethod] + public void Multifactor_Password_MatchPartialSuccessLimit() + { + // configure server to require a number of successfull authentications from a given method that exactly + // matches our partial success limit (5) + + _remoteSshdConfig.WithAuthenticationMethods(Users.Regular.UserName, "password,password,password,password,password") + .Update() + .Restart(); + + var connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegulatUserPasswordAuthenticationMethod()); + using (var client = new SftpClient(connectionInfo)) + { + client.Connect(); + } + } + + [TestMethod] + public void Multifactor_Password_Or_PublicKeyAndKeyboardInteractive() + { + _remoteSshdConfig.WithAuthenticationMethods(Users.Regular.UserName, "password publickey,keyboard-interactive") + .WithChallengeResponseAuthentication(true) + .WithKeyboardInteractiveAuthentication(true) + .WithUsePAM(true) + .Update() + .Restart(); + + var connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegularUserPrivateKeyAuthenticationMethod(), + _authenticationMethodFactory.CreateRegulatUserPasswordAuthenticationMethod()); + using (var client = new SftpClient(connectionInfo)) + { + client.Connect(); + } + } + + [TestMethod] + public void Multifactor_Password_Or_PublicKeyAndPassword_BadPassword() + { + _remoteSshdConfig.WithAuthenticationMethods(Users.Regular.UserName, "password publickey,password") + .Update() + .Restart(); + + var connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegularUserPasswordAuthenticationMethodWithBadPassword(), + _authenticationMethodFactory.CreateRegularUserPrivateKeyAuthenticationMethod()); + using (var client = new SftpClient(connectionInfo)) + { + try + { + client.Connect(); + Assert.Fail(); + } + catch (SshAuthenticationException ex) + { + Assert.IsNull(ex.InnerException); + Assert.AreEqual("Permission denied (password).", ex.Message); + } + } + } + + [TestMethod] + public void Multifactor_PasswordAndPublicKey_Or_PasswordAndPassword() + { + _remoteSshdConfig.WithAuthenticationMethods(Users.Regular.UserName, "password,publickey password,password") + .Update() + .Restart(); + + var connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegulatUserPasswordAuthenticationMethod(), + _authenticationMethodFactory.CreateRegularUserPrivateKeyAuthenticationMethodWithBadKey()); + using (var client = new SftpClient(connectionInfo)) + { + client.Connect(); + } + + connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegularUserPasswordAuthenticationMethodWithBadPassword(), + _authenticationMethodFactory.CreateRegularUserPrivateKeyAuthenticationMethod()); + using (var client = new SftpClient(connectionInfo)) + { + try + { + client.Connect(); + Assert.Fail(); + } + catch (SshAuthenticationException ex) + { + Assert.IsNull(ex.InnerException); + Assert.AreEqual("Permission denied (password).", ex.Message); + } + } + + } + + [TestMethod] + public void Multifactor_PasswordAndPassword_Or_PublicKey() + { + _remoteSshdConfig.WithAuthenticationMethods(Users.Regular.UserName, "password,password publickey") + .Update() + .Restart(); + + var connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegulatUserPasswordAuthenticationMethod(), + _authenticationMethodFactory.CreateRegularUserPrivateKeyAuthenticationMethodWithBadKey()); + using (var client = new SftpClient(connectionInfo)) + { + client.Connect(); + } + + connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegulatUserPasswordAuthenticationMethod()); + using (var client = new SftpClient(connectionInfo)) + { + client.Connect(); + } + + } + + [TestMethod] + public void Multifactor_Password_Or_Password() + { + _remoteSshdConfig.WithAuthenticationMethods(Users.Regular.UserName, "password password") + .Update() + .Restart(); + + var connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegulatUserPasswordAuthenticationMethod()); + using (var client = new SftpClient(connectionInfo)) + { + client.Connect(); + } + + connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegulatUserPasswordAuthenticationMethod(), + _authenticationMethodFactory.CreateRegularUserPrivateKeyAuthenticationMethodWithBadKey()); + using (var client = new SftpClient(connectionInfo)) + { + client.Connect(); + } + } + + [TestMethod] + public void KeyboardInteractive_PasswordExpired() + { + var temporaryPassword = new Random().Next().ToString(); + + using (var client = new SshClient(_adminConnectionInfoFactory.Create())) + { + client.Connect(); + + // Temporarity modify password so that when we expire this password, we change reset the password back to + // the "regular" password. + using (var cmd = client.RunCommand($"echo \"{temporaryPassword}\n{temporaryPassword}\" | sudo passwd " + Users.Regular.UserName)) + { + Assert.AreEqual(0, cmd.ExitStatus, cmd.Error); + } + + // Force the password to expire immediately + using (var cmd = client.RunCommand($"sudo chage -d 0 " + Users.Regular.UserName)) + { + Assert.AreEqual(0, cmd.ExitStatus, cmd.Error); + } + } + + _remoteSshdConfig.WithAuthenticationMethods(Users.Regular.UserName, "keyboard-interactive") + .WithChallengeResponseAuthentication(true) + .WithKeyboardInteractiveAuthentication(true) + .WithUsePAM(true) + .Update() + .Restart(); + + var keyboardInteractive = new KeyboardInteractiveAuthenticationMethod(Users.Regular.UserName); + int authenticationPromptCount = 0; + keyboardInteractive.AuthenticationPrompt += (sender, args) => + { + Console.WriteLine(args.Instruction); + foreach (var authenticationPrompt in args.Prompts) + { + Console.WriteLine(authenticationPrompt.Request); + switch (authenticationPromptCount) + { + case 0: + // Regular password prompt + authenticationPrompt.Response = temporaryPassword; + break; + case 1: + // Password expired, provide current password + authenticationPrompt.Response = temporaryPassword; + break; + case 2: + // Password expired, provide new password + authenticationPrompt.Response = Users.Regular.Password; + break; + case 3: + // Password expired, retype new password + authenticationPrompt.Response = Users.Regular.Password; + break; + } + + authenticationPromptCount++; + } + }; + + var connectionInfo = _connectionInfoFactory.Create(keyboardInteractive); + using (var client = new SftpClient(connectionInfo)) + { + client.Connect(); + Assert.AreEqual(4, authenticationPromptCount); + } + } + + [TestMethod] + public void KeyboardInteractiveConnectionInfo() + { + _remoteSshdConfig.WithAuthenticationMethods(Users.Regular.UserName, "keyboard-interactive") + .WithChallengeResponseAuthentication(true) + .WithKeyboardInteractiveAuthentication(true) + .WithUsePAM(true) + .Update() + .Restart(); + + var host = SshServerHostName; + var port = SshServerPort; + var username = User.UserName; + var password = User.Password; + + #region Example KeyboardInteractiveConnectionInfo AuthenticationPrompt + + var connectionInfo = new KeyboardInteractiveConnectionInfo(host, port, username); + connectionInfo.AuthenticationPrompt += delegate (object sender, AuthenticationPromptEventArgs e) + { + Console.WriteLine(e.Instruction); + + foreach (var prompt in e.Prompts) + { + Console.WriteLine(prompt.Request); + prompt.Response = password; + } + }; + + using (var client = new SftpClient(connectionInfo)) + { + client.Connect(); + + // Do something here + client.Disconnect(); + } + + #endregion + + Assert.AreEqual(connectionInfo.Host, SshServerHostName); + Assert.AreEqual(connectionInfo.Username, User.UserName); + } + } +} diff --git a/src/Renci.SshNet.IntegrationTests/CipherTests.cs b/src/Renci.SshNet.IntegrationTests/CipherTests.cs new file mode 100644 index 00000000..1a11f981 --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/CipherTests.cs @@ -0,0 +1,81 @@ +using Renci.SshNet.IntegrationTests.Common; +using Renci.SshNet.TestTools.OpenSSH; + +namespace Renci.SshNet.IntegrationTests +{ + [TestClass] + public class CipherTests : IntegrationTestBase + { + private IConnectionInfoFactory _connectionInfoFactory; + private RemoteSshdConfig _remoteSshdConfig; + + [TestInitialize] + public void SetUp() + { + _connectionInfoFactory = new LinuxVMConnectionFactory(SshServerHostName, SshServerPort); + _remoteSshdConfig = new RemoteSshd(new LinuxAdminConnectionFactory(SshServerHostName, SshServerPort)).OpenConfig(); + } + + [TestCleanup] + public void TearDown() + { + _remoteSshdConfig?.Reset(); + } + + [TestMethod] + public void TripledesCbc() + { + DoTest(Cipher.TripledesCbc); + } + + [TestMethod] + public void Aes128Cbc() + { + DoTest(Cipher.Aes128Cbc); + } + + [TestMethod] + public void Aes192Cbc() + { + DoTest(Cipher.Aes192Cbc); + } + + [TestMethod] + public void Aes256Cbc() + { + DoTest(Cipher.Aes256Cbc); + } + + [TestMethod] + public void Aes128Ctr() + { + DoTest(Cipher.Aes128Ctr); + } + + [TestMethod] + public void Aes192Ctr() + { + DoTest(Cipher.Aes192Ctr); + } + + [TestMethod] + public void Aes256Ctr() + { + DoTest(Cipher.Aes256Ctr); + } + + private void DoTest(Cipher cipher) + { + _remoteSshdConfig.ClearCiphers() + .AddCipher(cipher) + .Update() + .Restart(); + + using (var client = new SshClient(_connectionInfoFactory.Create())) + { + client.Connect(); + client.Disconnect(); + } + } + } +} diff --git a/src/Renci.SshNet.IntegrationTests/Common/ArrayBuilder.cs b/src/Renci.SshNet.IntegrationTests/Common/ArrayBuilder.cs new file mode 100644 index 00000000..1720c19c --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/Common/ArrayBuilder.cs @@ -0,0 +1,32 @@ +namespace Renci.SshNet.IntegrationTests.Common +{ + public class ArrayBuilder + { + private readonly List _buffer; + + public ArrayBuilder() + { + _buffer = new List(); + } + + public ArrayBuilder Add(T[] array) + { + return Add(array, 0, array.Length); + } + + public ArrayBuilder Add(T[] array, int index, int length) + { + for (var i = 0; i < length; i++) + { + _buffer.Add(array[index + i]); + } + + return this; + } + + public T[] Build() + { + return _buffer.ToArray(); + } + } +} diff --git a/src/Renci.SshNet.IntegrationTests/Common/AsyncSocketListener.cs b/src/Renci.SshNet.IntegrationTests/Common/AsyncSocketListener.cs new file mode 100644 index 00000000..75338558 --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/Common/AsyncSocketListener.cs @@ -0,0 +1,393 @@ +using System.Net; +using System.Net.Sockets; + +namespace Renci.SshNet.IntegrationTests.Common +{ + public class AsyncSocketListener : IDisposable + { + private readonly IPEndPoint _endPoint; + private readonly ManualResetEvent _acceptCallbackDone; + private readonly List _connectedClients; + private readonly object _syncLock; + private Socket _listener; + private Thread _receiveThread; + private bool _started; + private string _stackTrace; + + public delegate void BytesReceivedHandler(byte[] bytesReceived, Socket socket); + public delegate void ConnectedHandler(Socket socket); + + public event BytesReceivedHandler BytesReceived; + public event ConnectedHandler Connected; + public event ConnectedHandler Disconnected; + + public AsyncSocketListener(IPEndPoint endPoint) + { + _endPoint = endPoint; + _acceptCallbackDone = new ManualResetEvent(false); + _connectedClients = new List(); + _syncLock = new object(); + ShutdownRemoteCommunicationSocket = true; + } + + /// + /// Gets a value indicating whether the is invoked on the + /// that is used to handle the communication with the remote host, when the remote host has closed the connection. + /// + /// + /// to invoke on the that is used + /// to handle the communication with the remote host, when the remote host has closed the connection; otherwise, + /// . The default is . + /// + public bool ShutdownRemoteCommunicationSocket { get; set; } + + public void Start() + { + _listener = new Socket(_endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); + _listener.Bind(_endPoint); + _listener.Listen(1); + + _started = true; + + _receiveThread = new Thread(StartListener); + _receiveThread.Start(_listener); + + _stackTrace = Environment.StackTrace; + } + + public void Stop() + { + _started = false; + + lock (_syncLock) + { + foreach (var connectedClient in _connectedClients) + { + try + { + connectedClient.Shutdown(SocketShutdown.Send); + } + catch (Exception ex) + { + Console.Error.WriteLine("[{0}] Failure shutting down socket: {1}", + typeof(AsyncSocketListener).FullName, + ex); + } + + DrainSocket(connectedClient); + connectedClient.Dispose(); + } + + _connectedClients.Clear(); + } + + _listener?.Dispose(); + + if (_receiveThread != null) + { + _receiveThread.Join(); + _receiveThread = null; + } + } + + public void Dispose() + { + Stop(); + GC.SuppressFinalize(this); + } + + private void StartListener(object state) + { + try + { + var listener = (Socket) state; + while (_started) + { + _ = _acceptCallbackDone.Reset(); + _ = listener.BeginAccept(AcceptCallback, listener); + _ = _acceptCallbackDone.WaitOne(); + } + } + catch (Exception ex) + { + // On .NET framework when Thread throws an exception then unit tests + // were executed without any problem. + // On new .NET exceptions from Thread breaks unit tests session. + Console.Error.WriteLine("[{0}] Failure in StartListener: {1}", + typeof(AsyncSocketListener).FullName, + ex); + } + } + + private void AcceptCallback(IAsyncResult ar) + { + // Signal the main thread to continue + _ = _acceptCallbackDone.Set(); + + // Get the socket that listens for inbound connections + var listener = (Socket) ar.AsyncState; + + // Get the socket that handles the client request + Socket handler; + + try + { + handler = listener.EndAccept(ar); + } + catch (SocketException ex) + { + // The listener is stopped through a Dispose() call, which in turn causes + // Socket.EndAccept(...) to throw a SocketException or + // ObjectDisposedException + // + // Since we consider such an exception normal when the listener is being + // stopped, we only write a message to stderr if the listener is considered + // to be up and running + if (_started) + { + Console.Error.WriteLine("[{0}] Failure accepting new connection: {1}", + typeof(AsyncSocketListener).FullName, + ex); + } + return; + } + catch (ObjectDisposedException ex) + { + // The listener is stopped through a Dispose() call, which in turn causes + // Socket.EndAccept(IAsyncResult) to throw a SocketException or + // ObjectDisposedException + // + // Since we consider such an exception normal when the listener is being + // stopped, we only write a message to stderr if the listener is considered + // to be up and running + if (_started) + { + Console.Error.WriteLine("[{0}] Failure accepting new connection: {1}", + typeof(AsyncSocketListener).FullName, + ex); + } + return; + } + + // Signal new connection + SignalConnected(handler); + + lock (_syncLock) + { + // Register client socket + _connectedClients.Add(handler); + } + + var state = new SocketStateObject(handler); + + try + { + _ = handler.BeginReceive(state.Buffer, 0, state.Buffer.Length, 0, ReadCallback, state); + } + catch (SocketException ex) + { + // The listener is stopped through a Dispose() call, which in turn causes + // Socket.BeginReceive(...) to throw a SocketException or + // ObjectDisposedException + // + // Since we consider such an exception normal when the listener is being + // stopped, we only write a message to stderr if the listener is considered + // to be up and running + if (_started) + { + Console.Error.WriteLine("[{0}] Failure receiving new data: {1}", + typeof(AsyncSocketListener).FullName, + ex); + } + } + catch (ObjectDisposedException ex) + { + // The listener is stopped through a Dispose() call, which in turn causes + // Socket.BeginReceive(...) to throw a SocketException or + // ObjectDisposedException + // + // Since we consider such an exception normal when the listener is being + // stopped, we only write a message to stderr if the listener is considered + // to be up and running + if (_started) + { + Console.Error.WriteLine("[{0}] Failure receiving new data: {1}", + typeof(AsyncSocketListener).FullName, + ex); + } + } + } + + private void ReadCallback(IAsyncResult ar) + { + // Retrieve the state object and the handler socket + // from the asynchronous state object + var state = (SocketStateObject) ar.AsyncState; + var handler = state.Socket; + + int bytesRead; + try + { + // Read data from the client socket. + bytesRead = handler.EndReceive(ar, out var errorCode); + if (errorCode != SocketError.Success) + { + bytesRead = 0; + } + } + catch (SocketException ex) + { + // The listener is stopped through a Dispose() call, which in turn causes + // Socket.EndReceive(...) to throw a SocketException or + // ObjectDisposedException + // + // Since we consider such an exception normal when the listener is being + // stopped, we only write a message to stderr if the listener is considered + // to be up and running + if (_started) + { + Console.Error.WriteLine("[{0}] Failure receiving new data: {1}", + typeof(AsyncSocketListener).FullName, + ex); + } + return; + } + catch (ObjectDisposedException ex) + { + // The listener is stopped through a Dispose() call, which in turn causes + // Socket.EndReceive(...) to throw a SocketException or + // ObjectDisposedException + // + // Since we consider such an exception normal when the listener is being + // stopped, we only write a message to stderr if the listener is considered + // to be up and running + if (_started) + { + Console.Error.WriteLine("[{0}] Failure receiving new data: {1}", + typeof(AsyncSocketListener).FullName, + ex); + } + return; + } + + void ConnectionDisconnected() + { + SignalDisconnected(handler); + + if (ShutdownRemoteCommunicationSocket) + { + lock (_syncLock) + { + if (!_started) + { + return; + } + + try + { + handler.Shutdown(SocketShutdown.Send); + handler.Close(); + } + catch (SocketException ex) when (ex.SocketErrorCode == SocketError.ConnectionReset) + { + // On .NET 7 we got Socker Exception with ConnectionReset from Shutdown method + // when the socket is disposed + } + catch (SocketException ex) + { + throw new Exception("Exception in ReadCallback: " + ex.SocketErrorCode + " " + _stackTrace, ex); + } + catch (Exception ex) + { + throw new Exception("Exception in ReadCallback: " + _stackTrace, ex); + } + + _ = _connectedClients.Remove(handler); + } + } + } + + if (bytesRead > 0) + { + var bytesReceived = new byte[bytesRead]; + Array.Copy(state.Buffer, bytesReceived, bytesRead); + SignalBytesReceived(bytesReceived, handler); + + try + { + _ = handler.BeginReceive(state.Buffer, 0, state.Buffer.Length, 0, ReadCallback, state); + } + catch (ObjectDisposedException) + { + // TODO On .NET 7, sometimes we get ObjectDisposedException when _started but only on appveyor, locally it works + ConnectionDisconnected(); + } + catch (SocketException ex) + { + if (!_started) + { + throw new Exception("BeginReceive while stopping!", ex); + } + + throw new Exception("BeginReceive while started!: " + ex.SocketErrorCode + " " + _stackTrace, ex); + } + + } + else + { + ConnectionDisconnected(); + } + } + + private void SignalBytesReceived(byte[] bytesReceived, Socket client) + { + BytesReceived?.Invoke(bytesReceived, client); + } + + private void SignalConnected(Socket client) + { + Connected?.Invoke(client); + } + + private void SignalDisconnected(Socket client) + { + Disconnected?.Invoke(client); + } + + private static void DrainSocket(Socket socket) + { + var buffer = new byte[128]; + + try + { + while (true && socket.Connected) + { + var bytesRead = socket.Receive(buffer); + if (bytesRead == 0) + { + break; + } + } + } + catch (SocketException ex) + { + Console.Error.WriteLine("[{0}] Failure draining socket ({1}): {2}", + typeof(AsyncSocketListener).FullName, + ex.SocketErrorCode.ToString("G"), + ex); + } + } + + private class SocketStateObject + { + public Socket Socket { get; private set; } + + public readonly byte[] Buffer = new byte[1024]; + + public SocketStateObject(Socket handler) + { + Socket = handler; + } + } + } +} diff --git a/src/Renci.SshNet.IntegrationTests/Common/DateTimeAssert.cs b/src/Renci.SshNet.IntegrationTests/Common/DateTimeAssert.cs new file mode 100644 index 00000000..036a122d --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/Common/DateTimeAssert.cs @@ -0,0 +1,11 @@ +namespace Renci.SshNet.IntegrationTests.Common +{ + public static class DateTimeAssert + { + public static void AreEqual(DateTime expected, DateTime actual) + { + Assert.AreEqual(expected, actual, $"Expected {expected:o}, but was {actual:o}."); + Assert.AreEqual(expected.Kind, actual.Kind); + } + } +} diff --git a/src/Renci.SshNet.IntegrationTests/Common/DateTimeExtensions.cs b/src/Renci.SshNet.IntegrationTests/Common/DateTimeExtensions.cs new file mode 100644 index 00000000..d9f0d22e --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/Common/DateTimeExtensions.cs @@ -0,0 +1,12 @@ +namespace Renci.SshNet.IntegrationTests.Common +{ + public static class DateTimeExtensions + { + public static DateTime TruncateToWholeSeconds(this DateTime dateTime) + { + return dateTime.AddMilliseconds(-dateTime.Millisecond) + .AddMicroseconds(-dateTime.Microsecond) + .AddTicks(-(dateTime.Nanosecond / 100)); + } + } +} diff --git a/src/Renci.SshNet.IntegrationTests/Common/RemoteSshdConfigExtensions.cs b/src/Renci.SshNet.IntegrationTests/Common/RemoteSshdConfigExtensions.cs new file mode 100644 index 00000000..865154bf --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/Common/RemoteSshdConfigExtensions.cs @@ -0,0 +1,30 @@ +using Renci.SshNet.TestTools.OpenSSH; + +namespace Renci.SshNet.IntegrationTests.Common +{ + internal static class RemoteSshdConfigExtensions + { + private const string DefaultAuthenticationMethods = "password publickey"; + + public static void Reset(this RemoteSshdConfig remoteSshdConfig) + { + remoteSshdConfig.WithAuthenticationMethods(Users.Regular.UserName, DefaultAuthenticationMethods) + .WithChallengeResponseAuthentication(false) + .WithKeyboardInteractiveAuthentication(false) + .PrintMotd() + .WithLogLevel(LogLevel.Debug3) + .ClearHostKeyFiles() + .AddHostKeyFile(HostKeyFile.Rsa.FilePath) + .ClearSubsystems() + .AddSubsystem(new Subsystem("sftp", "/usr/lib/ssh/sftp-server")) + .ClearCiphers() + .ClearKeyExchangeAlgorithms() + .ClearHostKeyAlgorithms() + .ClearPublicKeyAcceptedAlgorithms() + .ClearMessageAuthenticationCodeAlgorithms() + .WithUsePAM(true) + .Update() + .Restart(); + } + } +} diff --git a/src/Renci.SshNet.IntegrationTests/Common/Socks5Handler.cs b/src/Renci.SshNet.IntegrationTests/Common/Socks5Handler.cs new file mode 100644 index 00000000..e50858c3 --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/Common/Socks5Handler.cs @@ -0,0 +1,255 @@ +using System.Net; +using System.Net.Sockets; + +using Renci.SshNet.Abstractions; +using Renci.SshNet.Common; +using Renci.SshNet.Messages.Transport; + +namespace Renci.SshNet.IntegrationTests.Common +{ + class Socks5Handler + { + private readonly IPEndPoint _proxyEndPoint; + private readonly string _userName; + private readonly string _password; + + public Socks5Handler(IPEndPoint proxyEndPoint, string userName, string password) + { + _proxyEndPoint = proxyEndPoint; + _userName = userName; + _password = password; + } + + public Socket Connect(IPEndPoint endPoint) + { + if (endPoint == null) + { + throw new ArgumentNullException("endPoint"); + } + + var addressBytes = GetAddressBytes(endPoint); + return Connect(addressBytes, endPoint.Port); + } + + public Socket Connect(string host, int port) + { + if (host == null) + { + throw new ArgumentNullException(nameof(host)); + } + + if (host.Length > byte.MaxValue) + { + throw new ArgumentException($@"Cannot be more than {byte.MaxValue} characters.", nameof(host)); + } + + var addressBytes = new byte[host.Length + 2]; + addressBytes[0] = 0x03; + addressBytes[1] = (byte) host.Length; + Encoding.ASCII.GetBytes(host, 0, host.Length, addressBytes, 2); + return Connect(addressBytes, port); + } + + private Socket Connect(byte[] addressBytes, int port) + { + var socket = SocketAbstraction.Connect(_proxyEndPoint, TimeSpan.FromSeconds(5)); + + // Send socks version number + SocketWriteByte(socket, 0x05); + + // Send number of supported authentication methods + SocketWriteByte(socket, 0x02); + + // Send supported authentication methods + SocketWriteByte(socket, 0x00); // No authentication + SocketWriteByte(socket, 0x02); // Username/Password + + var socksVersion = SocketReadByte(socket); + if (socksVersion != 0x05) + { + throw new ProxyException(string.Format("SOCKS Version '{0}' is not supported.", socksVersion)); + } + + var authenticationMethod = SocketReadByte(socket); + switch (authenticationMethod) + { + case 0x00: + break; + case 0x02: + + // Send version + SocketWriteByte(socket, 0x01); + + var username = Encoding.ASCII.GetBytes(_userName); + if (username.Length > byte.MaxValue) + { + throw new ProxyException("Proxy username is too long."); + } + + // Send username length + SocketWriteByte(socket, (byte) username.Length); + + // Send username + SocketAbstraction.Send(socket, username); + + var password = Encoding.ASCII.GetBytes(_password); + + if (password.Length > byte.MaxValue) + { + throw new ProxyException("Proxy password is too long."); + } + + // Send username length + SocketWriteByte(socket, (byte) password.Length); + + // Send username + SocketAbstraction.Send(socket, password); + + var serverVersion = SocketReadByte(socket); + + if (serverVersion != 1) + { + throw new ProxyException("SOCKS5: Server authentication version is not valid."); + } + + var statusCode = SocketReadByte(socket); + if (statusCode != 0) + { + throw new ProxyException("SOCKS5: Username/Password authentication failed."); + } + + break; + case 0xFF: + throw new ProxyException("SOCKS5: No acceptable authentication methods were offered."); + default: + throw new ProxyException("SOCKS5: No acceptable authentication methods were offered."); + } + + // Send socks version number + SocketWriteByte(socket, 0x05); + + // Send command code + SocketWriteByte(socket, 0x01); // establish a TCP/IP stream connection + + // Send reserved, must be 0x00 + SocketWriteByte(socket, 0x00); + + // Send address type and address + SocketAbstraction.Send(socket, addressBytes); + + // Send port + SocketWriteByte(socket, (byte)(port / 0xFF)); + SocketWriteByte(socket, (byte)(port % 0xFF)); + + // Read Server SOCKS5 version + if (SocketReadByte(socket) != 5) + { + throw new ProxyException("SOCKS5: Version 5 is expected."); + } + + // Read response code + var status = SocketReadByte(socket); + + switch (status) + { + case 0x00: + break; + case 0x01: + throw new ProxyException("SOCKS5: General failure."); + case 0x02: + throw new ProxyException("SOCKS5: Connection not allowed by ruleset."); + case 0x03: + throw new ProxyException("SOCKS5: Network unreachable."); + case 0x04: + throw new ProxyException("SOCKS5: Host unreachable."); + case 0x05: + throw new ProxyException("SOCKS5: Connection refused by destination host."); + case 0x06: + throw new ProxyException("SOCKS5: TTL expired."); + case 0x07: + throw new ProxyException("SOCKS5: Command not supported or protocol error."); + case 0x08: + throw new ProxyException("SOCKS5: Address type not supported."); + default: + throw new ProxyException("SOCKS4: Not valid response."); + } + + // Read 0 + if (SocketReadByte(socket) != 0) + { + throw new ProxyException("SOCKS5: 0 byte is expected."); + } + + var addressType = SocketReadByte(socket); + var responseIp = new byte[16]; + + switch (addressType) + { + case 0x01: + SocketRead(socket, responseIp, 0, 4); + break; + case 0x04: + SocketRead(socket, responseIp, 0, 16); + break; + default: + throw new ProxyException(string.Format("Address type '{0}' is not supported.", addressType)); + } + + var portBytes = new byte[2]; + + // Read 2 bytes to be ignored + SocketRead(socket, portBytes, 0, 2); + + return socket; + } + + private static byte[] GetAddressBytes(IPEndPoint endPoint) + { + if (endPoint.AddressFamily == AddressFamily.InterNetwork) + { + var addressBytes = new byte[4 + 1]; + addressBytes[0] = 0x01; + var address = endPoint.Address.GetAddressBytes(); + Buffer.BlockCopy(address, 0, addressBytes, 1, address.Length); + return addressBytes; + } + + if (endPoint.AddressFamily == AddressFamily.InterNetworkV6) + { + var addressBytes = new byte[16 + 1]; + addressBytes[0] = 0x04; + var address = endPoint.Address.GetAddressBytes(); + Buffer.BlockCopy(address, 0, addressBytes, 1, address.Length); + return addressBytes; + } + + throw new ProxyException(string.Format("SOCKS5: IP address '{0}' is not supported.", endPoint.Address)); + } + + private static void SocketWriteByte(Socket socket, byte data) + { + SocketAbstraction.Send(socket, new[] { data }); + } + + private static byte SocketReadByte(Socket socket) + { + var buffer = new byte[1]; + SocketRead(socket, buffer, 0, 1); + return buffer[0]; + } + + private static int SocketRead(Socket socket, byte[] buffer, int offset, int length) + { + var bytesRead = SocketAbstraction.Read(socket, buffer, offset, length, TimeSpan.FromMilliseconds(-1)); + if (bytesRead == 0) + { + // when we're in the disconnecting state (either triggered by client or server), then the + // SshConnectionException will interrupt the message listener loop (if not already interrupted) + // and the exception itself will be ignored (in RaiseError) + throw new SshConnectionException("An established connection was aborted by the server.", + DisconnectReason.ConnectionLost); + } + return bytesRead; + } + } +} diff --git a/src/Renci.SshNet.IntegrationTests/ConnectivityTests.cs b/src/Renci.SshNet.IntegrationTests/ConnectivityTests.cs new file mode 100644 index 00000000..2f94ba7f --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/ConnectivityTests.cs @@ -0,0 +1,462 @@ +using Renci.SshNet.Common; +using Renci.SshNet.IntegrationTests.Common; +using Renci.SshNet.Messages.Transport; + +namespace Renci.SshNet.IntegrationTests +{ + [TestClass] + public class ConnectivityTests : IntegrationTestBase + { + private AuthenticationMethodFactory _authenticationMethodFactory; + private IConnectionInfoFactory _connectionInfoFactory; + private IConnectionInfoFactory _adminConnectionInfoFactory; + private RemoteSshdConfig _remoteSshdConfig; + private SshConnectionDisruptor _sshConnectionDisruptor; + + [TestInitialize] + public void SetUp() + { + _authenticationMethodFactory = new AuthenticationMethodFactory(); + _connectionInfoFactory = new LinuxVMConnectionFactory(SshServerHostName, SshServerPort, _authenticationMethodFactory); + _adminConnectionInfoFactory = new LinuxAdminConnectionFactory(SshServerHostName, SshServerPort); + _remoteSshdConfig = new RemoteSshd(_adminConnectionInfoFactory).OpenConfig(); + _sshConnectionDisruptor = new SshConnectionDisruptor(_adminConnectionInfoFactory); + } + + [TestCleanup] + public void TearDown() + { + _remoteSshdConfig?.Reset(); + } + + [TestMethod] + public void Common_CreateMoreChannelsThanMaxSessions() + { + var connectionInfo = _connectionInfoFactory.Create(); + connectionInfo.MaxSessions = 2; + + using (var client = new SshClient(connectionInfo)) + { + client.Connect(); + + // create one more channel than the maximum number of sessions + // as that would block indefinitely when creating the last channel + // if the channel would not be properly closed + for (var i = 0; i < connectionInfo.MaxSessions + 1; i++) + { + using (var stream = client.CreateShellStream("vt220", 20, 20, 20, 20, 0)) + { + stream.WriteLine("echo test"); + stream.ReadLine(); + } + } + } + } + + [TestMethod] + public void Common_DisposeAfterLossOfNetworkConnectivity() + { + var hostNetworkConnectionDisabled = false; + SshConnectionRestorer disruptor = null; + try + { + Exception errorOccurred = null; + int count = 0; + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.ErrorOccurred += (sender, args) => + { + Console.WriteLine("Exception " + count++); + Console.WriteLine(args.Exception); + errorOccurred = args.Exception; + }; + client.Connect(); + + disruptor = _sshConnectionDisruptor.BreakConnections(); + hostNetworkConnectionDisabled = true; + WaitForConnectionInterruption(client); + } + + Assert.IsNotNull(errorOccurred); + Assert.AreEqual(typeof(SshConnectionException), errorOccurred.GetType()); + + var connectionException = (SshConnectionException) errorOccurred; + Assert.AreEqual(DisconnectReason.ConnectionLost, connectionException.DisconnectReason); + Assert.IsNull(connectionException.InnerException); + Assert.AreEqual("An established connection was aborted by the server.", connectionException.Message); + } + finally + { + if (hostNetworkConnectionDisabled) + { + disruptor?.RestoreConnections(); + disruptor?.Dispose(); + } + } + } + + [TestMethod] + public void Common_DetectLossOfNetworkConnectivityThroughKeepAlive() + { + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + Exception errorOccurred = null; + int count = 0; + client.ErrorOccurred += (sender, args) => + { + Console.WriteLine("Exception "+ count++); + Console.WriteLine(args.Exception); + errorOccurred = args.Exception; + }; + client.KeepAliveInterval = new TimeSpan(0, 0, 0, 0, 50); + client.Connect(); + + var disruptor = _sshConnectionDisruptor.BreakConnections(); + + try + { + WaitForConnectionInterruption(client); + + Assert.IsFalse(client.IsConnected); + + Assert.IsNotNull(errorOccurred); + Assert.AreEqual(typeof(SshConnectionException), errorOccurred.GetType()); + + var connectionException = (SshConnectionException) errorOccurred; + Assert.AreEqual(DisconnectReason.ConnectionLost, connectionException.DisconnectReason); + Assert.IsNull(connectionException.InnerException); + Assert.AreEqual("An established connection was aborted by the server.", connectionException.Message); + } + finally + { + disruptor?.RestoreConnections(); + disruptor?.Dispose(); + } + } + } + + private static void WaitForConnectionInterruption(SftpClient client) + { + for (var i = 0; i < 500; i++) + { + if (!client.IsConnected) + { + break; + } + + Thread.Sleep(100); + } + + // After interruption, you have to wait for the events to propagate. + Thread.Sleep(100); + } + + [TestMethod] + public void Common_DetectConnectionResetThroughSftpInvocation() + { + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.KeepAliveInterval = TimeSpan.FromSeconds(1); + client.OperationTimeout = TimeSpan.FromSeconds(60); + ManualResetEvent errorOccurredSignaled = new ManualResetEvent(false); + Exception errorOccurred = null; + client.ErrorOccurred += (sender, args) => + { + errorOccurred = args.Exception; + errorOccurredSignaled.Set(); + }; + client.Connect(); + + var disruptor = _sshConnectionDisruptor.BreakConnections(); + + try + { + client.ListDirectory("/"); + Assert.Fail(); + } + catch (SshConnectionException ex) + { + Assert.IsNull(ex.InnerException); + Assert.AreEqual("Client not connected.", ex.Message); + + Assert.IsNotNull(errorOccurred); + Assert.AreEqual(typeof(SshConnectionException), errorOccurred.GetType()); + + var connectionException = (SshConnectionException) errorOccurred; + Assert.AreEqual(DisconnectReason.ConnectionLost, connectionException.DisconnectReason); + Assert.IsNull(connectionException.InnerException); + Assert.AreEqual("An established connection was aborted by the server.", connectionException.Message); + } + finally + { + disruptor.RestoreConnections(); + disruptor.Dispose(); + } + } + } + + [TestMethod] + public void Common_LossOfNetworkConnectivityDisconnectAndConnect() + { + bool vmNetworkConnectionDisabled = false; + SshConnectionRestorer disruptor = null; + try + { + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + Exception errorOccurred = null; + client.ErrorOccurred += (sender, args) => errorOccurred = args.Exception; + + client.Connect(); + + disruptor = _sshConnectionDisruptor.BreakConnections(); + vmNetworkConnectionDisabled = true; + + WaitForConnectionInterruption(client); + // disconnect while network connectivity is lost + client.Disconnect(); + + Assert.IsFalse(client.IsConnected); + + disruptor.RestoreConnections(); + vmNetworkConnectionDisabled = false; + + // connect when network connectivity is restored + client.Connect(); + client.ChangeDirectory(client.WorkingDirectory); + client.Dispose(); + + Assert.IsNotNull(errorOccurred); + Assert.AreEqual(typeof(SshConnectionException), errorOccurred.GetType()); + + var connectionException = (SshConnectionException) errorOccurred; + Assert.AreEqual(DisconnectReason.ConnectionLost, connectionException.DisconnectReason); + Assert.IsNull(connectionException.InnerException); + Assert.AreEqual("An established connection was aborted by the server.", connectionException.Message); + } + } + finally + { + if (vmNetworkConnectionDisabled) + { + disruptor.RestoreConnections(); + } + disruptor?.Dispose(); + } + } + + [TestMethod] + public void Common_DetectLossOfNetworkConnectivityThroughSftpInvocation() + { + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + ManualResetEvent errorOccurredSignaled = new ManualResetEvent(false); + Exception errorOccurred = null; + client.ErrorOccurred += (sender, args) => + { + errorOccurred = args.Exception; + errorOccurredSignaled.Set(); + }; + client.Connect(); + + var disruptor = _sshConnectionDisruptor.BreakConnections(); + Thread.Sleep(100); + try + { + client.ListDirectory("/"); + Assert.Fail(); + } + catch (SshConnectionException ex) + { + Assert.IsNull(ex.InnerException); + Assert.AreEqual("Client not connected.", ex.Message); + + Assert.IsNotNull(errorOccurred); + Assert.AreEqual(typeof(SshConnectionException), errorOccurred.GetType()); + + var connectionException = (SshConnectionException) errorOccurred; + Assert.AreEqual(DisconnectReason.ConnectionLost, connectionException.DisconnectReason); + Assert.IsNull(connectionException.InnerException); + Assert.AreEqual("An established connection was aborted by the server.", connectionException.Message); + } + finally + { + disruptor.RestoreConnections(); + disruptor.Dispose(); + } + } + } + + [TestMethod] + public void Common_DetectSessionKilledOnServer() + { + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + ManualResetEvent errorOccurredSignaled = new ManualResetEvent(false); + Exception errorOccurred = null; + client.ErrorOccurred += (sender, args) => + { + errorOccurred = args.Exception; + errorOccurredSignaled.Set(); + }; + client.Connect(); + + // Kill the server session + using (var adminClient = new SshClient(_adminConnectionInfoFactory.Create())) + { + adminClient.Connect(); + + var command = $"sudo ps --no-headers -u {client.ConnectionInfo.Username} -f | grep \"{client.ConnectionInfo.Username}@notty\" | awk '{{print $2}}' | xargs sudo kill -9"; + var sshCommand = adminClient.CreateCommand(command); + var result = sshCommand.Execute(); + Assert.AreEqual(0, sshCommand.ExitStatus, sshCommand.Error); + } + + Assert.IsTrue(errorOccurredSignaled.WaitOne(200)); + Assert.IsNotNull(errorOccurred); + Assert.AreEqual(typeof(SshConnectionException), errorOccurred.GetType()); + Assert.IsNull(errorOccurred.InnerException); + Assert.AreEqual("An established connection was aborted by the server.", errorOccurred.Message); + Assert.IsFalse(client.IsConnected); + } + } + + [TestMethod] + public void Common_HostKeyValidation_Failure() + { + using (var client = new SshClient(_connectionInfoFactory.Create())) + { + client.HostKeyReceived += (sender, e) => { e.CanTrust = false; }; + + try + { + client.Connect(); + Assert.Fail(); + } + catch (SshConnectionException ex) + { + Assert.IsNull(ex.InnerException); + Assert.AreEqual("Key exchange negotiation failed.", ex.Message); + } + } + } + + [TestMethod] + public void Common_HostKeyValidation_Success() + { + byte[] host_rsa_key_openssh_fingerprint = + { + 0x3d, 0x90, 0xd8, 0x0d, 0xd5, 0xe0, 0xb6, 0x13, + 0x42, 0x7c, 0x78, 0x1e, 0x19, 0xa3, 0x99, 0x2b + }; + + var hostValidationSuccessful = false; + + using (var client = new SshClient(_connectionInfoFactory.Create())) + { + client.HostKeyReceived += (sender, e) => + { + if (host_rsa_key_openssh_fingerprint.Length == e.FingerPrint.Length) + { + for (var i = 0; i < host_rsa_key_openssh_fingerprint.Length; i++) + { + if (host_rsa_key_openssh_fingerprint[i] != e.FingerPrint[i]) + { + e.CanTrust = false; + break; + } + } + + hostValidationSuccessful = e.CanTrust; + } + else + { + e.CanTrust = false; + } + }; + client.Connect(); + } + + Assert.IsTrue(hostValidationSuccessful); + } + + [TestMethod] + public void Common_HostKeyValidationSHA256_Success() + { + var hostValidationSuccessful = false; + + using (var client = new SshClient(_connectionInfoFactory.Create())) + { + client.HostKeyReceived += (sender, e) => + { + if (e.FingerPrintSHA256 == "9fa6vbz64gimzsGZ/xZi3aaYE1o7E96iU2NjcfQNGwI") + { + hostValidationSuccessful = e.CanTrust; + } + else + { + e.CanTrust = false; + } + }; + client.Connect(); + } + + Assert.IsTrue(hostValidationSuccessful); + } + + [TestMethod] + public void Common_HostKeyValidationMD5_Success() + { + var hostValidationSuccessful = false; + + using (var client = new SshClient(_connectionInfoFactory.Create())) + { + client.HostKeyReceived += (sender, e) => + { + if (e.FingerPrintMD5 == "3d:90:d8:0d:d5:e0:b6:13:42:7c:78:1e:19:a3:99:2b") + { + hostValidationSuccessful = e.CanTrust; + } + else + { + e.CanTrust = false; + } + }; + client.Connect(); + } + + Assert.IsTrue(hostValidationSuccessful); + } + /// + /// Verifies whether we handle a disconnect initiated by the SSH server (through a SSH_MSG_DISCONNECT message). + /// + /// + /// We force this by only configuring keyboard-interactive as authentication method, while ChallengeResponseAuthentication + /// is not enabled. This causes OpenSSH to terminate the connection because there are no authentication methods left. + /// + [TestMethod] + public void Common_ServerRejectsConnection() + { + _remoteSshdConfig.WithAuthenticationMethods(Users.Regular.UserName, "keyboard-interactive") + .Update() + .Restart(); + + var connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegularUserKeyboardInteractiveAuthenticationMethod()); + using (var client = new SftpClient(connectionInfo)) + { + try + { + client.Connect(); + Assert.Fail(); + } + catch (SshConnectionException ex) + { + Assert.AreEqual(DisconnectReason.ProtocolError, ex.DisconnectReason); + Assert.IsNull(ex.InnerException); + Assert.AreEqual("The connection was closed by the server: no authentication methods enabled (ProtocolError).", ex.Message); + } + } + } + + } +} diff --git a/src/Renci.SshNet.IntegrationTests/Credential.cs b/src/Renci.SshNet.IntegrationTests/Credential.cs new file mode 100644 index 00000000..ee62d0f7 --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/Credential.cs @@ -0,0 +1,14 @@ +namespace Renci.SshNet.IntegrationTests +{ + internal class Credential + { + public Credential(string userName, string password) + { + UserName = userName; + Password = password; + } + + public string UserName { get; } + public string Password { get; } + } +} diff --git a/src/Renci.SshNet.IntegrationTests/Dockerfile b/src/Renci.SshNet.IntegrationTests/Dockerfile new file mode 100644 index 00000000..19ef6e19 --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/Dockerfile @@ -0,0 +1,50 @@ +FROM alpine:latest + +COPY --chown=root:root server/ssh /etc/ssh/ +COPY --chown=root:root server/script /opt/sshnet +COPY user/sshnet /home/sshnet/.ssh + +RUN apk update && apk upgrade --no-cache && \ + apk add --no-cache syslog-ng && \ + # install and configure sshd + apk add --no-cache openssh && \ + # install openssh-server-pam to allow for keyboard-interactive authentication + apk add --no-cache openssh-server-pam && \ + dos2unix /etc/ssh/* && \ + chmod 400 /etc/ssh/ssh*key && \ + sed -i 's/#PasswordAuthentication yes/PasswordAuthentication yes/' /etc/ssh/sshd_config && \ + sed -i 's/#LogLevel\s*INFO/LogLevel DEBUG3/' /etc/ssh/sshd_config && \ + # Set the default RSA key + echo 'HostKey /etc/ssh/ssh_host_rsa_key' >> /etc/ssh/sshd_config && \ + chmod 646 /etc/ssh/sshd_config && \ + # install and configure sudo + apk add --no-cache sudo && \ + addgroup sudo && \ + # allow root to run any command + echo 'root ALL=(ALL) ALL' > /etc/sudoers && \ + # allow everyone in the 'sudo' group to run any command without a password + echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers && \ + # add user to run most of the integration tests + adduser -D sshnet && \ + passwd -u sshnet && \ + echo 'sshnet:ssh4ever' | chpasswd && \ + dos2unix /home/sshnet/.ssh/* && \ + chown -R sshnet:sshnet /home/sshnet && \ + chmod -R 700 /home/sshnet/.ssh && \ + chmod -R 644 /home/sshnet/.ssh/authorized_keys && \ + # add user to administer container (update configs, restart sshd) + adduser -D sshnetadm && \ + passwd -u sshnetadm && \ + echo 'sshnetadm:ssh4ever' | chpasswd && \ + addgroup sshnetadm sudo && \ + dos2unix /opt/sshnet/* && \ + # install shadow package; we use chage command in this package to expire/unexpire password of the sshnet user + apk add --no-cache shadow && \ + # allow us to use telnet command; we use this in the remote port forwarding tests + apk --no-cache add busybox-extras && \ + # install full-fledged ps command + apk add --no-cache procps + +EXPOSE 22 22 + +ENTRYPOINT ["/opt/sshnet/start.sh"] diff --git a/src/Renci.SshNet.IntegrationTests/HmacTests.cs b/src/Renci.SshNet.IntegrationTests/HmacTests.cs new file mode 100644 index 00000000..993e5ec9 --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/HmacTests.cs @@ -0,0 +1,75 @@ +using Renci.SshNet.IntegrationTests.Common; +using Renci.SshNet.TestTools.OpenSSH; + +namespace Renci.SshNet.IntegrationTests +{ + [TestClass] + public class HmacTests : IntegrationTestBase + { + private IConnectionInfoFactory _connectionInfoFactory; + private RemoteSshdConfig _remoteSshdConfig; + + [TestInitialize] + public void SetUp() + { + _connectionInfoFactory = new LinuxVMConnectionFactory(SshServerHostName, SshServerPort); + _remoteSshdConfig = new RemoteSshd(new LinuxAdminConnectionFactory(SshServerHostName, SshServerPort)).OpenConfig(); + } + + [TestCleanup] + public void TearDown() + { + _remoteSshdConfig?.Reset(); + } + + [TestMethod] + public void HmacMd5() + { + DoTest(MessageAuthenticationCodeAlgorithm.HmacMd5); + } + + [TestMethod] + public void HmacMd5_96() + { + DoTest(MessageAuthenticationCodeAlgorithm.HmacMd5_96); + } + + [TestMethod] + public void HmacSha1() + { + DoTest(MessageAuthenticationCodeAlgorithm.HmacSha1); + } + + [TestMethod] + public void HmacSha1_96() + { + DoTest(MessageAuthenticationCodeAlgorithm.HmacSha1_96); + } + + [TestMethod] + public void HmacSha2_256() + { + DoTest(MessageAuthenticationCodeAlgorithm.HmacSha2_256); + } + + [TestMethod] + public void HmacSha2_512() + { + DoTest(MessageAuthenticationCodeAlgorithm.HmacSha2_512); + } + + private void DoTest(MessageAuthenticationCodeAlgorithm macAlgorithm) + { + _remoteSshdConfig.ClearMessageAuthenticationCodeAlgorithms() + .AddMessageAuthenticationCodeAlgorithm(macAlgorithm) + .Update() + .Restart(); + + using (var client = new SshClient(_connectionInfoFactory.Create())) + { + client.Connect(); + client.Disconnect(); + } + } + } +} diff --git a/src/Renci.SshNet.IntegrationTests/HostConfig.cs b/src/Renci.SshNet.IntegrationTests/HostConfig.cs new file mode 100644 index 00000000..817bd702 --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/HostConfig.cs @@ -0,0 +1,115 @@ +using System.Net; +using System.Text.RegularExpressions; + +namespace Renci.SshNet.IntegrationTests +{ + class HostConfig + { + private static readonly Regex HostsEntryRegEx = new Regex(@"^(?[\S]+)\s+(?[a-zA-Z]+[a-zA-Z\-\.]*[a-zA-Z]+)\s*(?.+)*$", RegexOptions.Singleline); + + public List Entries { get; } + + private HostConfig() + { + Entries = new List(); + } + + public static HostConfig Read(ScpClient scpClient, string path) + { + HostConfig hostConfig = new HostConfig(); + + using (var ms = new MemoryStream()) + { + scpClient.Download(path, ms); + ms.Position = 0; + + using (var sr = new StreamReader(ms, Encoding.ASCII)) + { + string line; + while ((line = sr.ReadLine()) != null) + { + // skip comments + if (line.StartsWith("#")) + { + continue; + } + + var hostEntryMatch = HostsEntryRegEx.Match(line); + if (!hostEntryMatch.Success) + { + continue; + } + + var entryIPAddress = hostEntryMatch.Groups["IPAddress"].Value; + var entryAliasesGroup = hostEntryMatch.Groups["Aliases"]; + + var entry = new HostEntry(IPAddress.Parse(entryIPAddress), hostEntryMatch.Groups["HostName"].Value); + + if (entryAliasesGroup.Success) + { + var aliases = entryAliasesGroup.Value.Split(' '); + foreach (var alias in aliases) + { + entry.Aliases.Add(alias); + } + } + + hostConfig.Entries.Add(entry); + } + } + } + + return hostConfig; + } + + public void Write(ScpClient scpClient, string path) + { + using (var ms = new MemoryStream()) + using (var sw = new StreamWriter(ms, Encoding.ASCII)) + { + // Use linux line ending + sw.NewLine = "\n"; + + foreach (var hostEntry in Entries) + { + sw.Write(hostEntry.IPAddress); + sw.Write(" "); + sw.Write(hostEntry.HostName); + + if (hostEntry.Aliases.Count > 0) + { + sw.Write(" "); + for (var i = 0; i < hostEntry.Aliases.Count; i++) + { + if (i > 0) + { + sw.Write(' '); + } + sw.Write(hostEntry.Aliases[i]); + } + } + sw.WriteLine(); + } + + sw.Flush(); + ms.Position = 0; + + scpClient.Upload(ms, path); + } + } + } + + public class HostEntry + { + public HostEntry(IPAddress ipAddress, string hostName) + { + IPAddress = ipAddress; + HostName = hostName; + Aliases = new List(); + } + + public IPAddress IPAddress { get; private set; } + public string HostName { get; set; } + public List Aliases { get; } + } +} diff --git a/src/Renci.SshNet.IntegrationTests/HostKeyAlgorithmTests.cs b/src/Renci.SshNet.IntegrationTests/HostKeyAlgorithmTests.cs new file mode 100644 index 00000000..d827fb47 --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/HostKeyAlgorithmTests.cs @@ -0,0 +1,80 @@ +using Renci.SshNet.Common; +using Renci.SshNet.IntegrationTests.Common; +using Renci.SshNet.TestTools.OpenSSH; + +namespace Renci.SshNet.IntegrationTests +{ + [TestClass] + public class HostKeyAlgorithmTests : IntegrationTestBase + { + private IConnectionInfoFactory _connectionInfoFactory; + private RemoteSshdConfig _remoteSshdConfig; + + [TestInitialize] + public void SetUp() + { + _connectionInfoFactory = new LinuxVMConnectionFactory(SshServerHostName, SshServerPort); + _remoteSshdConfig = new RemoteSshd(new LinuxAdminConnectionFactory(SshServerHostName, SshServerPort)).OpenConfig(); + } + + [TestCleanup] + public void TearDown() + { + _remoteSshdConfig?.Reset(); + } + + [TestMethod] + public void SshDss() + { + DoTest(HostKeyAlgorithm.SshDss, HostKeyFile.Dsa, 2048); + } + + [TestMethod] + public void SshRsa() + { + DoTest(HostKeyAlgorithm.SshRsa, HostKeyFile.Rsa, 3072); + } + + [TestMethod] + public void SshRsaSha256() + { + DoTest(HostKeyAlgorithm.RsaSha2256, HostKeyFile.Rsa, 3072); + } + + [TestMethod] + public void SshRsaSha512() + { + DoTest(HostKeyAlgorithm.RsaSha2512, HostKeyFile.Rsa, 3072); + } + + [TestMethod] + public void SshEd25519() + { + DoTest(HostKeyAlgorithm.SshEd25519, HostKeyFile.Ed25519, 256); + } + + private void DoTest(HostKeyAlgorithm hostKeyAlgorithm, HostKeyFile hostKeyFile, int keyLength) + { + _remoteSshdConfig.ClearHostKeyAlgorithms() + .AddHostKeyAlgorithm(hostKeyAlgorithm) + .ClearHostKeyFiles() + .AddHostKeyFile(hostKeyFile.FilePath) + .Update() + .Restart(); + + HostKeyEventArgs hostKeyEventsArgs = null; + + using (var client = new SshClient(_connectionInfoFactory.Create())) + { + client.HostKeyReceived += (sender, e) => hostKeyEventsArgs = e; + client.Connect(); + client.Disconnect(); + } + + Assert.IsNotNull(hostKeyEventsArgs); + Assert.AreEqual(hostKeyAlgorithm.Name, hostKeyEventsArgs.HostKeyName); + Assert.AreEqual(keyLength, hostKeyEventsArgs.KeyLength); + CollectionAssert.AreEqual(hostKeyFile.FingerPrint, hostKeyEventsArgs.FingerPrint); + } + } +} diff --git a/src/Renci.SshNet.IntegrationTests/HostKeyFile.cs b/src/Renci.SshNet.IntegrationTests/HostKeyFile.cs new file mode 100644 index 00000000..66d09fd2 --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/HostKeyFile.cs @@ -0,0 +1,23 @@ +namespace Renci.SshNet.IntegrationTests +{ + public sealed class HostKeyFile + { + public static readonly HostKeyFile Rsa = new HostKeyFile("ssh-rsa", "/etc/ssh/ssh_host_rsa_key", new byte[] { 0x3d, 0x90, 0xd8, 0x0d, 0xd5, 0xe0, 0xb6, 0x13, 0x42, 0x7c, 0x78, 0x1e, 0x19, 0xa3, 0x99, 0x2b }); + public static readonly HostKeyFile Dsa = new HostKeyFile("ssh-dsa", "/etc/ssh/ssh_host_dsa_key", new byte[] { 0x50, 0xe0, 0xd5, 0x11, 0xf7, 0xed, 0x54, 0x75, 0x0d, 0x03, 0xc6, 0x52, 0x9b, 0x3b, 0x3c, 0x9f }); + public static readonly HostKeyFile Ed25519 = new HostKeyFile("ssh-ed25519", "/etc/ssh/ssh_host_ed25519_key", new byte[] { 0xb3, 0xb9, 0xd0, 0x1b, 0x73, 0xc4, 0x60, 0xb4, 0xce, 0xed, 0x06, 0xf8, 0x58, 0x49, 0xa3, 0xda }); + public const string Ecdsa = "/etc/ssh/ssh_host_ecdsa_key"; + + private HostKeyFile(string keyName, string filePath, byte[] fingerPrint) + { + KeyName = keyName; + FilePath = filePath; + FingerPrint = fingerPrint; + } + + public string KeyName {get; } + public string FilePath { get; } + public byte[] FingerPrint { get; } + } + + +} diff --git a/src/Renci.SshNet.IntegrationTests/IConnectionInfoFactory.cs b/src/Renci.SshNet.IntegrationTests/IConnectionInfoFactory.cs new file mode 100644 index 00000000..858dd087 --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/IConnectionInfoFactory.cs @@ -0,0 +1,10 @@ +namespace Renci.SshNet.IntegrationTests +{ + public interface IConnectionInfoFactory + { + ConnectionInfo Create(); + ConnectionInfo Create(params AuthenticationMethod[] authenticationMethods); + ConnectionInfo CreateWithProxy(); + ConnectionInfo CreateWithProxy(params AuthenticationMethod[] authenticationMethods); + } +} diff --git a/src/Renci.SshNet.IntegrationTests/KeyExchangeAlgorithmTests.cs b/src/Renci.SshNet.IntegrationTests/KeyExchangeAlgorithmTests.cs new file mode 100644 index 00000000..dfd6b639 --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/KeyExchangeAlgorithmTests.cs @@ -0,0 +1,206 @@ +using Renci.SshNet.IntegrationTests.Common; +using Renci.SshNet.TestTools.OpenSSH; + +namespace Renci.SshNet.IntegrationTests +{ + [TestClass] + public class KeyExchangeAlgorithmTests : IntegrationTestBase + { + private IConnectionInfoFactory _connectionInfoFactory; + private RemoteSshdConfig _remoteSshdConfig; + + [TestInitialize] + public void SetUp() + { + _connectionInfoFactory = new LinuxVMConnectionFactory(SshServerHostName, SshServerPort); + _remoteSshdConfig = new RemoteSshd(new LinuxAdminConnectionFactory(SshServerHostName, SshServerPort)).OpenConfig(); + } + + [TestCleanup] + public void TearDown() + { + _remoteSshdConfig?.Reset(); + } + + [TestMethod] + public void Curve25519Sha256() + { + _remoteSshdConfig.ClearKeyExchangeAlgorithms() + .AddKeyExchangeAlgorithm(KeyExchangeAlgorithm.Curve25519Sha256) + .Update() + .Restart(); + + using (var client = new SshClient(_connectionInfoFactory.Create())) + { + client.Connect(); + client.Disconnect(); + } + } + + [TestMethod] + public void Curve25519Sha256Libssh() + { + _remoteSshdConfig.ClearKeyExchangeAlgorithms() + .AddKeyExchangeAlgorithm(KeyExchangeAlgorithm.Curve25519Sha256Libssh) + .Update() + .Restart(); + + using (var client = new SshClient(_connectionInfoFactory.Create())) + { + client.Connect(); + client.Disconnect(); + } + } + + [TestMethod] + public void DiffieHellmanGroup1Sha1() + { + _remoteSshdConfig.ClearKeyExchangeAlgorithms() + .AddKeyExchangeAlgorithm(KeyExchangeAlgorithm.DiffieHellmanGroup1Sha1) + .Update() + .Restart(); + + using (var client = new SshClient(_connectionInfoFactory.Create())) + { + client.Connect(); + client.Disconnect(); + } + } + + [TestMethod] + public void DiffieHellmanGroup14Sha1() + { + _remoteSshdConfig.ClearKeyExchangeAlgorithms() + .AddKeyExchangeAlgorithm(KeyExchangeAlgorithm.DiffieHellmanGroup14Sha1) + .Update() + .Restart(); + + using (var client = new SshClient(_connectionInfoFactory.Create())) + { + client.Connect(); + client.Disconnect(); + } + } + + [TestMethod] + public void DiffieHellmanGroup14Sha256() + { + _remoteSshdConfig.ClearKeyExchangeAlgorithms() + .AddKeyExchangeAlgorithm(KeyExchangeAlgorithm.DiffieHellmanGroup14Sha256) + .Update() + .Restart(); + + using (var client = new SshClient(_connectionInfoFactory.Create())) + { + client.Connect(); + client.Disconnect(); + } + } + + [TestMethod] + public void DiffieHellmanGroup16Sha512() + { + _remoteSshdConfig.ClearKeyExchangeAlgorithms() + .AddKeyExchangeAlgorithm(KeyExchangeAlgorithm.DiffieHellmanGroup16Sha512) + .Update() + .Restart(); + + using (var client = new SshClient(_connectionInfoFactory.Create())) + { + client.Connect(); + client.Disconnect(); + } + } + + [TestMethod] + [Ignore] + public void DiffieHellmanGroup18Sha512() + { + _remoteSshdConfig.ClearKeyExchangeAlgorithms() + .AddKeyExchangeAlgorithm(KeyExchangeAlgorithm.DiffieHellmanGroup18Sha512) + .Update() + .Restart(); + + using (var client = new SshClient(_connectionInfoFactory.Create())) + { + client.Connect(); + client.Disconnect(); + } + } + + [TestMethod] + public void DiffieHellmanGroupExchangeSha1() + { + _remoteSshdConfig.ClearKeyExchangeAlgorithms() + .AddKeyExchangeAlgorithm(KeyExchangeAlgorithm.DiffieHellmanGroupExchangeSha1) + .Update() + .Restart(); + + using (var client = new SshClient(_connectionInfoFactory.Create())) + { + client.Connect(); + client.Disconnect(); + } + } + + [TestMethod] + public void DiffieHellmanGroupExchangeSha256() + { + _remoteSshdConfig.ClearKeyExchangeAlgorithms() + .AddKeyExchangeAlgorithm(KeyExchangeAlgorithm.DiffieHellmanGroupExchangeSha256) + .Update() + .Restart(); + + using (var client = new SshClient(_connectionInfoFactory.Create())) + { + client.Connect(); + client.Disconnect(); + } + } + + [TestMethod] + public void EcdhSha2Nistp256() + { + _remoteSshdConfig.ClearKeyExchangeAlgorithms() + .AddKeyExchangeAlgorithm(KeyExchangeAlgorithm.EcdhSha2Nistp256) + .Update() + .Restart(); + + using (var client = new SshClient(_connectionInfoFactory.Create())) + { + client.Connect(); + client.Disconnect(); + } + } + + [TestMethod] + public void EcdhSha2Nistp384() + { + _remoteSshdConfig.ClearKeyExchangeAlgorithms() + .AddKeyExchangeAlgorithm(KeyExchangeAlgorithm.EcdhSha2Nistp384) + .Update() + .Restart(); + + using (var client = new SshClient(_connectionInfoFactory.Create())) + { + client.Connect(); + client.Disconnect(); + } + } + + [TestMethod] + public void EcdhSha2Nistp521() + { + _remoteSshdConfig.ClearKeyExchangeAlgorithms() + .AddKeyExchangeAlgorithm(KeyExchangeAlgorithm.EcdhSha2Nistp521) + .Update() + .Restart(); + + using (var client = new SshClient(_connectionInfoFactory.Create())) + { + client.Connect(); + client.Disconnect(); + } + } + } +} diff --git a/src/Renci.SshNet.IntegrationTests/LinuxAdminConnectionFactory.cs b/src/Renci.SshNet.IntegrationTests/LinuxAdminConnectionFactory.cs new file mode 100644 index 00000000..dfb5c136 --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/LinuxAdminConnectionFactory.cs @@ -0,0 +1,36 @@ +namespace Renci.SshNet.IntegrationTests +{ + public class LinuxAdminConnectionFactory : IConnectionInfoFactory + { + private readonly string _host; + private readonly int _port; + + public LinuxAdminConnectionFactory(string sshServerHostName, ushort sshServerPort) + { + _host = sshServerHostName; + _port = sshServerPort; + } + + public ConnectionInfo Create() + { + var user = Users.Admin; + return new ConnectionInfo(_host, _port, user.UserName, new PasswordAuthenticationMethod(user.UserName, user.Password)); + } + + public ConnectionInfo Create(params AuthenticationMethod[] authenticationMethods) + { + throw new NotImplementedException(); + } + + public ConnectionInfo CreateWithProxy() + { + throw new NotImplementedException(); + } + + public ConnectionInfo CreateWithProxy(params AuthenticationMethod[] authenticationMethods) + { + throw new NotImplementedException(); + } + } +} + diff --git a/src/Renci.SshNet.IntegrationTests/LinuxVMConnectionFactory.cs b/src/Renci.SshNet.IntegrationTests/LinuxVMConnectionFactory.cs new file mode 100644 index 00000000..f2f04dbf --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/LinuxVMConnectionFactory.cs @@ -0,0 +1,62 @@ +namespace Renci.SshNet.IntegrationTests +{ + public class LinuxVMConnectionFactory : IConnectionInfoFactory + { + + + private const string ProxyHost = "127.0.0.1"; + private const int ProxyPort = 1234; + private const string ProxyUserName = "test"; + private const string ProxyPassword = "123"; + private readonly string _host; + private readonly int _port; + private readonly AuthenticationMethodFactory _authenticationMethodFactory; + + + public LinuxVMConnectionFactory(string sshServerHostName, ushort sshServerPort) + { + _host = sshServerHostName; + _port = sshServerPort; + + _authenticationMethodFactory = new AuthenticationMethodFactory(); + } + + public LinuxVMConnectionFactory(string sshServerHostName, ushort sshServerPort, AuthenticationMethodFactory authenticationMethodFactory) + { + _host = sshServerHostName; + _port = sshServerPort; + + _authenticationMethodFactory = authenticationMethodFactory; + } + + public ConnectionInfo Create() + { + return Create(_authenticationMethodFactory.CreateRegularUserPrivateKeyAuthenticationMethod()); + } + + public ConnectionInfo Create(params AuthenticationMethod[] authenticationMethods) + { + return new ConnectionInfo(_host, _port, Users.Regular.UserName, authenticationMethods); + } + + public ConnectionInfo CreateWithProxy() + { + return CreateWithProxy(_authenticationMethodFactory.CreateRegularUserPrivateKeyAuthenticationMethod()); + } + + public ConnectionInfo CreateWithProxy(params AuthenticationMethod[] authenticationMethods) + { + return new ConnectionInfo( + _host, + _port, + Users.Regular.UserName, + ProxyTypes.Socks4, + ProxyHost, + ProxyPort, + ProxyUserName, + ProxyPassword, + authenticationMethods); + } + } +} + diff --git a/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/ForwardedPortLocalTest.cs b/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/ForwardedPortLocalTest.cs new file mode 100644 index 00000000..bf6292fa --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/ForwardedPortLocalTest.cs @@ -0,0 +1,158 @@ +using System.Diagnostics; + +using Renci.SshNet.Common; + +namespace Renci.SshNet.IntegrationTests.OldIntegrationTests +{ + /// + /// Provides functionality for local port forwarding + /// + [TestClass] + public class ForwardedPortLocalTest : IntegrationTestBase + { + [TestMethod] + [WorkItem(713)] + [Owner("Kenneth_aa")] + [TestCategory("PortForwarding")] + [Description("Test if calling Stop on ForwardedPortLocal instance causes wait.")] + public void Test_PortForwarding_Local_Stop_Hangs_On_Wait() + { + using (var client = new SshClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) + { + client.Connect(); + + var port1 = new ForwardedPortLocal("localhost", 8084, "www.google.com", 80); + client.AddForwardedPort(port1); + port1.Exception += delegate (object sender, ExceptionEventArgs e) + { + Assert.Fail(e.Exception.ToString()); + }; + + port1.Start(); + + var hasTestedTunnel = false; + + _ = ThreadPool.QueueUserWorkItem(delegate (object state) + { + try + { + var url = "http://www.google.com/"; + Debug.WriteLine("Starting web request to \"" + url + "\""); + +#if NET6_0_OR_GREATER + var httpClient = new HttpClient(); + var response = httpClient.GetAsync(url) + .ConfigureAwait(false) + .GetAwaiter() + .GetResult(); +#else + var request = (HttpWebRequest) WebRequest.Create(url); + var response = (HttpWebResponse) request.GetResponse(); +#endif // NET6_0_OR_GREATER + + Assert.IsNotNull(response); + + Debug.WriteLine("Http Response status code: " + response.StatusCode.ToString()); + + response.Dispose(); + + hasTestedTunnel = true; + } + catch (Exception ex) + { + Assert.Fail(ex.ToString()); + } + }); + + // Wait for the web request to complete. + while (!hasTestedTunnel) + { + Thread.Sleep(1000); + } + + try + { + // Try stop the port forwarding, wait 3 seconds and fail if it is still started. + _ = ThreadPool.QueueUserWorkItem(delegate (object state) + { + Debug.WriteLine("Trying to stop port forward."); + port1.Stop(); + Debug.WriteLine("Port forwarding stopped."); + }); + + Thread.Sleep(3000); + if (port1.IsStarted) + { + Assert.Fail("Port forwarding not stopped."); + } + } + catch (Exception ex) + { + Assert.Fail(ex.ToString()); + } + client.RemoveForwardedPort(port1); + client.Disconnect(); + Debug.WriteLine("Success."); + } + } + + [TestMethod] + [ExpectedException(typeof(SshConnectionException))] + public void Test_PortForwarding_Local_Without_Connecting() + { + using (var client = new SshClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) + { + var port1 = new ForwardedPortLocal("localhost", 8084, "www.renci.org", 80); + client.AddForwardedPort(port1); + port1.Exception += delegate (object sender, ExceptionEventArgs e) + { + Assert.Fail(e.Exception.ToString()); + }; + port1.Start(); + + var test = Parallel.For(0, + 100, + counter => + { + var start = DateTime.Now; + +#if NET6_0_OR_GREATER + var httpClient = new HttpClient(); + using (var response = httpClient.GetAsync("http://localhost:8084").GetAwaiter().GetResult()) + { + var data = ReadStream(response.Content.ReadAsStream()); +#else + var request = (HttpWebRequest) WebRequest.Create("http://localhost:8084"); + using (var response = (HttpWebResponse) request.GetResponse()) + { + var data = ReadStream(response.GetResponseStream()); +#endif // NET6_0_OR_GREATER + var end = DateTime.Now; + + Debug.WriteLine(string.Format("Request# {2}: Lenght: {0} Time: {1}", data.Length, end - start, counter)); + } + }); + } + } + + private static byte[] ReadStream(Stream stream) + { + var buffer = new byte[1024]; + using (var ms = new MemoryStream()) + { + while (true) + { + var read = stream.Read(buffer, 0, buffer.Length); + if (read > 0) + { + ms.Write(buffer, 0, read); + } + else + { + return ms.ToArray(); + } + } + } + } + } +} diff --git a/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/ScpClientTest.cs b/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/ScpClientTest.cs new file mode 100644 index 00000000..e9015a3c --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/ScpClientTest.cs @@ -0,0 +1,336 @@ +using System.Security.Cryptography; + +using Renci.SshNet.Common; + +namespace Renci.SshNet.IntegrationTests.OldIntegrationTests +{ + /// + /// Provides SCP client functionality. + /// + [TestClass] + public partial class ScpClientTest : IntegrationTestBase + { + [TestMethod] + [TestCategory("Scp")] + public void Test_Scp_File_Upload_Download() + { + RemoveAllFiles(); + + using (var scp = new ScpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) + { + scp.Connect(); + + var uploadedFileName = Path.GetTempFileName(); + var downloadedFileName = Path.GetTempFileName(); + + CreateTestFile(uploadedFileName, 1); + + scp.Upload(new FileInfo(uploadedFileName), Path.GetFileName(uploadedFileName)); + + scp.Download(Path.GetFileName(uploadedFileName), new FileInfo(downloadedFileName)); + + // Calculate MD5 value + var uploadedHash = CalculateMD5(uploadedFileName); + var downloadedHash = CalculateMD5(downloadedFileName); + + File.Delete(uploadedFileName); + File.Delete(downloadedFileName); + + scp.Disconnect(); + + Assert.AreEqual(uploadedHash, downloadedHash); + } + } + + [TestMethod] + [TestCategory("Scp")] + public void Test_Scp_Stream_Upload_Download() + { + RemoveAllFiles(); + + using (var scp = new ScpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) + { + scp.Connect(); + + var uploadedFileName = Path.GetTempFileName(); + var downloadedFileName = Path.GetTempFileName(); + + CreateTestFile(uploadedFileName, 1); + + // Calculate has value + using (var stream = File.OpenRead(uploadedFileName)) + { + scp.Upload(stream, Path.GetFileName(uploadedFileName)); + } + + using (var stream = File.OpenWrite(downloadedFileName)) + { + scp.Download(Path.GetFileName(uploadedFileName), stream); + } + + // Calculate MD5 value + var uploadedHash = CalculateMD5(uploadedFileName); + var downloadedHash = CalculateMD5(downloadedFileName); + + File.Delete(uploadedFileName); + File.Delete(downloadedFileName); + + scp.Disconnect(); + + Assert.AreEqual(uploadedHash, downloadedHash); + } + } + + [TestMethod] + [TestCategory("Scp")] + public void Test_Scp_10MB_File_Upload_Download() + { + RemoveAllFiles(); + + using (var scp = new ScpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) + { + scp.Connect(); + + var uploadedFileName = Path.GetTempFileName(); + var downloadedFileName = Path.GetTempFileName(); + + CreateTestFile(uploadedFileName, 10); + + scp.Upload(new FileInfo(uploadedFileName), Path.GetFileName(uploadedFileName)); + + scp.Download(Path.GetFileName(uploadedFileName), new FileInfo(downloadedFileName)); + + // Calculate MD5 value + var uploadedHash = CalculateMD5(uploadedFileName); + var downloadedHash = CalculateMD5(downloadedFileName); + + File.Delete(uploadedFileName); + File.Delete(downloadedFileName); + + scp.Disconnect(); + + Assert.AreEqual(uploadedHash, downloadedHash); + } + } + + [TestMethod] + [TestCategory("Scp")] + public void Test_Scp_10MB_Stream_Upload_Download() + { + RemoveAllFiles(); + + using (var scp = new ScpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) + { + scp.Connect(); + + var uploadedFileName = Path.GetTempFileName(); + var downloadedFileName = Path.GetTempFileName(); + + CreateTestFile(uploadedFileName, 10); + + // Calculate has value + using (var stream = File.OpenRead(uploadedFileName)) + { + scp.Upload(stream, Path.GetFileName(uploadedFileName)); + } + + using (var stream = File.OpenWrite(downloadedFileName)) + { + scp.Download(Path.GetFileName(uploadedFileName), stream); + } + + // Calculate MD5 value + var uploadedHash = CalculateMD5(uploadedFileName); + var downloadedHash = CalculateMD5(downloadedFileName); + + File.Delete(uploadedFileName); + File.Delete(downloadedFileName); + + scp.Disconnect(); + + Assert.AreEqual(uploadedHash, downloadedHash); + } + } + + [TestMethod] + [TestCategory("Scp")] + public void Test_Scp_Directory_Upload_Download() + { + RemoveAllFiles(); + using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) + { + sftp.Connect(); + sftp.CreateDirectory("uploaded_dir"); + } + + using (var scp = new ScpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) + { + scp.Connect(); + + var uploadDirectory = + Directory.CreateDirectory(string.Format("{0}\\{1}", Path.GetTempPath(), Path.GetRandomFileName())); + for (var i = 0; i < 3; i++) + { + var subfolder = Directory.CreateDirectory(string.Format(@"{0}\folder_{1}", uploadDirectory.FullName, i)); + + for (var j = 0; j < 5; j++) + { + CreateTestFile(string.Format(@"{0}\file_{1}", subfolder.FullName, j), 1); + } + + CreateTestFile(string.Format(@"{0}\file_{1}", uploadDirectory.FullName, i), 1); + } + + scp.Upload(uploadDirectory, "uploaded_dir"); + + var downloadDirectory = + Directory.CreateDirectory(string.Format("{0}\\{1}", Path.GetTempPath(), Path.GetRandomFileName())); + + scp.Download("uploaded_dir", downloadDirectory); + + var uploadedFiles = uploadDirectory.GetFiles("*.*", SearchOption.AllDirectories); + var downloadFiles = downloadDirectory.GetFiles("*.*", SearchOption.AllDirectories); + + var result = from f1 in uploadedFiles + from f2 in downloadFiles + where + f1.FullName.Substring(uploadDirectory.FullName.Length) == + f2.FullName.Substring(downloadDirectory.FullName.Length) + && CalculateMD5(f1.FullName) == CalculateMD5(f2.FullName) + select f1; + + var counter = result.Count(); + + scp.Disconnect(); + + Assert.IsTrue(counter == uploadedFiles.Length && uploadedFiles.Length == downloadFiles.Length); + } + RemoveAllFiles(); + } + + [TestMethod] + [TestCategory("Scp")] + public void Test_Scp_File_20_Parallel_Upload_Download() + { + using (var scp = new ScpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) + { + scp.Connect(); + + var uploadFilenames = new string[20]; + for (var i = 0; i < uploadFilenames.Length; i++) + { + uploadFilenames[i] = Path.GetTempFileName(); + CreateTestFile(uploadFilenames[i], 1); + } + + _ = Parallel.ForEach(uploadFilenames, + filename => + { + scp.Upload(new FileInfo(filename), Path.GetFileName(filename)); + }); + _ = Parallel.ForEach(uploadFilenames, + filename => + { + scp.Download(Path.GetFileName(filename), new FileInfo(string.Format("{0}.down", filename))); + }); + + var result = from file in uploadFilenames + where CalculateMD5(file) == CalculateMD5(string.Format("{0}.down", file)) + select file; + + scp.Disconnect(); + + Assert.IsTrue(result.Count() == uploadFilenames.Length); + } + } + + [TestMethod] + [TestCategory("Scp")] + public void Test_Scp_File_Upload_Download_Events() + { + using (var scp = new ScpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) + { + scp.Connect(); + + var uploadFilenames = new string[10]; + + for (var i = 0; i < uploadFilenames.Length; i++) + { + uploadFilenames[i] = Path.GetTempFileName(); + CreateTestFile(uploadFilenames[i], 1); + } + + var uploadedFiles = uploadFilenames.ToDictionary(Path.GetFileName, (filename) => 0L); + var downloadedFiles = uploadFilenames.ToDictionary((filename) => string.Format("{0}.down", Path.GetFileName(filename)), (filename) => 0L); + + scp.Uploading += delegate (object sender, ScpUploadEventArgs e) + { + uploadedFiles[e.Filename] = e.Uploaded; + }; + + scp.Downloading += delegate (object sender, ScpDownloadEventArgs e) + { + downloadedFiles[string.Format("{0}.down", e.Filename)] = e.Downloaded; + }; + + _ = Parallel.ForEach(uploadFilenames, + filename => + { + scp.Upload(new FileInfo(filename), Path.GetFileName(filename)); + }); + _ = Parallel.ForEach(uploadFilenames, + filename => + { + scp.Download(Path.GetFileName(filename), new FileInfo(string.Format("{0}.down", filename))); + }); + + var result = from uf in uploadedFiles + from df in downloadedFiles + where string.Format("{0}.down", uf.Key) == df.Key && uf.Value == df.Value + select uf; + + scp.Disconnect(); + + Assert.IsTrue(result.Count() == uploadFilenames.Length && uploadFilenames.Length == uploadedFiles.Count && uploadedFiles.Count == downloadedFiles.Count); + } + } + + protected static string CalculateMD5(string fileName) + { + using (var file = new FileStream(fileName, FileMode.Open)) + { +#if NET7_0_OR_GREATER + var hash = MD5.HashData(file); +#else +#if NET6_0 + var md5 = MD5.Create(); +#else + MD5 md5 = new MD5CryptoServiceProvider(); +#endif // NET6_0 + var hash = md5.ComputeHash(file); +#endif // NET7_0_OR_GREATER + + file.Close(); + + var sb = new StringBuilder(); + + for (var i = 0; i < hash.Length; i++) + { + _ = sb.Append(i.ToString("x2")); + } + + return sb.ToString(); + } + } + + private void RemoveAllFiles() + { + using (var client = new SshClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) + { + client.Connect(); + _ = client.RunCommand("rm -rf *"); + client.Disconnect(); + } + } + } +} diff --git a/src/Renci.SshNet.Tests/Classes/SftpClientTest.ChangeDirectory.cs b/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.ChangeDirectory.cs similarity index 66% rename from src/Renci.SshNet.Tests/Classes/SftpClientTest.ChangeDirectory.cs rename to src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.ChangeDirectory.cs index 5c58e1fa..6fb51850 100644 --- a/src/Renci.SshNet.Tests/Classes/SftpClientTest.ChangeDirectory.cs +++ b/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.ChangeDirectory.cs @@ -1,21 +1,18 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Renci.SshNet.Common; -using Renci.SshNet.Tests.Properties; +using Renci.SshNet.Common; -namespace Renci.SshNet.Tests.Classes +namespace Renci.SshNet.IntegrationTests.OldIntegrationTests { /// /// Implementation of the SSH File Transfer Protocol (SFTP) over SSH. /// - public partial class SftpClientTest + public partial class SftpClientTest : IntegrationTestBase { [TestMethod] [TestCategory("Sftp")] - [TestCategory("integration")] [ExpectedException(typeof(SftpPathNotFoundException))] public void Test_Sftp_ChangeDirectory_Root_Dont_Exists() { - using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) + using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) { sftp.Connect(); sftp.ChangeDirectory("/asdasd"); @@ -24,11 +21,10 @@ namespace Renci.SshNet.Tests.Classes [TestMethod] [TestCategory("Sftp")] - [TestCategory("integration")] [ExpectedException(typeof(SftpPathNotFoundException))] public void Test_Sftp_ChangeDirectory_Root_With_Slash_Dont_Exists() { - using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) + using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) { sftp.Connect(); sftp.ChangeDirectory("/asdasd/"); @@ -37,11 +33,10 @@ namespace Renci.SshNet.Tests.Classes [TestMethod] [TestCategory("Sftp")] - [TestCategory("integration")] [ExpectedException(typeof(SftpPathNotFoundException))] public void Test_Sftp_ChangeDirectory_Subfolder_Dont_Exists() { - using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) + using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) { sftp.Connect(); sftp.ChangeDirectory("/asdasd/sssddds"); @@ -50,11 +45,10 @@ namespace Renci.SshNet.Tests.Classes [TestMethod] [TestCategory("Sftp")] - [TestCategory("integration")] [ExpectedException(typeof(SftpPathNotFoundException))] public void Test_Sftp_ChangeDirectory_Subfolder_With_Slash_Dont_Exists() { - using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) + using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) { sftp.Connect(); sftp.ChangeDirectory("/asdasd/sssddds/"); @@ -63,10 +57,9 @@ namespace Renci.SshNet.Tests.Classes [TestMethod] [TestCategory("Sftp")] - [TestCategory("integration")] public void Test_Sftp_ChangeDirectory_Which_Exists() { - using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) + using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) { sftp.Connect(); sftp.ChangeDirectory("/usr"); @@ -76,10 +69,9 @@ namespace Renci.SshNet.Tests.Classes [TestMethod] [TestCategory("Sftp")] - [TestCategory("integration")] public void Test_Sftp_ChangeDirectory_Which_Exists_With_Slash() { - using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) + using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) { sftp.Connect(); sftp.ChangeDirectory("/usr/"); @@ -87,4 +79,4 @@ namespace Renci.SshNet.Tests.Classes } } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.CreateDirectory.cs b/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.CreateDirectory.cs new file mode 100644 index 00000000..43b17669 --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.CreateDirectory.cs @@ -0,0 +1,72 @@ + +using Renci.SshNet.Common; + +namespace Renci.SshNet.IntegrationTests.OldIntegrationTests +{ + /// + /// Implementation of the SSH File Transfer Protocol (SFTP) over SSH. + /// + public partial class SftpClientTest : IntegrationTestBase + { + [TestMethod] + [TestCategory("Sftp")] + public void Test_Sftp_CreateDirectory_In_Current_Location() + { + using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) + { + sftp.Connect(); + + sftp.CreateDirectory("test-in-current"); + + sftp.Disconnect(); + } + } + + [TestMethod] + [TestCategory("Sftp")] + [ExpectedException(typeof(SftpPermissionDeniedException))] + public void Test_Sftp_CreateDirectory_In_Forbidden_Directory() + { + using (var sftp = new SftpClient(SshServerHostName, SshServerPort, AdminUser.UserName, AdminUser.Password)) + { + sftp.Connect(); + + sftp.CreateDirectory("/sbin/test"); + + sftp.Disconnect(); + } + } + + [TestMethod] + [TestCategory("Sftp")] + [ExpectedException(typeof(SftpPathNotFoundException))] + public void Test_Sftp_CreateDirectory_Invalid_Path() + { + using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) + { + sftp.Connect(); + + sftp.CreateDirectory("/abcdefg/abcefg"); + + sftp.Disconnect(); + } + } + + [TestMethod] + [TestCategory("Sftp")] + [ExpectedException(typeof(SshException))] + public void Test_Sftp_CreateDirectory_Already_Exists() + { + using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) + { + sftp.Connect(); + + sftp.CreateDirectory("test"); + + sftp.CreateDirectory("test"); + + sftp.Disconnect(); + } + } + } +} diff --git a/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.DeleteDirectory.cs b/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.DeleteDirectory.cs new file mode 100644 index 00000000..30697ddc --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.DeleteDirectory.cs @@ -0,0 +1,71 @@ +using Renci.SshNet.Common; + +namespace Renci.SshNet.IntegrationTests.OldIntegrationTests +{ + /// + /// Implementation of the SSH File Transfer Protocol (SFTP) over SSH. + /// + public partial class SftpClientTest : IntegrationTestBase + { + [TestMethod] + [TestCategory("Sftp")] + [ExpectedException(typeof(SftpPathNotFoundException))] + public void Test_Sftp_DeleteDirectory_Which_Doesnt_Exists() + { + using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) + { + sftp.Connect(); + + sftp.DeleteDirectory("abcdef"); + + sftp.Disconnect(); + } + } + + [TestMethod] + [TestCategory("Sftp")] + [ExpectedException(typeof(SftpPermissionDeniedException))] + public void Test_Sftp_DeleteDirectory_Which_No_Permissions() + { + using (var sftp = new SftpClient(SshServerHostName, SshServerPort, AdminUser.UserName, AdminUser.Password)) + { + sftp.Connect(); + + sftp.DeleteDirectory("/usr"); + + sftp.Disconnect(); + } + } + + [TestMethod] + [TestCategory("Sftp")] + public void Test_Sftp_DeleteDirectory() + { + using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) + { + sftp.Connect(); + + sftp.CreateDirectory("abcdef"); + sftp.DeleteDirectory("abcdef"); + + sftp.Disconnect(); + } + } + + [TestMethod] + [TestCategory("Sftp")] + [Description("Test passing null to DeleteDirectory.")] + [ExpectedException(typeof(ArgumentException))] + public void Test_Sftp_DeleteDirectory_Null() + { + using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) + { + sftp.Connect(); + + sftp.DeleteDirectory(null); + + sftp.Disconnect(); + } + } + } +} diff --git a/src/Renci.SshNet.Tests/Classes/SftpClientTest.Download.cs b/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.Download.cs similarity index 70% rename from src/Renci.SshNet.Tests/Classes/SftpClientTest.Download.cs rename to src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.Download.cs index 85a2d7ec..318834e4 100644 --- a/src/Renci.SshNet.Tests/Classes/SftpClientTest.Download.cs +++ b/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.Download.cs @@ -1,26 +1,18 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Renci.SshNet.Common; -using Renci.SshNet.Tests.Properties; -using System; -using System.IO; +using Renci.SshNet.Common; -namespace Renci.SshNet.Tests.Classes +namespace Renci.SshNet.IntegrationTests.OldIntegrationTests { /// /// Implementation of the SSH File Transfer Protocol (SFTP) over SSH. /// - public partial class SftpClientTest + public partial class SftpClientTest : IntegrationTestBase { [TestMethod] [TestCategory("Sftp")] - [TestCategory("integration")] [ExpectedException(typeof(SftpPermissionDeniedException))] public void Test_Sftp_Download_Forbidden() { - if (Resources.USERNAME == "root") - Assert.Fail("Must not run this test as root!"); - - using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) + using (var sftp = new SftpClient(SshServerHostName, SshServerPort, AdminUser.UserName, AdminUser.Password)) { sftp.Connect(); @@ -37,11 +29,10 @@ namespace Renci.SshNet.Tests.Classes [TestMethod] [TestCategory("Sftp")] - [TestCategory("integration")] [ExpectedException(typeof(SftpPathNotFoundException))] public void Test_Sftp_Download_File_Not_Exists() { - using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) + using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) { sftp.Connect(); @@ -57,12 +48,11 @@ namespace Renci.SshNet.Tests.Classes [TestMethod] [TestCategory("Sftp")] - [TestCategory("integration")] [Description("Test passing null to BeginDownloadFile")] [ExpectedException(typeof(ArgumentNullException))] public void Test_Sftp_BeginDownloadFile_StreamIsNull() { - using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) + using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) { sftp.Connect(); sftp.BeginDownloadFile("aaaa", null, null, null); @@ -72,12 +62,11 @@ namespace Renci.SshNet.Tests.Classes [TestMethod] [TestCategory("Sftp")] - [TestCategory("integration")] [Description("Test passing null to BeginDownloadFile")] [ExpectedException(typeof(ArgumentException))] public void Test_Sftp_BeginDownloadFile_FileNameIsWhiteSpace() { - using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) + using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) { sftp.Connect(); sftp.BeginDownloadFile(" ", new MemoryStream(), null, null); @@ -87,12 +76,11 @@ namespace Renci.SshNet.Tests.Classes [TestMethod] [TestCategory("Sftp")] - [TestCategory("integration")] [Description("Test passing null to BeginDownloadFile")] [ExpectedException(typeof(ArgumentException))] public void Test_Sftp_BeginDownloadFile_FileNameIsNull() { - using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) + using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) { sftp.Connect(); sftp.BeginDownloadFile(null, new MemoryStream(), null, null); @@ -102,15 +90,14 @@ namespace Renci.SshNet.Tests.Classes [TestMethod] [TestCategory("Sftp")] - [TestCategory("integration")] [ExpectedException(typeof(ArgumentException))] public void Test_Sftp_EndDownloadFile_Invalid_Async_Handle() { - using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) + using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) { sftp.Connect(); var filename = Path.GetTempFileName(); - this.CreateTestFile(filename, 1); + CreateTestFile(filename, 1); sftp.UploadFile(File.OpenRead(filename), "test123"); var async1 = sftp.BeginListDirectory("/", null, null); var async2 = sftp.BeginDownloadFile("test123", new MemoryStream(), null, null); @@ -118,4 +105,4 @@ namespace Renci.SshNet.Tests.Classes } } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.ListDirectory.cs b/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.ListDirectory.cs new file mode 100644 index 00000000..e37e937f --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.ListDirectory.cs @@ -0,0 +1,263 @@ +using System.Diagnostics; + +using Renci.SshNet.Common; + +namespace Renci.SshNet.IntegrationTests.OldIntegrationTests +{ + /// + /// Implementation of the SSH File Transfer Protocol (SFTP) over SSH. + /// + public partial class SftpClientTest : IntegrationTestBase + { + [TestMethod] + [TestCategory("Sftp")] + [ExpectedException(typeof(SftpPermissionDeniedException))] + public void Test_Sftp_ListDirectory_Permission_Denied() + { + using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) + { + sftp.Connect(); + + var files = sftp.ListDirectory("/root"); + foreach (var file in files) + { + Debug.WriteLine(file.FullName); + } + + sftp.Disconnect(); + } + } + + [TestMethod] + [TestCategory("Sftp")] + [ExpectedException(typeof(SftpPathNotFoundException))] + public void Test_Sftp_ListDirectory_Not_Exists() + { + using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) + { + sftp.Connect(); + + var files = sftp.ListDirectory("/asdfgh"); + foreach (var file in files) + { + Debug.WriteLine(file.FullName); + } + + sftp.Disconnect(); + } + } + + [TestMethod] + [TestCategory("Sftp")] + public void Test_Sftp_ListDirectory_Current() + { + using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) + { + sftp.Connect(); + + var files = sftp.ListDirectory("."); + + Assert.IsTrue(files.Count() > 0); + + foreach (var file in files) + { + Debug.WriteLine(file.FullName); + } + + sftp.Disconnect(); + } + } + +#if NET6_0_OR_GREATER + [TestMethod] + [TestCategory("Sftp")] + public async Task Test_Sftp_ListDirectoryAsync_Current() + { + using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) + { + sftp.Connect(); + var cts = new CancellationTokenSource(); + cts.CancelAfter(TimeSpan.FromMinutes(1)); + var count = 0; + await foreach(var file in sftp.ListDirectoryAsync(".", cts.Token)) + { + count++; + Debug.WriteLine(file.FullName); + } + + Assert.IsTrue(count > 0); + + sftp.Disconnect(); + } + } +#endif + [TestMethod] + [TestCategory("Sftp")] + public void Test_Sftp_ListDirectory_Empty() + { + using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) + { + sftp.Connect(); + + var files = sftp.ListDirectory(string.Empty); + + Assert.IsTrue(files.Count() > 0); + + foreach (var file in files) + { + Debug.WriteLine(file.FullName); + } + + sftp.Disconnect(); + } + } + + [TestMethod] + [TestCategory("Sftp")] + [Description("Test passing null to ListDirectory.")] + [ExpectedException(typeof(ArgumentNullException))] + public void Test_Sftp_ListDirectory_Null() + { + using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) + { + sftp.Connect(); + + var files = sftp.ListDirectory(null); + + Assert.IsTrue(files.Count() > 0); + + foreach (var file in files) + { + Debug.WriteLine(file.FullName); + } + + sftp.Disconnect(); + } + } + + [TestMethod] + [TestCategory("Sftp")] + public void Test_Sftp_ListDirectory_HugeDirectory() + { + using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) + { + sftp.Connect(); + + // Create 10000 directory items + for (int i = 0; i < 10000; i++) + { + sftp.CreateDirectory(string.Format("test_{0}", i)); + } + + var files = sftp.ListDirectory("."); + + // Ensure that directory has at least 10000 items + Assert.IsTrue(files.Count() > 10000); + + sftp.Disconnect(); + } + + RemoveAllFiles(); + } + + [TestMethod] + [TestCategory("Sftp")] + public void Test_Sftp_Change_Directory() + { + using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) + { + sftp.Connect(); + + Assert.AreEqual(sftp.WorkingDirectory, "/home/sshnet"); + + sftp.CreateDirectory("test1"); + + sftp.ChangeDirectory("test1"); + + Assert.AreEqual(sftp.WorkingDirectory, "/home/sshnet/test1"); + + sftp.CreateDirectory("test1_1"); + sftp.CreateDirectory("test1_2"); + sftp.CreateDirectory("test1_3"); + + var files = sftp.ListDirectory("."); + + Assert.IsTrue(files.First().FullName.StartsWith(string.Format("{0}", sftp.WorkingDirectory))); + + sftp.ChangeDirectory("test1_1"); + + Assert.AreEqual(sftp.WorkingDirectory, "/home/sshnet/test1/test1_1"); + + sftp.ChangeDirectory("../test1_2"); + + Assert.AreEqual(sftp.WorkingDirectory, "/home/sshnet/test1/test1_2"); + + sftp.ChangeDirectory(".."); + + Assert.AreEqual(sftp.WorkingDirectory, "/home/sshnet/test1"); + + sftp.ChangeDirectory(".."); + + Assert.AreEqual(sftp.WorkingDirectory, "/home/sshnet"); + + files = sftp.ListDirectory("test1/test1_1"); + + Assert.IsTrue(files.First().FullName.StartsWith(string.Format("{0}/test1/test1_1", sftp.WorkingDirectory))); + + sftp.ChangeDirectory("test1/test1_1"); + + Assert.AreEqual(sftp.WorkingDirectory, "/home/sshnet/test1/test1_1"); + + sftp.ChangeDirectory("/home/sshnet/test1/test1_1"); + + Assert.AreEqual(sftp.WorkingDirectory, "/home/sshnet/test1/test1_1"); + + sftp.ChangeDirectory("/home/sshnet/test1/test1_1/../test1_2"); + + Assert.AreEqual(sftp.WorkingDirectory, "/home/sshnet/test1/test1_2"); + + sftp.ChangeDirectory("../../"); + + sftp.DeleteDirectory("test1/test1_1"); + sftp.DeleteDirectory("test1/test1_2"); + sftp.DeleteDirectory("test1/test1_3"); + sftp.DeleteDirectory("test1"); + + sftp.Disconnect(); + } + + RemoveAllFiles(); + } + + [TestMethod] + [TestCategory("Sftp")] + [Description("Test passing null to ChangeDirectory.")] + [ExpectedException(typeof(ArgumentNullException))] + public void Test_Sftp_ChangeDirectory_Null() + { + using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) + { + sftp.Connect(); + + sftp.ChangeDirectory(null); + + sftp.Disconnect(); + } + } + + [TestMethod] + [TestCategory("Sftp")] + [Description("Test calling EndListDirectory method more then once.")] + [ExpectedException(typeof(ArgumentException))] + public void Test_Sftp_Call_EndListDirectory_Twice() + { + using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) + { + sftp.Connect(); + var ar = sftp.BeginListDirectory("/", null, null); + var result = sftp.EndListDirectory(ar); + var result1 = sftp.EndListDirectory(ar); + } + } + } +} diff --git a/src/Renci.SshNet.Tests/Classes/SftpClientTest.RenameFile.cs b/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.RenameFile.cs similarity index 69% rename from src/Renci.SshNet.Tests/Classes/SftpClientTest.RenameFile.cs rename to src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.RenameFile.cs index e1685d09..e74a8e7e 100644 --- a/src/Renci.SshNet.Tests/Classes/SftpClientTest.RenameFile.cs +++ b/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.RenameFile.cs @@ -1,21 +1,15 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Renci.SshNet.Tests.Properties; -using System; -using System.IO; - -namespace Renci.SshNet.Tests.Classes +namespace Renci.SshNet.IntegrationTests.OldIntegrationTests { /// /// Implementation of the SSH File Transfer Protocol (SFTP) over SSH. /// - public partial class SftpClientTest + public partial class SftpClientTest : IntegrationTestBase { [TestMethod] [TestCategory("Sftp")] - [TestCategory("integration")] public void Test_Sftp_Rename_File() { - using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) + using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) { sftp.Connect(); @@ -23,7 +17,7 @@ namespace Renci.SshNet.Tests.Classes string remoteFileName1 = Path.GetRandomFileName(); string remoteFileName2 = Path.GetRandomFileName(); - this.CreateTestFile(uploadedFileName, 1); + CreateTestFile(uploadedFileName, 1); using (var file = File.OpenRead(uploadedFileName)) { @@ -42,12 +36,11 @@ namespace Renci.SshNet.Tests.Classes [TestMethod] [TestCategory("Sftp")] - [TestCategory("integration")] [Description("Test passing null to RenameFile.")] [ExpectedException(typeof(ArgumentNullException))] public void Test_Sftp_RenameFile_Null() { - using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) + using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) { sftp.Connect(); @@ -57,4 +50,4 @@ namespace Renci.SshNet.Tests.Classes } } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.RenameFileAsync.cs b/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.RenameFileAsync.cs new file mode 100644 index 00000000..ade91099 --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.RenameFileAsync.cs @@ -0,0 +1,56 @@ +namespace Renci.SshNet.IntegrationTests.OldIntegrationTests +{ + /// + /// Implementation of the SSH File Transfer Protocol (SFTP) over SSH. + /// + public partial class SftpClientTest : IntegrationTestBase + { + [TestMethod] + [TestCategory("Sftp")] + public async Task Test_Sftp_RenameFileAsync() + { + using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) + { + await sftp.ConnectAsync(default); + + string uploadedFileName = Path.GetTempFileName(); + string remoteFileName1 = Path.GetRandomFileName(); + string remoteFileName2 = Path.GetRandomFileName(); + + CreateTestFile(uploadedFileName, 1); + + using (var file = File.OpenRead(uploadedFileName)) + { + using (Stream remoteStream = await sftp.OpenAsync(remoteFileName1, FileMode.CreateNew, FileAccess.Write, default)) + { + await file.CopyToAsync(remoteStream, 81920, default); + } + } + + await sftp.RenameFileAsync(remoteFileName1, remoteFileName2, default); + + File.Delete(uploadedFileName); + + sftp.Disconnect(); + } + + RemoveAllFiles(); + } + + [TestMethod] + [TestCategory("Sftp")] + [Description("Test passing null to RenameFile.")] + [ExpectedException(typeof(ArgumentNullException))] + public async Task Test_Sftp_RenameFileAsync_Null() + { + using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) + { + await sftp.ConnectAsync(default); + + await sftp.RenameFileAsync(null, null, default); + + sftp.Disconnect(); + } + } + } +} diff --git a/src/Renci.SshNet.Tests/Classes/SftpClientTest.SynchronizeDirectories.cs b/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.SynchronizeDirectories.cs similarity index 78% rename from src/Renci.SshNet.Tests/Classes/SftpClientTest.SynchronizeDirectories.cs rename to src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.SynchronizeDirectories.cs index 2c9ddb3e..4964715e 100644 --- a/src/Renci.SshNet.Tests/Classes/SftpClientTest.SynchronizeDirectories.cs +++ b/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.SynchronizeDirectories.cs @@ -1,31 +1,26 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Renci.SshNet.Tests.Properties; -using System.Diagnostics; -using System.IO; -using System.Linq; +using System.Diagnostics; -namespace Renci.SshNet.Tests.Classes +namespace Renci.SshNet.IntegrationTests.OldIntegrationTests { /// /// Implementation of the SSH File Transfer Protocol (SFTP) over SSH. /// - public partial class SftpClientTest + public partial class SftpClientTest : IntegrationTestBase { [TestMethod] [TestCategory("Sftp")] - [TestCategory("integration")] public void Test_Sftp_SynchronizeDirectories() { RemoveAllFiles(); - using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) + using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) { sftp.Connect(); string uploadedFileName = Path.GetTempFileName(); string sourceDir = Path.GetDirectoryName(uploadedFileName); - string destDir = "/"; + string destDir = "/home/sshnet/"; string searchPattern = Path.GetFileName(uploadedFileName); var upLoadedFiles = sftp.SynchronizeDirectories(sourceDir, destDir, searchPattern); @@ -42,19 +37,18 @@ namespace Renci.SshNet.Tests.Classes [TestMethod] [TestCategory("Sftp")] - [TestCategory("integration")] public void Test_Sftp_BeginSynchronizeDirectories() { RemoveAllFiles(); - using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) + using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) { sftp.Connect(); string uploadedFileName = Path.GetTempFileName(); string sourceDir = Path.GetDirectoryName(uploadedFileName); - string destDir = "/"; + string destDir = "/home/sshnet/"; string searchPattern = Path.GetFileName(uploadedFileName); var asyncResult = sftp.BeginSynchronizeDirectories(sourceDir, @@ -83,4 +77,4 @@ namespace Renci.SshNet.Tests.Classes } } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.Tests/Classes/SftpClientTest.Upload.cs b/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.Upload.cs similarity index 69% rename from src/Renci.SshNet.Tests/Classes/SftpClientTest.Upload.cs rename to src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.Upload.cs index aa70e232..91c248bd 100644 --- a/src/Renci.SshNet.Tests/Classes/SftpClientTest.Upload.cs +++ b/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.Upload.cs @@ -1,34 +1,27 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Renci.SshNet.Common; +using Renci.SshNet.Common; using Renci.SshNet.Sftp; -using Renci.SshNet.Tests.Properties; -using System; -using System.Collections.Generic; -using System.IO; -using System.Threading; -namespace Renci.SshNet.Tests.Classes +namespace Renci.SshNet.IntegrationTests.OldIntegrationTests { /// /// Implementation of the SSH File Transfer Protocol (SFTP) over SSH. /// - public partial class SftpClientTest + public partial class SftpClientTest : IntegrationTestBase { [TestMethod] [TestCategory("Sftp")] - [TestCategory("integration")] public void Test_Sftp_Upload_And_Download_1MB_File() { RemoveAllFiles(); - using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) + using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) { sftp.Connect(); - string uploadedFileName = Path.GetTempFileName(); - string remoteFileName = Path.GetRandomFileName(); + var uploadedFileName = Path.GetTempFileName(); + var remoteFileName = Path.GetRandomFileName(); - this.CreateTestFile(uploadedFileName, 1); + CreateTestFile(uploadedFileName, 1); // Calculate has value var uploadedHash = CalculateMD5(uploadedFileName); @@ -38,7 +31,7 @@ namespace Renci.SshNet.Tests.Classes sftp.UploadFile(file, remoteFileName); } - string downloadedFileName = Path.GetTempFileName(); + var downloadedFileName = Path.GetTempFileName(); using (var file = File.OpenWrite(downloadedFileName)) { @@ -60,18 +53,17 @@ namespace Renci.SshNet.Tests.Classes [TestMethod] [TestCategory("Sftp")] - [TestCategory("integration")] [ExpectedException(typeof(SftpPermissionDeniedException))] public void Test_Sftp_Upload_Forbidden() { - using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) + using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) { sftp.Connect(); - string uploadedFileName = Path.GetTempFileName(); - string remoteFileName = "/root/1"; + var uploadedFileName = Path.GetTempFileName(); + var remoteFileName = "/root/1"; - this.CreateTestFile(uploadedFileName, 1); + CreateTestFile(uploadedFileName, 1); using (var file = File.OpenRead(uploadedFileName)) { @@ -84,7 +76,6 @@ namespace Renci.SshNet.Tests.Classes [TestMethod] [TestCategory("Sftp")] - [TestCategory("integration")] public void Test_Sftp_Multiple_Async_Upload_And_Download_10Files_5MB_Each() { var maxFiles = 10; @@ -92,20 +83,23 @@ namespace Renci.SshNet.Tests.Classes RemoveAllFiles(); - using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) + using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) { + sftp.OperationTimeout = TimeSpan.FromMinutes(1); sftp.Connect(); var testInfoList = new Dictionary(); - for (int i = 0; i < maxFiles; i++) + for (var i = 0; i < maxFiles; i++) { - var testInfo = new TestInfo(); - testInfo.UploadedFileName = Path.GetTempFileName(); - testInfo.DownloadedFileName = Path.GetTempFileName(); - testInfo.RemoteFileName = Path.GetRandomFileName(); + var testInfo = new TestInfo + { + UploadedFileName = Path.GetTempFileName(), + DownloadedFileName = Path.GetTempFileName(), + RemoteFileName = Path.GetRandomFileName() + }; - this.CreateTestFile(testInfo.UploadedFileName, maxSize); + CreateTestFile(testInfo.UploadedFileName, maxSize); // Calculate hash value testInfo.UploadedHash = CalculateMD5(testInfo.UploadedFileName); @@ -130,7 +124,7 @@ namespace Renci.SshNet.Tests.Classes } // Wait for upload to finish - bool uploadCompleted = false; + var uploadCompleted = false; while (!uploadCompleted) { // Assume upload completed @@ -174,7 +168,7 @@ namespace Renci.SshNet.Tests.Classes } // Wait for download to finish - bool downloadCompleted = false; + var downloadCompleted = false; while (!downloadCompleted) { // Assume download completed @@ -206,6 +200,11 @@ namespace Renci.SshNet.Tests.Classes testInfo.DownloadedHash = CalculateMD5(testInfo.DownloadedFileName); + Console.WriteLine(remoteFile); + Console.WriteLine("UploadedBytes: "+ testInfo.UploadResult.UploadedBytes); + Console.WriteLine("DownloadedBytes: " + testInfo.DownloadResult.DownloadedBytes); + Console.WriteLine("UploadedHash: " + testInfo.UploadedHash); + Console.WriteLine("DownloadedHash: " + testInfo.DownloadedHash); if (!(testInfo.UploadResult.UploadedBytes > 0 && testInfo.DownloadResult.DownloadedBytes > 0 && testInfo.DownloadResult.DownloadedBytes == testInfo.UploadResult.UploadedBytes)) { uploadDownloadSizeOk = false; @@ -238,21 +237,20 @@ namespace Renci.SshNet.Tests.Classes // TODO: Split this test into multiple tests [TestMethod] [TestCategory("Sftp")] - [TestCategory("integration")] [Description("Test that delegates passed to BeginUploadFile, BeginDownloadFile and BeginListDirectory are actually called.")] public void Test_Sftp_Ensure_Async_Delegates_Called_For_BeginFileUpload_BeginFileDownload_BeginListDirectory() { RemoveAllFiles(); - using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) + using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) { sftp.Connect(); - string remoteFileName = Path.GetRandomFileName(); - string localFileName = Path.GetRandomFileName(); - bool uploadDelegateCalled = false; - bool downloadDelegateCalled = false; - bool listDirectoryDelegateCalled = false; + var remoteFileName = Path.GetRandomFileName(); + var localFileName = Path.GetRandomFileName(); + var uploadDelegateCalled = false; + var downloadDelegateCalled = false; + var listDirectoryDelegateCalled = false; IAsyncResult asyncResult; // Test for BeginUploadFile. @@ -261,11 +259,14 @@ namespace Renci.SshNet.Tests.Classes using (var fileStream = File.OpenRead(localFileName)) { - asyncResult = sftp.BeginUploadFile(fileStream, remoteFileName, delegate(IAsyncResult ar) - { - sftp.EndUploadFile(ar); - uploadDelegateCalled = true; - }, null); + asyncResult = sftp.BeginUploadFile(fileStream, + remoteFileName, + delegate(IAsyncResult ar) + { + sftp.EndUploadFile(ar); + uploadDelegateCalled = true; + }, + null); while (!asyncResult.IsCompleted) { @@ -282,11 +283,14 @@ namespace Renci.SshNet.Tests.Classes asyncResult = null; using (var fileStream = File.OpenWrite(localFileName)) { - asyncResult = sftp.BeginDownloadFile(remoteFileName, fileStream, delegate(IAsyncResult ar) - { - sftp.EndDownloadFile(ar); - downloadDelegateCalled = true; - }, null); + asyncResult = sftp.BeginDownloadFile(remoteFileName, + fileStream, + delegate(IAsyncResult ar) + { + sftp.EndDownloadFile(ar); + downloadDelegateCalled = true; + }, + null); while (!asyncResult.IsCompleted) { @@ -301,11 +305,13 @@ namespace Renci.SshNet.Tests.Classes // Test for BeginListDirectory. asyncResult = null; - asyncResult = sftp.BeginListDirectory(sftp.WorkingDirectory, delegate(IAsyncResult ar) - { - sftp.EndListDirectory(ar); - listDirectoryDelegateCalled = true; - }, null); + asyncResult = sftp.BeginListDirectory(sftp.WorkingDirectory, + delegate(IAsyncResult ar) + { + _ = sftp.EndListDirectory(ar); + listDirectoryDelegateCalled = true; + }, + null); while (!asyncResult.IsCompleted) { @@ -318,64 +324,60 @@ namespace Renci.SshNet.Tests.Classes [TestMethod] [TestCategory("Sftp")] - [TestCategory("integration")] [Description("Test passing null to BeginUploadFile")] [ExpectedException(typeof(ArgumentNullException))] public void Test_Sftp_BeginUploadFile_StreamIsNull() { - using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) + using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) { sftp.Connect(); - sftp.BeginUploadFile(null, "aaaaa", null, null); + _ = sftp.BeginUploadFile(null, "aaaaa", null, null); sftp.Disconnect(); } } [TestMethod] [TestCategory("Sftp")] - [TestCategory("integration")] [Description("Test passing null to BeginUploadFile")] [ExpectedException(typeof(ArgumentException))] public void Test_Sftp_BeginUploadFile_FileNameIsWhiteSpace() { - using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) + using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) { sftp.Connect(); - sftp.BeginUploadFile(new MemoryStream(), " ", null, null); + _ = sftp.BeginUploadFile(new MemoryStream(), " ", null, null); sftp.Disconnect(); } } [TestMethod] [TestCategory("Sftp")] - [TestCategory("integration")] [Description("Test passing null to BeginUploadFile")] [ExpectedException(typeof(ArgumentException))] public void Test_Sftp_BeginUploadFile_FileNameIsNull() { - using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) + using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) { sftp.Connect(); - sftp.BeginUploadFile(new MemoryStream(), null, null, null); + _ = sftp.BeginUploadFile(new MemoryStream(), null, null, null); sftp.Disconnect(); } } [TestMethod] [TestCategory("Sftp")] - [TestCategory("integration")] [ExpectedException(typeof(ArgumentException))] public void Test_Sftp_EndUploadFile_Invalid_Async_Handle() { - using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) + using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) { sftp.Connect(); var async1 = sftp.BeginListDirectory("/", null, null); var filename = Path.GetTempFileName(); - this.CreateTestFile(filename, 100); + CreateTestFile(filename, 100); var async2 = sftp.BeginUploadFile(File.OpenRead(filename), "test", null, null); sftp.EndUploadFile(async1); } } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.cs b/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.cs new file mode 100644 index 00000000..1c7def55 --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.cs @@ -0,0 +1,68 @@ +using System.Security.Cryptography; + +using Renci.SshNet.Sftp; + +namespace Renci.SshNet.IntegrationTests.OldIntegrationTests +{ + /// + /// Implementation of the SSH File Transfer Protocol (SFTP) over SSH. + /// + [TestClass] + public partial class SftpClientTest + { + protected static string CalculateMD5(string fileName) + { + using (FileStream file = new FileStream(fileName, FileMode.Open)) + { + var hash = MD5.HashData(file); + + file.Close(); + + StringBuilder sb = new StringBuilder(); + for (var i = 0; i < hash.Length; i++) + { + sb.Append(hash[i].ToString("x2")); + } + return sb.ToString(); + } + } + + private void RemoveAllFiles() + { + using (var client = new SshClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) + { + client.Connect(); + client.RunCommand("rm -rf *"); + client.Disconnect(); + } + } + + /// + /// Helper class to help with upload and download testing + /// + private class TestInfo + { + public string RemoteFileName { get; set; } + + public string UploadedFileName { get; set; } + + public string DownloadedFileName { get; set; } + + //public ulong UploadedBytes { get; set; } + + //public ulong DownloadedBytes { get; set; } + + public FileStream UploadedFile { get; set; } + + public FileStream DownloadedFile { get; set; } + + public string UploadedHash { get; set; } + + public string DownloadedHash { get; set; } + + public SftpUploadAsyncResult UploadResult { get; set; } + + public SftpDownloadAsyncResult DownloadResult { get; set; } + } + } +} diff --git a/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpFileTest.cs b/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpFileTest.cs new file mode 100644 index 00000000..05844251 --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpFileTest.cs @@ -0,0 +1,120 @@ +using Renci.SshNet.Common; + +namespace Renci.SshNet.IntegrationTests.OldIntegrationTests +{ + /// + /// Represents SFTP file information + /// + [TestClass] + public class SftpFileTest : IntegrationTestBase + { + [TestMethod] + [TestCategory("Sftp")] + public void Test_Get_Root_Directory() + { + using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) + { + sftp.Connect(); + var directory = sftp.Get("/"); + + Assert.AreEqual("/", directory.FullName); + Assert.IsTrue(directory.IsDirectory); + Assert.IsFalse(directory.IsRegularFile); + } + } + + [TestMethod] + [TestCategory("Sftp")] + [ExpectedException(typeof(SftpPathNotFoundException))] + public void Test_Get_Invalid_Directory() + { + using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) + { + sftp.Connect(); + + sftp.Get("/xyz"); + } + } + + [TestMethod] + [TestCategory("Sftp")] + public void Test_Get_File() + { + using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) + { + sftp.Connect(); + + sftp.UploadFile(new MemoryStream(), "abc.txt"); + + var file = sftp.Get("abc.txt"); + + Assert.AreEqual("/home/sshnet/abc.txt", file.FullName); + Assert.IsTrue(file.IsRegularFile); + Assert.IsFalse(file.IsDirectory); + } + } + + [TestMethod] + [TestCategory("Sftp")] + [Description("Test passing null to Get.")] + [ExpectedException(typeof(ArgumentNullException))] + public void Test_Get_File_Null() + { + using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) + { + sftp.Connect(); + + var file = sftp.Get(null); + + sftp.Disconnect(); + } + } + + [TestMethod] + [TestCategory("Sftp")] + public void Test_Get_International_File() + { + using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) + { + sftp.Connect(); + + sftp.UploadFile(new MemoryStream(), "test-üöä-"); + + var file = sftp.Get("test-üöä-"); + + Assert.AreEqual("/home/sshnet/test-üöä-", file.FullName); + Assert.IsTrue(file.IsRegularFile); + Assert.IsFalse(file.IsDirectory); + } + } + + [TestMethod] + [TestCategory("Sftp")] + public void Test_Sftp_SftpFile_MoveTo() + { + using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) + { + sftp.Connect(); + + string uploadedFileName = Path.GetTempFileName(); + string remoteFileName = Path.GetRandomFileName(); + string newFileName = Path.GetRandomFileName(); + + CreateTestFile(uploadedFileName, 1); + + using (var file = File.OpenRead(uploadedFileName)) + { + sftp.UploadFile(file, remoteFileName); + } + + var sftpFile = sftp.Get(remoteFileName); + + sftpFile.MoveTo(newFileName); + + Assert.AreEqual(newFileName, sftpFile.Name); + + sftp.Disconnect(); + } + } + } +} diff --git a/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SshCommandTest.cs b/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SshCommandTest.cs new file mode 100644 index 00000000..d852a093 --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SshCommandTest.cs @@ -0,0 +1,545 @@ +using System.Diagnostics; + +using Renci.SshNet.Common; + +namespace Renci.SshNet.IntegrationTests.OldIntegrationTests +{ + /// + /// Represents SSH command that can be executed. + /// + [TestClass] + public class SshCommandTest : IntegrationTestBase + { + [TestMethod] + public void Test_Run_SingleCommand() + { + using (var client = new SshClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) + { + #region Example SshCommand RunCommand Result + client.Connect(); + + var testValue = Guid.NewGuid().ToString(); + var command = client.RunCommand(string.Format("echo {0}", testValue)); + var result = command.Result; + result = result.Substring(0, result.Length - 1); // Remove \n character returned by command + + client.Disconnect(); + #endregion + + Assert.IsTrue(result.Equals(testValue)); + } + } + + [TestMethod] + public void Test_Execute_SingleCommand() + { + using (var client = new SshClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) + { + #region Example SshCommand CreateCommand Execute + client.Connect(); + + var testValue = Guid.NewGuid().ToString(); + var command = string.Format("echo {0}", testValue); + var cmd = client.CreateCommand(command); + var result = cmd.Execute(); + result = result.Substring(0, result.Length - 1); // Remove \n character returned by command + + client.Disconnect(); + #endregion + + Assert.IsTrue(result.Equals(testValue)); + } + } + + [TestMethod] + public void Test_Execute_OutputStream() + { + using (var client = new SshClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) + { + #region Example SshCommand CreateCommand Execute OutputStream + client.Connect(); + + var cmd = client.CreateCommand("ls -l"); // very long list + var asynch = cmd.BeginExecute(); + + var reader = new StreamReader(cmd.OutputStream); + + while (!asynch.IsCompleted) + { + var result = reader.ReadToEnd(); + if (string.IsNullOrEmpty(result)) + { + continue; + } + + Console.Write(result); + } + + _ = cmd.EndExecute(asynch); + + client.Disconnect(); + #endregion + + Assert.Inconclusive(); + } + } + + [TestMethod] + public void Test_Execute_ExtendedOutputStream() + { + using (var client = new SshClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) + { + #region Example SshCommand CreateCommand Execute ExtendedOutputStream + + client.Connect(); + var cmd = client.CreateCommand("echo 12345; echo 654321 >&2"); + var result = cmd.Execute(); + + Console.Write(result); + + var reader = new StreamReader(cmd.ExtendedOutputStream); + Console.WriteLine("DEBUG:"); + Console.Write(reader.ReadToEnd()); + + client.Disconnect(); + + #endregion + + Assert.Inconclusive(); + } + } + + [TestMethod] + [ExpectedException(typeof(SshOperationTimeoutException))] + public void Test_Execute_Timeout() + { + using (var client = new SshClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) + { + #region Example SshCommand CreateCommand Execute CommandTimeout + client.Connect(); + var cmd = client.CreateCommand("sleep 10s"); + cmd.CommandTimeout = TimeSpan.FromSeconds(5); + cmd.Execute(); + client.Disconnect(); + #endregion + } + } + + [TestMethod] + public void Test_Execute_Infinite_Timeout() + { + using (var client = new SshClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) + { + client.Connect(); + var cmd = client.CreateCommand("sleep 10s"); + cmd.Execute(); + client.Disconnect(); + } + } + + [TestMethod] + public void Test_Execute_InvalidCommand() + { + using (var client = new SshClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) + { + client.Connect(); + + var cmd = client.CreateCommand(";"); + cmd.Execute(); + if (string.IsNullOrEmpty(cmd.Error)) + { + Assert.Fail("Operation should fail"); + } + Assert.IsTrue(cmd.ExitStatus > 0); + + client.Disconnect(); + } + } + + [TestMethod] + public void Test_Execute_InvalidCommand_Then_Execute_ValidCommand() + { + using (var client = new SshClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) + { + client.Connect(); + var cmd = client.CreateCommand(";"); + cmd.Execute(); + if (string.IsNullOrEmpty(cmd.Error)) + { + Assert.Fail("Operation should fail"); + } + Assert.IsTrue(cmd.ExitStatus > 0); + + var result = ExecuteTestCommand(client); + + client.Disconnect(); + + Assert.IsTrue(result); + } + } + + [TestMethod] + public void Test_Execute_Command_with_ExtendedOutput() + { + using (var client = new SshClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) + { + client.Connect(); + var cmd = client.CreateCommand("echo 12345; echo 654321 >&2"); + cmd.Execute(); + + //var extendedData = Encoding.ASCII.GetString(cmd.ExtendedOutputStream.ToArray()); + var extendedData = new StreamReader(cmd.ExtendedOutputStream, Encoding.ASCII).ReadToEnd(); + client.Disconnect(); + + Assert.AreEqual("12345\n", cmd.Result); + Assert.AreEqual("654321\n", extendedData); + } + } + + [TestMethod] + public void Test_Execute_Command_Reconnect_Execute_Command() + { + using (var client = new SshClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) + { + client.Connect(); + var result = ExecuteTestCommand(client); + Assert.IsTrue(result); + + client.Disconnect(); + client.Connect(); + result = ExecuteTestCommand(client); + Assert.IsTrue(result); + client.Disconnect(); + } + } + + [TestMethod] + public void Test_Execute_Command_ExitStatus() + { + using (var client = new SshClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) + { + #region Example SshCommand RunCommand ExitStatus + client.Connect(); + + var cmd = client.RunCommand("exit 128"); + + Console.WriteLine(cmd.ExitStatus); + + client.Disconnect(); + #endregion + + Assert.IsTrue(cmd.ExitStatus == 128); + } + } + + [TestMethod] + public void Test_Execute_Command_Asynchronously() + { + using (var client = new SshClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) + { + client.Connect(); + + var cmd = client.CreateCommand("sleep 5s; echo 'test'"); + var asyncResult = cmd.BeginExecute(null, null); + while (!asyncResult.IsCompleted) + { + Thread.Sleep(100); + } + + cmd.EndExecute(asyncResult); + + Assert.IsTrue(cmd.Result == "test\n"); + + client.Disconnect(); + } + } + + [TestMethod] + public void Test_Execute_Command_Asynchronously_With_Error() + { + using (var client = new SshClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) + { + client.Connect(); + + var cmd = client.CreateCommand("sleep 5s; ;"); + var asyncResult = cmd.BeginExecute(null, null); + while (!asyncResult.IsCompleted) + { + Thread.Sleep(100); + } + + cmd.EndExecute(asyncResult); + + Assert.IsFalse(string.IsNullOrEmpty(cmd.Error)); + + client.Disconnect(); + } + } + + [TestMethod] + public void Test_Execute_Command_Asynchronously_With_Callback() + { + using (var client = new SshClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) + { + client.Connect(); + + var callbackCalled = false; + + var cmd = client.CreateCommand("sleep 5s; echo 'test'"); + var asyncResult = cmd.BeginExecute(new AsyncCallback((s) => + { + callbackCalled = true; + }), null); + while (!asyncResult.IsCompleted) + { + Thread.Sleep(100); + } + + cmd.EndExecute(asyncResult); + + Assert.IsTrue(callbackCalled); + + client.Disconnect(); + } + } + + [TestMethod] + public void Test_Execute_Command_Asynchronously_With_Callback_On_Different_Thread() + { + using (var client = new SshClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) + { + client.Connect(); + + var currentThreadId = Thread.CurrentThread.ManagedThreadId; + int callbackThreadId = 0; + + var cmd = client.CreateCommand("sleep 5s; echo 'test'"); + var asyncResult = cmd.BeginExecute(new AsyncCallback((s) => + { + callbackThreadId = Thread.CurrentThread.ManagedThreadId; + }), null); + while (!asyncResult.IsCompleted) + { + Thread.Sleep(100); + } + + cmd.EndExecute(asyncResult); + + Assert.AreNotEqual(currentThreadId, callbackThreadId); + + client.Disconnect(); + } + } + + /// + /// Tests for Issue 563. + /// + [WorkItem(563), TestMethod] + public void Test_Execute_Command_Same_Object_Different_Commands() + { + using (var client = new SshClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) + { + client.Connect(); + var cmd = client.CreateCommand("echo 12345"); + cmd.Execute(); + Assert.AreEqual("12345\n", cmd.Result); + cmd.Execute("echo 23456"); + Assert.AreEqual("23456\n", cmd.Result); + client.Disconnect(); + } + } + + [TestMethod] + public void Test_Get_Result_Without_Execution() + { + using (var client = new SshClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) + { + client.Connect(); + var cmd = client.CreateCommand("ls -l"); + + Assert.IsTrue(string.IsNullOrEmpty(cmd.Result)); + client.Disconnect(); + } + } + + [TestMethod] + public void Test_Get_Error_Without_Execution() + { + using (var client = new SshClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) + { + client.Connect(); + var cmd = client.CreateCommand("ls -l"); + + Assert.IsTrue(string.IsNullOrEmpty(cmd.Error)); + client.Disconnect(); + } + } + + [WorkItem(703), TestMethod] + [ExpectedException(typeof(ArgumentNullException))] + public void Test_EndExecute_Before_BeginExecute() + { + using (var client = new SshClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) + { + client.Connect(); + var cmd = client.CreateCommand("ls -l"); + cmd.EndExecute(null); + client.Disconnect(); + } + } + + /// + ///A test for BeginExecute + /// + [TestMethod()] + public void BeginExecuteTest() + { + string expected = "123\n"; + string result; + + using (var client = new SshClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) + { + #region Example SshCommand CreateCommand BeginExecute IsCompleted EndExecute + + client.Connect(); + + var cmd = client.CreateCommand("sleep 15s;echo 123"); // Perform long running task + + var asynch = cmd.BeginExecute(); + + while (!asynch.IsCompleted) + { + // Waiting for command to complete... + Thread.Sleep(2000); + } + result = cmd.EndExecute(asynch); + client.Disconnect(); + + #endregion + + Assert.IsNotNull(asynch); + Assert.AreEqual(expected, result); + } + } + + [TestMethod] + public void Test_Execute_Invalid_Command() + { + using (var client = new SshClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) + { + #region Example SshCommand CreateCommand Error + + client.Connect(); + + var cmd = client.CreateCommand(";"); + cmd.Execute(); + if (!string.IsNullOrEmpty(cmd.Error)) + { + Console.WriteLine(cmd.Error); + } + + client.Disconnect(); + + #endregion + + Assert.Inconclusive(); + } + } + + [TestMethod] + public void Test_MultipleThread_Example_MultipleConnections() + { + try + { +#region Example SshCommand RunCommand Parallel + Parallel.For(0, 100, + () => + { + var client = new SshClient(SshServerHostName, SshServerPort, User.UserName, User.Password); + client.Connect(); + return client; + }, + (int counter, ParallelLoopState pls, SshClient client) => + { + var result = client.RunCommand("echo 123"); + Debug.WriteLine(string.Format("TestMultipleThreadMultipleConnections #{0}", counter)); + return client; + }, + (SshClient client) => + { + client.Disconnect(); + client.Dispose(); + } + ); +#endregion + + } + catch (Exception exp) + { + Assert.Fail(exp.ToString()); + } + } + + [TestMethod] + + public void Test_MultipleThread_100_MultipleConnections() + { + try + { + Parallel.For(0, 100, + () => + { + var client = new SshClient(SshServerHostName, SshServerPort, User.UserName, User.Password); + client.Connect(); + return client; + }, + (int counter, ParallelLoopState pls, SshClient client) => + { + var result = ExecuteTestCommand(client); + Debug.WriteLine(string.Format("TestMultipleThreadMultipleConnections #{0}", counter)); + Assert.IsTrue(result); + return client; + }, + (SshClient client) => + { + client.Disconnect(); + client.Dispose(); + } + ); + } + catch (Exception exp) + { + Assert.Fail(exp.ToString()); + } + } + + [TestMethod] + public void Test_MultipleThread_100_MultipleSessions() + { + using (var client = new SshClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) + { + client.Connect(); + Parallel.For(0, 100, + (counter) => + { + var result = ExecuteTestCommand(client); + Debug.WriteLine(string.Format("TestMultipleThreadMultipleConnections #{0}", counter)); + Assert.IsTrue(result); + } + ); + + client.Disconnect(); + } + } + + private static bool ExecuteTestCommand(SshClient s) + { + var testValue = Guid.NewGuid().ToString(); + var command = string.Format("echo {0}", testValue); + var cmd = s.CreateCommand(command); + var result = cmd.Execute(); + result = result.Substring(0, result.Length - 1); // Remove \n character returned by command + return result.Equals(testValue); + } + } +} diff --git a/src/Renci.SshNet.IntegrationTests/PrivateKeyAuthenticationTests.cs b/src/Renci.SshNet.IntegrationTests/PrivateKeyAuthenticationTests.cs new file mode 100644 index 00000000..05b6c478 --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/PrivateKeyAuthenticationTests.cs @@ -0,0 +1,102 @@ +using Renci.SshNet.IntegrationTests.Common; +using Renci.SshNet.TestTools.OpenSSH; + +namespace Renci.SshNet.IntegrationTests +{ + [TestClass] + public class PrivateKeyAuthenticationTests : TestBase + { + private IConnectionInfoFactory _connectionInfoFactory; + private RemoteSshdConfig _remoteSshdConfig; + + [TestInitialize] + public void SetUp() + { + _connectionInfoFactory = new LinuxVMConnectionFactory(SshServerHostName, SshServerPort); + _remoteSshdConfig = new RemoteSshd(new LinuxAdminConnectionFactory(SshServerHostName, SshServerPort)).OpenConfig(); + } + + [TestCleanup] + public void TearDown() + { + _remoteSshdConfig?.Reset(); + } + + [TestMethod] + public void SshDss() + { + DoTest(PublicKeyAlgorithm.SshDss, "id_dsa"); + } + + [TestMethod] + public void SshRsa() + { + DoTest(PublicKeyAlgorithm.SshRsa, "id_rsa"); + } + + [TestMethod] + public void SshRsaSha256() + { + DoTest(PublicKeyAlgorithm.RsaSha2256, "id_rsa"); + } + + [TestMethod] + public void SshRsaSha512() + { + DoTest(PublicKeyAlgorithm.RsaSha2512, "id_rsa"); + } + + [TestMethod] + public void Ecdsa256() + { + DoTest(PublicKeyAlgorithm.EcdsaSha2Nistp256, "key_ecdsa_256_openssh"); + } + + [TestMethod] + public void Ecdsa384() + { + DoTest(PublicKeyAlgorithm.EcdsaSha2Nistp384, "key_ecdsa_384_openssh"); + } + + [TestMethod] + public void Ecdsa521() + { + DoTest(PublicKeyAlgorithm.EcdsaSha2Nistp521, "key_ecdsa_521_openssh"); + } + + [TestMethod] + public void Ed25519() + { + DoTest(PublicKeyAlgorithm.SshEd25519, "key_ed25519_openssh"); + } + + private void DoTest(PublicKeyAlgorithm publicKeyAlgorithm, string keyResource) + { + _remoteSshdConfig.ClearPublicKeyAcceptedAlgorithms() + .AddPublicKeyAcceptedAlgorithm(publicKeyAlgorithm) + .Update() + .Restart(); + + var connectionInfo = _connectionInfoFactory.Create(CreatePrivateKeyAuthenticationMethod(keyResource)); + + using (var client = new SshClient(connectionInfo)) + { + client.Connect(); + } + } + + private PrivateKeyAuthenticationMethod CreatePrivateKeyAuthenticationMethod(string keyResource) + { + var privateKey = CreatePrivateKeyFromManifestResource("Renci.SshNet.IntegrationTests.resources.client." + keyResource); + return new PrivateKeyAuthenticationMethod(Users.Regular.UserName, privateKey); + } + + private PrivateKeyFile CreatePrivateKeyFromManifestResource(string resourceName) + { + using (var stream = GetManifestResourceStream(resourceName)) + { + return new PrivateKeyFile(stream); + } + } + } +} diff --git a/src/Renci.SshNet.IntegrationTests/Program.cs b/src/Renci.SshNet.IntegrationTests/Program.cs new file mode 100644 index 00000000..af2b60d5 --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/Program.cs @@ -0,0 +1,11 @@ +namespace Renci.SshNet.IntegrationTests +{ + class Program + { +#if NETFRAMEWORK + private static void Main() + { + } +#endif + } +} diff --git a/src/Renci.SshNet.IntegrationTests/RemoteSshd.cs b/src/Renci.SshNet.IntegrationTests/RemoteSshd.cs new file mode 100644 index 00000000..b9a32e67 --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/RemoteSshd.cs @@ -0,0 +1,258 @@ +using Renci.SshNet.TestTools.OpenSSH; + +namespace Renci.SshNet.IntegrationTests +{ + internal class RemoteSshd + { + private readonly IConnectionInfoFactory _connectionInfoFactory; + + public RemoteSshd(IConnectionInfoFactory connectionInfoFactory) + { + _connectionInfoFactory = connectionInfoFactory; + } + + public RemoteSshdConfig OpenConfig() + { + return new RemoteSshdConfig(this, _connectionInfoFactory); + } + + public RemoteSshd Restart() + { + // Restart SSH daemon + using (var client = new SshClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + // Kill all processes that start with 'sshd' and that run as root + var stopCommand = client.CreateCommand("sudo pkill -9 -U 0 sshd.pam"); + var stopOutput = stopCommand.Execute(); + if (stopCommand.ExitStatus != 0) + { + throw new ApplicationException($"Stopping ssh service failed with exit code {stopCommand.ExitStatus}.\r\n{stopOutput}\r\n{stopCommand.Error}"); + } + + var resetFailedCommand = client.CreateCommand("sudo /usr/sbin/sshd.pam"); + var resetFailedOutput = resetFailedCommand.Execute(); + if (resetFailedCommand.ExitStatus != 0) + { + throw new ApplicationException($"Reset failures for ssh service failed with exit code {resetFailedCommand.ExitStatus}.\r\n{resetFailedOutput}\r\n{resetFailedCommand.Error}"); + } + } + + return this; + } + } + + internal class RemoteSshdConfig + { + private const string SshdConfigFilePath = "/etc/ssh/sshd_config"; + private static readonly Encoding Utf8NoBom = new UTF8Encoding(false, true); + + private readonly RemoteSshd _remoteSshd; + private readonly IConnectionInfoFactory _connectionInfoFactory; + private readonly SshdConfig _config; + + public RemoteSshdConfig(RemoteSshd remoteSshd, IConnectionInfoFactory connectionInfoFactory) + { + _remoteSshd = remoteSshd; + _connectionInfoFactory = connectionInfoFactory; + + using (var client = new ScpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + using (var memoryStream = new MemoryStream()) + { + client.Download(SshdConfigFilePath, memoryStream); + + memoryStream.Position = 0; + _config = SshdConfig.LoadFrom(memoryStream, Encoding.UTF8); + } + } + } + + /// + /// Specifies whether challenge-response authentication is allowed. + /// + /// to allow challenge-response authentication. + /// + /// The current instance. + /// + public RemoteSshdConfig WithChallengeResponseAuthentication(bool? value) + { + _config.ChallengeResponseAuthentication = value; + return this; + } + + /// + /// Specifies whether to allow keyboard-interactive authentication. + /// + /// to allow keyboard-interactive authentication. + /// + /// The current instance. + /// + public RemoteSshdConfig WithKeyboardInteractiveAuthentication(bool value) + { + _config.KeyboardInteractiveAuthentication = value; + return this; + } + + /// + /// Specifies whether sshd should print /etc/motd when a user logs in interactively. + /// + /// if sshd should print /etc/motd when a user logs in interactively. + /// + /// The current instance. + /// + public RemoteSshdConfig PrintMotd(bool? value = true) + { + _config.PrintMotd = value; + return this; + } + + /// + /// Specifies whether TCP forwarding is permitted. + /// + /// to allow TCP forwarding. + /// + /// The current instance. + /// + public RemoteSshdConfig AllowTcpForwarding(bool? value = true) + { + _config.AllowTcpForwarding = value; + return this; + } + + public RemoteSshdConfig WithAuthenticationMethods(string user, string authenticationMethods) + { + var sshNetMatch = _config.Matches.FirstOrDefault(m => m.Users.Contains(user)); + if (sshNetMatch == null) + { + sshNetMatch = new Match(new[] { user }, new string[0]); + _config.Matches.Add(sshNetMatch); + } + + sshNetMatch.AuthenticationMethods = authenticationMethods; + + return this; + } + + public RemoteSshdConfig ClearCiphers() + { + _config.Ciphers.Clear(); + return this; + } + + public RemoteSshdConfig AddCipher(Cipher cipher) + { + _config.Ciphers.Add(cipher); + return this; + } + + public RemoteSshdConfig ClearKeyExchangeAlgorithms() + { + _config.KeyExchangeAlgorithms.Clear(); + return this; + } + + public RemoteSshdConfig AddKeyExchangeAlgorithm(KeyExchangeAlgorithm keyExchangeAlgorithm) + { + _config.KeyExchangeAlgorithms.Add(keyExchangeAlgorithm); + return this; + } + + public RemoteSshdConfig ClearPublicKeyAcceptedAlgorithms() + { + _config.PublicKeyAcceptedAlgorithms.Clear(); + return this; + } + + public RemoteSshdConfig AddPublicKeyAcceptedAlgorithm(PublicKeyAlgorithm publicKeyAlgorithm) + { + _config.PublicKeyAcceptedAlgorithms.Add(publicKeyAlgorithm); + return this; + } + + public RemoteSshdConfig ClearMessageAuthenticationCodeAlgorithms() + { + _config.MessageAuthenticationCodeAlgorithms.Clear(); + return this; + } + + public RemoteSshdConfig AddMessageAuthenticationCodeAlgorithm(MessageAuthenticationCodeAlgorithm messageAuthenticationCodeAlgorithm) + { + _config.MessageAuthenticationCodeAlgorithms.Add(messageAuthenticationCodeAlgorithm); + return this; + } + + public RemoteSshdConfig ClearHostKeyAlgorithms() + { + _config.HostKeyAlgorithms.Clear(); + return this; + } + + public RemoteSshdConfig AddHostKeyAlgorithm(HostKeyAlgorithm hostKeyAlgorithm) + { + _config.HostKeyAlgorithms.Add(hostKeyAlgorithm); + return this; + } + + public RemoteSshdConfig ClearSubsystems() + { + _config.Subsystems.Clear(); + return this; + } + + public RemoteSshdConfig AddSubsystem(Subsystem subsystem) + { + _config.Subsystems.Add(subsystem); + return this; + } + + public RemoteSshdConfig WithLogLevel(LogLevel logLevel) + { + _config.LogLevel = logLevel; + return this; + } + + public RemoteSshdConfig WithUsePAM(bool usePAM) + { + _config.UsePAM = usePAM; + return this; + } + + public RemoteSshdConfig ClearHostKeyFiles() + { + _config.HostKeyFiles.Clear(); + return this; + } + + public RemoteSshdConfig AddHostKeyFile(string hostKeyFile) + { + _config.HostKeyFiles.Add(hostKeyFile); + return this; + } + + public RemoteSshd Update() + { + using (var client = new ScpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + using (var memoryStream = new MemoryStream()) + using (var sw = new StreamWriter(memoryStream, Utf8NoBom)) + { + sw.NewLine = "\n"; + _config.SaveTo(sw); + sw.Flush(); + + memoryStream.Position = 0; + + client.Upload(memoryStream, SshdConfigFilePath); + } + } + + return _remoteSshd; + } + } +} diff --git a/src/Renci.SshNet.IntegrationTests/Renci.SshNet.IntegrationTests.csproj b/src/Renci.SshNet.IntegrationTests/Renci.SshNet.IntegrationTests.csproj new file mode 100644 index 00000000..db411f36 --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/Renci.SshNet.IntegrationTests.csproj @@ -0,0 +1,66 @@ + + + + net7.0 + enable + + false + true + + $(NoWarn);CS1591;SYSLIB0021;SYSLIB1045;SYSLIB0014;IDE0220;IDE0010 + + + + + TRACE;FEATURE_MSTEST_DATATEST;FEATURE_SOCKET_EAP;FEATURE_ENCODING_ASCII;FEATURE_THREAD_SLEEP;FEATURE_THREAD_THREADPOOL + + + + + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + + + + + + + + + + + diff --git a/src/Renci.SshNet.IntegrationTests/ScpClientTests.cs b/src/Renci.SshNet.IntegrationTests/ScpClientTests.cs new file mode 100644 index 00000000..fc90554a --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/ScpClientTests.cs @@ -0,0 +1,41 @@ +namespace Renci.SshNet.IntegrationTests +{ + /// + /// The SCP client integration tests + /// + [TestClass] + public class ScpClientTests : IntegrationTestBase, IDisposable + { + private readonly ScpClient _scpClient; + + public ScpClientTests() + { + _scpClient = new ScpClient(SshServerHostName, SshServerPort, User.UserName, User.Password); + _scpClient.Connect(); + } + + [TestMethod] + + public void Upload_And_Download_FileStream() + { + var file = $"/tmp/{Guid.NewGuid()}.txt"; + var fileContent = "File content !@#$%^&*()_+{}:,./<>[];'\\|"; + + using var uploadStream = new MemoryStream(Encoding.UTF8.GetBytes(fileContent)); + _scpClient.Upload(uploadStream, file); + + using var downloadStream = new MemoryStream(); + _scpClient.Download(file, downloadStream); + + var result = Encoding.UTF8.GetString(downloadStream.ToArray()); + + Assert.AreEqual(fileContent, result); + } + + public void Dispose() + { + _scpClient.Disconnect(); + _scpClient.Dispose(); + } + } +} diff --git a/src/Renci.SshNet.IntegrationTests/ScpTests.cs b/src/Renci.SshNet.IntegrationTests/ScpTests.cs new file mode 100644 index 00000000..2fd4ea0c --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/ScpTests.cs @@ -0,0 +1,2379 @@ +using Renci.SshNet.Common; + +namespace Renci.SshNet.IntegrationTests +{ + // TODO SCP: UPLOAD / DOWNLOAD ZERO LENGTH FILES + // TODO SCP: UPLOAD / DOWNLOAD EMPTY DIRECTORY + // TODO SCP: UPLOAD DIRECTORY THAT ALREADY EXISTS ON REMOTE HOST + + [TestClass] + public class ScpTests : TestBase + { + private IConnectionInfoFactory _connectionInfoFactory; + private IRemotePathTransformation _remotePathTransformation; + + [TestInitialize] + public void SetUp() + { + _connectionInfoFactory = new LinuxVMConnectionFactory(SshServerHostName, SshServerPort); + _remotePathTransformation = RemotePathTransformation.ShellQuote; + } + +#if FEATURE_MSTEST_DATATEST + [DataTestMethod] + [DynamicData(nameof(GetScpDownloadStreamDirectoryDoesNotExistData), DynamicDataSourceType.Method)] +#else + [TestMethod] + public void Scp_Download_Stream_DirectoryDoesNotExist() + { + foreach (var data in GetScpDownloadStreamDirectoryDoesNotExistData()) + { + Scp_Download_Stream_DirectoryDoesNotExist((IRemotePathTransformation) data[0], + (string) data[1], + (string) data[2]); + } + } +#endif + public void Scp_Download_Stream_DirectoryDoesNotExist(IRemotePathTransformation remotePathTransformation, + string remotePath, + string remoteFile) + { + var completeRemotePath = CombinePaths(remotePath, remoteFile); + + // remove complete directory if it's not the home directory of the user + // or else remove the remote file + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + if (client.Exists(completeRemotePath)) + { + client.DeleteFile(completeRemotePath); + } + + if (remotePath.Length > 0 && remotePath != client.WorkingDirectory) + { + if (client.Exists(remotePath)) + { + client.DeleteDirectory(remotePath); + } + } + } + + try + { + using (var downloaded = new MemoryStream()) + using (var client = new ScpClient(_connectionInfoFactory.Create())) + { + if (remotePathTransformation != null) + { + client.RemotePathTransformation = remotePathTransformation; + } + + client.Connect(); + + try + { + client.Download(completeRemotePath, downloaded); + Assert.Fail(); + } + catch (ScpException ex) + { + Assert.IsNull(ex.InnerException); + Assert.AreEqual($"scp: {completeRemotePath}: No such file or directory", ex.Message); + } + } + } + finally + { + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + if (client.Exists(completeRemotePath)) + { + client.DeleteFile(completeRemotePath); + } + + if (remotePath.Length > 0 && remotePath != client.WorkingDirectory) + { + if (client.Exists(remotePath)) + { + client.DeleteDirectory(remotePath); + } + } + } + } + } + +#if FEATURE_MSTEST_DATATEST + [DataTestMethod] + [DynamicData(nameof(GetScpDownloadStreamFileDoesNotExistData), DynamicDataSourceType.Method)] +#else + [TestMethod] + public void Scp_Download_Stream_FileDoesNotExist() + { + foreach (var data in GetScpDownloadStreamFileDoesNotExistData()) + { + Scp_Download_Stream_FileDoesNotExist((IRemotePathTransformation)data[0], + (string)data[1], + (string)data[2]); + } + } +#endif + public void Scp_Download_Stream_FileDoesNotExist(IRemotePathTransformation remotePathTransformation, + string remotePath, + string remoteFile) + { + var completeRemotePath = CombinePaths(remotePath, remoteFile); + + // remove complete directory if it's not the home directory of the user + // or else remove the remote file + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + if (client.Exists(completeRemotePath)) + { + client.DeleteFile(completeRemotePath); + } + + if (remotePath.Length > 0 && remotePath != client.WorkingDirectory) + { + if (client.Exists(remotePath)) + { + client.DeleteDirectory(remotePath); + } + + client.CreateDirectory(remotePath); + } + } + + try + { + using (var downloaded = new MemoryStream()) + using (var client = new ScpClient(_connectionInfoFactory.Create())) + { + if (remotePathTransformation != null) + { + client.RemotePathTransformation = remotePathTransformation; + } + + client.Connect(); + + try + { + client.Download(completeRemotePath, downloaded); + Assert.Fail(); + } + catch (ScpException ex) + { + Assert.IsNull(ex.InnerException); + Assert.AreEqual($"scp: {completeRemotePath}: No such file or directory", ex.Message); + } + } + } + finally + { + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + if (client.Exists(completeRemotePath)) + { + client.DeleteFile(completeRemotePath); + } + + if (remotePath.Length > 0 && remotePath != client.WorkingDirectory) + { + if (client.Exists(remotePath)) + { + client.DeleteDirectory(remotePath); + } + } + } + } + } + +#if FEATURE_MSTEST_DATATEST + [DataTestMethod] + [DynamicData(nameof(GetScpDownloadDirectoryInfoDirectoryDoesNotExistData), DynamicDataSourceType.Method)] +#else + [TestMethod] + public void Scp_Download_DirectoryInfo_DirectoryDoesNotExist() + { + foreach (var data in GetScpDownloadDirectoryInfoDirectoryDoesNotExistData()) + { + Scp_Download_DirectoryInfo_DirectoryDoesNotExist((IRemotePathTransformation)data[0], + (string)data[1]); + } + } +#endif + public void Scp_Download_DirectoryInfo_DirectoryDoesNotExist(IRemotePathTransformation remotePathTransformation, + string remotePath) + { + var localDirectory = Path.GetTempFileName(); + File.Delete(localDirectory); + Directory.CreateDirectory(localDirectory); + + try + { + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + if (client.Exists(remotePath)) + { + client.DeleteDirectory(remotePath); + } + } + + using (var client = new ScpClient(_connectionInfoFactory.Create())) + { + if (remotePathTransformation != null) + { + client.RemotePathTransformation = remotePathTransformation; + } + + client.Connect(); + + try + { + client.Download(remotePath, new DirectoryInfo(localDirectory)); + Assert.Fail(); + } + catch (ScpException ex) + { + Assert.IsNull(ex.InnerException); + Assert.AreEqual($"scp: {remotePath}: No such file or directory", ex.Message); + } + } + } + finally + { + Directory.Delete(localDirectory, true); + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + if (client.Exists(remotePath)) + { + client.DeleteDirectory(remotePath); + } + } + } + } + +#if FEATURE_MSTEST_DATATEST + [DataTestMethod] + [DynamicData(nameof(GetScpDownloadDirectoryInfoExistingFileData), DynamicDataSourceType.Method)] +#else + [TestMethod] + public void Scp_Download_DirectoryInfo_ExistingFile() + { + foreach (var data in GetScpDownloadDirectoryInfoExistingFileData()) + { + Scp_Download_DirectoryInfo_ExistingFile((IRemotePathTransformation)data[0], + (string)data[1]); + } + } +#endif + public void Scp_Download_DirectoryInfo_ExistingFile(IRemotePathTransformation remotePathTransformation, + string remotePath) + { + var content = CreateMemoryStream(100); + content.Position = 0; + + var localDirectory = Path.GetTempFileName(); + File.Delete(localDirectory); + Directory.CreateDirectory(localDirectory); + + var localFile = Path.Combine(localDirectory, PosixPath.GetFileName(remotePath)); + + try + { + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + client.UploadFile(content, remotePath); + } + + using (var client = new ScpClient(_connectionInfoFactory.Create())) + { + if (remotePathTransformation != null) + { + client.RemotePathTransformation = remotePathTransformation; + } + + client.Connect(); + client.Download(remotePath, new DirectoryInfo(localDirectory)); + } + + Assert.IsTrue(File.Exists(localFile)); + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + using (var downloaded = new MemoryStream()) + { + client.DownloadFile(remotePath, downloaded); + downloaded.Position = 0; + Assert.AreEqual(CreateFileHash(localFile), CreateHash(downloaded)); + } + } + } + finally + { + Directory.Delete(localDirectory, true); + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + if (client.Exists(remotePath)) + { + client.DeleteFile(remotePath); + } + } + } + } + +#if FEATURE_MSTEST_DATATEST + [DataTestMethod] + [DynamicData(nameof(GetScpDownloadDirectoryInfoExistingDirectoryData), DynamicDataSourceType.Method)] +#else + [TestMethod] + public void Scp_Download_DirectoryInfo_ExistingDirectory() + { + foreach (var data in GetScpDownloadDirectoryInfoExistingDirectoryData()) + { + Scp_Download_DirectoryInfo_ExistingDirectory((IRemotePathTransformation)data[0], + (string)data[1]); + } + } +#endif + public void Scp_Download_DirectoryInfo_ExistingDirectory(IRemotePathTransformation remotePathTransformation, + string remotePath) + { + var localDirectory = Path.GetTempFileName(); + File.Delete(localDirectory); + Directory.CreateDirectory(localDirectory); + + var localPathFile1 = Path.Combine(localDirectory, "file1 23"); + var remotePathFile1 = CombinePaths(remotePath, "file1 23"); + var contentFile1 = CreateMemoryStream(1024); + contentFile1.Position = 0; + + var localPathFile2 = Path.Combine(localDirectory, "file2 #$%"); + var remotePathFile2 = CombinePaths(remotePath, "file2 #$%"); + var contentFile2 = CreateMemoryStream(2048); + contentFile2.Position = 0; + + var localPathSubDirectory = Path.Combine(localDirectory, "subdir $1%#"); + var remotePathSubDirectory = CombinePaths(remotePath, "subdir $1%#"); + + var localPathFile3 = Path.Combine(localPathSubDirectory, "file3 %$#"); + var remotePathFile3 = CombinePaths(remotePathSubDirectory, "file3 %$#"); + var contentFile3 = CreateMemoryStream(256); + contentFile3.Position = 0; + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + if (client.Exists(remotePathFile1)) + { + client.DeleteFile(remotePathFile1); + } + + if (client.Exists(remotePathFile2)) + { + client.DeleteFile(remotePathFile2); + } + + if (client.Exists(remotePathFile3)) + { + client.DeleteFile(remotePathFile3); + } + + if (client.Exists(remotePathSubDirectory)) + { + client.DeleteDirectory(remotePathSubDirectory); + } + + if (remotePath.Length > 0 && remotePath != client.WorkingDirectory) + { + if (client.Exists(remotePath)) + { + client.DeleteDirectory(remotePath); + } + } + } + + try + { + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + if (!client.Exists(remotePath)) + { + client.CreateDirectory(remotePath); + } + + client.UploadFile(contentFile1, remotePathFile1); + client.UploadFile(contentFile1, remotePathFile2); + + client.CreateDirectory(remotePathSubDirectory); + client.UploadFile(contentFile3, remotePathFile3); + } + + using (var client = new ScpClient(_connectionInfoFactory.Create())) + { + if (remotePathTransformation != null) + { + client.RemotePathTransformation = remotePathTransformation; + } + + client.Connect(); + client.Download(remotePath, new DirectoryInfo(localDirectory)); + } + + var localFiles = Directory.GetFiles(localDirectory); + Assert.AreEqual(2, localFiles.Length); + Assert.IsTrue(localFiles.Contains(localPathFile1)); + Assert.IsTrue(localFiles.Contains(localPathFile2)); + + var localSubDirecties = Directory.GetDirectories(localDirectory); + Assert.AreEqual(1, localSubDirecties.Length); + Assert.AreEqual(localPathSubDirectory, localSubDirecties[0]); + + var localFilesSubDirectory = Directory.GetFiles(localPathSubDirectory); + Assert.AreEqual(1, localFilesSubDirectory.Length); + Assert.AreEqual(localPathFile3, localFilesSubDirectory[0]); + + Assert.AreEqual(0, Directory.GetDirectories(localPathSubDirectory).Length); + } + finally + { + Directory.Delete(localDirectory, true); + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + if (client.Exists(remotePathFile1)) + { + client.DeleteFile(remotePathFile1); + } + + if (client.Exists(remotePathFile2)) + { + client.DeleteFile(remotePathFile2); + } + + if (client.Exists(remotePathFile3)) + { + client.DeleteFile(remotePathFile3); + } + + if (client.Exists(remotePathSubDirectory)) + { + client.DeleteDirectory(remotePathSubDirectory); + } + + if (remotePath.Length > 0 && remotePath != client.WorkingDirectory) + { + if (client.Exists(remotePath)) + { + client.DeleteDirectory(remotePath); + } + } + } + } + } + +#if FEATURE_MSTEST_DATATEST + [DataTestMethod] + [DynamicData(nameof(GetScpDownloadFileInfoDirectoryDoesNotExistData), DynamicDataSourceType.Method)] +#else + [TestMethod] + public void Scp_Download_FileInfo_DirectoryDoesNotExist() + { + foreach (var data in GetScpDownloadFileInfoDirectoryDoesNotExistData()) + { + Scp_Download_FileInfo_DirectoryDoesNotExist((IRemotePathTransformation)data[0], + (string)data[1], + (string)data[2]); + } + } +#endif + public void Scp_Download_FileInfo_DirectoryDoesNotExist(IRemotePathTransformation remotePathTransformation, + string remotePath, + string remoteFile) + { + var completeRemotePath = CombinePaths(remotePath, remoteFile); + + // remove complete directory if it's not the home directory of the user + // or else remove the remote file + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + if (remotePath.Length > 0 && remotePath != client.WorkingDirectory) + { + if (client.Exists(remotePath)) + { + client.DeleteDirectory(remotePath); + } + } + } + + var fileInfo = new FileInfo(Path.GetTempFileName()); + + try + { + using (var client = new ScpClient(_connectionInfoFactory.Create())) + { + if (remotePathTransformation != null) + { + client.RemotePathTransformation = remotePathTransformation; + } + + client.Connect(); + + try + { + client.Download(completeRemotePath, fileInfo); + Assert.Fail(); + } + catch (ScpException ex) + { + Assert.IsNull(ex.InnerException); + Assert.AreEqual($"scp: {completeRemotePath}: No such file or directory", ex.Message); + } + } + } + finally + { + fileInfo.Delete(); + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + if (client.Exists(completeRemotePath)) + { + client.DeleteFile(completeRemotePath); + } + + if (remotePath.Length > 0 && remotePath != client.WorkingDirectory) + { + if (client.Exists(remotePath)) + { + client.DeleteDirectory(remotePath); + } + } + } + } + } + +#if FEATURE_MSTEST_DATATEST + [DataTestMethod] + [DynamicData(nameof(GetScpDownloadFileInfoFileDoesNotExistData), DynamicDataSourceType.Method)] +#else + [TestMethod] + public void Scp_Download_FileInfo_FileDoesNotExist() + { + foreach (var data in GetScpDownloadFileInfoFileDoesNotExistData()) + { + Scp_Download_FileInfo_FileDoesNotExist((IRemotePathTransformation)data[0], + (string)data[1], + (string)data[2]); + } + } +#endif + public void Scp_Download_FileInfo_FileDoesNotExist(IRemotePathTransformation remotePathTransformation, + string remotePath, + string remoteFile) + { + var completeRemotePath = CombinePaths(remotePath, remoteFile); + + // remove complete directory if it's not the home directory of the user + // or else remove the remote file + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + if (remotePath.Length > 0 && remotePath != client.WorkingDirectory) + { + if (client.Exists(remotePath)) + { + client.DeleteDirectory(remotePath); + } + + client.CreateDirectory(remotePath); + } + } + + var fileInfo = new FileInfo(Path.GetTempFileName()); + + try + { + using (var client = new ScpClient(_connectionInfoFactory.Create())) + { + if (remotePathTransformation != null) + { + client.RemotePathTransformation = remotePathTransformation; + } + + client.Connect(); + + try + { + client.Download(completeRemotePath, fileInfo); + Assert.Fail(); + } + catch (ScpException ex) + { + Assert.IsNull(ex.InnerException); + Assert.AreEqual($"scp: {completeRemotePath}: No such file or directory", ex.Message); + } + } + } + finally + { + fileInfo.Delete(); + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + if (client.Exists(completeRemotePath)) + { + client.DeleteFile(completeRemotePath); + } + + if (remotePath.Length > 0 && remotePath != client.WorkingDirectory) + { + if (client.Exists(remotePath)) + { + client.DeleteDirectory(remotePath); + } + } + } + } + } + +#if FEATURE_MSTEST_DATATEST + [DataTestMethod] + [DynamicData(nameof(GetScpDownloadFileInfoExistingDirectoryData), DynamicDataSourceType.Method)] +#else + [TestMethod] + public void Scp_Download_FileInfo_ExistingDirectory() + { + foreach (var data in GetScpDownloadFileInfoExistingDirectoryData()) + { + Scp_Download_FileInfo_ExistingDirectory((IRemotePathTransformation)data[0], + (string)data[1]); + } + } +#endif + public void Scp_Download_FileInfo_ExistingDirectory(IRemotePathTransformation remotePathTransformation, + string remotePath) + { + // remove complete directory if it's not the home directory of the user + // or else remove the remote file + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + if (remotePath.Length > 0 && remotePath != client.WorkingDirectory) + { + if (client.Exists(remotePath)) + { + client.DeleteDirectory(remotePath); + } + + client.CreateDirectory(remotePath); + } + } + + var fileInfo = new FileInfo(Path.GetTempFileName()); + fileInfo.Delete(); + + try + { + using (var client = new ScpClient(_connectionInfoFactory.Create())) + { + if (remotePathTransformation != null) + { + client.RemotePathTransformation = remotePathTransformation; + } + + client.Connect(); + + try + { + client.Download(remotePath, fileInfo); + Assert.Fail(); + } + catch (ScpException ex) + { + Assert.IsNull(ex.InnerException); + Assert.AreEqual($"scp: {remotePath}: not a regular file", ex.Message); + } + + Assert.IsFalse(fileInfo.Exists); + } + } + finally + { + fileInfo.Delete(); + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + if (remotePath.Length > 0 && remotePath != client.WorkingDirectory) + { + if (client.Exists(remotePath)) + { + client.DeleteDirectory(remotePath); + } + } + } + } + } + +#if FEATURE_MSTEST_DATATEST + [DataTestMethod] + [DynamicData(nameof(GetScpDownloadFileInfoExistingFileData), DynamicDataSourceType.Method)] +#else + [TestMethod] + public void Scp_Download_FileInfo_ExistingFile() + { + foreach (var data in GetScpDownloadFileInfoExistingFileData()) + { + Scp_Download_FileInfo_ExistingFile((IRemotePathTransformation)data[0], + (string)data[1], + (string)data[2], + (int)data[3]); + } + } +#endif + public void Scp_Download_FileInfo_ExistingFile(IRemotePathTransformation remotePathTransformation, + string remotePath, + string remoteFile, + int size) + { + var completeRemotePath = CombinePaths(remotePath, remoteFile); + + // remove complete directory if it's not the home directory of the user + // or else remove the remote file + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + if (client.Exists(completeRemotePath)) + { + client.DeleteFile(completeRemotePath); + } + + if (remotePath.Length > 0 && remotePath != client.WorkingDirectory) + { + if (client.Exists(remotePath)) + { + client.DeleteDirectory(remotePath); + } + + client.CreateDirectory(remotePath); + } + } + + var fileInfo = new FileInfo(Path.GetTempFileName()); + + try + { + var content = CreateMemoryStream(size); + content.Position = 0; + + using (var client = new ScpClient(_connectionInfoFactory.Create())) + { + if (remotePathTransformation != null) + { + client.RemotePathTransformation = remotePathTransformation; + } + + client.Connect(); + client.Upload(content, completeRemotePath); + } + + using (var client = new ScpClient(_connectionInfoFactory.Create())) + { + if (remotePathTransformation != null) + { + client.RemotePathTransformation = remotePathTransformation; + } + + client.Connect(); + client.Download(completeRemotePath, fileInfo); + } + + using (var fs = fileInfo.OpenRead()) + { + var downloadedBytes = new byte[fs.Length]; + Assert.AreEqual(downloadedBytes.Length, fs.Read(downloadedBytes, 0, downloadedBytes.Length)); + content.Position = 0; + Assert.AreEqual(CreateHash(content), CreateHash(downloadedBytes)); + } + } + finally + { + fileInfo.Delete(); + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + if (client.Exists(completeRemotePath)) + { + client.DeleteFile(completeRemotePath); + } + + if (remotePath.Length > 0 && remotePath != client.WorkingDirectory) + { + if (client.Exists(remotePath)) + { + client.DeleteDirectory(remotePath); + } + } + } + } + } + +#if FEATURE_MSTEST_DATATEST + [DataTestMethod] + [DynamicData(nameof(GetScpDownloadStreamExistingDirectoryData), DynamicDataSourceType.Method)] +#else + [TestMethod] + public void Scp_Download_Stream_ExistingDirectory() + { + foreach (var data in GetScpDownloadStreamExistingDirectoryData()) + { + Scp_Download_Stream_ExistingDirectory((IRemotePathTransformation)data[0], + (string)data[1]); + } + } +#endif + public void Scp_Download_Stream_ExistingDirectory(IRemotePathTransformation remotePathTransformation, + string remotePath) + { + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + if (remotePath.Length > 0 && remotePath != client.WorkingDirectory) + { + if (client.Exists(remotePath)) + { + client.DeleteDirectory(remotePath); + } + + client.CreateDirectory(remotePath); + } + } + + var file = Path.GetTempFileName(); + File.Delete(file); + + try + { + using (var fs = File.OpenWrite(file)) + using (var client = new ScpClient(_connectionInfoFactory.Create())) + { + if (remotePathTransformation != null) + { + client.RemotePathTransformation = remotePathTransformation; + } + + client.Connect(); + + try + { + client.Download(remotePath, fs); + Assert.Fail(); + } + catch (ScpException ex) + { + Assert.IsNull(ex.InnerException); + Assert.AreEqual($"scp: {remotePath}: not a regular file", ex.Message); + } + + Assert.AreEqual(0, fs.Length); + } + } + finally + { + File.Delete(file); + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + if (remotePath.Length > 0 && remotePath != client.WorkingDirectory) + { + if (client.Exists(remotePath)) + { + client.DeleteDirectory(remotePath); + } + } + } + } + } + +#if FEATURE_MSTEST_DATATEST + [DataTestMethod] + [DynamicData(nameof(GetScpDownloadStreamExistingFileData), DynamicDataSourceType.Method)] +#else + [TestMethod] + public void Scp_Download_Stream_ExistingFile() + { + foreach (var data in GetScpDownloadStreamExistingFileData()) + { + Scp_Download_Stream_ExistingFile((IRemotePathTransformation)data[0], + (string)data[1], + (string)data[2], + (int)data[3]); + } + } +#endif + public void Scp_Download_Stream_ExistingFile(IRemotePathTransformation remotePathTransformation, + string remotePath, + string remoteFile, + int size) + { + var completeRemotePath = CombinePaths(remotePath, remoteFile); + + // remove complete directory if it's not the home directory of the user + // or else remove the remote file + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + if (client.Exists(completeRemotePath)) + { + client.DeleteFile(completeRemotePath); + } + + if (remotePath.Length > 0 && remotePath != client.WorkingDirectory) + { + if (client.Exists(remotePath)) + { + client.DeleteDirectory(remotePath); + } + + client.CreateDirectory(remotePath); + } + } + + var file = CreateTempFile(size); + + try + { + using (var fs = File.OpenRead(file)) + using (var client = new ScpClient(_connectionInfoFactory.Create())) + { + if (remotePathTransformation != null) + { + client.RemotePathTransformation = remotePathTransformation; + } + + client.Connect(); + client.Upload(fs, completeRemotePath); + } + + using (var fs = File.OpenRead(file)) + using (var downloaded = new MemoryStream(size)) + using (var client = new ScpClient(_connectionInfoFactory.Create())) + { + if (remotePathTransformation != null) + { + client.RemotePathTransformation = remotePathTransformation; + } + + client.Connect(); + + client.Download(completeRemotePath, downloaded); + downloaded.Position = 0; + Assert.AreEqual(CreateHash(fs), CreateHash(downloaded)); + } + } + finally + { + File.Delete(file); + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + if (client.Exists(completeRemotePath)) + { + client.DeleteFile(completeRemotePath); + } + + if (remotePath.Length > 0 && remotePath != client.WorkingDirectory) + { + if (client.Exists(remotePath)) + { + client.DeleteDirectory(remotePath); + } + } + } + } + } + +#if FEATURE_MSTEST_DATATEST + [DataTestMethod] + [DynamicData(nameof(GetScpUploadFileStreamDirectoryDoesNotExistData), DynamicDataSourceType.Method)] +#else + [TestMethod] + public void Scp_Upload_FileStream_DirectoryDoesNotExist() + { + foreach (var data in GetScpUploadFileStreamDirectoryDoesNotExistData()) + { + Scp_Upload_FileStream_DirectoryDoesNotExist((IRemotePathTransformation)data[0], + (string)data[1], + (string)data[2]); + } + } +#endif + public void Scp_Upload_FileStream_DirectoryDoesNotExist(IRemotePathTransformation remotePathTransformation, + string remotePath, + string remoteFile) + { + var completeRemotePath = CombinePaths(remotePath, remoteFile); + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + if (client.Exists(completeRemotePath)) + { + client.DeleteFile(completeRemotePath); + } + + if (client.Exists(remotePath)) + { + client.DeleteDirectory(remotePath); + } + } + + var file = CreateTempFile(1000); + + try + { + using (var fs = File.OpenRead(file)) + using (var client = new ScpClient(_connectionInfoFactory.Create())) + { + if (remotePathTransformation != null) + { + client.RemotePathTransformation = remotePathTransformation; + } + + client.Connect(); + + try + { + client.Upload(fs, completeRemotePath); + Assert.Fail(); + } + catch (ScpException ex) + { + Assert.IsNull(ex.InnerException); + Assert.AreEqual($"scp: {remotePath}: No such file or directory", ex.Message); + } + } + } + finally + { + File.Delete(file); + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + if (client.Exists(completeRemotePath)) + { + client.DeleteFile(completeRemotePath); + } + + if (client.Exists(remotePath)) + { + client.DeleteDirectory(remotePath); + } + } + } + } + +#if FEATURE_MSTEST_DATATEST + [DataTestMethod] + [DynamicData(nameof(GetScpUploadFileStreamExistingDirectoryData), DynamicDataSourceType.Method)] +#else + [TestMethod] + public void Scp_Upload_FileStream_ExistingDirectory() + { + foreach (var data in GetScpUploadFileStreamExistingDirectoryData()) + { + Scp_Upload_FileStream_ExistingDirectory((IRemotePathTransformation)data[0], + (string)data[1]); + } + } +#endif + public void Scp_Upload_FileStream_ExistingDirectory(IRemotePathTransformation remotePathTransformation, + string remoteFile) + { + using (var client = new SshClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + using (var command = client.CreateCommand("rm -Rf " + _remotePathTransformation.Transform(remoteFile))) + { + command.Execute(); + } + } + + var file = CreateTempFile(1000); + + try + { + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + client.CreateDirectory(remoteFile); + } + + using (var fs = File.OpenRead(file)) + using (var client = new ScpClient(_connectionInfoFactory.Create())) + { + if (remotePathTransformation != null) + { + client.RemotePathTransformation = remotePathTransformation; + } + + client.Connect(); + + try + { + client.Upload(fs, remoteFile); + Assert.Fail(); + } + catch (ScpException ex) + { + Assert.IsNull(ex.InnerException); + Assert.AreEqual($"scp: {remoteFile}: Is a directory", ex.Message); + } + } + } + finally + { + File.Delete(file); + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + if (client.Exists(remoteFile)) + { + client.DeleteDirectory(remoteFile); + } + } + } + } + +#if FEATURE_MSTEST_DATATEST + [DataTestMethod] + [DynamicData(nameof(ScpUploadFileStreamExistingFileData), DynamicDataSourceType.Method)] +#else + [TestMethod] + public void Scp_Upload_FileStream_ExistingFile() + { + foreach (var data in ScpUploadFileStreamExistingFileData()) + { + Scp_Upload_FileStream_ExistingFile((IRemotePathTransformation)data[0], + (string)data[1]); + } + } +#endif + public void Scp_Upload_FileStream_ExistingFile(IRemotePathTransformation remotePathTransformation, + string remoteFile) + { + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + + // original content is bigger than new content to ensure file is fully overwritten + var originalContent = CreateMemoryStream(2000); + var file = CreateTempFile(1000); + + try + { + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + originalContent.Position = 0; + client.UploadFile(originalContent, remoteFile); + } + + using (var fs = File.OpenRead(file)) + using (var client = new ScpClient(_connectionInfoFactory.Create())) + { + if (remotePathTransformation != null) + { + client.RemotePathTransformation = remotePathTransformation; + } + + client.Connect(); + client.Upload(fs, remoteFile); + } + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + using (var downloaded = new MemoryStream(1000)) + { + client.DownloadFile(remoteFile, downloaded); + downloaded.Position = 0; + Assert.AreEqual(CreateFileHash(file), CreateHash(downloaded)); + } + } + } + finally + { + File.Delete(file); + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + +#if FEATURE_MSTEST_DATATEST + [DataTestMethod] + [DynamicData(nameof(GetScpUploadFileStreamFileDoesNotExistData), DynamicDataSourceType.Method)] +#else + [TestMethod] + public void Scp_Upload_FileStream_FileDoesNotExist() + { + foreach (var data in GetScpUploadFileStreamFileDoesNotExistData()) + { + Scp_Upload_FileStream_FileDoesNotExist((IRemotePathTransformation)data[0], + (string)data[1], + (string)data[2], + (int)data[3]); + } + } +#endif + public void Scp_Upload_FileStream_FileDoesNotExist(IRemotePathTransformation remotePathTransformation, + string remotePath, + string remoteFile, + int size) + { + var completeRemotePath = CombinePaths(remotePath, remoteFile); + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + if (client.Exists(completeRemotePath)) + { + client.DeleteFile(completeRemotePath); + } + + // remove complete directory if it's not the home directory of the user + if (remotePath.Length > 0 && remotePath != client.WorkingDirectory) + { + if (client.Exists(remotePath)) + { + client.DeleteDirectory(remotePath); + } + } + } + + var file = CreateTempFile(size); + + try + { + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + // create directory if it's not the home directory of the user + if (remotePath.Length > 0 && remotePath != client.WorkingDirectory) + { + if (!client.Exists((remotePath))) + { + client.CreateDirectory(remotePath); + } + } + } + + using (var fs = File.OpenRead(file)) + using (var client = new ScpClient(_connectionInfoFactory.Create())) + { + if (remotePathTransformation != null) + { + client.RemotePathTransformation = remotePathTransformation; + } + + client.Connect(); + client.Upload(fs, completeRemotePath); + } + + using (var fs = File.OpenRead(file)) + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var sftpFile = client.Get(completeRemotePath); + Assert.AreEqual(GetAbsoluteRemotePath(client, remotePath, remoteFile), sftpFile.FullName); + Assert.AreEqual(size, sftpFile.Length); + + var downloaded = client.ReadAllBytes(completeRemotePath); + Assert.AreEqual(CreateHash(fs), CreateHash(downloaded)); + } + } + finally + { + File.Delete(file); + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + if (client.Exists(completeRemotePath)) + { + client.DeleteFile(completeRemotePath); + } + + // remove complete directory if it's not the home directory of the user + if (remotePath.Length > 0 && remotePath != client.WorkingDirectory) + { + if (client.Exists(remotePath)) + { + client.DeleteDirectory(remotePath); + } + } + } + } + } + + /// + /// https://github.com/sshnet/SSH.NET/issues/289 + /// +#if FEATURE_MSTEST_DATATEST + [DataTestMethod] + [DynamicData(nameof(GetScpUploadFileInfoDirectoryDoesNotExistData), DynamicDataSourceType.Method)] +#else + [TestMethod] + public void Scp_Upload_FileInfo_DirectoryDoesNotExist() + { + foreach (var data in GetScpUploadFileInfoDirectoryDoesNotExistData()) + { + Scp_Upload_FileInfo_DirectoryDoesNotExist((IRemotePathTransformation)data[0], + (string)data[1], + (string)data[2]); + } + } +#endif + public void Scp_Upload_FileInfo_DirectoryDoesNotExist(IRemotePathTransformation remotePathTransformation, + string remotePath, + string remoteFile) + { + var completeRemotePath = CombinePaths(remotePath, remoteFile); + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + if (client.Exists(completeRemotePath)) + { + client.DeleteFile(completeRemotePath); + } + + if (client.Exists(remotePath)) + { + client.DeleteDirectory(remotePath); + } + } + + var file = CreateTempFile(1000); + + try + { + using (var client = new ScpClient(_connectionInfoFactory.Create())) + { + if (remotePathTransformation != null) + { + client.RemotePathTransformation = remotePathTransformation; + } + + client.Connect(); + + try + { + client.Upload(new FileInfo(file), completeRemotePath); + Assert.Fail(); + } + catch (ScpException ex) + { + Assert.IsNull(ex.InnerException); + Assert.AreEqual($"scp: {remotePath}: No such file or directory", ex.Message); + } + } + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + Assert.IsFalse(client.Exists(completeRemotePath)); + Assert.IsFalse(client.Exists(remotePath)); + } + } + finally + { + File.Delete(file); + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + if (client.Exists(completeRemotePath)) + { + client.DeleteFile(completeRemotePath); + } + + if (client.Exists(remotePath)) + { + client.DeleteDirectory(remotePath); + } + } + } + } + + /// + /// https://github.com/sshnet/SSH.NET/issues/286 + /// +#if FEATURE_MSTEST_DATATEST + [DataTestMethod] + [DynamicData(nameof(GetScpUploadFileInfoExistingDirectoryData), DynamicDataSourceType.Method)] +#else + [TestMethod] + public void Scp_Upload_FileInfo_ExistingDirectory() + { + foreach (var data in GetScpUploadFileInfoExistingDirectoryData()) + { + Scp_Upload_FileInfo_ExistingDirectory((IRemotePathTransformation)data[0], + (string)data[1]); + } + } +#endif + public void Scp_Upload_FileInfo_ExistingDirectory(IRemotePathTransformation remotePathTransformation, + string remoteFile) + { + using (var client = new SshClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + using (var command = client.CreateCommand("rm -Rf " + _remotePathTransformation.Transform(remoteFile))) + { + command.Execute(); + } + } + + var file = CreateTempFile(1000); + + try + { + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + client.CreateDirectory(remoteFile); + } + + using (var client = new ScpClient(_connectionInfoFactory.Create())) + { + if (remotePathTransformation != null) + { + client.RemotePathTransformation = remotePathTransformation; + } + + client.Connect(); + + try + { + client.Upload(new FileInfo(file), remoteFile); + Assert.Fail(); + } + catch (ScpException ex) + { + Assert.IsNull(ex.InnerException); + Assert.AreEqual($"scp: {remoteFile}: Is a directory", ex.Message); + } + } + } + finally + { + File.Delete(file); + + using (var client = new SshClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + using (var command = client.CreateCommand("rm -Rf " + _remotePathTransformation.Transform(remoteFile))) + { + command.Execute(); + } + } + } + } + +#if FEATURE_MSTEST_DATATEST + [DataTestMethod] + [DynamicData(nameof(GetScpUploadFileInfoExistingFileData), DynamicDataSourceType.Method)] +#else + [TestMethod] + public void Scp_Upload_FileInfo_ExistingFile() + { + foreach (var data in GetScpUploadFileInfoExistingFileData()) + { + Scp_Upload_FileInfo_ExistingFile((IRemotePathTransformation)data[0], + (string)data[1]); + } + } +#endif + public void Scp_Upload_FileInfo_ExistingFile(IRemotePathTransformation remotePathTransformation, + string remoteFile) + { + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + + // original content is bigger than new content to ensure file is fully overwritten + var originalContent = CreateMemoryStream(2000); + var file = CreateTempFile(1000); + + try + { + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + originalContent.Position = 0; + client.UploadFile(originalContent, remoteFile); + } + + var fileInfo = new FileInfo(file) + { + LastAccessTimeUtc = new DateTime(1973, 8, 13, 20, 15, 33, DateTimeKind.Utc), + LastWriteTimeUtc = new DateTime(1974, 1, 24, 3, 55, 12, DateTimeKind.Utc) + }; + + using (var client = new ScpClient(_connectionInfoFactory.Create())) + { + if (remotePathTransformation != null) + { + client.RemotePathTransformation = remotePathTransformation; + } + + client.Connect(); + client.Upload(fileInfo, remoteFile); + } + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var uploadedFile = client.Get(remoteFile); + Assert.AreEqual(fileInfo.LastAccessTimeUtc, uploadedFile.LastAccessTimeUtc); + Assert.AreEqual(fileInfo.LastWriteTimeUtc, uploadedFile.LastWriteTimeUtc); + + using (var downloaded = new MemoryStream(1000)) + { + client.DownloadFile(remoteFile, downloaded); + downloaded.Position = 0; + Assert.AreEqual(CreateFileHash(file), CreateHash(downloaded)); + } + } + } + finally + { + File.Delete(file); + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + +#if FEATURE_MSTEST_DATATEST + [DataTestMethod] + [DynamicData(nameof(GetScpUploadFileInfoFileDoesNotExistData), DynamicDataSourceType.Method)] +#else + [TestMethod] + public void Scp_Upload_FileInfo_FileDoesNotExist() + { + foreach (var data in GetScpUploadFileInfoFileDoesNotExistData()) + { + Scp_Upload_FileInfo_FileDoesNotExist((IRemotePathTransformation)data[0], + (string)data[1], + (string)data[2], + (int)data[3]); + } + } +#endif + public void Scp_Upload_FileInfo_FileDoesNotExist(IRemotePathTransformation remotePathTransformation, + string remotePath, + string remoteFile, + int size) + { + var completeRemotePath = CombinePaths(remotePath, remoteFile); + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + if (client.Exists(completeRemotePath)) + { + client.DeleteFile(completeRemotePath); + } + + // remove complete directory if it's not the home directory of the user + if (remotePath.Length > 0 && remotePath != client.WorkingDirectory) + { + if (client.Exists(remotePath)) + { + client.DeleteDirectory(remotePath); + } + } + } + + var file = CreateTempFile(size); + + try + { + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + // create directory if it's not the home directory of the user + if (remotePath.Length > 0 && remotePath != client.WorkingDirectory) + { + if (!client.Exists(remotePath)) + { + client.CreateDirectory(remotePath); + } + } + } + + var fileInfo = new FileInfo(file) + { + LastAccessTimeUtc = new DateTime(1973, 8, 13, 20, 15, 33, DateTimeKind.Utc), + LastWriteTimeUtc = new DateTime(1974, 1, 24, 3, 55, 12, DateTimeKind.Utc) + }; + + using (var client = new ScpClient(_connectionInfoFactory.Create())) + { + if (remotePathTransformation != null) + { + client.RemotePathTransformation = remotePathTransformation; + } + + client.Connect(); + client.Upload(fileInfo, completeRemotePath); + } + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var uploadedFile = client.Get(completeRemotePath); + Assert.AreEqual(fileInfo.LastAccessTimeUtc, uploadedFile.LastAccessTimeUtc); + Assert.AreEqual(fileInfo.LastWriteTimeUtc, uploadedFile.LastWriteTimeUtc); + Assert.AreEqual(size, uploadedFile.Length); + + using (var downloaded = new MemoryStream(size)) + { + client.DownloadFile(completeRemotePath, downloaded); + downloaded.Position = 0; + Assert.AreEqual(CreateFileHash(file), CreateHash(downloaded)); + } + } + } + finally + { + File.Delete(file); + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + if (client.Exists(completeRemotePath)) + { + client.Delete(completeRemotePath); + } + + // remove complete directory if it's not the home directory of the user + if (remotePath.Length > 0 && remotePath != client.WorkingDirectory) + { + if (client.Exists(remotePath)) + { + client.DeleteDirectory(remotePath); + } + } + } + } + } + +#if FEATURE_MSTEST_DATATEST + [DataTestMethod] + [DynamicData(nameof(GetScpUploadDirectoryInfoDirectoryDoesNotExistData), DynamicDataSourceType.Method)] +#else + [TestMethod] + public void Scp_Upload_DirectoryInfo_DirectoryDoesNotExist() + { + foreach (var data in GetScpUploadDirectoryInfoDirectoryDoesNotExistData()) + { + Scp_Upload_DirectoryInfo_DirectoryDoesNotExist((IRemotePathTransformation)data[0], + (string)data[1]); + } + } +#endif + public void Scp_Upload_DirectoryInfo_DirectoryDoesNotExist(IRemotePathTransformation remotePathTransformation, + string remoteDirectory) + { + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + if (client.Exists((remoteDirectory))) + { + client.DeleteDirectory(remoteDirectory); + } + } + + var localDirectory = Path.GetTempFileName(); + File.Delete(localDirectory); + Directory.CreateDirectory(localDirectory); + + try + { + using (var client = new ScpClient(_connectionInfoFactory.Create())) + { + if (remotePathTransformation != null) + { + client.RemotePathTransformation = remotePathTransformation; + } + + client.Connect(); + + try + { + client.Upload(new DirectoryInfo(localDirectory), remoteDirectory); + Assert.Fail(); + } + catch (ScpException ex) + { + Assert.IsNull(ex.InnerException); + Assert.AreEqual($"scp: {remoteDirectory}: No such file or directory", ex.Message); + } + } + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + Assert.IsFalse(client.Exists(remoteDirectory)); + } + } + finally + { + Directory.Delete(localDirectory, true); + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + if (client.Exists((remoteDirectory))) + { + client.DeleteDirectory(remoteDirectory); + } + } + } + } + +#if FEATURE_MSTEST_DATATEST + [DataTestMethod] + [DynamicData(nameof(GetScpUploadDirectoryInfoExistingDirectoryData), DynamicDataSourceType.Method)] +#else + [TestMethod] + public void Scp_Upload_DirectoryInfo_ExistingDirectory() + { + foreach (var data in GetScpUploadDirectoryInfoExistingDirectoryData()) + { + Scp_Upload_DirectoryInfo_ExistingDirectory((IRemotePathTransformation)data[0], + (string)data[1]); + } + } +#endif + public void Scp_Upload_DirectoryInfo_ExistingDirectory(IRemotePathTransformation remotePathTransformation, + string remoteDirectory) + { + string absoluteRemoteDirectory = GetAbsoluteRemotePath(_connectionInfoFactory, remoteDirectory); + + var remotePathFile1 = CombinePaths(remoteDirectory, "file1"); + var remotePathFile2 = CombinePaths(remoteDirectory, "file2"); + + var absoluteremoteSubDirectory1 = CombinePaths(absoluteRemoteDirectory, "sub1"); + var remoteSubDirectory1 = CombinePaths(remoteDirectory, "sub1"); + var remotePathSubFile1 = CombinePaths(remoteSubDirectory1, "file1"); + var remotePathSubFile2 = CombinePaths(remoteSubDirectory1, "file2"); + var absoluteremoteSubDirectory2 = CombinePaths(absoluteRemoteDirectory, "sub2"); + var remoteSubDirectory2 = CombinePaths(remoteDirectory, "sub2"); + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + if (client.Exists(remotePathSubFile1)) + { + client.DeleteFile(remotePathSubFile1); + } + if (client.Exists(remotePathSubFile2)) + { + client.DeleteFile(remotePathSubFile2); + } + if (client.Exists(remoteSubDirectory1)) + { + client.DeleteDirectory(remoteSubDirectory1); + } + if (client.Exists(remoteSubDirectory2)) + { + client.DeleteDirectory(remoteSubDirectory2); + } + if (client.Exists(remotePathFile1)) + { + client.DeleteFile(remotePathFile1); + } + if (client.Exists(remotePathFile2)) + { + client.DeleteFile(remotePathFile2); + } + + if (remoteDirectory.Length > 0 && remoteDirectory != "." && remoteDirectory != client.WorkingDirectory) + { + if (client.Exists(remoteDirectory)) + { + client.DeleteDirectory(remoteDirectory); + } + + client.CreateDirectory(remoteDirectory); + } + } + + var localDirectory = Path.GetTempFileName(); + File.Delete(localDirectory); + Directory.CreateDirectory(localDirectory); + + var localPathFile1 = Path.Combine(localDirectory, "file1"); + var localPathFile2 = Path.Combine(localDirectory, "file2"); + + var localSubDirectory1 = Path.Combine(localDirectory, "sub1"); + var localPathSubFile1 = Path.Combine(localSubDirectory1, "file1"); + var localPathSubFile2 = Path.Combine(localSubDirectory1, "file2"); + var localSubDirectory2 = Path.Combine(localDirectory, "sub2"); + + try + { + CreateFile(localPathFile1, 2000); + File.SetLastWriteTimeUtc(localPathFile1, new DateTime(2015, 8, 24, 5, 32, 16, DateTimeKind.Utc)); + + CreateFile(localPathFile2, 1000); + File.SetLastWriteTimeUtc(localPathFile2, new DateTime(2012, 3, 27, 23, 2, 54, DateTimeKind.Utc)); + + // create subdirectory with two files + Directory.CreateDirectory(localSubDirectory1); + CreateFile(localPathSubFile1, 1000); + File.SetLastWriteTimeUtc(localPathSubFile1, new DateTime(2013, 4, 12, 16, 54, 22, DateTimeKind.Utc)); + CreateFile(localPathSubFile2, 2000); + File.SetLastWriteTimeUtc(localPathSubFile2, new DateTime(2015, 8, 4, 12, 43, 12, DateTimeKind.Utc)); + Directory.SetLastWriteTimeUtc(localSubDirectory1, + new DateTime(2014, 6, 12, 13, 2, 44, DateTimeKind.Utc)); + + // create empty subdirectory + Directory.CreateDirectory(localSubDirectory2); + Directory.SetLastWriteTimeUtc(localSubDirectory2, + new DateTime(2011, 5, 14, 1, 5, 12, DateTimeKind.Utc)); + + Directory.SetLastWriteTimeUtc(localDirectory, new DateTime(2015, 10, 14, 22, 45, 11, DateTimeKind.Utc)); + + using (var client = new ScpClient(_connectionInfoFactory.Create())) + { + if (remotePathTransformation != null) + { + client.RemotePathTransformation = remotePathTransformation; + } + + client.Connect(); + client.Upload(new DirectoryInfo(localDirectory), remoteDirectory); + } + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + Assert.IsTrue(client.Exists(remoteDirectory)); + + var remoteSftpDirectory = client.Get(remoteDirectory); + Assert.IsNotNull(remoteSftpDirectory); + Assert.AreEqual(absoluteRemoteDirectory, remoteSftpDirectory.FullName); + Assert.IsTrue(remoteSftpDirectory.IsDirectory); + Assert.IsFalse(remoteSftpDirectory.IsRegularFile); + + // Due to CVE-2018-20685, we can no longer set the times or modes on a file or directory + // that refers to the current directory ('.'), the parent directory ('..') or a directory + // containing a forward slash ('/'). + Assert.AreNotEqual(Directory.GetLastWriteTimeUtc(localDirectory), remoteSftpDirectory.LastWriteTimeUtc); + + Assert.IsTrue(client.Exists(remotePathFile1)); + Assert.AreEqual(CreateFileHash(localPathFile1), CreateRemoteFileHash(client, remotePathFile1)); + var remoteSftpFile = client.Get(remotePathFile1); + Assert.IsNotNull(remoteSftpFile); + Assert.IsFalse(remoteSftpFile.IsDirectory); + Assert.IsTrue(remoteSftpFile.IsRegularFile); + Assert.AreEqual(File.GetLastWriteTimeUtc(localPathFile1), remoteSftpFile.LastWriteTimeUtc); + + Assert.IsTrue(client.Exists(remotePathFile2)); + Assert.AreEqual(CreateFileHash(localPathFile2), CreateRemoteFileHash(client, remotePathFile2)); + remoteSftpFile = client.Get(remotePathFile2); + Assert.IsNotNull(remoteSftpFile); + Assert.IsFalse(remoteSftpFile.IsDirectory); + Assert.IsTrue(remoteSftpFile.IsRegularFile); + Assert.AreEqual(File.GetLastWriteTimeUtc(localPathFile2), remoteSftpFile.LastWriteTimeUtc); + + Assert.IsTrue(client.Exists(remoteSubDirectory1)); + remoteSftpDirectory = client.Get(remoteSubDirectory1); + Assert.IsNotNull(remoteSftpDirectory); + Assert.AreEqual(absoluteremoteSubDirectory1, remoteSftpDirectory.FullName); + Assert.IsTrue(remoteSftpDirectory.IsDirectory); + Assert.IsFalse(remoteSftpDirectory.IsRegularFile); + Assert.AreEqual(Directory.GetLastWriteTimeUtc(localSubDirectory1), remoteSftpDirectory.LastWriteTimeUtc); + + Assert.IsTrue(client.Exists(remotePathSubFile1)); + Assert.AreEqual(CreateFileHash(localPathSubFile1), CreateRemoteFileHash(client, remotePathSubFile1)); + + Assert.IsTrue(client.Exists(remotePathSubFile2)); + Assert.AreEqual(CreateFileHash(localPathSubFile2), CreateRemoteFileHash(client, remotePathSubFile2)); + + Assert.IsTrue(client.Exists(remoteSubDirectory2)); + remoteSftpDirectory = client.Get(remoteSubDirectory2); + Assert.IsNotNull(remoteSftpDirectory); + Assert.AreEqual(absoluteremoteSubDirectory2, remoteSftpDirectory.FullName); + Assert.IsTrue(remoteSftpDirectory.IsDirectory); + Assert.IsFalse(remoteSftpDirectory.IsRegularFile); + Assert.AreEqual(Directory.GetLastWriteTimeUtc(localSubDirectory2), remoteSftpDirectory.LastWriteTimeUtc); + } + } + finally + { + Directory.Delete(localDirectory, true); + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + if (client.Exists(remotePathSubFile1)) + { + client.DeleteFile(remotePathSubFile1); + } + if (client.Exists(remotePathSubFile2)) + { + client.DeleteFile(remotePathSubFile2); + } + if (client.Exists(remoteSubDirectory1)) + { + client.DeleteDirectory(remoteSubDirectory1); + } + if (client.Exists(remoteSubDirectory2)) + { + client.DeleteDirectory(remoteSubDirectory2); + } + if (client.Exists(remotePathFile1)) + { + client.DeleteFile(remotePathFile1); + } + if (client.Exists(remotePathFile2)) + { + client.DeleteFile(remotePathFile2); + } + + if (remoteDirectory.Length > 0 && remoteDirectory != "." && remoteDirectory != client.WorkingDirectory) + { + if (client.Exists(remoteDirectory)) + { + client.DeleteDirectory(remoteDirectory); + } + } + } + } + } + +#if FEATURE_MSTEST_DATATEST + [DataTestMethod] + [DynamicData(nameof(GetScpUploadDirectoryInfoExistingFileData), DynamicDataSourceType.Method)] +#else + [TestMethod] + public void Scp_Upload_DirectoryInfo_ExistingFile() + { + foreach (var data in GetScpUploadDirectoryInfoExistingFileData()) + { + Scp_Upload_DirectoryInfo_ExistingFile((IRemotePathTransformation)data[0], + (string)data[1]); + } + } +#endif + public void Scp_Upload_DirectoryInfo_ExistingFile(IRemotePathTransformation remotePathTransformation, + string remoteDirectory) + { + var remotePathFile1 = CombinePaths(remoteDirectory, "file1"); + var remotePathFile2 = CombinePaths(remoteDirectory, "file2"); + + using (var client = new SshClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + Console.WriteLine(client.ConnectionInfo.CurrentKeyExchangeAlgorithm); + + using (var command = client.CreateCommand("rm -Rf " + _remotePathTransformation.Transform(remoteDirectory))) + { + command.Execute(); + } + } + + var localDirectory = Path.GetTempFileName(); + File.Delete(localDirectory); + Directory.CreateDirectory(localDirectory); + + var localPathFile1 = Path.Combine(localDirectory, "file1"); + var localPathFile2 = Path.Combine(localDirectory, "file2"); + + try + { + CreateFile(localPathFile1, 50); + CreateFile(localPathFile2, 50); + + using (var client = new ScpClient(_connectionInfoFactory.Create())) + { + if (remotePathTransformation != null) + { + client.RemotePathTransformation = remotePathTransformation; + } + + client.Connect(); + + CreateRemoteFile(client, remoteDirectory, 10); + + try + { + client.Upload(new DirectoryInfo(localDirectory), remoteDirectory); + Assert.Fail(); + } + catch (ScpException ex) + { + Assert.IsNull(ex.InnerException); + Assert.AreEqual($"scp: {remoteDirectory}: Not a directory", ex.Message); + } + } + } + finally + { + Directory.Delete(localDirectory, true); + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + if (client.Exists(remotePathFile1)) + { + client.DeleteFile(remotePathFile1); + } + if (client.Exists(remotePathFile2)) + { + client.DeleteFile(remotePathFile2); + } + if (client.Exists((remoteDirectory))) + { + client.DeleteFile(remoteDirectory); + } + } + } + } + + private static IEnumerable GetScpDownloadStreamDirectoryDoesNotExistData() + { + yield return new object[] { RemotePathTransformation.None, "/home/sshnet/scp-directorydoesnotexist", "scp-file" }; + yield return new object[] { RemotePathTransformation.None, "/home/sshnet/scp-directorydoesnotexist", "scp-file" }; + } + + private static IEnumerable GetScpUploadFileInfoFileDoesNotExistData() + { + yield return new object[] { RemotePathTransformation.None, "/home/sshnet", "test123", 0 }; + yield return new object[] { RemotePathTransformation.None, "/home/sshnet", "test123", 5 * 1024 * 1024 }; + yield return new object[] { RemotePathTransformation.ShellQuote, "/home/sshnet/dir|&;<>()$`\"'sp\u0100ce \\tab\tlf\n*?[#~=%", "file123", 1024 }; + yield return new object[] { null, "/home/sshnet/scp test", "file 123", 1024 }; + yield return new object[] { RemotePathTransformation.None, "/home/sshnet/scp-test", "file|&;<>()$`\"'sp\u0100ce \\tab\tlf*?[#~=%", 1024 }; + yield return new object[] { null, "", "scp-issue280", 1024 }; + } + + private static IEnumerable GetScpUploadFileStreamFileDoesNotExistData() + { + yield return new object[] { RemotePathTransformation.ShellQuote, "/home/sshnet/dir|&;<>()$`\"'sp\u0100ce \\tab\tlf\n*?[#~=%", "file123", 0 }; + yield return new object[] { RemotePathTransformation.ShellQuote, "/home/sshnet/dir|&;<>()$`\"'sp\u0100ce \\tab\tlf\n*?[#~=%", "file123", 1024 }; + yield return new object[] { null, "/home/sshnet/scp test", "file 123", 1024 }; + yield return new object[] { RemotePathTransformation.ShellQuote, "/home/sshnet/scp-test", "file|&;<>()$`\"'sp\u0100ce \\tab\tlf*?[#~=%", 1024 }; + yield return new object[] { RemotePathTransformation.None, "", "scp-issue280", 1024 }; + } + + private static IEnumerable GetScpUploadDirectoryInfoExistingDirectoryData() + { + yield return new object[] { RemotePathTransformation.None, "scp-directorydoesnotexist" }; + yield return new object[] { RemotePathTransformation.None, "." }; + yield return new object[] { RemotePathTransformation.ShellQuote, "/home/sshnet/dir|&;<>()$`\"'sp\u0100ce \\tab\tlf*?[#~=%" }; + } + + private static IEnumerable GetScpUploadDirectoryInfoExistingFileData() + { + yield return new object[] { RemotePathTransformation.None, "scp-upload-file" }; + } + + private static IEnumerable ScpUploadFileStreamExistingFileData() + { + yield return new object[] { RemotePathTransformation.None, "/home/sshnet/scp-upload-file" }; + } + + private static IEnumerable GetScpDownloadStreamFileDoesNotExistData() + { + yield return new object[] { RemotePathTransformation.None, "/home/sshnet", "scp-filedoesnotexist" }; + } + + private static IEnumerable GetScpDownloadDirectoryInfoDirectoryDoesNotExistData() + { + yield return new object[] { RemotePathTransformation.None, "/home/sshnet/scp-download" }; + } + + private static IEnumerable GetScpDownloadDirectoryInfoExistingFileData() + { + yield return new object[] { RemotePathTransformation.None, "scp-download" }; + } + + private static IEnumerable GetScpDownloadDirectoryInfoExistingDirectoryData() + { + yield return new object[] { RemotePathTransformation.None, "scp-download" }; + yield return new object[] { RemotePathTransformation.ShellQuote, "/home/sshnet/dir|&;<>()$`\"'space \\tab\tlf*?[#~=%" }; + } + + private static IEnumerable GetScpDownloadFileInfoDirectoryDoesNotExistData() + { + yield return new object[] { RemotePathTransformation.None, "/home/sshnet/scp-directorydoesnotexist", "scp-file" }; + } + + private static IEnumerable GetScpDownloadFileInfoFileDoesNotExistData() + { + yield return new object[] { RemotePathTransformation.None, "/home/sshnet", "scp-filedoesnotexist" }; + } + + private static IEnumerable GetScpDownloadFileInfoExistingDirectoryData() + { + yield return new object[] { RemotePathTransformation.None, "/home/sshnet/scp-test" }; + } + + private static IEnumerable GetScpDownloadFileInfoExistingFileData() + { + yield return new object[] { null, "", "file 123", 0 }; + yield return new object[] { null, "", "file 123", 1024 }; + yield return new object[] { RemotePathTransformation.ShellQuote, "", "file|&;<>()$`\"'sp\u0100ce \\tab\tlf*?[#~=%", 1024 }; + yield return new object[] { null, "/home/sshnet/scp test", "file 123", 1024 }; + yield return new object[] { RemotePathTransformation.ShellQuote, "/home/sshnet/dir|&;<>()$`\"'sp\u0100ce \\tab\tlf\n*?[#~=%", "file123", 1024 }; + yield return new object[] { RemotePathTransformation.ShellQuote, "/home/sshnet/scp-test", "file|&;<>()$`\"'sp\u0100ce \\tab\tlf*?[#~=%", 1024 }; + } + + private static IEnumerable GetScpDownloadStreamExistingDirectoryData() + { + yield return new object[] { RemotePathTransformation.None, "/home/sshnet/scp-test" }; + } + + private static IEnumerable GetScpDownloadStreamExistingFileData() + { + yield return new object[] { null, "", "file 123", 0 }; + yield return new object[] { null, "", "file 123", 1024 }; + yield return new object[] { RemotePathTransformation.ShellQuote, "", "file|&;<>()$`\"'sp\u0100ce \\tab\tlf*?[#~=%", 1024 }; + yield return new object[] { null, "/home/sshnet/scp test", "file 123", 1024 }; + yield return new object[] { RemotePathTransformation.ShellQuote, "/home/sshnet/dir|&;<>()$`\"'sp\u0100ce \\tab\tlf\n*?[#~=%", "file123", 1024 }; + yield return new object[] { RemotePathTransformation.ShellQuote, "/home/sshnet/scp-test", "file|&;<>()$`\"'sp\u0100ce \\tab\tlf*?[#~=%", 1024 }; + } + + private static IEnumerable GetScpUploadFileStreamDirectoryDoesNotExistData() + { + yield return new object[] { RemotePathTransformation.None, "/home/sshnet/scp-issue289", "file123" }; + } + + private static IEnumerable GetScpUploadFileStreamExistingDirectoryData() + { + yield return new object[] { RemotePathTransformation.None, "/home/sshnet/scp-issue286" }; + } + + private static IEnumerable GetScpUploadFileInfoDirectoryDoesNotExistData() + { + yield return new object[] { RemotePathTransformation.None, "/home/sshnet/scp-issue289", "file123" }; + } + + private static IEnumerable GetScpUploadFileInfoExistingDirectoryData() + { + yield return new object[] { RemotePathTransformation.None, "/home/sshnet/scp-issue286" }; + } + + private static IEnumerable GetScpUploadFileInfoExistingFileData() + { + yield return new object[] { RemotePathTransformation.None, "/home/sshnet/scp-upload-file" }; + } + + private static IEnumerable GetScpUploadDirectoryInfoDirectoryDoesNotExistData() + { + yield return new object[] { RemotePathTransformation.None, "scp-directorydoesnotexist" }; + } + + private static void CreateRemoteFile(ScpClient client, string remoteFile, int size) + { + var file = CreateTempFile(size); + + try + { + using (var fs = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read)) + { + client.Upload(fs, remoteFile); + } + } + finally + { + File.Delete(file); + } + } + + private static string GetAbsoluteRemotePath(SftpClient client, string directoryName, string fileName) + { + var absolutePath = string.Empty; + + if (directoryName.Length == 0) + { + absolutePath += client.WorkingDirectory; + } + else + { + if (directoryName[0] != '/') + { + absolutePath += client.WorkingDirectory + "/" + directoryName; + } + else + { + absolutePath = directoryName; + } + } + + return absolutePath + "/" + fileName; + } + + private static string GetAbsoluteRemotePath(IConnectionInfoFactory connectionInfoFactory, string directoryName) + { + var absolutePath = string.Empty; + + if (directoryName.Length == 0 || directoryName == ".") + { + using (var client = new SftpClient(connectionInfoFactory.Create())) + { + client.Connect(); + + absolutePath += client.WorkingDirectory; + } + } + else + { + if (directoryName[0] != '/') + { + using (var client = new SftpClient(connectionInfoFactory.Create())) + { + client.Connect(); + + absolutePath += client.WorkingDirectory + "/" + directoryName; + } + } + else + { + absolutePath = directoryName; + } + } + + return absolutePath; + } + + private static string CreateRemoteFileHash(SftpClient client, string remotePath) + { + using (var fs = client.OpenRead(remotePath)) + { + return CreateHash(fs); + } + } + + private static string CombinePaths(string path1, string path2) + { + if (path1.Length == 0) + { + return path2; + } + + if (path2.Length == 0) + { + return path1; + } + + return path1 + "/" + path2; + } + } +} diff --git a/src/Renci.SshNet.IntegrationTests/SftpClientTests.cs b/src/Renci.SshNet.IntegrationTests/SftpClientTests.cs new file mode 100644 index 00000000..ee0258cd --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/SftpClientTests.cs @@ -0,0 +1,102 @@ +using Renci.SshNet.Common; + +namespace Renci.SshNet.IntegrationTests +{ + /// + /// The SFTP client integration tests + /// + [TestClass] + public class SftpClientTests : IntegrationTestBase, IDisposable + { + private readonly SftpClient _sftpClient; + + public SftpClientTests() + { + _sftpClient = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password); + _sftpClient.Connect(); + } + + [TestMethod] + public void Create_directory_with_contents_and_list_it() + { + var testDirectory = "/home/sshnet/sshnet-test"; + var testFileName = "test-file.txt"; + var testFilePath = $"{testDirectory}/{testFileName}"; + var testContent = "file content"; + + // Create new directory and check if it exists + _sftpClient.CreateDirectory(testDirectory); + Assert.IsTrue(_sftpClient.Exists(testDirectory)); + + // Upload file and check if it exists + using var fileStream = new MemoryStream(Encoding.UTF8.GetBytes(testContent)); + _sftpClient.UploadFile(fileStream, testFilePath); + Assert.IsTrue(_sftpClient.Exists(testFilePath)); + + // Check if ListDirectory works + var files = _sftpClient.ListDirectory(testDirectory); + + _sftpClient.DeleteFile(testFilePath); + _sftpClient.DeleteDirectory(testDirectory); + + var builder = new StringBuilder(); + foreach (var file in files) + { + builder.AppendLine($"{file.FullName} {file.IsRegularFile} {file.IsDirectory}"); + } + + Assert.AreEqual(@"/home/sshnet/sshnet-test/. False True +/home/sshnet/sshnet-test/.. False True +/home/sshnet/sshnet-test/test-file.txt True False +", builder.ToString()); + } + + [TestMethod] + public async Task Create_directory_with_contents_and_list_it_async() + { + var testDirectory = "/home/sshnet/sshnet-test"; + var testFileName = "test-file.txt"; + var testFilePath = $"{testDirectory}/{testFileName}"; + var testContent = "file content"; + + // Create new directory and check if it exists + _sftpClient.CreateDirectory(testDirectory); + Assert.IsTrue(_sftpClient.Exists(testDirectory)); + + // Upload file and check if it exists + using var fileStream = new MemoryStream(Encoding.UTF8.GetBytes(testContent)); + _sftpClient.UploadFile(fileStream, testFilePath); + Assert.IsTrue(_sftpClient.Exists(testFilePath)); + + // Check if ListDirectory works + var files = _sftpClient.ListDirectoryAsync(testDirectory, CancellationToken.None); + + var builder = new StringBuilder(); + await foreach (var file in files) + { + builder.AppendLine($"{file.FullName} {file.IsRegularFile} {file.IsDirectory}"); + } + + _sftpClient.DeleteFile(testFilePath); + _sftpClient.DeleteDirectory(testDirectory); + + Assert.AreEqual(@"/home/sshnet/sshnet-test/. False True +/home/sshnet/sshnet-test/.. False True +/home/sshnet/sshnet-test/test-file.txt True False +", builder.ToString()); + } + + [TestMethod] + [ExpectedException(typeof(SftpPermissionDeniedException), "Permission denied")] + public void Test_Sftp_ListDirectory_Permission_Denied() + { + _sftpClient.ListDirectory("/root"); + } + + public void Dispose() + { + _sftpClient.Disconnect(); + _sftpClient.Dispose(); + } + } +} diff --git a/src/Renci.SshNet.IntegrationTests/SftpTests.cs b/src/Renci.SshNet.IntegrationTests/SftpTests.cs new file mode 100644 index 00000000..7fb18c70 --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/SftpTests.cs @@ -0,0 +1,6360 @@ +using System.Diagnostics; + +using Renci.SshNet.Common; +using Renci.SshNet.IntegrationTests.Common; +using Renci.SshNet.Sftp; + +namespace Renci.SshNet.IntegrationTests +{ + // TODO: DeleteDirectory (fail + success + // TODO: DeleteFile (fail + success + // TODO: Delete (fail + success + + [TestClass] + public class SftpTests : TestBase + { + private IConnectionInfoFactory _connectionInfoFactory; + private IConnectionInfoFactory _adminConnectionInfoFactory; + private IRemotePathTransformation _remotePathTransformation; + + [TestInitialize] + public void SetUp() + { + _connectionInfoFactory = new LinuxVMConnectionFactory(SshServerHostName, SshServerPort); + _adminConnectionInfoFactory = new LinuxAdminConnectionFactory(SshServerHostName, SshServerPort); + _remotePathTransformation = RemotePathTransformation.ShellQuote; + } + +#if FEATURE_MSTEST_DATATEST + [DataTestMethod] + [DynamicData(nameof(GetSftpUploadFileFileStreamData), DynamicDataSourceType.Method)] +#else + [TestMethod] + public void Sftp_Upload_DirectoryInfo_ExistingFile() + { + foreach (var data in GetSftpUploadFileFileStreamData()) + { + Sftp_UploadFile_FileStream((int) data[0]); + } + } +#endif + public void Sftp_UploadFile_FileStream(int size) + { + var file = CreateTempFile(size); + + using (var fs = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read)) + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.Delete(remoteFile); + } + + try + { + client.UploadFile(fs, remoteFile); + + using (var memoryStream = new MemoryStream(size)) + { + client.DownloadFile(remoteFile, memoryStream); + memoryStream.Position = 0; + Assert.AreEqual(CreateFileHash(file), CreateHash(memoryStream)); + } + } + finally + { + if (client.Exists(remoteFile)) + { + client.Delete(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_ConnectDisconnect_Serial() + { + const int iterations = 100; + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + for (var i = 1; i <= iterations; i++) + { + client.Connect(); + client.Disconnect(); + } + } + } + + [TestMethod] + public void Sftp_ConnectDisconnect_Parallel() + { + const int iterations = 10; + const int threads = 20; + + var startEvent = new ManualResetEvent(false); + + var tasks = Enumerable.Range(1, threads).Select(i => + { + return Task.Factory.StartNew(() => + { + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + startEvent.WaitOne(); + + for (var j = 0; j < iterations; j++) + { + client.Connect(); + client.Disconnect(); + } + } + }); + }).ToArray(); + + startEvent.Set(); + + Task.WaitAll(tasks); + } + + [TestMethod] + public void Sftp_BeginUploadFile() + { + const string content = "SftpBeginUploadFile"; + + var expectedByteCount = (ulong) Encoding.ASCII.GetByteCount(content); + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.Delete(remoteFile); + } + + try + { + using (var memoryStream = new MemoryStream(Encoding.ASCII.GetBytes(content))) + { + IAsyncResult asyncResultCallback = null; + + var asyncResult = client.BeginUploadFile(memoryStream, remoteFile, ar => asyncResultCallback = ar); + + Assert.IsTrue(asyncResult.AsyncWaitHandle.WaitOne(10000)); + + // check async result + var sftpUploadAsyncResult = asyncResult as SftpUploadAsyncResult; + Assert.IsNotNull(sftpUploadAsyncResult); + Assert.IsFalse(sftpUploadAsyncResult.IsUploadCanceled); + Assert.IsTrue(sftpUploadAsyncResult.IsCompleted); + Assert.IsFalse(sftpUploadAsyncResult.CompletedSynchronously); + Assert.AreEqual(expectedByteCount, sftpUploadAsyncResult.UploadedBytes); + + // check async result callback + var sftpUploadAsyncResultCallback = asyncResultCallback as SftpUploadAsyncResult; + Assert.IsNotNull(sftpUploadAsyncResultCallback); + Assert.IsFalse(sftpUploadAsyncResultCallback.IsUploadCanceled); + Assert.IsTrue(sftpUploadAsyncResultCallback.IsCompleted); + Assert.IsFalse(sftpUploadAsyncResultCallback.CompletedSynchronously); + Assert.AreEqual(expectedByteCount, sftpUploadAsyncResultCallback.UploadedBytes); + } + + // check uploaded file + using (var memoryStream = new MemoryStream()) + { + client.DownloadFile(remoteFile, memoryStream); + memoryStream.Position = 0; + var remoteContent = Encoding.ASCII.GetString(memoryStream.ToArray()); + Assert.AreEqual(content, remoteContent); + } + } + finally + { + if (client.Exists(remoteFile)) + { + client.Delete(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_Create_ExistingFile() + { + var encoding = Encoding.UTF8; + var initialContent = "Gert & Ann & Lisa"; + var newContent1 = "Sofie"; + var newContent1Bytes = GetBytesWithPreamble(newContent1, encoding); + var newContent2 = "Lisa & Sofie"; + var newContent2Bytes = GetBytesWithPreamble(newContent2, encoding); + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + client.WriteAllText(remoteFile, initialContent); + + #region Write less bytes than the current content, overwriting part of that content + + using (var fs = client.Create(remoteFile)) + using (var sw = new StreamWriter(fs, encoding)) + { + sw.Write(newContent1); + } + + var actualContent1 = client.ReadAllBytes(remoteFile); + Assert.IsTrue(newContent1Bytes.IsEqualTo(actualContent1)); + + #endregion Write less bytes than the current content, overwriting part of that content + + #region Write more bytes than the current content, overwriting and appending to that content + + using (var fs = client.Create(remoteFile)) + using (var sw = new StreamWriter(fs, encoding)) + { + sw.Write(newContent2); + } + + var actualContent2 = client.ReadAllBytes(remoteFile); + Assert.IsTrue(newContent2Bytes.IsEqualTo(actualContent2)); + + #endregion Write more bytes than the current content, overwriting and appending to that content + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_Create_DirectoryDoesNotExist() + { + const string remoteFile = "/home/sshnet/directorydoesnotexist/test"; + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + SftpFileStream fs = null; + + try + { + fs = client.Create(remoteFile); + Assert.Fail(); + } + catch (SftpPathNotFoundException ex) + { + Assert.IsNull(ex.InnerException); + Assert.AreEqual("No such file", ex.Message); + } + finally + { + fs?.Dispose(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_Create_FileDoesNotExist() + { + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.BufferSize = 512 * 1024; + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + using (var imageStream = GetResourceStream("Renci.SshNet.IntegrationTests.resources.issue #70.png")) + { + using (var fs = client.Create(remoteFile)) + { + byte[] buffer = new byte[Math.Min(client.BufferSize, imageStream.Length)]; + int bytesRead; + + while ((bytesRead = imageStream.Read(buffer, offset: 0, buffer.Length)) > 0) + { + fs.Write(buffer, offset: 0, bytesRead); + } + } + + using (var memoryStream = new MemoryStream()) + { + client.DownloadFile(remoteFile, memoryStream); + + memoryStream.Position = 0; + imageStream.Position = 0; + + Assert.AreEqual(CreateHash(imageStream), CreateHash(memoryStream)); + } + } + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_AppendAllLines_NoEncoding_ExistingFile() + { + var initialContent = "\u0100ert & Ann"; + IEnumerable linesToAppend = new[] { "Forever", "&", "\u0116ver" }; + var expectedContent = initialContent + string.Join(Environment.NewLine, linesToAppend) + + Environment.NewLine; + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + client.WriteAllText(remoteFile, initialContent); + client.AppendAllLines(remoteFile, linesToAppend); + + var text = client.ReadAllText(remoteFile); + Assert.AreEqual(expectedContent, text); + + // ensure we didn't write an UTF-8 BOM + using (var fs = client.OpenRead(remoteFile)) + { + var firstByte = fs.ReadByte(); + Assert.AreEqual(Encoding.UTF8.GetBytes(expectedContent)[0], firstByte); + } + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_AppendAllLines_NoEncoding_DirectoryDoesNotExist() + { + const string remoteFile = "/home/sshnet/directorydoesnotexist/test"; + + IEnumerable linesToAppend = new[] { "\u0139isa", "&", "Sofie" }; + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + client.AppendAllLines(remoteFile, linesToAppend); + Assert.Fail(); + } + catch (SftpPathNotFoundException ex) + { + Assert.IsNull(ex.InnerException); + Assert.AreEqual("No such file", ex.Message); + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_AppendAllLines_NoEncoding_FileDoesNotExist() + { + IEnumerable linesToAppend = new[] { "\u0139isa", "&", "Sofie" }; + var expectedContent = string.Join(Environment.NewLine, linesToAppend) + Environment.NewLine; + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + client.AppendAllLines(remoteFile, linesToAppend); + + var text = client.ReadAllText(remoteFile); + Assert.AreEqual(expectedContent, text); + + // ensure we didn't write an UTF-8 BOM + using (var fs = client.OpenRead(remoteFile)) + { + var firstByte = fs.ReadByte(); + Assert.AreEqual(Encoding.UTF8.GetBytes(expectedContent)[0], firstByte); + } + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_AppendAllText_NoEncoding_ExistingFile() + { + var initialContent = "\u0100ert & Ann"; + var contentToAppend = "Forever&\u0116ver"; + var expectedContent = initialContent + contentToAppend; + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + client.WriteAllText(remoteFile, initialContent); + client.AppendAllText(remoteFile, contentToAppend); + + var text = client.ReadAllText(remoteFile); + Assert.AreEqual(expectedContent, text); + + // ensure we didn't write an UTF-8 BOM + using (var fs = client.OpenRead(remoteFile)) + { + var firstByte = fs.ReadByte(); + Assert.AreEqual(Encoding.UTF8.GetBytes(expectedContent)[0], firstByte); + } + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_AppendAllText_NoEncoding_DirectoryDoesNotExist() + { + const string remoteFile = "/home/sshnet/directorydoesnotexist/test"; + + var contentToAppend = "Forever&\u0116ver"; + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + client.AppendAllText(remoteFile, contentToAppend); + Assert.Fail(); + } + catch (SftpPathNotFoundException ex) + { + Assert.IsNull(ex.InnerException); + Assert.AreEqual("No such file", ex.Message); + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_AppendAllText_NoEncoding_FileDoesNotExist() + { + var contentToAppend = "Forever&\u0116ver"; + var expectedContent = contentToAppend; + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + client.AppendAllText(remoteFile, contentToAppend); + + var text = client.ReadAllText(remoteFile); + Assert.AreEqual(expectedContent, text); + + // ensure we didn't write an UTF-8 BOM + using (var fs = client.OpenRead(remoteFile)) + { + var firstByte = fs.ReadByte(); + Assert.AreEqual(Encoding.UTF8.GetBytes(expectedContent)[0], firstByte); + } + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_AppendText_NoEncoding_ExistingFile() + { + var initialContent = "\u0100ert & Ann"; + var contentToAppend = "Forever&\u0116ver"; + var expectedContent = initialContent + contentToAppend; + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + client.WriteAllText(remoteFile, initialContent); + + using (var sw = client.AppendText(remoteFile)) + { + sw.Write(contentToAppend); + } + + var text = client.ReadAllText(remoteFile); + Assert.AreEqual(expectedContent, text); + + // ensure we didn't write an UTF-8 BOM + using (var fs = client.OpenRead(remoteFile)) + { + var firstByte = fs.ReadByte(); + Assert.AreEqual(Encoding.UTF8.GetBytes(expectedContent)[0], firstByte); + } + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_AppendText_NoEncoding_DirectoryDoesNotExist() + { + const string remoteFile = "/home/sshnet/directorydoesnotexist/test"; + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + StreamWriter sw = null; + + try + { + sw = client.AppendText(remoteFile); + Assert.Fail(); + } + catch (SftpPathNotFoundException ex) + { + Assert.IsNull(ex.InnerException); + Assert.AreEqual("No such file", ex.Message); + } + finally + { + sw?.Dispose(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_AppendText_NoEncoding_FileDoesNotExist() + { + var contentToAppend = "\u0100ert & Ann"; + var expectedContent = contentToAppend; + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + using (var sw = client.AppendText(remoteFile)) + { + sw.Write(contentToAppend); + } + + // ensure we didn't write an UTF-8 BOM + using (var fs = client.OpenRead(remoteFile)) + { + var firstByte = fs.ReadByte(); + Assert.AreEqual(Encoding.UTF8.GetBytes(expectedContent)[0], firstByte); + } + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_AppendAllLines_Encoding_ExistingFile() + { + var initialContent = "\u0100ert & Ann"; + IEnumerable linesToAppend = new[] { "Forever", "&", "\u0116ver" }; + var expectedContent = initialContent + string.Join(Environment.NewLine, linesToAppend) + + Environment.NewLine; + var encoding = GetRandomEncoding(); + var expectedBytes = GetBytesWithPreamble(expectedContent, encoding); + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + client.WriteAllText(remoteFile, initialContent, encoding); + client.AppendAllLines(remoteFile, linesToAppend, encoding); + + var text = client.ReadAllText(remoteFile, encoding); + Assert.AreEqual(expectedContent, text); + + using (var fs = client.OpenRead(remoteFile)) + { + var actualBytes = new byte[fs.Length]; + fs.Read(actualBytes, offset: 0, actualBytes.Length); + Assert.IsTrue(expectedBytes.IsEqualTo(actualBytes)); + } + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_AppendAllLines_Encoding_DirectoryDoesNotExist() + { + const string remoteFile = "/home/sshnet/directorydoesnotexist/test"; + + IEnumerable linesToAppend = new[] { "Forever", "&", "\u0116ver" }; + var encoding = GetRandomEncoding(); + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + client.AppendAllLines(remoteFile, linesToAppend, encoding); + Assert.Fail(); + } + catch (SftpPathNotFoundException ex) + { + Assert.IsNull(ex.InnerException); + Assert.AreEqual("No such file", ex.Message); + } + + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_AppendAllLines_Encoding_FileDoesNotExist() + { + IEnumerable linesToAppend = new[] { "\u0139isa", "&", "Sofie" }; + var expectedContent = string.Join(Environment.NewLine, linesToAppend) + Environment.NewLine; + var encoding = GetRandomEncoding(); + var expectedBytes = GetBytesWithPreamble(expectedContent, encoding); + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + client.AppendAllLines(remoteFile, linesToAppend, encoding); + + var text = client.ReadAllText(remoteFile, encoding); + Assert.AreEqual(expectedContent, text); + + using (var fs = client.OpenRead(remoteFile)) + { + var actualBytes = new byte[fs.Length]; + fs.Read(actualBytes, offset: 0, actualBytes.Length); + Assert.IsTrue(expectedBytes.IsEqualTo(actualBytes)); + } + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_AppendAllText_Encoding_ExistingFile() + { + var initialContent = "\u0100ert & Ann"; + var contentToAppend = "Forever&\u0116ver"; + var expectedContent = initialContent + contentToAppend; + var encoding = GetRandomEncoding(); + var expectedBytes = GetBytesWithPreamble(expectedContent, encoding); + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + client.WriteAllText(remoteFile, initialContent, encoding); + client.AppendAllText(remoteFile, contentToAppend, encoding); + + var text = client.ReadAllText(remoteFile, encoding); + Assert.AreEqual(expectedContent, text); + + using (var fs = client.OpenRead(remoteFile)) + { + var actualBytes = new byte[fs.Length]; + fs.Read(actualBytes, offset: 0, actualBytes.Length); + Assert.IsTrue(expectedBytes.IsEqualTo(actualBytes)); + } + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_AppendAllText_Encoding_DirectoryDoesNotExist() + { + const string remoteFile = "/home/sshnet/directorydoesnotexist/test"; + + const string contentToAppend = "Forever&\u0116ver"; + var encoding = GetRandomEncoding(); + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + client.AppendAllText(remoteFile, contentToAppend, encoding); + Assert.Fail(); + } + catch (SftpPathNotFoundException ex) + { + Assert.IsNull(ex.InnerException); + Assert.AreEqual("No such file", ex.Message); + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_AppendAllText_Encoding_FileDoesNotExist() + { + const string contentToAppend = "Forever&\u0116ver"; + var expectedContent = contentToAppend; + var encoding = GetRandomEncoding(); + var expectedBytes = GetBytesWithPreamble(expectedContent, encoding); + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + client.AppendAllText(remoteFile, contentToAppend, encoding); + + var text = client.ReadAllText(remoteFile, encoding); + Assert.AreEqual(expectedContent, text); + + using (var fs = client.OpenRead(remoteFile)) + { + var actualBytes = new byte[fs.Length]; + fs.Read(actualBytes, offset: 0, actualBytes.Length); + Assert.IsTrue(expectedBytes.IsEqualTo(actualBytes)); + } + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_AppendText_Encoding_ExistingFile() + { + const string initialContent = "\u0100ert & Ann"; + const string contentToAppend = "Forever&\u0116ver"; + var expectedContent = initialContent + contentToAppend; + var encoding = GetRandomEncoding(); + var expectedBytes = GetBytesWithPreamble(expectedContent, encoding); + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + client.WriteAllText(remoteFile, initialContent, encoding); + + using (var sw = client.AppendText(remoteFile, encoding)) + { + sw.Write(contentToAppend); + } + + var text = client.ReadAllText(remoteFile, encoding); + Assert.AreEqual(expectedContent, text); + + using (var fs = client.OpenRead(remoteFile)) + { + var actualBytes = new byte[fs.Length]; + fs.Read(actualBytes, offset: 0, actualBytes.Length); + Assert.IsTrue(expectedBytes.IsEqualTo(actualBytes)); + } + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + } + } + } + + [TestMethod] + public void Sftp_AppendText_Encoding_DirectoryDoesNotExist() + { + const string remoteFile = "/home/sshnet/directorydoesnotexist/test"; + + var encoding = GetRandomEncoding(); + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + StreamWriter sw = null; + + try + { + sw = client.AppendText(remoteFile, encoding); + Assert.Fail(); + } + catch (SftpPathNotFoundException ex) + { + Assert.IsNull(ex.InnerException); + Assert.AreEqual("No such file", ex.Message); + } + finally + { + sw?.Dispose(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_AppendText_Encoding_FileDoesNotExist() + { + var encoding = GetRandomEncoding(); + const string contentToAppend = "\u0100ert & Ann"; + var expectedBytes = GetBytesWithPreamble(contentToAppend, encoding); + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + using (var sw = client.AppendText(remoteFile, encoding)) + { + sw.Write(contentToAppend); + } + + using (var fs = client.OpenRead(remoteFile)) + { + var actualBytes = new byte[fs.Length]; + fs.Read(actualBytes, offset: 0, actualBytes.Length); + Assert.IsTrue(expectedBytes.IsEqualTo(actualBytes)); + } + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_CreateText_NoEncoding_ExistingFile() + { + var encoding = new UTF8Encoding(false, true); + const string initialContent = "\u0100ert & Ann"; + var initialContentBytes = GetBytesWithPreamble(initialContent, encoding); + const string newContent = "\u0116ver"; + const string expectedContent = "\u0116ver" + " & Ann"; + var expectedContentBytes = GetBytesWithPreamble(expectedContent, encoding); + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + client.WriteAllText(remoteFile, initialContent); + + using (client.CreateText(remoteFile)) + { + } + + // verify that original content is left untouched + using (var fs = client.OpenRead(remoteFile)) + { + var actualBytes = new byte[fs.Length]; + fs.Read(actualBytes, offset: 0, actualBytes.Length); + Assert.IsTrue(initialContentBytes.IsEqualTo(actualBytes)); + } + + // write content that is less bytes than original content + using (var sw = client.CreateText(remoteFile)) + { + sw.Write(newContent); + } + + // verify that original content is only partially overwritten + var text = client.ReadAllText(remoteFile); + Assert.AreEqual(expectedContent, text); + + using (var fs = client.OpenRead(remoteFile)) + { + var actualBytes = new byte[fs.Length]; + fs.Read(actualBytes, offset: 0, actualBytes.Length); + Assert.IsTrue(expectedContentBytes.IsEqualTo(actualBytes)); + } + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_CreateText_NoEncoding_DirectoryDoesNotExist() + { + const string remoteFile = "/home/sshnet/directorydoesnotexist/test"; + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + StreamWriter sw = null; + + try + { + sw = client.CreateText(remoteFile); + Assert.Fail(); + } + catch (SftpPathNotFoundException ex) + { + Assert.IsNull(ex.InnerException); + Assert.AreEqual("No such file", ex.Message); + } + finally + { + sw?.Dispose(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_CreateText_NoEncoding_FileDoesNotExist() + { + var encoding = new UTF8Encoding(false, true); + var initialContent = "\u0100ert & Ann"; + var initialContentBytes = GetBytesWithPreamble(initialContent, encoding); + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + using (client.CreateText(remoteFile)) + { + } + + // verify that empty file was created + Assert.IsTrue(client.Exists(remoteFile)); + + var file = client.GetAttributes(remoteFile); + Assert.AreEqual(0, file.Size); + + client.DeleteFile(remoteFile); + + using (var sw = client.CreateText(remoteFile)) + { + sw.Write(initialContent); + } + + // verify that content is written to file + var text = client.ReadAllText(remoteFile); + Assert.AreEqual(initialContent, text); + + using (var fs = client.OpenRead(remoteFile)) + { + var actualBytes = new byte[fs.Length]; + fs.Read(actualBytes, offset: 0, actualBytes.Length); + Assert.IsTrue(initialContentBytes.IsEqualTo(actualBytes)); + } + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_CreateText_Encoding_ExistingFile() + { + var encoding = GetRandomEncoding(); + var initialContent = "\u0100ert & Ann"; + var initialContentBytes = GetBytesWithPreamble(initialContent, encoding); + var newContent = "\u0116ver"; + var expectedContent = "\u0116ver" + " & Ann"; + var expectedContentBytes = GetBytesWithPreamble(expectedContent, encoding); + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + client.WriteAllText(remoteFile, initialContent, encoding); + + using (client.CreateText(remoteFile)) + { + } + + // verify that original content is left untouched + using (var fs = client.OpenRead(remoteFile)) + { + var actualBytes = new byte[fs.Length]; + fs.Read(actualBytes, offset: 0, actualBytes.Length); + Assert.IsTrue(initialContentBytes.IsEqualTo(actualBytes)); + } + + // write content that is less bytes than original content + using (var sw = client.CreateText(remoteFile, encoding)) + { + sw.Write(newContent); + } + + // verify that original content is only partially overwritten + var text = client.ReadAllText(remoteFile, encoding); + Assert.AreEqual(expectedContent, text); + + using (var fs = client.OpenRead(remoteFile)) + { + var actualBytes = new byte[fs.Length]; + fs.Read(actualBytes, offset: 0, actualBytes.Length); + Assert.IsTrue(expectedContentBytes.IsEqualTo(actualBytes)); + } + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_CreateText_Encoding_DirectoryDoesNotExist() + { + const string remoteFile = "/home/sshnet/directorydoesnotexist/test"; + + var encoding = GetRandomEncoding(); + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + StreamWriter sw = null; + + try + { + sw = client.CreateText(remoteFile, encoding); + Assert.Fail(); + } + catch (SftpPathNotFoundException ex) + { + Assert.IsNull(ex.InnerException); + Assert.AreEqual("No such file", ex.Message); + } + finally + { + sw?.Dispose(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_CreateText_Encoding_FileDoesNotExist() + { + var encoding = GetRandomEncoding(); + var initialContent = "\u0100ert & Ann"; + var initialContentBytes = GetBytesWithPreamble(initialContent, encoding); + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + using (client.CreateText(remoteFile, encoding)) + { + } + + // verify that file containing only preamble was created + Assert.IsTrue(client.Exists(remoteFile)); + + var file = client.GetAttributes(remoteFile); + Assert.AreEqual(encoding.GetPreamble().Length, file.Size); + + client.DeleteFile(remoteFile); + + using (var sw = client.CreateText(remoteFile, encoding)) + { + sw.Write(initialContent); + } + + // verify that content is written to file + var text = client.ReadAllText(remoteFile, encoding); + Assert.AreEqual(initialContent, text); + + using (var fs = client.OpenRead(remoteFile)) + { + var actualBytes = new byte[fs.Length]; + fs.Read(actualBytes, offset: 0, actualBytes.Length); + Assert.IsTrue(initialContentBytes.IsEqualTo(actualBytes)); + } + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_DownloadFile_FileDoesNotExist() + { + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + using (var ms = new MemoryStream()) + { + try + { + client.DownloadFile(remoteFile, ms); + Assert.Fail(); + } + catch (SftpPathNotFoundException ex) + { + Assert.IsNull(ex.InnerException); + Assert.AreEqual("No such file", ex.Message); + + // ensure file was not created by us + Assert.IsFalse(client.Exists(remoteFile)); + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + } + + [TestMethod] + public void Sftp_ReadAllBytes_ExistingFile() + { + var encoding = GetRandomEncoding(); + var content = "\u0100ert & Ann"; + var contentBytes = GetBytesWithPreamble(content, encoding); + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + client.WriteAllText(remoteFile, content, encoding); + + var actualBytes = client.ReadAllBytes(remoteFile); + Assert.IsTrue(contentBytes.IsEqualTo(actualBytes)); + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_ReadAllBytes_FileDoesNotExist() + { + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + client.ReadAllBytes(remoteFile); + Assert.Fail(); + } + catch (SftpPathNotFoundException ex) + { + Assert.IsNull(ex.InnerException); + Assert.AreEqual("No such file", ex.Message); + + // ensure file was not created by us + Assert.IsFalse(client.Exists(remoteFile)); + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_ReadAllLines_NoEncoding_ExistingFile() + { + var encoding = new UTF8Encoding(false, true); + var lines = new[] { "\u0100ert & Ann", "Forever", "&", "\u0116ver" }; + var linesBytes = GetBytesWithPreamble(string.Join(Environment.NewLine, lines) + Environment.NewLine, + encoding); + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + using (var sw = client.AppendText(remoteFile)) + { + for (var i = 0; i < lines.Length; i++) + { + sw.WriteLine(lines[i]); + } + } + + var actualLines = client.ReadAllLines(remoteFile); + Assert.IsNotNull(actualLines); + Assert.AreEqual(lines.Length, actualLines.Length); + + for (var i = 0; i < lines.Length; i++) + { + Assert.AreEqual(lines[i], actualLines[i]); + } + + using (var fs = client.OpenRead(remoteFile)) + { + var actualBytes = new byte[fs.Length]; + fs.Read(actualBytes, offset: 0, actualBytes.Length); + Assert.IsTrue(linesBytes.IsEqualTo(actualBytes)); + } + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_ReadAllLines_NoEncoding_FileDoesNotExist() + { + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + client.ReadAllLines(remoteFile); + Assert.Fail(); + } + catch (SftpPathNotFoundException ex) + { + Assert.IsNull(ex.InnerException); + Assert.AreEqual("No such file", ex.Message); + + // ensure file was not created by us + Assert.IsFalse(client.Exists(remoteFile)); + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_ReadAllLines_Encoding_ExistingFile() + { + var encoding = GetRandomEncoding(); + var lines = new[] { "\u0100ert & Ann", "Forever", "&", "\u0116ver" }; + var linesBytes = GetBytesWithPreamble(string.Join(Environment.NewLine, lines) + Environment.NewLine, + encoding); + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + using (var sw = client.AppendText(remoteFile, encoding)) + { + for (var i = 0; i < lines.Length; i++) + { + sw.WriteLine(lines[i]); + } + } + + var actualLines = client.ReadAllLines(remoteFile, encoding); + Assert.IsNotNull(actualLines); + Assert.AreEqual(lines.Length, actualLines.Length); + + for (var i = 0; i < lines.Length; i++) + { + Assert.AreEqual(lines[i], actualLines[i]); + } + + using (var fs = client.OpenRead(remoteFile)) + { + var actualBytes = new byte[fs.Length]; + fs.Read(actualBytes, offset: 0, actualBytes.Length); + Assert.IsTrue(linesBytes.IsEqualTo(actualBytes)); + } + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_ReadAllLines_Encoding_FileDoesNotExist() + { + var encoding = GetRandomEncoding(); + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + client.ReadAllLines(remoteFile, encoding); + Assert.Fail(); + } + catch (SftpPathNotFoundException ex) + { + Assert.IsNull(ex.InnerException); + Assert.AreEqual("No such file", ex.Message); + + // ensure file was not created by us + Assert.IsFalse(client.Exists(remoteFile)); + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_ReadAllText_NoEncoding_ExistingFile() + { + var encoding = new UTF8Encoding(false, true); + var lines = new[] { "\u0100ert & Ann", "Forever", "&", "\u0116ver" }; + var expectedText = string.Join(Environment.NewLine, lines) + Environment.NewLine; + var expectedBytes = GetBytesWithPreamble(expectedText, encoding); + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + using (var sw = client.AppendText(remoteFile)) + { + for (var i = 0; i < lines.Length; i++) + { + sw.WriteLine(lines[i]); + } + } + + var actualText = client.ReadAllText(remoteFile); + Assert.AreEqual(actualText, expectedText); + + using (var fs = client.OpenRead(remoteFile)) + { + var actualBytes = new byte[fs.Length]; + fs.Read(actualBytes, offset: 0, actualBytes.Length); + Assert.IsTrue(expectedBytes.IsEqualTo(actualBytes)); + } + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_ReadAllText_NoEncoding_FileDoesNotExist() + { + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + client.ReadAllText(remoteFile); + Assert.Fail(); + } + catch (SftpPathNotFoundException ex) + { + Assert.IsNull(ex.InnerException); + Assert.AreEqual("No such file", ex.Message); + + // ensure file was not created by us + Assert.IsFalse(client.Exists(remoteFile)); + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_ReadAllText_Encoding_ExistingFile() + { + var encoding = GetRandomEncoding(); + var lines = new[] { "\u0100ert & Ann", "Forever", "&", "\u0116ver" }; + var expectedText = string.Join(Environment.NewLine, lines) + Environment.NewLine; + var expectedBytes = GetBytesWithPreamble(expectedText, encoding); + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + using (var sw = client.AppendText(remoteFile, encoding)) + { + for (var i = 0; i < lines.Length; i++) + { + sw.WriteLine(lines[i]); + } + } + + var actualText = client.ReadAllText(remoteFile, encoding); + Assert.AreEqual(expectedText, actualText); + + using (var fs = client.OpenRead(remoteFile)) + { + var actualBytes = new byte[fs.Length]; + fs.Read(actualBytes, offset: 0, actualBytes.Length); + Assert.IsTrue(expectedBytes.IsEqualTo(actualBytes)); + } + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_ReadAllText_Encoding_FileDoesNotExist() + { + var encoding = GetRandomEncoding(); + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + client.ReadAllText(remoteFile, encoding); + Assert.Fail(); + } + catch (SftpPathNotFoundException ex) + { + Assert.IsNull(ex.InnerException); + Assert.AreEqual("No such file", ex.Message); + + // ensure file was not created by us + Assert.IsFalse(client.Exists(remoteFile)); + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_ReadLines_NoEncoding_ExistingFile() + { + var lines = new[] { "\u0100ert & Ann", "Forever", "&", "\u0116ver" }; + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + using (var sw = client.AppendText(remoteFile)) + { + for (var i = 0; i < lines.Length; i++) + { + sw.WriteLine(lines[i]); + } + } + + var actualLines = client.ReadLines(remoteFile); + Assert.IsNotNull(actualLines); + + var actualLinesEnum = actualLines.GetEnumerator(); + for (var i = 0; i < lines.Length; i++) + { + Assert.IsTrue(actualLinesEnum.MoveNext()); + var actualLine = actualLinesEnum.Current; + Assert.AreEqual(lines[i], actualLine); + } + + Assert.IsFalse(actualLinesEnum.MoveNext()); + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_ReadLines_NoEncoding_FileDoesNotExist() + { + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + client.ReadLines(remoteFile); + Assert.Fail(); + } + catch (SftpPathNotFoundException ex) + { + Assert.IsNull(ex.InnerException); + Assert.AreEqual("No such file", ex.Message); + + // ensure file was not created by us + Assert.IsFalse(client.Exists(remoteFile)); + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_ReadLines_Encoding_ExistingFile() + { + var encoding = GetRandomEncoding(); + var lines = new[] { "\u0100ert & Ann", "Forever", "&", "\u0116ver" }; + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + using (var sw = client.AppendText(remoteFile, encoding)) + { + for (var i = 0; i < lines.Length; i++) + { + sw.WriteLine(lines[i]); + } + } + + var actualLines = client.ReadLines(remoteFile, encoding); + Assert.IsNotNull(actualLines); + + using (var actualLinesEnum = actualLines.GetEnumerator()) + { + for (var i = 0; i < lines.Length; i++) + { + Assert.IsTrue(actualLinesEnum.MoveNext()); + + var actualLine = actualLinesEnum.Current; + Assert.AreEqual(lines[i], actualLine); + } + + Assert.IsFalse(actualLinesEnum.MoveNext()); + } + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_ReadLines_Encoding_FileDoesNotExist() + { + var encoding = GetRandomEncoding(); + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + client.ReadLines(remoteFile, encoding); + Assert.Fail(); + } + catch (SftpPathNotFoundException ex) + { + Assert.IsNull(ex.InnerException); + Assert.AreEqual("No such file", ex.Message); + + // ensure file was not created by us + Assert.IsFalse(client.Exists(remoteFile)); + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_WriteAllBytes_DirectoryDoesNotExist() + { + const string remoteFile = "/home/sshnet/directorydoesnotexist/test"; + + var content = GenerateRandom(size: 5); + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + client.WriteAllBytes(remoteFile, content); + Assert.Fail(); + } + catch (SftpPathNotFoundException ex) + { + Assert.IsNull(ex.InnerException); + Assert.AreEqual("No such file", ex.Message); + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_WriteAllBytes_ExistingFile() + { + var initialContent = GenerateRandom(size: 13); + var newContent1 = GenerateRandom(size: 5); + var expectedContent1 = new ArrayBuilder().Add(newContent1) + .Add(initialContent, newContent1.Length, initialContent.Length - newContent1.Length) + .Build(); + var newContent2 = GenerateRandom(size: 50000); + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + using (var fs = client.Create(remoteFile)) + { + fs.Write(initialContent, offset: 0, initialContent.Length); + } + + #region Write less bytes than the current content, overwriting part of that content + + client.WriteAllBytes(remoteFile, newContent1); + + var actualContent1 = client.ReadAllBytes(remoteFile); + Assert.IsTrue(expectedContent1.IsEqualTo(actualContent1)); + + #endregion Write less bytes than the initial content, overwriting part of that content + + #region Write more bytes than the current content, overwriting and appending to that content + + client.WriteAllBytes(remoteFile, newContent2); + + var actualContent2 = client.ReadAllBytes(remoteFile); + Assert.IsTrue(newContent2.IsEqualTo(actualContent2)); + + #endregion Write less bytes than the initial content, overwriting part of that content + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_WriteAllBytes_FileDoesNotExist() + { + var content = GenerateRandom(size: 13); + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + client.WriteAllBytes(remoteFile, content); + + using (var fs = client.OpenRead(remoteFile)) + { + var actualBytes = new byte[fs.Length]; + fs.Read(actualBytes, offset: 0, actualBytes.Length); + Assert.IsTrue(content.IsEqualTo(actualBytes)); + } + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + + [TestMethod] + public void Sftp_WriteAllLines_IEnumerable_NoEncoding_DirectoryDoesNotExist() + { + const string remoteFile = "/home/sshnet/directorydoesnotexist/test"; + + IEnumerable linesToWrite = new[] { "Forever", "&", "\u0116ver" }; + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + client.WriteAllLines(remoteFile, linesToWrite); + Assert.Fail(); + } + catch (SftpPathNotFoundException ex) + { + Assert.IsNull(ex.InnerException); + Assert.AreEqual("No such file", ex.Message); + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_WriteAllLines_IEnumerable_NoEncoding_ExistingFile() + { + var encoding = new UTF8Encoding(false, true); + var initialContent = "\u0100ert & Ann Forever & Ever Lisa & Sofie"; + var initialContentBytes = GetBytesWithPreamble(initialContent, encoding); + IEnumerable linesToWrite1 = new[] { "Forever", "&", "\u0116ver" }; + var linesToWrite1Bytes = + GetBytesWithPreamble(string.Join(Environment.NewLine, linesToWrite1) + Environment.NewLine, encoding); + var expectedBytes1 = new ArrayBuilder().Add(linesToWrite1Bytes) + .Add(initialContentBytes, + linesToWrite1Bytes.Length, + initialContentBytes.Length - linesToWrite1Bytes.Length) + .Build(); + IEnumerable linesToWrite2 = new[] { "Forever", "&", "\u0116ver", "Gert & Ann", "Lisa + Sofie" }; + var linesToWrite2Bytes = + GetBytesWithPreamble(string.Join(Environment.NewLine, linesToWrite2) + Environment.NewLine, encoding); + var expectedBytes2 = linesToWrite2Bytes; + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + // create initial content + client.WriteAllText(remoteFile, initialContent); + + #region Write less bytes than the current content, overwriting part of that content + + client.WriteAllLines(remoteFile, linesToWrite1); + + using (var fs = client.OpenRead(remoteFile)) + { + var actualBytes = new byte[fs.Length]; + fs.Read(actualBytes, offset: 0, actualBytes.Length); + Assert.IsTrue(expectedBytes1.IsEqualTo(actualBytes)); + } + + #endregion Write less bytes than the current content, overwriting part of that content + + #region Write more bytes than the current content, overwriting and appending to that content + + client.WriteAllLines(remoteFile, linesToWrite2); + + using (var fs = client.OpenRead(remoteFile)) + { + var actualBytes = new byte[fs.Length]; + fs.Read(actualBytes, offset: 0, actualBytes.Length); + Assert.IsTrue(expectedBytes2.IsEqualTo(actualBytes)); + } + + #endregion Write more bytes than the current content, overwriting and appending to that content + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_WriteAllLines_IEnumerable_NoEncoding_FileDoesNotExist() + { + var encoding = new UTF8Encoding(false, true); + IEnumerable linesToWrite = new[] { "\u0139isa", "&", "Sofie" }; + var linesToWriteBytes = GetBytesWithPreamble(string.Join(Environment.NewLine, linesToWrite) + Environment.NewLine, encoding); + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + client.WriteAllLines(remoteFile, linesToWrite); + + using (var fs = client.OpenRead(remoteFile)) + { + var actualBytes = new byte[fs.Length]; + fs.Read(actualBytes, offset: 0, actualBytes.Length); + Assert.IsTrue(linesToWriteBytes.IsEqualTo(actualBytes)); + } + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_WriteAllLines_IEnumerable_Encoding_DirectoryDoesNotExist() + { + const string remoteFile = "/home/sshnet/directorydoesnotexist/test"; + + var encoding = GetRandomEncoding(); + IEnumerable linesToWrite = new[] { "Forever", "&", "\u0116ver" }; + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + client.WriteAllLines(remoteFile, linesToWrite, encoding); + Assert.Fail(); + } + catch (SftpPathNotFoundException ex) + { + Assert.IsNull(ex.InnerException); + Assert.AreEqual("No such file", ex.Message); + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_WriteAllLines_IEnumerable_Encoding_ExistingFile() + { + var encoding = GetRandomEncoding(); + const string initialContent = "\u0100ert & Ann Forever & Ever Lisa & Sofie"; + var initialContentBytes = GetBytesWithPreamble(initialContent, encoding); + IEnumerable linesToWrite1 = new[] { "Forever", "&", "\u0116ver" }; + var linesToWrite1Bytes = + GetBytesWithPreamble(string.Join(Environment.NewLine, linesToWrite1) + Environment.NewLine, encoding); + var expectedBytes1 = new ArrayBuilder().Add(linesToWrite1Bytes) + .Add(initialContentBytes, + linesToWrite1Bytes.Length, + initialContentBytes.Length - linesToWrite1Bytes.Length) + .Build(); + IEnumerable linesToWrite2 = new[] { "Forever", "&", "\u0116ver", "Gert & Ann", "Lisa + Sofie" }; + var linesToWrite2Bytes = GetBytesWithPreamble(string.Join(Environment.NewLine, linesToWrite2) + Environment.NewLine, encoding); + var expectedBytes2 = linesToWrite2Bytes; + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + // create initial content + client.WriteAllText(remoteFile, initialContent, encoding); + + #region Write less bytes than the current content, overwriting part of that content + + client.WriteAllLines(remoteFile, linesToWrite1, encoding); + + using (var fs = client.OpenRead(remoteFile)) + { + var actualBytes = new byte[fs.Length]; + fs.Read(actualBytes, offset: 0, actualBytes.Length); + Assert.IsTrue(expectedBytes1.IsEqualTo(actualBytes)); + } + + #endregion Write less bytes than the current content, overwriting part of that content + + #region Write more bytes than the current content, overwriting and appending to that content + + client.WriteAllLines(remoteFile, linesToWrite2, encoding); + + using (var fs = client.OpenRead(remoteFile)) + { + var actualBytes = new byte[fs.Length]; + fs.Read(actualBytes, offset: 0, actualBytes.Length); + Assert.IsTrue(expectedBytes2.IsEqualTo(actualBytes)); + } + + #endregion Write more bytes than the current content, overwriting and appending to that content + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_WriteAllLines_IEnumerable_Encoding_FileDoesNotExist() + { + var encoding = GetRandomEncoding(); + IEnumerable linesToWrite = new[] { "\u0139isa", "&", "Sofie" }; + var linesToWriteBytes = GetBytesWithPreamble(string.Join(Environment.NewLine, linesToWrite) + Environment.NewLine, encoding); + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + client.WriteAllLines(remoteFile, linesToWrite, encoding); + + using (var fs = client.OpenRead(remoteFile)) + { + var actualBytes = new byte[fs.Length]; + fs.Read(actualBytes, offset: 0, actualBytes.Length); + Assert.IsTrue(linesToWriteBytes.IsEqualTo(actualBytes)); + } + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_WriteAllLines_Array_NoEncoding_DirectoryDoesNotExist() + { + const string remoteFile = "/home/sshnet/directorydoesnotexist/test"; + + var linesToWrite = new[] { "Forever", "&", "\u0116ver" }; + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + client.WriteAllLines(remoteFile, linesToWrite); + Assert.Fail(); + } + catch (SftpPathNotFoundException ex) + { + Assert.IsNull(ex.InnerException); + Assert.AreEqual("No such file", ex.Message); + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_WriteAllLines_Array_NoEncoding_ExistingFile() + { + var encoding = new UTF8Encoding(false, true); + const string initialContent = "\u0100ert & Ann Forever & Ever Lisa & Sofie"; + var initialContentBytes = GetBytesWithPreamble(initialContent, encoding); + var linesToWrite1 = new[] { "Forever", "&", "\u0116ver" }; + var linesToWrite1Bytes = GetBytesWithPreamble(string.Join(Environment.NewLine, linesToWrite1) + Environment.NewLine, encoding); + var expectedBytes1 = new ArrayBuilder().Add(linesToWrite1Bytes) + .Add(initialContentBytes, linesToWrite1Bytes.Length, initialContentBytes.Length - linesToWrite1Bytes.Length) + .Build(); + var linesToWrite2 = new[] { "Forever", "&", "\u0116ver", "Gert & Ann", "Lisa + Sofie" }; + var linesToWrite2Bytes = GetBytesWithPreamble(string.Join(Environment.NewLine, linesToWrite2) + Environment.NewLine, encoding); + var expectedBytes2 = linesToWrite2Bytes; + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + // create initial content + client.WriteAllText(remoteFile, initialContent); + + #region Write less bytes than the current content, overwriting part of that content + + client.WriteAllLines(remoteFile, linesToWrite1); + + using (var fs = client.OpenRead(remoteFile)) + { + var actualBytes = new byte[fs.Length]; + fs.Read(actualBytes, offset: 0, actualBytes.Length); + Assert.IsTrue(expectedBytes1.IsEqualTo(actualBytes)); + } + + #endregion Write less bytes than the current content, overwriting part of that content + + #region Write more bytes than the current content, overwriting and appending to that content + + client.WriteAllLines(remoteFile, linesToWrite2); + + using (var fs = client.OpenRead(remoteFile)) + { + var actualBytes = new byte[fs.Length]; + fs.Read(actualBytes, offset: 0, actualBytes.Length); + Assert.IsTrue(expectedBytes2.IsEqualTo(actualBytes)); + } + + #endregion Write more bytes than the current content, overwriting and appending to that content + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_WriteAllLines_Array_NoEncoding_FileDoesNotExist() + { + var encoding = new UTF8Encoding(false, true); + var linesToWrite = new[] { "\u0139isa", "&", "Sofie" }; + var linesToWriteBytes = GetBytesWithPreamble(string.Join(Environment.NewLine, linesToWrite) + Environment.NewLine, encoding); + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + client.WriteAllLines(remoteFile, linesToWrite); + + using (var fs = client.OpenRead(remoteFile)) + { + var actualBytes = new byte[fs.Length]; + fs.Read(actualBytes, offset: 0, actualBytes.Length); + Assert.IsTrue(linesToWriteBytes.IsEqualTo(actualBytes)); + } + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_WriteAllLines_Array_Encoding_DirectoryDoesNotExist() + { + const string remoteFile = "/home/sshnet/directorydoesnotexist/test"; + + var encoding = GetRandomEncoding(); + var linesToWrite = new[] { "Forever", "&", "\u0116ver" }; + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + client.WriteAllLines(remoteFile, linesToWrite, encoding); + Assert.Fail(); + } + catch (SftpPathNotFoundException ex) + { + Assert.IsNull(ex.InnerException); + Assert.AreEqual("No such file", ex.Message); + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_WriteAllLines_Array_Encoding_ExistingFile() + { + const string initialContent = "\u0100ert & Ann Forever & Ever Lisa & Sofie"; + + var encoding = GetRandomEncoding(); + var initialContentBytes = GetBytesWithPreamble(initialContent, encoding); + var linesToWrite1 = new[] { "Forever", "&", "\u0116ver" }; + var linesToWrite1Bytes = GetBytesWithPreamble(string.Join(Environment.NewLine, linesToWrite1) + Environment.NewLine, encoding); + var expectedBytes1 = new ArrayBuilder().Add(linesToWrite1Bytes) + .Add(initialContentBytes, linesToWrite1Bytes.Length, initialContentBytes.Length - linesToWrite1Bytes.Length) + .Build(); + var linesToWrite2 = new[] { "Forever", "&", "\u0116ver", "Gert & Ann", "Lisa + Sofie" }; + var linesToWrite2Bytes = GetBytesWithPreamble(string.Join(Environment.NewLine, linesToWrite2) + Environment.NewLine, encoding); + var expectedBytes2 = linesToWrite2Bytes; + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + // create initial content + client.WriteAllText(remoteFile, initialContent, encoding); + + #region Write less bytes than the current content, overwriting part of that content + + client.WriteAllLines(remoteFile, linesToWrite1, encoding); + + using (var fs = client.OpenRead(remoteFile)) + { + var actualBytes = new byte[fs.Length]; + fs.Read(actualBytes, offset: 0, actualBytes.Length); + Assert.IsTrue(expectedBytes1.IsEqualTo(actualBytes)); + } + + #endregion Write less bytes than the current content, overwriting part of that content + + #region Write more bytes than the current content, overwriting and appending to that content + + client.WriteAllLines(remoteFile, linesToWrite2, encoding); + + using (var fs = client.OpenRead(remoteFile)) + { + var actualBytes = new byte[fs.Length]; + fs.Read(actualBytes, offset: 0, actualBytes.Length); + Assert.IsTrue(expectedBytes2.IsEqualTo(actualBytes)); + } + + #endregion Write more bytes than the current content, overwriting and appending to that content + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_WriteAllLines_Array_Encoding_FileDoesNotExist() + { + var encoding = GetRandomEncoding(); + var linesToWrite = new[] { "\u0139isa", "&", "Sofie" }; + var linesToWriteBytes = GetBytesWithPreamble(string.Join(Environment.NewLine, linesToWrite) + Environment.NewLine, encoding); + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + client.WriteAllLines(remoteFile, linesToWrite, encoding); + + using (var fs = client.OpenRead(remoteFile)) + { + var actualBytes = new byte[fs.Length]; + fs.Read(actualBytes, offset: 0, actualBytes.Length); + Assert.IsTrue(linesToWriteBytes.IsEqualTo(actualBytes)); + } + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + + } + } + + [TestMethod] + public void Sftp_WriteAllText_NoEncoding_DirectoryDoesNotExist() + { + const string remoteFile = "/home/sshnet/directorydoesnotexist/test"; + + const string initialContent = "\u0100ert & Ann Forever & \u0116ver Lisa & Sofie"; + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + client.WriteAllText(remoteFile, initialContent); + Assert.Fail(); + } + catch (SftpPathNotFoundException ex) + { + Assert.IsNull(ex.InnerException); + Assert.AreEqual("No such file", ex.Message); + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_WriteAllText_NoEncoding_ExistingFile() + { + const string initialContent = "\u0100ert & Ann Forever & \u0116ver Lisa & Sofie"; + const string newContent1 = "For\u0116ver & Ever"; + + var encoding = new UTF8Encoding(false, true); + var initialContentBytes = GetBytesWithPreamble(initialContent, encoding); + var newContent1Bytes = GetBytesWithPreamble(newContent1, encoding); + var expectedBytes1 = new ArrayBuilder().Add(newContent1Bytes) + .Add(initialContentBytes, newContent1Bytes.Length, initialContentBytes.Length - newContent1Bytes.Length) + .Build(); + var newContent2 = "Sofie & Lisa For\u0116ver & Ever with \u0100ert & Ann"; + var newContent2Bytes = GetBytesWithPreamble(newContent2, encoding); + var expectedBytes2 = newContent2Bytes; + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + client.WriteAllText(remoteFile, initialContent); + + #region Write less bytes than the current content, overwriting part of that content + + client.WriteAllText(remoteFile, newContent1); + + using (var fs = client.OpenRead(remoteFile)) + { + var actualBytes = new byte[fs.Length]; + fs.Read(actualBytes, offset: 0, actualBytes.Length); + Assert.IsTrue(expectedBytes1.IsEqualTo(actualBytes)); + } + + #endregion Write less bytes than the current content, overwriting part of that content + + #region Write more bytes than the current content, overwriting and appending to that content + + client.WriteAllText(remoteFile, newContent2); + + using (var fs = client.OpenRead(remoteFile)) + { + var actualBytes = new byte[fs.Length]; + fs.Read(actualBytes, offset: 0, actualBytes.Length); + Assert.IsTrue(expectedBytes2.IsEqualTo(actualBytes)); + } + + #endregion Write more bytes than the current content, overwriting and appending to that content + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_WriteAllText_NoEncoding_FileDoesNotExist() + { + const string initialContent = "\u0100ert & Ann Forever & \u0116ver Lisa & Sofie"; + + var encoding = new UTF8Encoding(false, true); + var initialContentBytes = GetBytesWithPreamble(initialContent, encoding); + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + client.WriteAllText(remoteFile, initialContent); + + using (var fs = client.OpenRead(remoteFile)) + { + var actualBytes = new byte[fs.Length]; + fs.Read(actualBytes, offset: 0, actualBytes.Length); + Assert.IsTrue(initialContentBytes.IsEqualTo(actualBytes)); + } + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_WriteAllText_Encoding_DirectoryDoesNotExist() + { + const string remoteFile = "/home/sshnet/directorydoesnotexist/test"; + + var encoding = GetRandomEncoding(); + const string content = "For\u0116ver & Ever"; + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + client.WriteAllText(remoteFile, content, encoding); + Assert.Fail(); + } + catch (SftpPathNotFoundException ex) + { + Assert.IsNull(ex.InnerException); + Assert.AreEqual("No such file", ex.Message); + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_WriteAllText_Encoding_ExistingFile() + { + const string initialContent = "\u0100ert & Ann Forever & \u0116ver Lisa & Sofie"; + const string newContent1 = "For\u0116ver & Ever"; + const string newContent2 = "Sofie & Lisa For\u0116ver & Ever with \u0100ert & Ann"; + + var encoding = GetRandomEncoding(); + var initialContentBytes = GetBytesWithPreamble(initialContent, encoding); + var newContent1Bytes = GetBytesWithPreamble(newContent1, encoding); + var expectedBytes1 = new ArrayBuilder().Add(newContent1Bytes) + .Add(initialContentBytes, newContent1Bytes.Length, initialContentBytes.Length - newContent1Bytes.Length) + .Build(); + var newContent2Bytes = GetBytesWithPreamble(newContent2, encoding); + var expectedBytes2 = newContent2Bytes; + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + client.WriteAllText(remoteFile, initialContent, encoding); + + #region Write less bytes than the current content, overwriting part of that content + + client.WriteAllText(remoteFile, newContent1, encoding); + + using (var fs = client.OpenRead(remoteFile)) + { + var actualBytes = new byte[fs.Length]; + fs.Read(actualBytes, offset: 0, actualBytes.Length); + Assert.IsTrue(expectedBytes1.IsEqualTo(actualBytes)); + } + + #endregion Write less bytes than the current content, overwriting part of that content + + #region Write more bytes than the current content, overwriting and appending to that content + + client.WriteAllText(remoteFile, newContent2, encoding); + + using (var fs = client.OpenRead(remoteFile)) + { + var actualBytes = new byte[fs.Length]; + fs.Read(actualBytes, offset: 0, actualBytes.Length); + Assert.IsTrue(expectedBytes2.IsEqualTo(actualBytes)); + } + + #endregion Write more bytes than the current content, overwriting and appending to that content + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_WriteAllText_Encoding_FileDoesNotExist() + { + const string initialContent = "\u0100ert & Ann Forever & \u0116ver Lisa & Sofie"; + + var encoding = GetRandomEncoding(); + var initialContentBytes = GetBytesWithPreamble(initialContent, encoding); + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + client.WriteAllText(remoteFile, initialContent, encoding); + + using (var fs = client.OpenRead(remoteFile)) + { + var actualBytes = new byte[fs.Length]; + fs.Read(actualBytes, offset: 0, actualBytes.Length); + Assert.IsTrue(initialContentBytes.IsEqualTo(actualBytes)); + } + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_BeginDownloadFile_FileDoesNotExist() + { + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + using (var ms = new MemoryStream()) + { + var asyncResult = client.BeginDownloadFile(remoteFile, ms); + try + { + client.EndDownloadFile(asyncResult); + Assert.Fail(); + } + catch (SftpPathNotFoundException ex) + { + Assert.IsNull(ex.InnerException); + Assert.AreEqual("No such file", ex.Message); + + // ensure file was not created by us + Assert.IsFalse(client.Exists(remoteFile)); + } + } + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_BeginListDirectory_DirectoryDoesNotExist() + { + const string remoteDirectory = "/home/sshnet/test123"; + + using (var client = new SshClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + using (var command = client.CreateCommand("rm -Rf " + _remotePathTransformation.Transform(remoteDirectory))) + { + command.Execute(); + } + } + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var asyncResult = client.BeginListDirectory(remoteDirectory, null, null); + try + { + client.EndListDirectory(asyncResult); + Assert.Fail(); + } + catch (SftpPathNotFoundException ex) + { + Assert.IsNull(ex.InnerException); + Assert.AreEqual("No such file", ex.Message); + + // ensure directory was not created by us + Assert.IsFalse(client.Exists(remoteDirectory)); + } + } + } + + [TestMethod] + public void Sftp_BeginUploadFile_InputAndPath_DirectoryDoesNotExist() + { + const int size = 50 * 1024 * 1024; + const string remoteDirectory = "/home/sshnet/test123"; + const string remoteFile = remoteDirectory + "/test"; + + using (var client = new SshClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + using (var command = client.CreateCommand("rm -Rf " + _remotePathTransformation.Transform(remoteDirectory))) + { + command.Execute(); + } + } + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var memoryStream = CreateMemoryStream(size); + memoryStream.Position = 0; + + var asyncResult = client.BeginUploadFile(memoryStream, remoteFile); + try + { + client.EndUploadFile(asyncResult); + Assert.Fail(); + } + catch (SftpPathNotFoundException) + { + } + } + } + + [TestMethod] + public void Sftp_BeginUploadFile_InputAndPath_FileDoesNotExist() + { + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + var uploadMemoryStream = new MemoryStream(); + var sw = new StreamWriter(uploadMemoryStream, Encoding.UTF8); + sw.Write("Gert & Ann"); + sw.Flush(); + uploadMemoryStream.Position = 0; + + var asyncResult = client.BeginUploadFile(uploadMemoryStream, remoteFile); + client.EndUploadFile(asyncResult); + + using (var downloadMemoryStream = new MemoryStream()) + { + client.DownloadFile(remoteFile, downloadMemoryStream); + + downloadMemoryStream.Position = 0; + + using (var sr = new StreamReader(downloadMemoryStream, Encoding.UTF8)) + { + var content = sr.ReadToEnd(); + Assert.AreEqual("Gert & Ann", content); + } + } + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_BeginUploadFile_InputAndPath_ExistingFile() + { + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + client.WriteAllText(remoteFile, "Gert & Ann & Lisa"); + + var uploadMemoryStream = new MemoryStream(); + var sw = new StreamWriter(uploadMemoryStream, Encoding.UTF8); + sw.Write("Ann & Gert"); + sw.Flush(); + uploadMemoryStream.Position = 0; + + var asyncResult = client.BeginUploadFile(uploadMemoryStream, remoteFile); + client.EndUploadFile(asyncResult); + + using (var downloadMemoryStream = new MemoryStream()) + { + client.DownloadFile(remoteFile, downloadMemoryStream); + + downloadMemoryStream.Position = 0; + + using (var sr = new StreamReader(downloadMemoryStream, Encoding.UTF8)) + { + var content = sr.ReadToEnd(); + Assert.AreEqual("Ann & Gert", content); + } + } + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_BeginUploadFile_InputAndPathAndCanOverride_CanOverrideIsFalse_DirectoryDoesNotExist() + { + const int size = 50 * 1024 * 1024; + const string remoteDirectory = "/home/sshnet/test123"; + const string remoteFile = remoteDirectory + "/test"; + + using (var client = new SshClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + using (var command = client.CreateCommand("rm -Rf " + _remotePathTransformation.Transform(remoteDirectory))) + { + command.Execute(); + } + } + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var memoryStream = CreateMemoryStream(size); + memoryStream.Position = 0; + + var asyncResult = client.BeginUploadFile(memoryStream, remoteFile, false, null, null); + try + { + client.EndUploadFile(asyncResult); + Assert.Fail(); + } + catch (SftpPathNotFoundException) + { + } + } + } + + [TestMethod] + public void Sftp_BeginUploadFile_InputAndPathAndCanOverride_CanOverrideIsFalse_FileDoesNotExist() + { + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + using (var uploadMemoryStream = new MemoryStream()) + using (var sw = new StreamWriter(uploadMemoryStream, Encoding.UTF8)) + { + sw.Write("Gert & Ann"); + sw.Flush(); + uploadMemoryStream.Position = 0; + + var asyncResult = client.BeginUploadFile(uploadMemoryStream, remoteFile, false, null, null); + client.EndUploadFile(asyncResult); + } + + using (var downloadMemoryStream = new MemoryStream()) + { + client.DownloadFile(remoteFile, downloadMemoryStream); + + downloadMemoryStream.Position = 0; + + using (var sr = new StreamReader(downloadMemoryStream, Encoding.UTF8)) + { + var content = sr.ReadToEnd(); + Assert.AreEqual("Gert & Ann", content); + } + } + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_BeginUploadFile_InputAndPathAndCanOverride_CanOverrideIsFalse_ExistingFile() + { + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + client.WriteAllText(remoteFile, "Gert & Ann & Lisa"); + + var uploadMemoryStream = new MemoryStream(); + var sw = new StreamWriter(uploadMemoryStream, Encoding.UTF8); + sw.Write("Ann & Gert"); + sw.Flush(); + uploadMemoryStream.Position = 0; + + var asyncResult = client.BeginUploadFile(uploadMemoryStream, remoteFile, false, null, null); + + try + { + client.EndUploadFile(asyncResult); + Assert.Fail(); + } + catch (SshException ex) + { + Assert.AreEqual(typeof(SshException), ex.GetType()); + Assert.IsNull(ex.InnerException); + Assert.AreEqual("Failure", ex.Message); + } + } + finally + { + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_BeginUploadFile_InputAndPathAndCanOverride_CanOverrideIsTrue_DirectoryDoesNotExist() + { + const int size = 50 * 1024 * 1024; + const string remoteDirectory = "/home/sshnet/test123"; + const string remoteFile = remoteDirectory + "/test"; + + using (var client = new SshClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + using (var command = client.CreateCommand("rm -Rf " + _remotePathTransformation.Transform(remoteDirectory))) + { + command.Execute(); + } + } + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var memoryStream = CreateMemoryStream(size); + memoryStream.Position = 0; + + var asyncResult = client.BeginUploadFile(memoryStream, remoteFile, true, null, null); + try + { + client.EndUploadFile(asyncResult); + Assert.Fail(); + } + catch (SftpPathNotFoundException) + { + } + } + } + + [TestMethod] + public void Sftp_BeginUploadFile_InputAndPathAndCanOverride_CanOverrideIsTrue_FileDoesNotExist() + { + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + using (var uploadMemoryStream = new MemoryStream()) + using (var sw = new StreamWriter(uploadMemoryStream, Encoding.UTF8)) + { + sw.Write("Gert & Ann"); + sw.Flush(); + uploadMemoryStream.Position = 0; + + var asyncResult = client.BeginUploadFile(uploadMemoryStream, remoteFile, true, null, null); + client.EndUploadFile(asyncResult); + } + + using (var downloadMemoryStream = new MemoryStream()) + { + client.DownloadFile(remoteFile, downloadMemoryStream); + + downloadMemoryStream.Position = 0; + + using (var sr = new StreamReader(downloadMemoryStream, Encoding.UTF8)) + { + var content = sr.ReadToEnd(); + Assert.AreEqual("Gert & Ann", content); + } + } + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_BeginUploadFile_InputAndPathAndCanOverride_CanOverrideIsTrue_ExistingFile() + { + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + client.WriteAllText(remoteFile, "Gert & Ann & Lisa"); + + using (var uploadMemoryStream = new MemoryStream()) + using (var sw = new StreamWriter(uploadMemoryStream, Encoding.UTF8)) + { + sw.Write("Ann & Gert"); + sw.Flush(); + uploadMemoryStream.Position = 0; + + var asyncResult = client.BeginUploadFile(uploadMemoryStream, remoteFile, true, null, null); + client.EndUploadFile(asyncResult); + } + + using (var downloadMemoryStream = new MemoryStream()) + { + client.DownloadFile(remoteFile, downloadMemoryStream); + + downloadMemoryStream.Position = 0; + + using (var sr = new StreamReader(downloadMemoryStream, Encoding.UTF8)) + { + var content = sr.ReadToEnd(); + Assert.AreEqual("Ann & Gert", content); + } + } + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_UploadAndDownloadBigFile() + { + const int size = 50 * 1024 * 1024; + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.Delete(remoteFile); + } + + try + { + var memoryStream = CreateMemoryStream(size); + memoryStream.Position = 0; + + client.UploadFile(memoryStream, remoteFile); + + var stopwatch = new Stopwatch(); + stopwatch.Start(); + + // check uploaded file + memoryStream = new MemoryStream(); + client.DownloadFile(remoteFile, memoryStream); + + Assert.AreEqual(size, memoryStream.Length); + + stopwatch.Stop(); + + Console.WriteLine(@"Elapsed: {0} ms", stopwatch.ElapsedMilliseconds); + Console.WriteLine(@"Transfer speed: {0:N2} KB/s", + CalculateTransferSpeed(memoryStream.Length, stopwatch.ElapsedMilliseconds)); + } + finally + { + if (client.Exists(remoteFile)) + { + client.Delete(remoteFile); + } + } + } + } + + /// + /// Issue 1672 + /// + [TestMethod] + public void Sftp_CurrentWorkingDirectory() + { + const string homeDirectory = "/home/sshnet"; + const string otherDirectory = homeDirectory + "/dir"; + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + if (client.Exists(otherDirectory)) + { + client.DeleteDirectory(otherDirectory); + } + + try + { + client.CreateDirectory(otherDirectory); + client.ChangeDirectory(otherDirectory); + + using (var s = CreateStreamWithContent("A")) + { + client.UploadFile(s, "a.txt"); + } + + using (var s = new MemoryStream()) + { + client.DownloadFile("a.txt", s); + s.Position = 0; + + var content = Encoding.ASCII.GetString(s.ToArray()); + Assert.AreEqual("A", content); + } + + Assert.IsTrue(client.Exists(otherDirectory + "/a.txt")); + client.DeleteFile("a.txt"); + Assert.IsFalse(client.Exists(otherDirectory + "/a.txt")); + client.DeleteDirectory("."); + Assert.IsFalse(client.Exists(otherDirectory)); + } + finally + { + if (client.Exists(otherDirectory)) + { + client.DeleteDirectory(otherDirectory); + } + } + } + } + + [TestMethod] + public void Sftp_Exists() + { + const string remoteHome = "/home/sshnet"; + + #region Setup + + using (var client = new SshClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + #region Clean-up + + using (var command = client.CreateCommand($"rm -Rf {remoteHome + "/DoesNotExist"}")) + { + command.Execute(); + Assert.AreEqual(0, command.ExitStatus, command.Error); + } + + using (var command = client.CreateCommand($"rm -Rf {remoteHome + "/symlink.to.directory.exists"}")) + { + command.Execute(); + Assert.AreEqual(0, command.ExitStatus, command.Error); + } + + using (var command = client.CreateCommand($"rm -Rf {remoteHome + "/directory.exists"}") + ) + { + command.Execute(); + Assert.AreEqual(0, command.ExitStatus, command.Error); + } + + using (var command = client.CreateCommand($"rm -Rf {remoteHome + "/symlink.to.file.exists"}")) + { + command.Execute(); + Assert.AreEqual(0, command.ExitStatus, command.Error); + } + + using (var command = client.CreateCommand($"rm -f {remoteHome + "/file.exists"}")) + { + command.Execute(); + Assert.AreEqual(0, command.ExitStatus, command.Error); + } + + #endregion Clean-up + + #region Setup + + using (var command = client.CreateCommand($"touch {remoteHome + "/file.exists"}")) + { + command.Execute(); + Assert.AreEqual(0, command.ExitStatus, command.Error); + } + + using (var command = client.CreateCommand($"mkdir {remoteHome + "/directory.exists"}")) + { + command.Execute(); + Assert.AreEqual(0, command.ExitStatus, command.Error); + } + + using (var command = client.CreateCommand($"ln -s {remoteHome + "/file.exists"} {remoteHome + "/symlink.to.file.exists"}")) + { + command.Execute(); + Assert.AreEqual(0, command.ExitStatus, command.Error); + } + + using (var command = client.CreateCommand($"ln -s {remoteHome + "/directory.exists"} {remoteHome + "/symlink.to.directory.exists"}")) + { + command.Execute(); + Assert.AreEqual(0, command.ExitStatus, command.Error); + } + + #endregion Setup + } + + #endregion Setup + + #region Assert + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + Assert.IsFalse(client.Exists(remoteHome + "/DoesNotExist")); + Assert.IsTrue(client.Exists(remoteHome + "/file.exists")); + Assert.IsTrue(client.Exists(remoteHome + "/symlink.to.file.exists")); + Assert.IsTrue(client.Exists(remoteHome + "/directory.exists")); + Assert.IsTrue(client.Exists(remoteHome + "/symlink.to.directory.exists")); + } + + #endregion Assert + + #region Teardown + + using (var client = new SshClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + using (var command = client.CreateCommand($"rm -Rf {remoteHome + "/DoesNotExist"}")) + { + command.Execute(); + Assert.AreEqual(0, command.ExitStatus, command.Error); + } + + using (var command = client.CreateCommand($"rm -Rf {remoteHome + "/symlink.to.directory.exists"}")) + { + command.Execute(); + Assert.AreEqual(0, command.ExitStatus, command.Error); + } + + using (var command = client.CreateCommand($"rm -Rf {remoteHome + "/directory.exists"}")) + { + command.Execute(); + Assert.AreEqual(0, command.ExitStatus, command.Error); + } + + using (var command = client.CreateCommand($"rm -Rf {remoteHome + "/symlink.to.file.exists"}")) + { + command.Execute(); + Assert.AreEqual(0, command.ExitStatus, command.Error); + } + + using (var command = client.CreateCommand($"rm -f {remoteHome + "/file.exists"}")) + { + command.Execute(); + Assert.AreEqual(0, command.ExitStatus, command.Error); + } + } + + #endregion Teardown + } + + [TestMethod] + public void Sftp_ListDirectory() + { + const string remoteDirectory = "/home/sshnet/test123"; + + try + { + using (var client = new SshClient(_connectionInfoFactory.Create())) + { + client.Connect(); + client.RunCommand($@"rm -Rf ""{remoteDirectory}"""); + client.RunCommand($@"mkdir -p ""{remoteDirectory}"""); + client.RunCommand($@"mkdir -p ""{remoteDirectory}/sub"""); + client.RunCommand($@"touch ""{remoteDirectory}/file1"""); + client.RunCommand($@"touch ""{remoteDirectory}/file2"""); + } + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + client.ChangeDirectory(remoteDirectory); + + var directoryContent = client.ListDirectory(".").OrderBy(p => p.Name).ToList(); + Assert.AreEqual(5, directoryContent.Count); + + Assert.AreEqual(".", directoryContent[0].Name); + Assert.AreEqual($"{remoteDirectory}/.", directoryContent[0].FullName); + Assert.IsTrue(directoryContent[0].IsDirectory); + + Assert.AreEqual("..", directoryContent[1].Name); + Assert.AreEqual($"{remoteDirectory}/..", directoryContent[1].FullName); + Assert.IsTrue(directoryContent[1].IsDirectory); + + Assert.AreEqual("file1", directoryContent[2].Name); + Assert.AreEqual($"{remoteDirectory}/file1", directoryContent[2].FullName); + Assert.IsFalse(directoryContent[2].IsDirectory); + + Assert.AreEqual("file2", directoryContent[3].Name); + Assert.AreEqual($"{remoteDirectory}/file2", directoryContent[3].FullName); + Assert.IsFalse(directoryContent[3].IsDirectory); + + Assert.AreEqual("sub", directoryContent[4].Name); + Assert.AreEqual($"{remoteDirectory}/sub", directoryContent[4].FullName); + Assert.IsTrue(directoryContent[4].IsDirectory); + } + } + finally + { + using (var client = new SshClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + using (var command = client.CreateCommand("rm -Rf " + _remotePathTransformation.Transform(remoteDirectory))) + { + command.Execute(); + } + } + } + } + + [TestMethod] + public void Sftp_ChangeDirectory_DirectoryDoesNotExist() + { + const string remoteDirectory = "/home/sshnet/test123"; + + using (var client = new SshClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + using (var command = client.CreateCommand("rm -Rf " + _remotePathTransformation.Transform(remoteDirectory))) + { + command.Execute(); + } + } + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + try + { + client.ChangeDirectory(remoteDirectory); + Assert.Fail(); + } + catch (SftpPathNotFoundException) + { + } + } + } + + [TestMethod] + public void Sftp_ChangeDirectory_DirectoryExists() + { + const string remoteDirectory = "/home/sshnet/test123"; + + using (var client = new SshClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + using (var command = client.CreateCommand("rm -Rf " + _remotePathTransformation.Transform(remoteDirectory))) + { + command.Execute(); + } + + using (var command = client.CreateCommand("mkdir -p " + _remotePathTransformation.Transform(remoteDirectory))) + { + command.Execute(); + } + } + + try + { + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + client.ChangeDirectory(remoteDirectory); + + Assert.AreEqual(remoteDirectory, client.WorkingDirectory); + + using (var uploadStream = CreateMemoryStream(100)) + { + uploadStream.Position = 0; + + client.UploadFile(uploadStream, "gert.txt"); + + uploadStream.Position = 0; + + using (var downloadStream = client.OpenRead(remoteDirectory + "/gert.txt")) + { + Assert.AreEqual(CreateHash(uploadStream), CreateHash(downloadStream)); + } + } + } + } + finally + { + using (var client = new SshClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + using (var command = client.CreateCommand("rm -Rf " + _remotePathTransformation.Transform(remoteDirectory))) + { + command.Execute(); + } + } + } + } + + [TestMethod] + public void Sftp_DownloadFile_MemoryStream() + { + const int fileSize = 500 * 1024; + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + SftpCreateRemoteFile(client, remoteFile, fileSize); + + try + { + using (var memoryStream = new MemoryStream()) + { + var stopwatch = new Stopwatch(); + stopwatch.Start(); + + client.DownloadFile(remoteFile, memoryStream); + stopwatch.Stop(); + + var transferSpeed = CalculateTransferSpeed(memoryStream.Length, stopwatch.ElapsedMilliseconds); + Console.WriteLine(@"Elapsed: {0} ms", stopwatch.ElapsedMilliseconds); + Console.WriteLine(@"Transfer speed: {0:N2} KB/s", transferSpeed); + + Assert.AreEqual(fileSize, memoryStream.Length); + } + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_SubsystemExecution_Failed() + { + var remoteSshdConfig = new RemoteSshd(_adminConnectionInfoFactory).OpenConfig(); + + // Disable SFTP subsystem + remoteSshdConfig.ClearSubsystems() + .Update() + .Restart(); + + var remoteSshdReconfiguredToDefaultState = false; + + try + { + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + try + { + client.Connect(); + Assert.Fail("Establishing SFTP connection should have failed."); + } + catch (SshException ex) + { + Assert.IsNull(ex.InnerException); + Assert.AreEqual("Subsystem 'sftp' could not be executed.", ex.Message); + } + + // Re-enable SFTP subsystem + remoteSshdConfig.Reset(); + + remoteSshdReconfiguredToDefaultState = true; + + // ensure we can reconnect the same SftpClient instance + client.Connect(); + // ensure SFTP session is correctly established + Assert.IsTrue(client.Exists(".")); + } + } + finally + { + if (!remoteSshdReconfiguredToDefaultState) + { + remoteSshdConfig.Reset(); + } + } + } + + [TestMethod] + public void Sftp_SftpFileStream_ReadAndWrite() + { + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + using (var s = client.Open(remoteFile, FileMode.CreateNew, FileAccess.Write)) + { + s.Write(new byte[] { 5, 4, 3, 2, 1 }, 1, 3); + } + + // switch from read to write mode + using (var s = client.Open(remoteFile, FileMode.Open, FileAccess.ReadWrite)) + { + Assert.AreEqual(4, s.ReadByte()); + Assert.AreEqual(3, s.ReadByte()); + + Assert.AreEqual(2, s.Position); + + s.WriteByte(7); + s.Write(new byte[] { 8, 9, 10, 11, 12 }, 1, 3); + + Assert.AreEqual(6, s.Position); + } + + using (var s = client.Open(remoteFile, FileMode.Open, FileAccess.Read)) + { + Assert.AreEqual(6, s.Length); + + var buffer = new byte[s.Length]; + Assert.AreEqual(6, s.Read(buffer, offset: 0, buffer.Length)); + + CollectionAssert.AreEqual(new byte[] { 4, 3, 7, 9, 10, 11 }, buffer); + + // Ensure we've reached end of the stream + Assert.AreEqual(-1, s.ReadByte()); + } + + // switch from read to write mode, and back to read mode and finally + // append a byte + using (var s = client.Open(remoteFile, FileMode.Open, FileAccess.ReadWrite)) + { + Assert.AreEqual(4, s.ReadByte()); + Assert.AreEqual(3, s.ReadByte()); + Assert.AreEqual(7, s.ReadByte()); + + s.Write(new byte[] { 0, 1, 6, 4 }, 1, 2); + + Assert.AreEqual(11, s.ReadByte()); + + // Ensure we've reached end of the stream + Assert.AreEqual(-1, s.ReadByte()); + + s.WriteByte(12); + } + + // switch from write to read mode, and back to write mode + using (var s = client.Open(remoteFile, FileMode.Open, FileAccess.ReadWrite)) + { + s.WriteByte(5); + Assert.AreEqual(3, s.ReadByte()); + s.WriteByte(13); + + Assert.AreEqual(3, s.Position); + } + + using (var s = client.Open(remoteFile, FileMode.Open, FileAccess.Read)) + { + Assert.AreEqual(7, s.Length); + + var buffer = new byte[s.Length]; + Assert.AreEqual(7, s.Read(buffer, offset: 0, buffer.Length)); + + CollectionAssert.AreEqual(new byte[] { 5, 3, 13, 1, 6, 11, 12 }, buffer); + + // Ensure we've reached end of the stream + Assert.AreEqual(-1, s.ReadByte()); + } + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_SftpFileStream_SetLength_ReduceLength() + { + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + using (var s = client.Open(remoteFile, FileMode.CreateNew, FileAccess.Write)) + { + s.Write(new byte[] { 5, 4, 3, 2, 1 }, 1, 3); + } + + // reduce length while in write mode, with data in write buffer, and before + // current position + using (var s = client.Open(remoteFile, FileMode.Append, FileAccess.Write)) + { + s.Position = 3; + s.Write(new byte[] { 6, 7, 8, 9 }, offset: 0, count: 4); + + Assert.AreEqual(7, s.Position); + + // verify buffer has not yet been flushed + using (var fs = client.Open(remoteFile, FileMode.Open, FileAccess.Read)) + { + Assert.AreEqual(4, fs.ReadByte()); + Assert.AreEqual(3, fs.ReadByte()); + Assert.AreEqual(2, fs.ReadByte()); + + // Ensure we've reached end of the stream + Assert.AreEqual(-1, fs.ReadByte()); + } + + s.SetLength(5); + + Assert.AreEqual(5, s.Position); + + // verify that buffer was flushed and size has been modified + using (var fs = client.Open(remoteFile, FileMode.Open, FileAccess.Read)) + { + Assert.AreEqual(4, fs.ReadByte()); + Assert.AreEqual(3, fs.ReadByte()); + Assert.AreEqual(2, fs.ReadByte()); + Assert.AreEqual(6, fs.ReadByte()); + Assert.AreEqual(7, fs.ReadByte()); + + // Ensure we've reached end of the stream + Assert.AreEqual(-1, fs.ReadByte()); + } + + s.WriteByte(1); + } + + // verify that last byte was correctly written to the file + using (var s = client.Open(remoteFile, FileMode.Open, FileAccess.Read)) + { + Assert.AreEqual(6, s.Length); + + var buffer = new byte[s.Length + 2]; + Assert.AreEqual(6, s.Read(buffer, offset: 0, buffer.Length)); + + CollectionAssert.AreEqual(new byte[] { 4, 3, 2, 6, 7, 1, 0, 0 }, buffer); + + // Ensure we've reached end of the stream + Assert.AreEqual(-1, s.ReadByte()); + } + + // reduce length while in read mode, but beyond current position + using (var s = client.Open(remoteFile, FileMode.Open, FileAccess.ReadWrite)) + { + var buffer = new byte[1]; + Assert.AreEqual(1, s.Read(buffer, offset: 0, buffer.Length)); + + CollectionAssert.AreEqual(new byte[] { 4 }, buffer); + + s.SetLength(3); + + using (var w = client.Open(remoteFile, FileMode.Open, FileAccess.Write)) + { + w.Write(new byte[] { 8, 1, 6, 2 }, offset: 0, count: 4); + } + + // verify that position was not changed + Assert.AreEqual(1, s.Position); + + // verify that read buffer was cleared + Assert.AreEqual(1, s.ReadByte()); + Assert.AreEqual(6, s.ReadByte()); + Assert.AreEqual(2, s.ReadByte()); + + // Ensure we've reached end of the stream + Assert.AreEqual(-1, s.ReadByte()); + + Assert.AreEqual(4, s.Length); + } + + // reduce length while in read mode, but before current position + using (var s = client.Open(remoteFile, FileMode.Open, FileAccess.ReadWrite)) + { + var buffer = new byte[4]; + Assert.AreEqual(4, s.Read(buffer, offset: 0, buffer.Length)); + + CollectionAssert.AreEqual(new byte[] { 8, 1, 6, 2 }, buffer); + + Assert.AreEqual(4, s.Position); + + s.SetLength(3); + + // verify that position was moved to last byte + Assert.AreEqual(3, s.Position); + + using (var w = client.Open(remoteFile, FileMode.Open, FileAccess.Read)) + { + Assert.AreEqual(3, w.Length); + + Assert.AreEqual(8, w.ReadByte()); + Assert.AreEqual(1, w.ReadByte()); + Assert.AreEqual(6, w.ReadByte()); + + // Ensure we've reached end of the stream + Assert.AreEqual(-1, w.ReadByte()); + } + + // Ensure we've reached end of the stream + Assert.AreEqual(-1, s.ReadByte()); + } + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_SftpFileStream_Seek_BeyondEndOfFile_SeekOriginBegin() + { + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.BufferSize = 500; + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + // create single-byte file + using (var fs = client.OpenWrite(remoteFile)) + { + fs.WriteByte(0x04); + } + + // seek beyond EOF but not beyond buffer size + // do not write anything + using (var fs = client.OpenWrite(remoteFile)) + { + var newPosition = fs.Seek(offset: 3L, SeekOrigin.Begin); + + Assert.AreEqual(3, newPosition); + Assert.AreEqual(3, fs.Position); + } + + using (var fs = client.OpenRead(remoteFile)) + { + Assert.AreEqual(1, fs.Length); + Assert.AreEqual(0x04, fs.ReadByte()); + + // Ensure we've reached end of the stream + Assert.AreEqual(-1, fs.ReadByte()); + } + + client.DeleteFile(remoteFile); + + // create single-byte file + using (var fs = client.OpenWrite(remoteFile)) + { + fs.WriteByte(0x04); + } + + // seek beyond EOF and beyond buffer size + // do not write anything + using (var fs = client.OpenWrite(remoteFile)) + { + var newPosition = fs.Seek(offset: 700L, SeekOrigin.Begin); + + Assert.AreEqual(700, newPosition); + Assert.AreEqual(700, fs.Position); + } + + using (var fs = client.OpenRead(remoteFile)) + { + Assert.AreEqual(1, fs.Length); + Assert.AreEqual(0x04, fs.ReadByte()); + + // Ensure we've reached end of the stream + Assert.AreEqual(-1, fs.ReadByte()); + } + + client.DeleteFile(remoteFile); + + // create single-byte file + using (var fs = client.OpenWrite(remoteFile)) + { + fs.WriteByte(0x04); + } + + // seek beyond EOF but not beyond buffer size + // write less bytes than buffer size + var seekOffset = 3L; + + // buffer holding the data that we'll write to the file + var writeBuffer = GenerateRandom(size: 7); + + using (var fs = client.OpenWrite(remoteFile)) + { + var newPosition = fs.Seek(seekOffset, SeekOrigin.Begin); + + Assert.AreEqual(seekOffset, newPosition); + Assert.AreEqual(seekOffset, fs.Position); + + fs.Write(writeBuffer, offset: 0, writeBuffer.Length); + } + + using (var fs = client.OpenRead(remoteFile)) + { + Assert.AreEqual(seekOffset + writeBuffer.Length, fs.Length); + Assert.AreEqual(0x04, fs.ReadByte()); + + var soughtOverReadBufferffer = new byte[seekOffset - 1]; + Assert.AreEqual(soughtOverReadBufferffer.Length, fs.Read(soughtOverReadBufferffer, offset: 0, soughtOverReadBufferffer.Length)); + Assert.IsTrue(new byte[soughtOverReadBufferffer.Length].IsEqualTo(soughtOverReadBufferffer)); + + var readBuffer = new byte[writeBuffer.Length]; + Assert.AreEqual(readBuffer.Length, fs.Read(readBuffer, offset: 0, readBuffer.Length)); + Assert.IsTrue(writeBuffer.IsEqualTo(readBuffer)); + + // Ensure we've reached end of the stream + Assert.AreEqual(-1, fs.ReadByte()); + } + + client.DeleteFile(remoteFile); + + // create single-byte file + using (var fs = client.OpenWrite(remoteFile)) + { + fs.WriteByte(0x04); + } + + // seek beyond EOF and beyond buffer size + // write less bytes than buffer size + seekOffset = 700L; + + // buffer holding the data that we'll write to the file + writeBuffer = GenerateRandom(size: 4); + + using (var fs = client.OpenWrite(remoteFile)) + { + var newPosition = fs.Seek(seekOffset, SeekOrigin.Begin); + + Assert.AreEqual(seekOffset, newPosition); + Assert.AreEqual(seekOffset, fs.Position); + + fs.Write(writeBuffer, offset: 0, writeBuffer.Length); + } + + using (var fs = client.OpenRead(remoteFile)) + { + Assert.AreEqual(seekOffset + writeBuffer.Length, fs.Length); + Assert.AreEqual(0x04, fs.ReadByte()); + + var soughtOverReadBufferffer = new byte[seekOffset - 1]; + Assert.AreEqual(soughtOverReadBufferffer.Length, fs.Read(soughtOverReadBufferffer, offset: 0, soughtOverReadBufferffer.Length)); + Assert.IsTrue(new byte[soughtOverReadBufferffer.Length].IsEqualTo(soughtOverReadBufferffer)); + + var readBuffer = new byte[writeBuffer.Length]; + Assert.AreEqual(readBuffer.Length, fs.Read(readBuffer, offset: 0, readBuffer.Length)); + Assert.IsTrue(writeBuffer.IsEqualTo(readBuffer)); + + // Ensure we've reached end of the stream + Assert.AreEqual(-1, fs.ReadByte()); + } + + client.DeleteFile(remoteFile); + + // create single-byte file + using (var fs = client.OpenWrite(remoteFile)) + { + fs.WriteByte(0x04); + } + + // seek beyond EOF but not beyond buffer size + // write more bytes than buffer size + writeBuffer = GenerateRandom(size: 600); + + using (var fs = client.OpenWrite(remoteFile)) + { + var newPosition = fs.Seek(offset: 3L, SeekOrigin.Begin); + + Assert.AreEqual(3, newPosition); + Assert.AreEqual(3, fs.Position); + + fs.Write(writeBuffer, offset: 0, writeBuffer.Length); + } + + using (var fs = client.OpenRead(remoteFile)) + { + Assert.AreEqual(3 + writeBuffer.Length, fs.Length); + Assert.AreEqual(0x04, fs.ReadByte()); + Assert.AreEqual(0x00, fs.ReadByte()); + Assert.AreEqual(0x00, fs.ReadByte()); + + var readBuffer = new byte[writeBuffer.Length]; + Assert.AreEqual(writeBuffer.Length, fs.Read(readBuffer, offset: 0, readBuffer.Length)); + Assert.IsTrue(writeBuffer.IsEqualTo(readBuffer)); + + // Ensure we've reached end of the stream + Assert.AreEqual(-1, fs.ReadByte()); + } + + client.DeleteFile(remoteFile); + + // create single-byte file + using (var fs = client.OpenWrite(remoteFile)) + { + fs.WriteByte(0x04); + } + + // seek beyond EOF and beyond buffer size + // write more bytes than buffer size + writeBuffer = GenerateRandom(size: 600); + + using (var fs = client.OpenWrite(remoteFile)) + { + var newPosition = fs.Seek(offset: 550, SeekOrigin.Begin); + + Assert.AreEqual(550, newPosition); + Assert.AreEqual(550, fs.Position); + + fs.Write(writeBuffer, offset: 0, writeBuffer.Length); + } + + using (var fs = client.OpenRead(remoteFile)) + { + Assert.AreEqual(550 + writeBuffer.Length, fs.Length); + + Assert.AreEqual(0x04, fs.ReadByte()); + + var soughtOverReadBuffer = new byte[550 - 1]; + Assert.AreEqual(550 - 1, fs.Read(soughtOverReadBuffer, offset: 0, soughtOverReadBuffer.Length)); + Assert.IsTrue(new byte[550 - 1].IsEqualTo(soughtOverReadBuffer)); + + var readBuffer = new byte[writeBuffer.Length]; + Assert.AreEqual(writeBuffer.Length, fs.Read(readBuffer, offset: 0, readBuffer.Length)); + Assert.IsTrue(writeBuffer.IsEqualTo(readBuffer)); + + // Ensure we've reached end of the stream + Assert.AreEqual(-1, fs.ReadByte()); + } + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_SftpFileStream_Seek_BeyondEndOfFile_SeekOriginEnd() + { + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.BufferSize = 500; + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + // create single-byte file + using (var fs = client.OpenWrite(remoteFile)) + { + fs.WriteByte(0x04); + } + + // seek beyond EOF but not beyond buffer size + // do not write anything + using (var fs = client.OpenWrite(remoteFile)) + { + var newPosition = fs.Seek(offset: 3L, SeekOrigin.End); + + Assert.AreEqual(4, newPosition); + Assert.AreEqual(4, fs.Position); + } + + using (var fs = client.OpenRead(remoteFile)) + { + Assert.AreEqual(1, fs.Length); + Assert.AreEqual(0x04, fs.ReadByte()); + + // Ensure we've reached end of the stream + Assert.AreEqual(-1, fs.ReadByte()); + } + + client.DeleteFile(remoteFile); + + // create single-byte file + using (var fs = client.OpenWrite(remoteFile)) + { + fs.WriteByte(0x04); + } + + // seek beyond EOF and beyond buffer size + // do not write anything + using (var fs = client.OpenWrite(remoteFile)) + { + var newPosition = fs.Seek(offset: 700L, SeekOrigin.End); + + Assert.AreEqual(701, newPosition); + Assert.AreEqual(701, fs.Position); + } + + using (var fs = client.OpenRead(remoteFile)) + { + Assert.AreEqual(1, fs.Length); + Assert.AreEqual(0x04, fs.ReadByte()); + + // Ensure we've reached end of the stream + Assert.AreEqual(-1, fs.ReadByte()); + } + + client.DeleteFile(remoteFile); + + // create single-byte file + using (var fs = client.OpenWrite(remoteFile)) + { + fs.WriteByte(0x04); + } + + // seek beyond EOF but not beyond buffer size + // write less bytes than buffer size + var seekOffset = 3L; + + // buffer holding the data that we'll write to the file + var writeBuffer = GenerateRandom(size: 7); + + using (var fs = client.OpenWrite(remoteFile)) + { + var newPosition = fs.Seek(seekOffset, SeekOrigin.End); + + Assert.AreEqual(4, newPosition); + Assert.AreEqual(4, fs.Position); + + fs.Write(writeBuffer, offset: 0, writeBuffer.Length); + } + + using (var fs = client.OpenRead(remoteFile)) + { + Assert.AreEqual(1 + seekOffset + writeBuffer.Length, fs.Length); + Assert.AreEqual(0x04, fs.ReadByte()); + + var soughtOverReadBuffer = new byte[seekOffset]; + Assert.AreEqual(soughtOverReadBuffer.Length, fs.Read(soughtOverReadBuffer, offset: 0, soughtOverReadBuffer.Length)); + Assert.IsTrue(new byte[soughtOverReadBuffer.Length].IsEqualTo(soughtOverReadBuffer)); + + var readBuffer = new byte[writeBuffer.Length]; + Assert.AreEqual(readBuffer.Length, fs.Read(readBuffer, offset: 0, readBuffer.Length)); + Assert.IsTrue(writeBuffer.IsEqualTo(readBuffer)); + + // Ensure we've reached end of the stream + Assert.AreEqual(-1, fs.ReadByte()); + } + + client.DeleteFile(remoteFile); + + // create single-byte file + using (var fs = client.OpenWrite(remoteFile)) + { + fs.WriteByte(0x04); + } + + // seek beyond EOF and beyond buffer size + // write less bytes than buffer size + seekOffset = 700L; + + // buffer holding the data that we'll write to the file + writeBuffer = GenerateRandom(size: 4); + + using (var fs = client.OpenWrite(remoteFile)) + { + var newPosition = fs.Seek(seekOffset, SeekOrigin.End); + + Assert.AreEqual(1 + seekOffset, newPosition); + Assert.AreEqual(1 + seekOffset, fs.Position); + + fs.Write(writeBuffer, offset: 0, writeBuffer.Length); + } + + using (var fs = client.OpenRead(remoteFile)) + { + Assert.AreEqual(1 + seekOffset + writeBuffer.Length, fs.Length); + Assert.AreEqual(0x04, fs.ReadByte()); + + var soughtOverReadBuffer = new byte[seekOffset]; + Assert.AreEqual(soughtOverReadBuffer.Length, fs.Read(soughtOverReadBuffer, offset: 0, soughtOverReadBuffer.Length)); + Assert.IsTrue(new byte[soughtOverReadBuffer.Length].IsEqualTo(soughtOverReadBuffer)); + + var readBuffer = new byte[writeBuffer.Length]; + Assert.AreEqual(readBuffer.Length, fs.Read(readBuffer, offset: 0, readBuffer.Length)); + Assert.IsTrue(writeBuffer.IsEqualTo(readBuffer)); + + // Ensure we've reached end of the stream + Assert.AreEqual(-1, fs.ReadByte()); + } + + client.DeleteFile(remoteFile); + + // create single-byte file + using (var fs = client.OpenWrite(remoteFile)) + { + fs.WriteByte(0x04); + } + + // seek beyond EOF but not beyond buffer size + // write more bytes than buffer size + seekOffset = 3L; + writeBuffer = GenerateRandom(size: 600); + + using (var fs = client.OpenWrite(remoteFile)) + { + var newPosition = fs.Seek(seekOffset, SeekOrigin.End); + + Assert.AreEqual(1 + seekOffset, newPosition); + Assert.AreEqual(1 + seekOffset, fs.Position); + + fs.Write(writeBuffer, offset: 0, writeBuffer.Length); + } + + using (var fs = client.OpenRead(remoteFile)) + { + Assert.AreEqual(1 + seekOffset + writeBuffer.Length, fs.Length); + Assert.AreEqual(0x04, fs.ReadByte()); + + var soughtOverReadBuffer = new byte[seekOffset]; + Assert.AreEqual(soughtOverReadBuffer.Length, fs.Read(soughtOverReadBuffer, offset: 0, soughtOverReadBuffer.Length)); + Assert.IsTrue(new byte[soughtOverReadBuffer.Length].IsEqualTo(soughtOverReadBuffer)); + + var readBuffer = new byte[writeBuffer.Length]; + Assert.AreEqual(writeBuffer.Length, fs.Read(readBuffer, offset: 0, readBuffer.Length)); + Assert.IsTrue(writeBuffer.IsEqualTo(readBuffer)); + + // Ensure we've reached end of the stream + Assert.AreEqual(-1, fs.ReadByte()); + } + + client.DeleteFile(remoteFile); + + // create single-byte file + using (var fs = client.OpenWrite(remoteFile)) + { + fs.WriteByte(0x04); + } + + // seek beyond EOF and beyond buffer size + // write more bytes than buffer size + seekOffset = 550L; + writeBuffer = GenerateRandom(size: 600); + + using (var fs = client.OpenWrite(remoteFile)) + { + var newPosition = fs.Seek(seekOffset, SeekOrigin.End); + + Assert.AreEqual(1 + seekOffset, newPosition); + Assert.AreEqual(1 + seekOffset, fs.Position); + + fs.Write(writeBuffer, offset: 0, writeBuffer.Length); + } + + using (var fs = client.OpenRead(remoteFile)) + { + Assert.AreEqual(1 + seekOffset + writeBuffer.Length, fs.Length); + + Assert.AreEqual(0x04, fs.ReadByte()); + + var soughtOverReadBuffer = new byte[seekOffset]; + Assert.AreEqual(soughtOverReadBuffer.Length, fs.Read(soughtOverReadBuffer, offset: 0, soughtOverReadBuffer.Length)); + Assert.IsTrue(new byte[soughtOverReadBuffer.Length].IsEqualTo(soughtOverReadBuffer)); + + var readBuffer = new byte[writeBuffer.Length]; + Assert.AreEqual(writeBuffer.Length, fs.Read(readBuffer, offset: 0, readBuffer.Length)); + Assert.IsTrue(writeBuffer.IsEqualTo(readBuffer)); + + // Ensure we've reached end of the stream + Assert.AreEqual(-1, fs.ReadByte()); + } + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_SftpFileStream_Seek_NegativeOffSet_SeekOriginEnd() + { + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.BufferSize = 500; + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + using (var fs = client.OpenWrite(remoteFile)) + { + fs.WriteByte(0x04); + fs.WriteByte(0x07); + fs.WriteByte(0x05); + } + + // seek within file and not beyond buffer size + // do not write anything + using (var fs = client.OpenWrite(remoteFile)) + { + var newPosition = fs.Seek(offset: -2L, SeekOrigin.End); + + Assert.AreEqual(1, newPosition); + Assert.AreEqual(1, fs.Position); + } + + using (var fs = client.OpenRead(remoteFile)) + { + Assert.AreEqual(3, fs.Length); + Assert.AreEqual(0x04, fs.ReadByte()); + Assert.AreEqual(0x07, fs.ReadByte()); + Assert.AreEqual(0x05, fs.ReadByte()); + + // Ensure we've reached end of the stream + Assert.AreEqual(-1, fs.ReadByte()); + } + + client.DeleteFile(remoteFile); + + // buffer holding the data that we'll write to the file + var writeBuffer = GenerateRandom(size: (int) client.BufferSize + 200); + + using (var fs = client.OpenWrite(remoteFile)) + { + fs.Write(writeBuffer, offset: 0, writeBuffer.Length); + } + + // seek within EOF and beyond buffer size + // do not write anything + using (var fs = client.OpenWrite(remoteFile)) + { + var newPosition = fs.Seek(offset: -100L, SeekOrigin.End); + + Assert.AreEqual(600, newPosition); + Assert.AreEqual(600, fs.Position); + } + + using (var fs = client.OpenRead(remoteFile)) + { + Assert.AreEqual(writeBuffer.Length, fs.Length); + + var readBuffer = new byte[writeBuffer.Length]; + Assert.AreEqual(writeBuffer.Length, fs.Read(readBuffer, offset: 0, readBuffer.Length)); + Assert.IsTrue(writeBuffer.IsEqualTo(readBuffer)); + + // Ensure we've reached end of the stream + Assert.AreEqual(-1, fs.ReadByte()); + } + + client.DeleteFile(remoteFile); + + // seek within EOF and within buffer size + // write less bytes than buffer size + using (var fs = client.OpenWrite(remoteFile)) + { + fs.Write(writeBuffer, offset: 0, writeBuffer.Length); + + var newPosition = fs.Seek(offset: -3, SeekOrigin.End); + + Assert.AreEqual(697, newPosition); + Assert.AreEqual(697, fs.Position); + + fs.WriteByte(0x01); + fs.WriteByte(0x05); + fs.WriteByte(0x04); + fs.WriteByte(0x07); + } + + using (var fs = client.OpenRead(remoteFile)) + { + Assert.AreEqual(writeBuffer.Length + 1, fs.Length); + + var readBuffer = new byte[writeBuffer.Length - 3]; + Assert.AreEqual(readBuffer.Length, fs.Read(readBuffer, offset: 0, readBuffer.Length)); + Assert.IsTrue(readBuffer.SequenceEqual(writeBuffer.Take(readBuffer.Length))); + + Assert.AreEqual(0x01, fs.ReadByte()); + Assert.AreEqual(0x05, fs.ReadByte()); + Assert.AreEqual(0x04, fs.ReadByte()); + Assert.AreEqual(0x07, fs.ReadByte()); + + // Ensure we've reached end of the stream + Assert.AreEqual(-1, fs.ReadByte()); + } + + client.DeleteFile(remoteFile); + + // buffer holding the data that we'll write to the file + writeBuffer = GenerateRandom(size: (int) client.BufferSize * 4); + + // seek within EOF and beyond buffer size + // write less bytes than buffer size + using (var fs = client.OpenWrite(remoteFile)) + { + fs.Write(writeBuffer, offset: 0, writeBuffer.Length); + + var newPosition = fs.Seek(offset: -(client.BufferSize * 2), SeekOrigin.End); + + Assert.AreEqual(1000, newPosition); + Assert.AreEqual(1000, fs.Position); + + fs.WriteByte(0x01); + fs.WriteByte(0x05); + fs.WriteByte(0x04); + fs.WriteByte(0x07); + } + + using (var fs = client.OpenRead(remoteFile)) + { + Assert.AreEqual(writeBuffer.Length, fs.Length); + + // First part of file should not have been touched + var readBuffer = new byte[(int) client.BufferSize * 2]; + Assert.AreEqual(readBuffer.Length, fs.Read(readBuffer, offset: 0, readBuffer.Length)); + Assert.IsTrue(readBuffer.SequenceEqual(writeBuffer.Take(readBuffer.Length))); + + // Check part that should have been updated + Assert.AreEqual(0x01, fs.ReadByte()); + Assert.AreEqual(0x05, fs.ReadByte()); + Assert.AreEqual(0x04, fs.ReadByte()); + Assert.AreEqual(0x07, fs.ReadByte()); + + // Remaining bytes should not have been touched + readBuffer = new byte[((int) client.BufferSize * 2) - 4]; + Assert.AreEqual(readBuffer.Length, fs.Read(readBuffer, offset: 0, readBuffer.Length)); + Assert.IsTrue(readBuffer.SequenceEqual(writeBuffer.Skip(((int)client.BufferSize * 2) + 4).Take(readBuffer.Length))); + + // Ensure we've reached end of the stream + Assert.AreEqual(-1, fs.ReadByte()); + } + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + /// https://github.com/sshnet/SSH.NET/issues/253 + [TestMethod] + public void Sftp_SftpFileStream_Seek_Issue253() + { + var buf = Encoding.UTF8.GetBytes("123456"); + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + using (var ws = client.OpenWrite(remoteFile)) + { + ws.Write(buf, offset: 0, count: 3); + } + + using (var ws = client.OpenWrite(remoteFile)) + { + var newPosition = ws.Seek(offset: 3, SeekOrigin.Begin); + + Assert.AreEqual(3, newPosition); + Assert.AreEqual(3, ws.Position); + + ws.Write(buf, 3, 3); + } + + var actual = client.ReadAllText(remoteFile, Encoding.UTF8); + Assert.AreEqual("123456", actual); + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_SftpFileStream_Seek_WithinReadBuffer() + { + var originalContent = GenerateRandom(size: 800); + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.BufferSize = 500; + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + using (var fs = client.OpenWrite(remoteFile)) + { + fs.Write(originalContent, offset: 0, originalContent.Length); + } + + using (var fs = client.OpenRead(remoteFile)) + { + var readBuffer = new byte[200]; + + Assert.AreEqual(readBuffer.Length, fs.Read(readBuffer, offset: 0, readBuffer.Length)); + + var newPosition = fs.Seek(offset: 3L, SeekOrigin.Begin); + + Assert.AreEqual(3L, newPosition); + Assert.AreEqual(3L, fs.Position); + } + + client.DeleteFile(remoteFile); + + #region Seek beyond EOF and beyond buffer size do not write anything + + // create single-byte file + using (var fs = client.OpenWrite(remoteFile)) + { + fs.WriteByte(0x04); + } + + using (var fs = client.OpenRead(remoteFile)) + { + Assert.AreEqual(1, fs.Length); + Assert.AreEqual(0x04, fs.ReadByte()); + + // Ensure we've reached end of the stream + Assert.AreEqual(-1, fs.ReadByte()); + } + + using (var fs = client.OpenWrite(remoteFile)) + { + var newPosition = fs.Seek(offset: 700L, SeekOrigin.Begin); + + Assert.AreEqual(700L, newPosition); + Assert.AreEqual(700L, fs.Position); + } + + using (var fs = client.OpenRead(remoteFile)) + { + Assert.AreEqual(1, fs.Length); + Assert.AreEqual(0x04, fs.ReadByte()); + + // Ensure we've reached end of the stream + Assert.AreEqual(-1, fs.ReadByte()); + } + + client.DeleteFile(remoteFile); + + #endregion Seek beyond EOF and beyond buffer size do not write anything + + #region Seek beyond EOF but not beyond buffer size and write less bytes than buffer size + + // create single-byte file + using (var fs = client.OpenWrite(remoteFile)) + { + fs.WriteByte(0x04); + } + + var seekOffset = 3L; + var writeBuffer = GenerateRandom(size: 7); + + using (var fs = client.OpenWrite(remoteFile)) + { + var newPosition = fs.Seek(seekOffset, SeekOrigin.Begin); + + Assert.AreEqual(seekOffset, newPosition); + Assert.AreEqual(seekOffset, fs.Position); + + fs.Write(writeBuffer, offset: 0, writeBuffer.Length); + } + + using (var fs = client.OpenRead(remoteFile)) + { + Assert.AreEqual(seekOffset + writeBuffer.Length, fs.Length); + Assert.AreEqual(0x04, fs.ReadByte()); + + var soughtOverReadBuffer = new byte[seekOffset - 1]; + Assert.AreEqual(soughtOverReadBuffer.Length, fs.Read(soughtOverReadBuffer, offset: 0, soughtOverReadBuffer.Length)); + Assert.IsTrue(new byte[soughtOverReadBuffer.Length].IsEqualTo(soughtOverReadBuffer)); + + var readBuffer = new byte[writeBuffer.Length]; + Assert.AreEqual(readBuffer.Length, fs.Read(readBuffer, offset: 0, readBuffer.Length)); + Assert.IsTrue(writeBuffer.IsEqualTo(readBuffer)); + + // Ensure we've reached end of the stream + Assert.AreEqual(-1, fs.ReadByte()); + } + + client.DeleteFile(remoteFile); + + #endregion Seek beyond EOF but not beyond buffer size and write less bytes than buffer size + + #region Seek beyond EOF and beyond buffer size and write less bytes than buffer size + + // create single-byte file + using (var fs = client.OpenWrite(remoteFile)) + { + fs.WriteByte(0x04); + } + + seekOffset = 700L; + writeBuffer = GenerateRandom(size: 4); + + using (var fs = client.OpenWrite(remoteFile)) + { + var newPosition = fs.Seek(seekOffset, SeekOrigin.Begin); + + Assert.AreEqual(seekOffset, newPosition); + Assert.AreEqual(seekOffset, fs.Position); + + fs.Write(writeBuffer, offset: 0, writeBuffer.Length); + } + + using (var fs = client.OpenRead(remoteFile)) + { + Assert.AreEqual(seekOffset + writeBuffer.Length, fs.Length); + Assert.AreEqual(0x04, fs.ReadByte()); + + var soughtOverReadBufferffer = new byte[seekOffset - 1]; + Assert.AreEqual(soughtOverReadBufferffer.Length, fs.Read(soughtOverReadBufferffer, offset: 0, soughtOverReadBufferffer.Length)); + Assert.IsTrue(new byte[soughtOverReadBufferffer.Length].IsEqualTo(soughtOverReadBufferffer)); + + var readBuffer = new byte[writeBuffer.Length]; + Assert.AreEqual(readBuffer.Length, fs.Read(readBuffer, offset: 0, readBuffer.Length)); + Assert.IsTrue(writeBuffer.IsEqualTo(readBuffer)); + + // Ensure we've reached end of the stream + Assert.AreEqual(-1, fs.ReadByte()); + } + + client.DeleteFile(remoteFile); + + #endregion Seek beyond EOF and beyond buffer size and write less bytes than buffer size + + #region Seek beyond EOF but not beyond buffer size and write more bytes than buffer size + + // create single-byte file + using (var fs = client.OpenWrite(remoteFile)) + { + fs.WriteByte(0x04); + } + + seekOffset = 3L; + writeBuffer = GenerateRandom(size: 600); + + using (var fs = client.OpenWrite(remoteFile)) + { + var newPosition = fs.Seek(seekOffset, SeekOrigin.Begin); + + Assert.AreEqual(seekOffset, newPosition); + Assert.AreEqual(seekOffset, fs.Position); + + fs.Write(writeBuffer, offset: 0, writeBuffer.Length); + } + + using (var fs = client.OpenRead(remoteFile)) + { + Assert.AreEqual(seekOffset + writeBuffer.Length, fs.Length); + Assert.AreEqual(0x04, fs.ReadByte()); + Assert.AreEqual(0x00, fs.ReadByte()); + Assert.AreEqual(0x00, fs.ReadByte()); + + var readBuffer = new byte[writeBuffer.Length]; + Assert.AreEqual(writeBuffer.Length, fs.Read(readBuffer, offset: 0, readBuffer.Length)); + Assert.IsTrue(writeBuffer.IsEqualTo(readBuffer)); + + // Ensure we've reached end of the stream + Assert.AreEqual(-1, fs.ReadByte()); + } + + client.DeleteFile(remoteFile); + + #endregion Seek beyond EOF but not beyond buffer size and write more bytes than buffer size + + #region Seek beyond EOF and beyond buffer size and write more bytes than buffer size + + // create single-byte file + using (var fs = client.OpenWrite(remoteFile)) + { + fs.WriteByte(0x04); + } + + seekOffset = 550L; + writeBuffer = GenerateRandom(size: 600); + + using (var fs = client.OpenWrite(remoteFile)) + { + var newPosition = fs.Seek(seekOffset, SeekOrigin.Begin); + + Assert.AreEqual(seekOffset, newPosition); + Assert.AreEqual(seekOffset, fs.Position); + + fs.Write(writeBuffer, offset: 0, writeBuffer.Length); + } + + using (var fs = client.OpenRead(remoteFile)) + { + Assert.AreEqual(seekOffset + writeBuffer.Length, fs.Length); + + Assert.AreEqual(0x04, fs.ReadByte()); + + var soughtOverReadBufferffer = new byte[seekOffset - 1]; + Assert.AreEqual(seekOffset - 1, fs.Read(soughtOverReadBufferffer, offset: 0, soughtOverReadBufferffer.Length)); + Assert.IsTrue(new byte[seekOffset - 1].IsEqualTo(soughtOverReadBufferffer)); + + var readBuffer = new byte[writeBuffer.Length]; + Assert.AreEqual(writeBuffer.Length, fs.Read(readBuffer, offset: 0, readBuffer.Length)); + Assert.IsTrue(writeBuffer.IsEqualTo(readBuffer)); + + // Ensure we've reached end of the stream + Assert.AreEqual(-1, fs.ReadByte()); + } + + client.DeleteFile(remoteFile); + + #endregion Seek beyond EOF and beyond buffer size and write more bytes than buffer size + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_SftpFileStream_SetLength_FileDoesNotExist() + { + var size = new Random().Next(500, 5000); + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + using (var s = client.Open(remoteFile, FileMode.Append, FileAccess.Write)) + { + s.SetLength(size); + } + + Assert.IsTrue(client.Exists(remoteFile)); + + var attributes = client.GetAttributes(remoteFile); + Assert.IsTrue(attributes.IsRegularFile); + Assert.AreEqual(size, attributes.Size); + + using (var downloaded = new MemoryStream()) + { + client.DownloadFile(remoteFile, downloaded); + downloaded.Position = 0; + Assert.AreEqual(CreateHash(new byte[size]), CreateHash(downloaded)); + } + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_Open_Append_Write_ExistingFile() + { + const int fileSize = 5 * 1024; + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + using (var input = CreateMemoryStream(fileSize)) + { + input.Position = 0; + + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + client.UploadFile(input, remoteFile); + + using (var s = client.Open(remoteFile, FileMode.Append, FileAccess.Write)) + { + var buffer = new byte[] { 0x05, 0x0f, 0x0d, 0x0a, 0x04 }; + s.Write(buffer, offset: 0, buffer.Length); + input.Write(buffer, offset: 0, buffer.Length); + } + + using (var downloaded = new MemoryStream()) + { + client.DownloadFile(remoteFile, downloaded); + + input.Position = 0; + downloaded.Position = 0; + Assert.AreEqual(CreateHash(input), CreateHash(downloaded)); + } + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_Open_Append_Write_FileDoesNotExist() + { + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + #region Verify if merely opening the file for append creates a zero-byte file + + using (client.Open(remoteFile, FileMode.Append, FileAccess.Write)) + { + } + + Assert.IsTrue(client.Exists(remoteFile)); + + var attributes = client.GetAttributes(remoteFile); + Assert.IsTrue(attributes.IsRegularFile); + Assert.AreEqual(0L, attributes.Size); + + #endregion Verify if merely opening the file for append creates it + + client.DeleteFile(remoteFile); + + #region Verify if content is actually written to the file + + var content = GenerateRandom(size: 100); + + using (var s = client.Open(remoteFile, FileMode.Append, FileAccess.Write)) + { + s.Write(content, offset: 0, content.Length); + } + + using (var downloaded = new MemoryStream()) + { + client.DownloadFile(remoteFile, downloaded); + downloaded.Position = 0; + Assert.AreEqual(CreateHash(content), CreateHash(downloaded)); + } + + #endregion Verify if content is actually written to the file + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + + + [TestMethod] + public void Sftp_Open_PathAndMode_ModeIsCreate_FileDoesNotExist() + { + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + #region Verify if merely opening the file for create creates a zero-byte file + + using (client.Open(remoteFile, FileMode.Create)) + { + } + + Assert.IsTrue(client.Exists(remoteFile)); + + var attributes = client.GetAttributes(remoteFile); + Assert.IsTrue(attributes.IsRegularFile); + Assert.AreEqual(0L, attributes.Size); + + #endregion Verify if merely opening the file for create creates a zero-byte file + + client.DeleteFile(remoteFile); + + #region Verify if content is actually written to the file + + var content = GenerateRandom(size: 100); + + using (var s = client.Open(remoteFile, FileMode.Create)) + { + s.Write(content, offset: 0, content.Length); + } + + using (var downloaded = new MemoryStream()) + { + client.DownloadFile(remoteFile, downloaded); + downloaded.Position = 0; + Assert.AreEqual(CreateHash(content), CreateHash(downloaded)); + } + + #endregion Verify if content is actually written to the file + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_Open_PathAndMode_ModeIsCreate_ExistingFile() + { + const int fileSize = 5 * 1024; + var newContent = new byte[] { 0x07, 0x03, 0x02, 0x0b }; + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + using (var input = CreateMemoryStream(fileSize)) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + input.Position = 0; + client.UploadFile(input, remoteFile); + + using (var stream = client.Open(remoteFile, FileMode.Create)) + { + // Verify if merely opening the file for create overwrites the file + var attributes = client.GetAttributes(remoteFile); + Assert.IsTrue(attributes.IsRegularFile); + Assert.AreEqual(0L, attributes.Size); + + stream.Write(newContent, offset: 0, newContent.Length); + stream.Position = 0; + + Assert.AreEqual(CreateHash(newContent), CreateHash(stream)); + } + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_Open_PathAndModeAndAccess_ModeIsCreate_AccessIsReadWrite_FileDoesNotExist() + { + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + #region Verify if merely opening the file for create creates a zero-byte file + + using (client.Open(remoteFile, FileMode.Create, FileAccess.ReadWrite)) + { + } + + Assert.IsTrue(client.Exists(remoteFile)); + + var attributes = client.GetAttributes(remoteFile); + Assert.IsTrue(attributes.IsRegularFile); + Assert.AreEqual(0L, attributes.Size); + + #endregion Verify if merely opening the file for create creates a zero-byte file + + client.DeleteFile(remoteFile); + + #region Verify if content is actually written to the file + + var content = GenerateRandom(size: 100); + + using (var s = client.Open(remoteFile, FileMode.Create, FileAccess.ReadWrite)) + { + s.Write(content, offset: 0, content.Length); + } + + using (var downloaded = new MemoryStream()) + { + client.DownloadFile(remoteFile, downloaded); + downloaded.Position = 0; + Assert.AreEqual(CreateHash(content), CreateHash(downloaded)); + } + + #endregion Verify if content is actually written to the file + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_Open_PathAndModeAndAccess_ModeIsCreate_AccessIsReadWrite_ExistingFile() + { + const int fileSize = 5 * 1024; + var newContent = new byte[] { 0x07, 0x03, 0x02, 0x0b }; + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + using (var input = CreateMemoryStream(fileSize)) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + input.Position = 0; + client.UploadFile(input, remoteFile); + + using (var stream = client.Open(remoteFile, FileMode.Create, FileAccess.ReadWrite)) + { + // Verify if merely opening the file for create overwrites the file + var attributes = client.GetAttributes(remoteFile); + Assert.IsTrue(attributes.IsRegularFile); + Assert.AreEqual(0L, attributes.Size); + + stream.Write(newContent, offset: 0, newContent.Length); + stream.Position = 0; + + Assert.AreEqual(CreateHash(newContent), CreateHash(stream)); + } + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_Open_PathAndModeAndAccess_ModeIsCreate_AccessIsWrite_ExistingFile() + { + // use new content that contains less bytes than original content to + // verify whether file is first truncated + var originalContent = new byte[] { 0x05, 0x0f, 0x0d, 0x0a, 0x04 }; + var newContent = new byte[] { 0x07, 0x03, 0x02, 0x0b }; + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + client.WriteAllBytes(remoteFile, originalContent); + + using (var s = client.Open(remoteFile, FileMode.Create, FileAccess.Write)) + { + s.Write(newContent, offset: 0, newContent.Length); + } + + using (var downloaded = new MemoryStream()) + { + client.DownloadFile(remoteFile, downloaded); + + downloaded.Position = 0; + Assert.AreEqual(CreateHash(newContent), CreateHash(downloaded)); + } + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_Open_PathAndModeAndAccess_ModeIsCreate_AccessIsWrite_FileDoesNotExist() + { + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + #region Verify if merely opening the file for create creates a zero-byte file + + using (client.Open(remoteFile, FileMode.Create, FileAccess.Write)) + { + } + + Assert.IsTrue(client.Exists(remoteFile)); + + var attributes = client.GetAttributes(remoteFile); + Assert.IsTrue(attributes.IsRegularFile); + Assert.AreEqual(0L, attributes.Size); + + #endregion Verify if merely opening the file for create creates a zero-byte file + + client.DeleteFile(remoteFile); + + #region Verify if content is actually written to the file + + var content = GenerateRandom(size: 100); + + using (var s = client.Open(remoteFile, FileMode.Create, FileAccess.Write)) + { + s.Write(content, offset: 0, content.Length); + } + + using (var downloaded = new MemoryStream()) + { + client.DownloadFile(remoteFile, downloaded); + downloaded.Position = 0; + Assert.AreEqual(CreateHash(content), CreateHash(downloaded)); + } + + #endregion Verify if content is actually written to the file + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_Open_CreateNew_Write_ExistingFile() + { + const int fileSize = 5 * 1024; + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + using (var input = CreateMemoryStream(fileSize)) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + input.Position = 0; + + try + { + client.UploadFile(input, remoteFile); + + Stream stream = null; + + try + { + stream = client.Open(remoteFile, FileMode.CreateNew, FileAccess.Write); + Assert.Fail(); + } + catch (SshException) + { + } + finally + { + stream?.Dispose(); + } + + // Verify that the file was not modified + using (var downloaded = new MemoryStream()) + { + client.DownloadFile(remoteFile, downloaded); + + input.Position = 0; + downloaded.Position = 0; + Assert.AreEqual(CreateHash(input), CreateHash(downloaded)); + } + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_Open_CreateNew_Write_FileDoesNotExist() + { + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + #region Verify if merely opening the file creates a zero-byte file + + using (client.Open(remoteFile, FileMode.CreateNew, FileAccess.Write)) + { + } + + Assert.IsTrue(client.Exists(remoteFile)); + + var attributes = client.GetAttributes(remoteFile); + Assert.IsTrue(attributes.IsRegularFile); + Assert.AreEqual(0L, attributes.Size); + + #endregion Verify if merely opening the file creates it + + client.DeleteFile(remoteFile); + + #region Verify if content is actually written to the file + + var content = GenerateRandom(size: 100); + + using (var s = client.Open(remoteFile, FileMode.CreateNew, FileAccess.Write)) + { + s.Write(content, offset: 0, content.Length); + } + + using (var downloaded = new MemoryStream()) + { + client.DownloadFile(remoteFile, downloaded); + downloaded.Position = 0; + Assert.AreEqual(CreateHash(content), CreateHash(downloaded)); + } + + #endregion Verify if content is actually written to the file + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_Open_Open_Write_ExistingFile() + { + // use new content that contains less bytes than original content to + // verify whether file is first truncated + var originalContent = new byte[] { 0x05, 0x0f, 0x0d, 0x0a, 0x04 }; + var newContent = new byte[] { 0x07, 0x03, 0x02, 0x0b }; + var expectedContent = new byte[] { 0x07, 0x03, 0x02, 0x0b, 0x04 }; + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + client.WriteAllBytes(remoteFile, originalContent); + + using (var s = client.Open(remoteFile, FileMode.Open, FileAccess.Write)) + { + s.Write(newContent, offset: 0, newContent.Length); + } + + using (var downloaded = new MemoryStream()) + { + client.DownloadFile(remoteFile, downloaded); + + downloaded.Position = 0; + Assert.AreEqual(CreateHash(expectedContent), CreateHash(downloaded)); + } + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_Open_Open_Write_FileDoesNotExist() + { + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + Stream stream = null; + + try + { + stream = client.Open(remoteFile, FileMode.Open, FileAccess.Write); + Assert.Fail(); + } + catch (SshException) + { + } + finally + { + stream?.Dispose(); + } + + Assert.IsFalse(client.Exists(remoteFile)); + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_Open_OpenOrCreate_Write_ExistingFile() + { + // use new content that contains less bytes than original content to + // verify whether file is first truncated + var originalContent = new byte[] { 0x05, 0x0f, 0x0d, 0x0a, 0x04 }; + var newContent = new byte[] { 0x07, 0x03, 0x02, 0x0b }; + var expectedContent = new byte[] { 0x07, 0x03, 0x02, 0x0b, 0x04 }; + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + client.WriteAllBytes(remoteFile, originalContent); + + using (var s = client.Open(remoteFile, FileMode.OpenOrCreate, FileAccess.Write)) + { + s.Write(newContent, offset: 0, newContent.Length); + } + + using (var downloaded = new MemoryStream()) + { + client.DownloadFile(remoteFile, downloaded); + + downloaded.Position = 0; + Assert.AreEqual(CreateHash(expectedContent), CreateHash(downloaded)); + } + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_Open_OpenOrCreate_Write_FileDoesNotExist() + { + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + #region Verify if merely opening the file creates a zero-byte file + + using (client.Open(remoteFile, FileMode.OpenOrCreate, FileAccess.Write)) + { + } + + Assert.IsTrue(client.Exists(remoteFile)); + + var attributes = client.GetAttributes(remoteFile); + Assert.IsTrue(attributes.IsRegularFile); + Assert.AreEqual(0L, attributes.Size); + + #endregion Verify if merely opening the file creates it + + client.DeleteFile(remoteFile); + + #region Verify if content is actually written to the file + + var content = GenerateRandom(size: 100); + + using (var s = client.Open(remoteFile, FileMode.OpenOrCreate, FileAccess.Write)) + { + s.Write(content, offset: 0, content.Length); + } + + using (var downloaded = new MemoryStream()) + { + client.DownloadFile(remoteFile, downloaded); + downloaded.Position = 0; + Assert.AreEqual(CreateHash(content), CreateHash(downloaded)); + } + + #endregion Verify if content is actually written to the file + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + + + + + + + [TestMethod] + public void Sftp_Open_Truncate_Write_ExistingFile() + { + const int fileSize = 5 * 1024; + + // use new content that contains less bytes than original content to + // verify whether file is first truncated + var originalContent = new byte[] { 0x05, 0x0f, 0x0d, 0x0a, 0x04 }; + var newContent = new byte[] { 0x07, 0x03, 0x02, 0x0b }; + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + using (var input = CreateMemoryStream(fileSize)) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + input.Position = 0; + + try + { + client.WriteAllBytes(remoteFile, originalContent); + + using (var s = client.Open(remoteFile, FileMode.Truncate, FileAccess.Write)) + { + s.Write(newContent, offset: 0, newContent.Length); + } + + using (var downloaded = new MemoryStream()) + { + client.DownloadFile(remoteFile, downloaded); + + input.Position = 0; + downloaded.Position = 0; + Assert.AreEqual(CreateHash(newContent), CreateHash(downloaded)); + } + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_Open_Truncate_Write_FileDoesNotExist() + { + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + Stream stream = null; + + try + { + stream = client.Open(remoteFile, FileMode.Truncate, FileAccess.Write); + Assert.Fail(); + } + catch (SshException) + { + } + + Assert.IsFalse(client.Exists(remoteFile)); + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_OpenRead() + { + const int fileSize = 5 * 1024 * 1024; + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var remoteFile = GenerateUniqueRemoteFileName(); + + SftpCreateRemoteFile(client, remoteFile, fileSize); + + try + { + using (var s = client.OpenRead(remoteFile)) + { + var buffer = new byte[s.Length]; + + var stopwatch = new Stopwatch(); + stopwatch.Start(); + + var bytesRead = s.Read(buffer, offset: 0, buffer.Length); + + stopwatch.Stop(); + + var transferSpeed = CalculateTransferSpeed(bytesRead, stopwatch.ElapsedMilliseconds); + Console.WriteLine(@"Elapsed: {0} ms", stopwatch.ElapsedMilliseconds); + Console.WriteLine(@"Transfer speed: {0:N2} KB/s", transferSpeed); + + Assert.AreEqual(fileSize, bytesRead); + } + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_SetLastAccessTime() + { + var testFilePath = "/home/sshnet/test-file.txt"; + var testContent = "File"; + using var client = new SftpClient(_connectionInfoFactory.Create()); + client.Connect(); + + using var fileStream = new MemoryStream(Encoding.UTF8.GetBytes(testContent)); + var currentTime = DateTime.Now; + + client.UploadFile(fileStream, testFilePath); + + try + { + var time = client.GetLastAccessTime(testFilePath); + + DateTimeAssert.AreEqual(currentTime.TruncateToWholeSeconds(), time); + + var newTime = new DateTime(1986, 03, 15, 01, 02, 03, 123, DateTimeKind.Local); + + client.SetLastAccessTime(testFilePath, newTime); + time = client.GetLastAccessTime(testFilePath); + + DateTimeAssert.AreEqual(newTime.TruncateToWholeSeconds(), time); + } + finally + { + client.DeleteFile(testFilePath); + } + } + + + [TestMethod] + public void Sftp_SetLastAccessTimeUtc() + { + var testFilePath = "/home/sshnet/test-file.txt"; + var testContent = "File"; + using var client = new SftpClient(_connectionInfoFactory.Create()); + client.Connect(); + + using var fileStream = new MemoryStream(Encoding.UTF8.GetBytes(testContent)); + var currentTime = DateTime.UtcNow; + + client.UploadFile(fileStream, testFilePath); + try + { + var time = client.GetLastAccessTimeUtc(testFilePath); + + DateTimeAssert.AreEqual(currentTime.TruncateToWholeSeconds(), time); + + var newTime = new DateTime(1986, 03, 15, 01, 02, 03, 123, DateTimeKind.Utc); + + client.SetLastAccessTimeUtc(testFilePath, newTime); + time = client.GetLastAccessTimeUtc(testFilePath); + + DateTimeAssert.AreEqual(newTime.TruncateToWholeSeconds(), time); + } + finally + { + client.DeleteFile(testFilePath); + } + } + + [TestMethod] + public void Sftp_SetLastWriteTime() + { + var testFilePath = "/home/sshnet/test-file.txt"; + var testContent = "File"; + using var client = new SftpClient(_connectionInfoFactory.Create()); + client.Connect(); + + using var fileStream = new MemoryStream(Encoding.UTF8.GetBytes(testContent)); + var currentTime = DateTime.Now; + + client.UploadFile(fileStream, testFilePath); + try + { + var time = client.GetLastWriteTime(testFilePath); + + DateTimeAssert.AreEqual(currentTime.TruncateToWholeSeconds(), time); + + var newTime = new DateTime(1986, 03, 15, 01, 02, 03, 123, DateTimeKind.Local); + + client.SetLastWriteTime(testFilePath, newTime); + time = client.GetLastWriteTime(testFilePath); + + DateTimeAssert.AreEqual(newTime.TruncateToWholeSeconds(), time); + } + finally + { + client.DeleteFile(testFilePath); + } + } + + [TestMethod] + public void Sftp_SetLastWriteTimeUtc() + { + var testFilePath = "/home/sshnet/test-file.txt"; + var testContent = "File"; + using var client = new SftpClient(_connectionInfoFactory.Create()); + client.Connect(); + + using var fileStream = new MemoryStream(Encoding.UTF8.GetBytes(testContent)); + var currentTime = DateTime.UtcNow; + + client.UploadFile(fileStream, testFilePath); + try + { + var time = client.GetLastWriteTimeUtc(testFilePath); + + DateTimeAssert.AreEqual(currentTime.TruncateToWholeSeconds(), time); + + var newTime = new DateTime(1986, 03, 15, 01, 02, 03, 123, DateTimeKind.Utc); + + client.SetLastWriteTimeUtc(testFilePath, newTime); + time = client.GetLastWriteTimeUtc(testFilePath); + + DateTimeAssert.AreEqual(newTime.TruncateToWholeSeconds(), time); + } + finally + { + client.DeleteFile(testFilePath); + } + } + + private static IEnumerable GetSftpUploadFileFileStreamData() + { + yield return new object[] { 0 }; + yield return new object[] { 5 * 1024 * 1024 }; + } + + private static Encoding GetRandomEncoding() + { + var random = new Random().Next(1, 3); + switch (random) + { + case 1: + return Encoding.Unicode; + case 2: + return Encoding.UTF8; + case 3: + return Encoding.UTF32; + default: + throw new NotImplementedException(); + } + } + + private static byte[] GetBytesWithPreamble(string text, Encoding encoding) + { + var preamble = encoding.GetPreamble(); + var textBytes = encoding.GetBytes(text); + + if (preamble.Length != 0) + { + var textAndPreambleBytes = new byte[preamble.Length + textBytes.Length]; + Buffer.BlockCopy(preamble, srcOffset: 0, textAndPreambleBytes, dstOffset: 0, preamble.Length); + Buffer.BlockCopy(textBytes, srcOffset: 0, textAndPreambleBytes, preamble.Length, textBytes.Length); + return textAndPreambleBytes; + } + + return textBytes; + } + + private static Stream GetResourceStream(string resourceName) + { + var type = typeof(SftpTests); + var resourceStream = type.Assembly.GetManifestResourceStream(resourceName); + if (resourceStream == null) + { + throw new ArgumentException($"Resource '{resourceName}' not found in assembly '{type.Assembly.FullName}'.", nameof(resourceName)); + } + return resourceStream; + } + + private static decimal CalculateTransferSpeed(long length, long elapsedMilliseconds) + { + return (length / 1024m) / (elapsedMilliseconds / 1000m); + } + + private static void SftpCreateRemoteFile(SftpClient client, string remoteFile, int size) + { + var file = CreateTempFile(size); + + try + { + using (var fs = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read)) + { + client.UploadFile(fs, remoteFile); + } + } + finally + { + File.Delete(file); + } + } + + private static byte[] GenerateRandom(int size) + { + var random = new Random(); + var randomContent = new byte[size]; + random.NextBytes(randomContent); + return randomContent; + } + + private static Stream CreateStreamWithContent(string content) + { + var memoryStream = new MemoryStream(); + var sw = new StreamWriter(memoryStream, Encoding.ASCII, 1024); + sw.Write(content); + sw.Flush(); + memoryStream.Position = 0; + return memoryStream; + } + + private static string GenerateUniqueRemoteFileName() + { + return $"/home/sshnet/{Guid.NewGuid():D}"; + } + } +} diff --git a/src/Renci.SshNet.IntegrationTests/SshClientTests.cs b/src/Renci.SshNet.IntegrationTests/SshClientTests.cs new file mode 100644 index 00000000..b737b343 --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/SshClientTests.cs @@ -0,0 +1,32 @@ +namespace Renci.SshNet.IntegrationTests +{ + /// + /// The SSH client integration tests + /// + [TestClass] + public class SshClientTests : IntegrationTestBase, IDisposable + { + private readonly SshClient _sshClient; + + public SshClientTests() + { + _sshClient = new SshClient(SshServerHostName, SshServerPort, User.UserName, User.Password); + _sshClient.Connect(); + } + + [TestMethod] + public void Echo_Command_with_all_characters() + { + var builder = new StringBuilder(); + var response = _sshClient.RunCommand("echo $'test !@#$%^&*()_+{}:,./<>[];\\|'"); + + Assert.AreEqual("test !@#$%^&*()_+{}:,./<>[];\\|\n", response.Result); + } + + public void Dispose() + { + _sshClient.Disconnect(); + _sshClient.Dispose(); + } + } +} diff --git a/src/Renci.SshNet.IntegrationTests/SshConnectionDisruptor.cs b/src/Renci.SshNet.IntegrationTests/SshConnectionDisruptor.cs new file mode 100644 index 00000000..74113411 --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/SshConnectionDisruptor.cs @@ -0,0 +1,41 @@ +namespace Renci.SshNet.IntegrationTests +{ + internal class SshConnectionDisruptor + { + private readonly IConnectionInfoFactory _connectionInfoFactory; + + public SshConnectionDisruptor(IConnectionInfoFactory connectionInfoFactory) + { + _connectionInfoFactory = connectionInfoFactory; + } + + public SshConnectionRestorer BreakConnections() + { + var client = new SshClient(_connectionInfoFactory.Create()); + + client.Connect(); + + PauseSshd(client); + + return new SshConnectionRestorer(client); + } + + private static void PauseSshd(SshClient client) + { + var command = client.CreateCommand("sudo echo 'DenyUsers sshnet' >> /etc/ssh/sshd_config"); + var output = command.Execute(); + if (command.ExitStatus != 0) + { + throw new ApplicationException( + $"Blocking user sshnet failed with exit code {command.ExitStatus}.\r\n{output}\r\n{command.Error}"); + } + command = client.CreateCommand("sudo pkill -9 -U sshnet -f sshd.pam"); + output = command.Execute(); + if (command.ExitStatus != 0) + { + throw new ApplicationException( + $"Killing sshd.pam service failed with exit code {command.ExitStatus}.\r\n{output}\r\n{command.Error}"); + } + } + } +} diff --git a/src/Renci.SshNet.IntegrationTests/SshConnectionRestorer.cs b/src/Renci.SshNet.IntegrationTests/SshConnectionRestorer.cs new file mode 100644 index 00000000..d7c6437d --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/SshConnectionRestorer.cs @@ -0,0 +1,36 @@ +namespace Renci.SshNet.IntegrationTests +{ + internal class SshConnectionRestorer : IDisposable + { + private SshClient _sshClient; + + public SshConnectionRestorer(SshClient sshClient) + { + _sshClient = sshClient; + } + + public void RestoreConnections() + { + var command = _sshClient.CreateCommand("sudo sed -i '/DenyUsers sshnet/d' /etc/ssh/sshd_config"); + var output = command.Execute(); + if (command.ExitStatus != 0) + { + throw new ApplicationException( + $"Unblocking user sshnet failed with exit code {command.ExitStatus}.\r\n{output}\r\n{command.Error}"); + } + command = _sshClient.CreateCommand("sudo /usr/sbin/sshd.pam"); + output = command.Execute(); + if (command.ExitStatus != 0) + { + throw new ApplicationException( + $"Resuming ssh service failed with exit code {command.ExitStatus}.\r\n{output}\r\n{command.Error}"); + } + } + + public void Dispose() + { + _sshClient?.Dispose(); + _sshClient = null; + } + } +} diff --git a/src/Renci.SshNet.IntegrationTests/SshTests.cs b/src/Renci.SshNet.IntegrationTests/SshTests.cs new file mode 100644 index 00000000..5f3e5898 --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/SshTests.cs @@ -0,0 +1,972 @@ +using System.ComponentModel; +using System.Net; +using System.Net.Sockets; + +using Renci.SshNet.Common; +using Renci.SshNet.IntegrationTests.Common; + +namespace Renci.SshNet.IntegrationTests +{ + [TestClass] + public class SshTests : TestBase + { + private IConnectionInfoFactory _connectionInfoFactory; + private IConnectionInfoFactory _adminConnectionInfoFactory; + private RemoteSshdConfig _remoteSshdConfig; + + [TestInitialize] + public void SetUp() + { + _connectionInfoFactory = new LinuxVMConnectionFactory(SshServerHostName, SshServerPort); + _adminConnectionInfoFactory = new LinuxAdminConnectionFactory(SshServerHostName, SshServerPort); + + _remoteSshdConfig = new RemoteSshd(_adminConnectionInfoFactory).OpenConfig(); + _remoteSshdConfig.AllowTcpForwarding() + .PrintMotd(false) + .Update() + .Restart(); + } + + [TestCleanup] + public void TearDown() + { + _remoteSshdConfig?.Reset(); + } + + /// + /// Test for a channel that is being closed by the server. + /// + [TestMethod] + public void Ssh_ShellStream_Exit() + { + using (var client = new SshClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var terminalModes = new Dictionary + { + { TerminalModes.ECHO, 0 } + }; + + using (var shellStream = client.CreateShellStream("xterm", 80, 24, 800, 600, 1024, terminalModes)) + { + shellStream.WriteLine("echo Hello!"); + shellStream.WriteLine("exit"); + + Thread.Sleep(1000); + + try + { + shellStream.Write("ABC"); + Assert.Fail(); + } + catch (ObjectDisposedException ex) + { + Assert.IsNull(ex.InnerException); + Assert.AreEqual("ShellStream", ex.ObjectName); + Assert.AreEqual($"Cannot access a disposed object.{Environment.NewLine}Object name: '{ex.ObjectName}'.", ex.Message); + } + + var line = shellStream.ReadLine(); + Assert.IsNotNull(line); + Assert.IsTrue(line.EndsWith("Hello!"), line); + + // TODO: ReadLine should return null when the buffer is empty and the channel has been closed (issue #672) + try + { + line = shellStream.ReadLine(); + Assert.Fail(line); + } + catch (NullReferenceException) + { + + } + } + } + } + + /// + /// https://github.com/sshnet/SSH.NET/issues/63 + /// + [TestMethod] + [Category("Reproduction Tests")] + [Ignore] + public void Ssh_ShellStream_IntermittendOutput() + { + const string remoteFile = "/home/sshnet/test.sh"; + + var expectedResult = string.Join("\n", + "Line 1 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "Line 2 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "Line 3 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "Line 4 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "Line 5 ", + "Line 6"); + + var scriptBuilder = new StringBuilder(); + scriptBuilder.Append("#!/bin/sh\n"); + scriptBuilder.Append("echo Line 1 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n"); + scriptBuilder.Append("sleep .5\n"); + scriptBuilder.Append("echo Line 2 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n"); + scriptBuilder.Append("echo Line 3 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n"); + scriptBuilder.Append("echo Line 4 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n"); + scriptBuilder.Append("sleep 2\n"); + scriptBuilder.Append("echo \"Line 5 \"\n"); + scriptBuilder.Append("echo Line 6 \n"); + scriptBuilder.Append("exit 13\n"); + + using (var sshClient = new SshClient(_connectionInfoFactory.Create())) + { + sshClient.Connect(); + + CreateShellScript(_connectionInfoFactory, remoteFile, scriptBuilder.ToString()); + + try + { + var terminalModes = new Dictionary + { + { TerminalModes.ECHO, 0 } + }; + + using (var shellStream = sshClient.CreateShellStream("xterm", 80, 24, 800, 600, 1024, terminalModes)) + { + shellStream.WriteLine(remoteFile); + Thread.Sleep(1200); + using (var reader = new StreamReader(shellStream, new UTF8Encoding(false), false, 10)) + { + var lines = new List(); + string line = null; + while ((line = reader.ReadLine()) != null) + { + lines.Add(line); + } + Assert.AreEqual(6, lines.Count, string.Join("\n", lines)); + Assert.AreEqual(expectedResult, string.Join("\n", lines)); + } + } + } + finally + { + RemoveFileOrDirectory(sshClient, remoteFile); + } + } + } + + /// + /// Issue 1555 + /// + [TestMethod] + public void Ssh_CreateShell() + { + using (var client = new SshClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + using (var input = new MemoryStream()) + using (var output = new MemoryStream()) + using (var extOutput = new MemoryStream()) + { + var shell = client.CreateShell(input, output, extOutput); + shell.Start(); + + var inputWriter = new StreamWriter(input, Encoding.ASCII, 1024); + inputWriter.WriteLine("echo $PATH"); + + var outputReader = new StreamReader(output, Encoding.ASCII, false, 1024); + Console.WriteLine(outputReader.ReadToEnd()); + + shell.Stop(); + } + } + } + + [TestMethod] + public void Ssh_Command_IntermittendOutput_EndExecute() + { + const string remoteFile = "/home/sshnet/test.sh"; + + var expectedResult = string.Join("\n", + "Line 1 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "Line 2 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "Line 3 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "Line 4 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "Line 5 ", + "Line 6", + ""); + + var scriptBuilder = new StringBuilder(); + scriptBuilder.Append("#!/bin/sh\n"); + scriptBuilder.Append("echo Line 1 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n"); + scriptBuilder.Append("sleep .5\n"); + scriptBuilder.Append("echo Line 2 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n"); + scriptBuilder.Append("echo Line 3 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n"); + scriptBuilder.Append("echo Line 4 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n"); + scriptBuilder.Append("sleep 2\n"); + scriptBuilder.Append("echo \"Line 5 \"\n"); + scriptBuilder.Append("echo Line 6 \n"); + scriptBuilder.Append("exit 13\n"); + + using (var sshClient = new SshClient(_connectionInfoFactory.Create())) + { + sshClient.Connect(); + + CreateShellScript(_connectionInfoFactory, remoteFile, scriptBuilder.ToString()); + + try + { + using (var cmd = sshClient.CreateCommand("chmod 777 " + remoteFile)) + { + cmd.Execute(); + + Assert.AreEqual(0, cmd.ExitStatus, cmd.Error); + } + + using (var command = sshClient.CreateCommand(remoteFile)) + { + var asyncResult = command.BeginExecute(); + var actualResult = command.EndExecute(asyncResult); + + Assert.AreEqual(expectedResult, actualResult); + Assert.AreEqual(expectedResult, command.Result); + Assert.AreEqual(13, command.ExitStatus); + } + } + finally + { + RemoveFileOrDirectory(sshClient, remoteFile); + } + } + } + + /// + /// Ignored for now, because: + /// * OutputStream.Read(...) does not block when no data is available + /// * SshCommand.(Begin)Execute consumes *OutputStream*, advancing its position. + /// + /// https://github.com/sshnet/SSH.NET/issues/650 + /// + [TestMethod] + [Ignore] + public void Ssh_Command_IntermittendOutput_OutputStream() + { + const string remoteFile = "/home/sshnet/test.sh"; + + var expectedResult = string.Join("\n", + "Line 1 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "Line 2 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "Line 3 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "Line 4 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "Line 5 ", + "Line 6"); + + var scriptBuilder = new StringBuilder(); + scriptBuilder.Append("#!/bin/sh\n"); + scriptBuilder.Append("echo Line 1 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n"); + scriptBuilder.Append("sleep .5\n"); + scriptBuilder.Append("echo Line 2 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n"); + scriptBuilder.Append("echo Line 3 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n"); + scriptBuilder.Append("echo Line 4 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n"); + scriptBuilder.Append("sleep 2\n"); + scriptBuilder.Append("echo \"Line 5 \"\n"); + scriptBuilder.Append("echo Line 6 \n"); + scriptBuilder.Append("exit 13\n"); + + using (var sshClient = new SshClient(_connectionInfoFactory.Create())) + { + sshClient.Connect(); + + CreateShellScript(_connectionInfoFactory, remoteFile, scriptBuilder.ToString()); + + try + { + using (var cmd = sshClient.CreateCommand("chmod 777 " + remoteFile)) + { + cmd.Execute(); + + Assert.AreEqual(0, cmd.ExitStatus, cmd.Error); + } + + using (var command = sshClient.CreateCommand(remoteFile)) + { + var asyncResult = command.BeginExecute(); + + using (var reader = new StreamReader(command.OutputStream, new UTF8Encoding(false), false, 10)) + { + var lines = new List(); + string line = null; + while ((line = reader.ReadLine()) != null) + { + lines.Add(line); + } + + Assert.AreEqual(6, lines.Count, string.Join("\n", lines)); + Assert.AreEqual(expectedResult, string.Join("\n", lines)); + Assert.AreEqual(13, command.ExitStatus); + } + + var actualResult = command.EndExecute(asyncResult); + + Assert.AreEqual(expectedResult, actualResult); + Assert.AreEqual(expectedResult, command.Result); + } + } + finally + { + RemoveFileOrDirectory(sshClient, remoteFile); + } + } + } + + [TestMethod] + public void Ssh_DynamicPortForwarding_DisposeSshClientWithoutStoppingPort() + { + const string searchText = "HTTP/1.1 301 Moved Permanently"; + const string hostName = "github.com"; + + var httpGetRequest = Encoding.ASCII.GetBytes($"GET / HTTP/1.1\r\nHost: {hostName}\r\n\r\n"); + Socket socksSocket; + + using (var client = new SshClient(_connectionInfoFactory.Create())) + { + client.ConnectionInfo.Timeout = TimeSpan.FromSeconds(200); + client.Connect(); + + var forwardedPort = new ForwardedPortDynamic(1080); + forwardedPort.Exception += (sender, args) => Console.WriteLine(args.Exception.ToString()); + client.AddForwardedPort(forwardedPort); + forwardedPort.Start(); + + var socksClient = new Socks5Handler(new IPEndPoint(IPAddress.Loopback, 1080), + string.Empty, + string.Empty); + + socksSocket = socksClient.Connect(hostName, 80); + socksSocket.Send(httpGetRequest); + + var httpResponse = GetHttpResponse(socksSocket, Encoding.ASCII); + Assert.IsTrue(httpResponse.Contains(searchText), httpResponse); + } + + Assert.IsTrue(socksSocket.Connected); + + // check if client socket was properly closed + Assert.AreEqual(0, socksSocket.Receive(new byte[1], 0, 1, SocketFlags.None)); + } + + [TestMethod] + public void Ssh_DynamicPortForwarding_DomainName() + { + const string searchText = "HTTP/1.1 301 Moved Permanently"; + const string hostName = "github.com"; + + // Set-up a host alias for google.be on the remote server that is not known locally; this allows us to + // verify whether the host name is resolved remotely. + const string hostNameAlias = "dynamicportforwarding-test.for.sshnet"; + + // Construct a HTTP request for which we expected the response to contain the search text. + var httpGetRequest = Encoding.ASCII.GetBytes($"GET / HTTP/1.1\r\nHost: {hostName}\r\n\r\n"); + + var ipAddresses = Dns.GetHostAddresses(hostName); + var hostsFileUpdated = AddOrUpdateHostsEntry(_adminConnectionInfoFactory, ipAddresses[0], hostNameAlias); + + try + { + using (var client = new SshClient(_connectionInfoFactory.Create())) + { + client.ConnectionInfo.Timeout = TimeSpan.FromSeconds(200); + client.Connect(); + + var forwardedPort = new ForwardedPortDynamic(1080); + forwardedPort.Exception += (sender, args) => Console.WriteLine(args.Exception.ToString()); + client.AddForwardedPort(forwardedPort); + forwardedPort.Start(); + + var socksClient = new Socks5Handler(new IPEndPoint(IPAddress.Loopback, 1080), + string.Empty, + string.Empty); + var socksSocket = socksClient.Connect(hostNameAlias, 80); + + socksSocket.Send(httpGetRequest); + var httpResponse = GetHttpResponse(socksSocket, Encoding.ASCII); + Assert.IsTrue(httpResponse.Contains(searchText), httpResponse); + + // Verify if port is still open + socksSocket.Send(httpGetRequest); + GetHttpResponse(socksSocket, Encoding.ASCII); + + forwardedPort.Stop(); + + Assert.IsTrue(socksSocket.Connected); + + // check if client socket was properly closed + Assert.AreEqual(0, socksSocket.Receive(new byte[1], 0, 1, SocketFlags.None)); + + forwardedPort.Start(); + + // create new SOCKS connection and very whether the forwarded port is functional again + socksSocket = socksClient.Connect(hostNameAlias, 80); + + socksSocket.Send(httpGetRequest); + httpResponse = GetHttpResponse(socksSocket, Encoding.ASCII); + Assert.IsTrue(httpResponse.Contains(searchText), httpResponse); + + forwardedPort.Dispose(); + + Assert.IsTrue(socksSocket.Connected); + + // check if client socket was properly closed + Assert.AreEqual(0, socksSocket.Receive(new byte[1], 0, 1, SocketFlags.None)); + + forwardedPort.Dispose(); + } + } + finally + { + if (hostsFileUpdated) + { + RemoveHostsEntry(_adminConnectionInfoFactory, ipAddresses[0], hostNameAlias); + } + } + } + + [TestMethod] + public void Ssh_DynamicPortForwarding_IPv4() + { + const string searchText = "HTTP/1.1 301 Moved Permanently"; + const string hostName = "github.com"; + + var httpGetRequest = Encoding.ASCII.GetBytes($"GET /null HTTP/1.1\r\nHost: {hostName}\r\n\r\n"); + + var ipv4 = Dns.GetHostAddresses(hostName).FirstOrDefault(p => p.AddressFamily == AddressFamily.InterNetwork); + Assert.IsNotNull(ipv4, $@"No IPv4 address found for '{hostName}'."); + + using (var client = new SshClient(_connectionInfoFactory.Create())) + { + client.ConnectionInfo.Timeout = TimeSpan.FromSeconds(200); + client.Connect(); + + var forwardedPort = new ForwardedPortDynamic(1080); + forwardedPort.Exception += (sender, args) => Console.WriteLine(args.Exception.ToString()); + client.AddForwardedPort(forwardedPort); + forwardedPort.Start(); + + var socksClient = new Socks5Handler(new IPEndPoint(IPAddress.Loopback, 1080), + string.Empty, + string.Empty); + var socksSocket = socksClient.Connect(new IPEndPoint(ipv4, 80)); + + socksSocket.Send(httpGetRequest); + var httpResponse = GetHttpResponse(socksSocket, Encoding.ASCII); + Assert.IsTrue(httpResponse.Contains(searchText), httpResponse); + + forwardedPort.Dispose(); + + // check if client socket was properly closed + Assert.AreEqual(0, socksSocket.Receive(new byte[1], 0, 1, SocketFlags.None)); + } + } + + /// + /// Verifies whether channels are effectively closed. + /// + [TestMethod] + public void Ssh_LocalPortForwardingCloseChannels() + { + const string hostNameAlias = "localportforwarding-test.for.sshnet"; + const string hostName = "github.com"; + + var ipAddress = Dns.GetHostAddresses(hostName)[0]; + + var hostsFileUpdated = AddOrUpdateHostsEntry(_adminConnectionInfoFactory, ipAddress, hostNameAlias); + + try + { + var connectionInfo = _connectionInfoFactory.Create(); + connectionInfo.MaxSessions = 1; + + using (var client = new SshClient(connectionInfo)) + { + client.Connect(); + + var localEndPoint = new IPEndPoint(IPAddress.Loopback, 1225); + + for (var i = 0; i < (connectionInfo.MaxSessions + 1); i++) + { + var forwardedPort = new ForwardedPortLocal(localEndPoint.Address.ToString(), + (uint)localEndPoint.Port, + hostNameAlias, + 80); + client.AddForwardedPort(forwardedPort); + forwardedPort.Start(); + + try + { + var httpRequest = (HttpWebRequest) WebRequest.Create("http://" + localEndPoint); + httpRequest.Host = hostName; + httpRequest.Method = "GET"; + httpRequest.AllowAutoRedirect = false; + + try + { + using (var httpResponse = (HttpWebResponse)httpRequest.GetResponse()) + { + Assert.AreEqual(HttpStatusCode.MovedPermanently, httpResponse.StatusCode); + } + } + catch (WebException ex) + { + Assert.AreEqual(WebExceptionStatus.ProtocolError, ex.Status); + Assert.IsNotNull(ex.Response); + + using (var httpResponse = ex.Response as HttpWebResponse) + { + Assert.IsNotNull(httpResponse); + Assert.AreEqual(HttpStatusCode.MovedPermanently, httpResponse.StatusCode); + } + } + } + finally + { + client.RemoveForwardedPort(forwardedPort); + } + } + } + } + finally + { + if (hostsFileUpdated) + { + RemoveHostsEntry(_adminConnectionInfoFactory, ipAddress, hostNameAlias); + } + } + } + + [TestMethod] + public void Ssh_LocalPortForwarding() + { + const string hostNameAlias = "localportforwarding-test.for.sshnet"; + const string hostName = "github.com"; + + var ipAddress = Dns.GetHostAddresses(hostName)[0]; + + var hostsFileUpdated = AddOrUpdateHostsEntry(_adminConnectionInfoFactory, ipAddress, hostNameAlias); + + try + { + using (var client = new SshClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + var localEndPoint = new IPEndPoint(IPAddress.Loopback, 1225); + + var forwardedPort = new ForwardedPortLocal(localEndPoint.Address.ToString(), + (uint)localEndPoint.Port, + hostNameAlias, + 80); + forwardedPort.Exception += + (sender, args) => Console.WriteLine(@"ForwardedPort exception: " + args.Exception); + client.AddForwardedPort(forwardedPort); + forwardedPort.Start(); + + try + { + var httpRequest = (HttpWebRequest) WebRequest.Create("http://" + localEndPoint); + httpRequest.Host = hostName; + httpRequest.Method = "GET"; + httpRequest.Accept = "text/html"; + httpRequest.AllowAutoRedirect = false; + + try + { + using (var httpResponse = (HttpWebResponse)httpRequest.GetResponse()) + { + Assert.AreEqual(HttpStatusCode.MovedPermanently, httpResponse.StatusCode); + } + } + catch (WebException ex) + { + Assert.AreEqual(WebExceptionStatus.ProtocolError, ex.Status); + Assert.IsNotNull(ex.Response); + + using (var httpResponse = ex.Response as HttpWebResponse) + { + Assert.IsNotNull(httpResponse); + Assert.AreEqual(HttpStatusCode.MovedPermanently, httpResponse.StatusCode); + } + } + } + finally + { + client.RemoveForwardedPort(forwardedPort); + } + } + } + finally + { + if (hostsFileUpdated) + { + RemoveHostsEntry(_adminConnectionInfoFactory, ipAddress, hostNameAlias); + } + } + } + + [TestMethod] + public void Ssh_RemotePortForwarding() + { + var hostAddresses = Dns.GetHostAddresses(Dns.GetHostName()); + var ipv4HostAddress = hostAddresses.First(p => p.AddressFamily == AddressFamily.InterNetwork); + + var endpoint1 = new IPEndPoint(ipv4HostAddress, 666); + var endpoint2 = new IPEndPoint(ipv4HostAddress, 667); + + var bytesReceivedOnListener1 = new List(); + var bytesReceivedOnListener2 = new List(); + + using (var socketListener1 = new AsyncSocketListener(endpoint1)) + using (var socketListener2 = new AsyncSocketListener(endpoint2)) + using (var client = new SshClient(_connectionInfoFactory.Create())) + { + socketListener1.BytesReceived += (received, socket) => bytesReceivedOnListener1.AddRange(received); + socketListener1.Start(); + + socketListener2.BytesReceived += (received, socket) => bytesReceivedOnListener2.AddRange(received); + socketListener2.Start(); + + client.Connect(); + + var forwardedPort1 = new ForwardedPortRemote(IPAddress.Loopback, + 10000, + endpoint1.Address, + (uint)endpoint1.Port); + forwardedPort1.Exception += (sender, args) => Console.WriteLine(@"forwardedPort1 exception: " + args.Exception); + client.AddForwardedPort(forwardedPort1); + forwardedPort1.Start(); + + var forwardedPort2 = new ForwardedPortRemote(IPAddress.Loopback, + 10001, + endpoint2.Address, + (uint)endpoint2.Port); + forwardedPort2.Exception += (sender, args) => Console.WriteLine(@"forwardedPort2 exception: " + args.Exception); + client.AddForwardedPort(forwardedPort2); + forwardedPort2.Start(); + + using (var s = client.CreateShellStream("a", 80, 25, 800, 600, 200)) + { + s.WriteLine($"telnet {forwardedPort1.BoundHost} {forwardedPort1.BoundPort}"); + s.Expect($"Connected to {forwardedPort1.BoundHost}\r\n"); + s.WriteLine("ABC"); + s.Flush(); + s.Expect("ABC"); + s.Close(); + } + + using (var s = client.CreateShellStream("b", 80, 25, 800, 600, 200)) + { + s.WriteLine($"telnet {forwardedPort2.BoundHost} {forwardedPort2.BoundPort}"); + s.Expect($"Connected to {forwardedPort2.BoundHost}\r\n"); + s.WriteLine("DEF"); + s.Flush(); + s.Expect("DEF"); + s.Close(); + } + + forwardedPort1.Stop(); + forwardedPort2.Stop(); + } + + var textReceivedOnListener1 = Encoding.ASCII.GetString(bytesReceivedOnListener1.ToArray()); + Assert.AreEqual("ABC\r\n", textReceivedOnListener1); + + var textReceivedOnListener2 = Encoding.ASCII.GetString(bytesReceivedOnListener2.ToArray()); + Assert.AreEqual("DEF\r\n", textReceivedOnListener2); + } + + /// + /// Issue 1591 + /// + [TestMethod] + public void Ssh_ExecuteShellScript() + { + const string remoteFile = "/home/sshnet/run.sh"; + const string content = "#\bin\bash\necho Hello World!"; + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + using (var memoryStream = new MemoryStream()) + using (var sw = new StreamWriter(memoryStream, Encoding.ASCII)) + { + sw.Write(content); + sw.Flush(); + memoryStream.Position = 0; + client.UploadFile(memoryStream, remoteFile); + } + } + + using (var client = new SshClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + try + { + var runChmod = client.RunCommand("chmod u+x " + remoteFile); + runChmod.Execute(); + Assert.AreEqual(0, runChmod.ExitStatus, runChmod.Error); + + var runLs = client.RunCommand("ls " + remoteFile); + var asyncResultLs = runLs.BeginExecute(); + + var runScript = client.RunCommand(remoteFile); + var asyncResultScript = runScript.BeginExecute(); + + Assert.IsTrue(asyncResultScript.AsyncWaitHandle.WaitOne(10000)); + var resultScript = runScript.EndExecute(asyncResultScript); + Assert.AreEqual("Hello World!\n", resultScript); + + Assert.IsTrue(asyncResultLs.AsyncWaitHandle.WaitOne(10000)); + var resultLs = runLs.EndExecute(asyncResultLs); + Assert.AreEqual(remoteFile + "\n", resultLs); + } + finally + { + RemoveFileOrDirectory(client, remoteFile); + } + } + } + + /// + /// Verifies if a hosts file contains an entry for a given combination of IP address and hostname, + /// and if necessary add either the host entry or an alias to an exist entry for the specified IP + /// address. + /// + /// + /// + /// + /// + /// if an entry was added or updated in the specified hosts file; otherwise, + /// . + /// + private static bool AddOrUpdateHostsEntry(IConnectionInfoFactory linuxAdminConnectionFactory, + IPAddress ipAddress, + string hostName) + { + const string hostsFile = "/etc/hosts"; + + using (var client = new ScpClient(linuxAdminConnectionFactory.Create())) + { + client.Connect(); + + var hostConfig = HostConfig.Read(client, hostsFile); + + var hostEntry = hostConfig.Entries.SingleOrDefault(h => h.IPAddress.Equals(ipAddress)); + if (hostEntry != null) + { + if (hostEntry.HostName == hostName) + { + return false; + } + + foreach (var alias in hostEntry.Aliases) + { + if (alias == hostName) + { + return false; + } + } + + hostEntry.Aliases.Add(hostName); + } + else + { + bool mappingFound = false; + + for (var i = (hostConfig.Entries.Count - 1); i >= 0; i--) + { + hostEntry = hostConfig.Entries[i]; + + if (hostEntry.HostName == hostName) + { + if (hostEntry.IPAddress.Equals(ipAddress)) + { + mappingFound = true; + continue; + } + + // If hostname is currently mapped to another IP address, then remove the + // current mapping + hostConfig.Entries.RemoveAt(i); + } + else + { + for (var j = (hostEntry.Aliases.Count - 1); j >= 0; j--) + { + var alias = hostEntry.Aliases[j]; + + if (alias == hostName) + { + hostEntry.Aliases.RemoveAt(j); + } + } + } + } + + if (!mappingFound) + { + hostEntry = new HostEntry(ipAddress, hostName); + hostConfig.Entries.Add(hostEntry); + } + } + + hostConfig.Write(client, hostsFile); + return true; + } + } + + /// + /// Remove the mapping between a given IP address and host name from the remote hosts file either by + /// removing a host entry entirely (if no other aliases are defined for the IP address) or removing + /// the aliases that match the host name for the IP address. + /// + /// + /// + /// + /// + /// if the hosts file was updated; otherwise, . + /// + private static bool RemoveHostsEntry(IConnectionInfoFactory linuxAdminConnectionFactory, + IPAddress ipAddress, + string hostName) + { + const string hostsFile = "/etc/hosts"; + + using (var client = new ScpClient(linuxAdminConnectionFactory.Create())) + { + client.Connect(); + + var hostConfig = HostConfig.Read(client, hostsFile); + + var hostEntry = hostConfig.Entries.SingleOrDefault(h => h.IPAddress.Equals(ipAddress)); + if (hostEntry == null) + { + return false; + } + + if (hostEntry.HostName == hostName) + { + if (hostEntry.Aliases.Count == 0) + { + hostConfig.Entries.Remove(hostEntry); + } + else + { + // Use one of the aliases (that are different from the specified host name) as host name + // of the host entry. + + for (var i = hostEntry.Aliases.Count - 1; i >= 0; i--) + { + var alias = hostEntry.Aliases[i]; + if (alias == hostName) + { + hostEntry.Aliases.RemoveAt(i); + } + else if (hostEntry.HostName == hostName) + { + // If we haven't already used one of the aliases as host name of the host entry + // then do this now and remove the alias. + + hostEntry.HostName = alias; + hostEntry.Aliases.RemoveAt(i); + } + } + + // If for some reason the host name of the host entry matched the specified host name + // and it only had aliases that match the host name, then remove the host entry altogether. + if (hostEntry.Aliases.Count == 0 && hostEntry.HostName == hostName) + { + hostConfig.Entries.Remove(hostEntry); + } + } + } + else + { + var aliasRemoved = false; + + for (var i = hostEntry.Aliases.Count - 1; i >= 0; i--) + { + if (hostEntry.Aliases[i] == hostName) + { + hostEntry.Aliases.RemoveAt(i); + aliasRemoved = true; + } + } + + if (!aliasRemoved) + { + return false; + } + } + + hostConfig.Write(client, hostsFile); + return true; + } + } + + private static string GetHttpResponse(Socket socket, Encoding encoding) + { + var httpResponseBuffer = new byte[2048]; + + // We expect: + // * The response to contain the searchText in the first receive. + // * The full response to be returned in the first receive. + + var bytesReceived = socket.Receive(httpResponseBuffer, + 0, + httpResponseBuffer.Length, + SocketFlags.None); + if (bytesReceived == 0) + { + return null; + } + + if (bytesReceived == httpResponseBuffer.Length) + { + throw new Exception("We expect the HTTP response to be less than the buffer size. If not, we won't consume the full response."); + } + + using (var sr = new StringReader(encoding.GetString(httpResponseBuffer, 0, bytesReceived))) + { + return sr.ReadToEnd(); + } + } + + private static void CreateShellScript(IConnectionInfoFactory connectionInfoFactory, string remoteFile, string script) + { + using (var sftpClient = new SftpClient(connectionInfoFactory.Create())) + { + sftpClient.Connect(); + + using (var sw = sftpClient.CreateText(remoteFile, new UTF8Encoding(false))) + { + sw.Write(script); + } + + sftpClient.ChangePermissions(remoteFile, 0x1FF); + } + } + + private static void RemoveFileOrDirectory(SshClient client, string remoteFile) + { + using (var cmd = client.CreateCommand("rm -Rf " + remoteFile)) + { + cmd.Execute(); + Assert.AreEqual(0, cmd.ExitStatus, cmd.Error); + } + } + } +} diff --git a/src/Renci.SshNet.IntegrationTests/TestBase.cs b/src/Renci.SshNet.IntegrationTests/TestBase.cs new file mode 100644 index 00000000..511bb144 --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/TestBase.cs @@ -0,0 +1,80 @@ +using System.Security.Cryptography; + +namespace Renci.SshNet.IntegrationTests +{ + public abstract class TestBase : IntegrationTestBase + { + protected static MemoryStream CreateMemoryStream(int size) + { + var memoryStream = new MemoryStream(); + FillStream(memoryStream, size); + return memoryStream; + } + + protected static void FillStream(Stream stream, int size) + { + var randomContent = new byte[50]; + var random = new Random(); + + var numberOfBytesToWrite = size; + + while (numberOfBytesToWrite > 0) + { + random.NextBytes(randomContent); + + var numberOfCharsToWrite = Math.Min(numberOfBytesToWrite, randomContent.Length); + stream.Write(randomContent, 0, numberOfCharsToWrite); + numberOfBytesToWrite -= numberOfCharsToWrite; + } + } + + protected static string CreateHash(Stream stream) + { + MD5 md5 = new MD5CryptoServiceProvider(); + var hash = md5.ComputeHash(stream); + return Encoding.ASCII.GetString(hash); + } + + protected static string CreateHash(byte[] buffer) + { + using (var ms = new MemoryStream(buffer)) + { + return CreateHash(ms); + } + } + + protected static string CreateFileHash(string path) + { + using (var fs = File.OpenRead(path)) + { + return CreateHash(fs); + } + } + + protected static string CreateTempFile(int size) + { + var file = Path.GetTempFileName(); + CreateFile(file, size); + return file; + } + + protected static void CreateFile(string fileName, int size) + { + using (var fs = File.OpenWrite(fileName)) + { + FillStream(fs, size); + } + } + + protected Stream GetManifestResourceStream(string resourceName) + { + var type = GetType(); + var resourceStream = type.Assembly.GetManifestResourceStream(resourceName); + if (resourceStream == null) + { + throw new ArgumentException($"Resource '{resourceName}' not found in assembly '{type.Assembly.FullName}'.", nameof(resourceName)); + } + return resourceStream; + } + } +} diff --git a/src/Renci.SshNet.IntegrationTests/TestInitializer.cs b/src/Renci.SshNet.IntegrationTests/TestInitializer.cs new file mode 100644 index 00000000..0c058781 --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/TestInitializer.cs @@ -0,0 +1,19 @@ +namespace Renci.SshNet.IntegrationTests +{ + [TestClass] + public class TestInitializer + { + [AssemblyInitialize] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "MSTests requires context parameter")] + public static async Task Initialize(TestContext context) + { + await InfrastructureFixture.Instance.InitializeAsync(); + } + + [AssemblyCleanup] + public static async Task Cleanup() + { + await InfrastructureFixture.Instance.DisposeAsync(); + } + } +} diff --git a/src/Renci.SshNet.IntegrationTests/TestsFixtures/InfrastructureFixture.cs b/src/Renci.SshNet.IntegrationTests/TestsFixtures/InfrastructureFixture.cs new file mode 100644 index 00000000..b98de126 --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/TestsFixtures/InfrastructureFixture.cs @@ -0,0 +1,76 @@ +using DotNet.Testcontainers.Builders; +using DotNet.Testcontainers.Containers; +using DotNet.Testcontainers.Images; + +namespace Renci.SshNet.IntegrationTests.TestsFixtures +{ + public sealed class InfrastructureFixture : IDisposable + { + private InfrastructureFixture() + { + } + + private static readonly Lazy InstanceLazy = new Lazy(() => new InfrastructureFixture()); + + public static InfrastructureFixture Instance + { + get + { + return InstanceLazy.Value; + } + } + + private IContainer _sshServer; + + private IFutureDockerImage _sshServerImage; + + public string SshServerHostName { get; set; } + + public ushort SshServerPort { get; set; } + + public SshUser AdminUser = new SshUser("sshnetadm", "ssh4ever"); + + public SshUser User = new SshUser("sshnet", "ssh4ever"); + + public async Task InitializeAsync() + { + _sshServerImage = new ImageFromDockerfileBuilder() + .WithName("renci-ssh-tests-server-image") + .WithDockerfileDirectory(CommonDirectoryPath.GetSolutionDirectory(), "Renci.SshNet.IntegrationTests") + .WithDockerfile("Dockerfile") + .WithDeleteIfExists(true) + + .Build(); + + await _sshServerImage.CreateAsync(); + + _sshServer = new ContainerBuilder() + .WithHostname("renci-ssh-tests-server") + .WithImage(_sshServerImage) + .WithPortBinding(22, true) + .Build(); + + await _sshServer.StartAsync(); + + SshServerPort = _sshServer.GetMappedPublicPort(22); + SshServerHostName = _sshServer.Hostname; + } + + public async Task DisposeAsync() + { + if (_sshServer != null) + { + await _sshServer.DisposeAsync(); + } + + if (_sshServerImage != null) + { + await _sshServerImage.DisposeAsync(); + } + } + + public void Dispose() + { + } + } +} diff --git a/src/Renci.SshNet.IntegrationTests/TestsFixtures/IntegrationTestBase.cs b/src/Renci.SshNet.IntegrationTests/TestsFixtures/IntegrationTestBase.cs new file mode 100644 index 00000000..1d6658fc --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/TestsFixtures/IntegrationTestBase.cs @@ -0,0 +1,85 @@ +namespace Renci.SshNet.IntegrationTests.TestsFixtures +{ + /// + /// The base class for integration tests + /// + public abstract class IntegrationTestBase + { + private readonly InfrastructureFixture _infrastructureFixture; + + /// + /// The SSH Server host name. + /// + public string SshServerHostName + { + get + { + return _infrastructureFixture.SshServerHostName; + } + } + + /// + /// The SSH Server host name + /// + public ushort SshServerPort + { + get + { + return _infrastructureFixture.SshServerPort; + } + } + + /// + /// The admin user that can use SSH Server. + /// + public SshUser AdminUser + { + get + { + return _infrastructureFixture.AdminUser; + } + } + + /// + /// The normal user that can use SSH Server. + /// + public SshUser User + { + get + { + return _infrastructureFixture.User; + } + } + + protected IntegrationTestBase() + { + _infrastructureFixture = InfrastructureFixture.Instance; + ShowInfrastructureInformation(); + } + + private void ShowInfrastructureInformation() + { + Console.WriteLine($"SSH Server host name: {_infrastructureFixture.SshServerHostName}"); + Console.WriteLine($"SSH Server port: {_infrastructureFixture.SshServerPort}"); + } + + /// + /// Creates the test file. + /// + /// Name of the file. + /// Size in megabytes. + protected void CreateTestFile(string fileName, int size) + { + using (var testFile = File.Create(fileName)) + { + var random = new Random(); + for (int i = 0; i < 1024 * size; i++) + { + var buffer = new byte[1024]; + random.NextBytes(buffer); + testFile.Write(buffer, 0, buffer.Length); + } + } + } + } +} diff --git a/src/Renci.SshNet.IntegrationTests/TestsFixtures/SshUser.cs b/src/Renci.SshNet.IntegrationTests/TestsFixtures/SshUser.cs new file mode 100644 index 00000000..9a67f65c --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/TestsFixtures/SshUser.cs @@ -0,0 +1,16 @@ +namespace Renci.SshNet.IntegrationTests.TestsFixtures +{ + public class SshUser + { + public string UserName { get; } + + public string Password { get; } + + public SshUser(string userName, string password) + { + UserName = userName; + Password = password; + } + } +} + diff --git a/src/Renci.SshNet.IntegrationTests/Users.cs b/src/Renci.SshNet.IntegrationTests/Users.cs new file mode 100644 index 00000000..043ab63e --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/Users.cs @@ -0,0 +1,8 @@ +namespace Renci.SshNet.IntegrationTests +{ + internal static class Users + { + public static readonly Credential Regular = new Credential("sshnet", "ssh4ever"); + public static readonly Credential Admin = new Credential("sshnetadm", "ssh4ever"); + } +} diff --git a/src/Renci.SshNet.IntegrationTests/Usings.cs b/src/Renci.SshNet.IntegrationTests/Usings.cs new file mode 100644 index 00000000..8eba0e51 --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/Usings.cs @@ -0,0 +1,5 @@ +global using System.Text; + +global using Microsoft.VisualStudio.TestTools.UnitTesting; + +global using Renci.SshNet.IntegrationTests.TestsFixtures; diff --git a/src/Renci.SshNet.IntegrationTests/resources/client/id_dsa b/src/Renci.SshNet.IntegrationTests/resources/client/id_dsa new file mode 100644 index 00000000..6c84e0c6 --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/resources/client/id_dsa @@ -0,0 +1,12 @@ +-----BEGIN DSA PRIVATE KEY----- +MIIBuwIBAAKBgQC1Zd32ntjuKsLACveUlEoV9CjFDT/spf7k95Rh/U/2abZx0pa0 +8Z01lwpdIPdShHgmhJww4S8ZuGgr9QIOAf8r3DOLt+KpJmjS8ti4gMYqnaG2XDRu +tT6sKneSA5Kd/CwbJ/LZm9dsbvTWpaVHzokhAdXMgq+MxWTK5tMLXciUFQIVAK76 +I2Sp/9g4BiNisdIIcWZYB8RhAoGADlSeN+FAEdx5+pQOZ1jXxTrlFR91u5yWj9BU +CYiD8exlG3cTvarQzU21pFi93PasefgezpXuMTO3L8lz6zUFGAxwhZUvlHtsdyHi +a5HX2ZB/Xjz9ucuQNCeP3PvF170Go+MwOZ38Nd6MuT7cne3dyqubRAzPColXSIcJ +F41ANz0CgYEAm8IGZQatS7M6AfNITNWG4TI7Z2aRQjLb9/MWJIID7c/VQ4zdTZdG +3kpk0Gj9n4xreopK5NmYAdj8rtFfPBgmXltsLqt+bBcXkpxW//7WC29WOXW3t90y +STh+cWuWfr9fV7mf4Ql/6u/ZIgpQNvnNYezazt3fK8EXjI1dAXEuQxECFBhGOzk+ +Aimeob964E8+HsQNlyde +-----END DSA PRIVATE KEY----- diff --git a/src/Renci.SshNet.IntegrationTests/resources/client/id_dsa.ppk b/src/Renci.SshNet.IntegrationTests/resources/client/id_dsa.ppk new file mode 100644 index 00000000..b73384f8 --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/resources/client/id_dsa.ppk @@ -0,0 +1,17 @@ +PuTTY-User-Key-File-2: ssh-dss +Encryption: none +Comment: imported-openssh-key +Public-Lines: 10 +AAAAB3NzaC1kc3MAAACBALVl3fae2O4qwsAK95SUShX0KMUNP+yl/uT3lGH9T/Zp +tnHSlrTxnTWXCl0g91KEeCaEnDDhLxm4aCv1Ag4B/yvcM4u34qkmaNLy2LiAxiqd +obZcNG61Pqwqd5IDkp38LBsn8tmb12xu9NalpUfOiSEB1cyCr4zFZMrm0wtdyJQV +AAAAFQCu+iNkqf/YOAYjYrHSCHFmWAfEYQAAAIAOVJ434UAR3Hn6lA5nWNfFOuUV +H3W7nJaP0FQJiIPx7GUbdxO9qtDNTbWkWL3c9qx5+B7Ole4xM7cvyXPrNQUYDHCF +lS+Ue2x3IeJrkdfZkH9ePP25y5A0J4/c+8XXvQaj4zA5nfw13oy5Ptyd7d3Kq5tE +DM8KiVdIhwkXjUA3PQAAAIEAm8IGZQatS7M6AfNITNWG4TI7Z2aRQjLb9/MWJIID +7c/VQ4zdTZdG3kpk0Gj9n4xreopK5NmYAdj8rtFfPBgmXltsLqt+bBcXkpxW//7W +C29WOXW3t90ySTh+cWuWfr9fV7mf4Ql/6u/ZIgpQNvnNYezazt3fK8EXjI1dAXEu +QxE= +Private-Lines: 1 +AAAAFBhGOzk+Aimeob964E8+HsQNlyde +Private-MAC: 1c254f3882a6661c98fb82dea1a55638a23633e5 diff --git a/src/Renci.SshNet.IntegrationTests/resources/client/id_noaccess.rsa b/src/Renci.SshNet.IntegrationTests/resources/client/id_noaccess.rsa new file mode 100644 index 00000000..cf2cc979 --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/resources/client/id_noaccess.rsa @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEoQIBAAKCAQEAuTtXn+BatX1oJuvhqfJZw5jc/pcIxJUPmuoFCH3+bXfKBJ/9 +4ixNETzZBasyvT/ozboAbCG3qcJOYxf2BEeTAIXe1jLAoTd1GKCwMvZOyjnsPN95 +/lChwfdnBbMzpZYTGfoUylXme/mzjjLu/J0qXgR5lyk9HFT+x5YEtRl8VSHiDkLK +TZ37dwhsqgcs+PkfvYMUK+C8evnfE0tgWgKZk0Eatl87nLWyVXB4LzhSDtGKLCPA +OgrX7fYfplDwJ2WK1N6nG0FnxW1HhDeSK7e2TbAa2vZQgvFXMWnO4O/NZKp4COpO +ReyliWhdtKAjr/+cD4yDfPjhjjKOYfxbvdRG4QIBIwKCAQAqVrTxV9o4HKoXhl93 +TVZYl/f/rX5Y0Z0quSW4zFdpendRg6e+qwpNFTjrWlS9ivNiOSSrAGR+ktAWpmQe +PD7bjFAw9ahfXSIUQfxja3+5Mc+Y4p+KlhZYOIyTlqy4Ik2CR8o84G8yR7QDPteK +Mo1XUXrguPgGedPV2SWlvK60XyAXqsewDhi7SeImZomKzbh33SXjVxakzHfa8BEU +eIIeR9oFlQMuYdo4GrHhFO2T+g/gqw/kVd1zkeEwt06fZVDErVwp+twewxxvwrk4 +CKUCzavfhDfi5sJ5YdzhDBRgkyBgJI+f15dKyqqOiAparV9+uzrD6vIuNnlVoqQA +iugLAoGBAPBliy32e83nshBknBn5HOK2rO3a1zHxvYr/NzITXtdZOjatNyfXtkwi +Ll/el5tZhJvKe9nItSI/4w7mvlvXZfW8h3MR0qb8at4jWa8ya2hwEerqaJonqjjb ++eBhg27ltZIQRk8Bv6ApXTAWkc+dFGhEIysokDQX7V72Bdrizup1AoGBAMVBLHK0 +5IFb8x7danlAmDX6bqCObId4Pce2OeONFIj1jIowvCXaE0t9zU4X5SdN5ujqu4Dq +XgzUdNeKcJxWpFO74MDRxT3CbMz36fikJnvxWl/+q0HalYuCY8gm14VYcThUBAro +3c941INueybGNLIA9jc7RMnsFtyVTvNYpaU9AoGAFJr9TRUgjf3qsPKuS15+0Zqh +G7OsC5hgtCSBEuu3rA72XHU/Pe3rDdcLSgvD2h2dpvQZPo2L3l0/WQx2t2o78H3f +uWftfAcB2Iav6nIJNNZn75BvXaug4E1ej5NUaJdYtL+Q/3UtrqR1s6opwVabWWTt +ElPvGmhzboodwk30en8CgYAyuPzNCfGdm00lMZ8JPH7pTwaBDq4xdrDM9FgHUCna +E0FlXP0uTgT2J6nSQKijtPI75JadfhgvL1E+vTLmX2wViBU45XvcrlZ92Vlr0nBL +wbgnUB1otIzauyD49AuIsFegxSWcZ8QCJmKIMlouir0X1FyR3Apfzv6Qfio+kyNH +vwKBgQCtwxojkzUSfV3zDt6bYSLBzgXgo/Zr9lS+gSggP72DzINmW2gbA0fkM2Zu +JltcfakKv4gVX/1zooz+7t+4bj6dqt+bl7hYz0VnTSDZGuo5LKDif/4gSGrdblC2 +QLTuX2HjWCZdsue7mRwL7cXR4zlIoE99+Ryhdxvc5wHSfYr/JA== +-----END RSA PRIVATE KEY----- \ No newline at end of file diff --git a/src/Renci.SshNet.IntegrationTests/resources/client/id_rsa b/src/Renci.SshNet.IntegrationTests/resources/client/id_rsa new file mode 100644 index 00000000..da8f397e --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/resources/client/id_rsa @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAuDdE+laKZ8D0mEFMNMGkDYQY89A2BUML5L+DsFmEn//3yRdz +Omx+Nk59ifiJUPuP3whZxN5vfbHleU9ZshI+37LqTbXmNrChRlxDSxrQA/++hTNI +gZl8e+sZUk8mUkpjQyEVOMVEdt3H3PmpfOBeZjtpRJPuWa90J+SVKbnAX4rOL7bt +LDA4GeMNXdLXLl5E/zBCZ9ol9nJ8W0suajQDx7u2/ixH1wb9jqzA6j68f1+kERII +t8OUcS+ZM+274rVrCMCL290k9gEQAKwcG/KRMqdgP8Oadtq5ELS/t4m2fH5JqYJ9 +9399QGT4LTPvoiTwZjyhYwK3FhCfXyFQf+gwIwIDAQABAoIBAGLChsFrKfJr2PXT +dAaIleoFGteDlaKGilbNcc1WgKrCsNXnM4hr59I3jEgurXd0FnKs6GuKEN2jRPIf +X2f/LiQBqGmXDl/dm+i7x/v42PJ75mlE0Cdi4QESTlX5RwMxDDxN/TGdWJIdXmwS +kRH4u8M1ML9qS4tba/uDKZDgG8lcF/z6K0RbXDMxL0azfbd5e+jca1e8Fs93X5s7 +mJotXaA+L33R7lCpBBOa1OY517Ug7bdI+uWh59o0bw8v2q8vr8ISOHU3N+JvKZ62 +2z5O6lLyB94sF6ltXRv9pmfcSBjfB8CJx5q1yejUZKM6VfN98MTSKo8WKMEtGmqk +BtZFTlECgYEA8WYG6vntqXPCHhFFfgCymh8OoXL/mPDSkqsswKfeD4O+Ml9hRUtg +xl6FDxFAMR7WJ4Vb8u9IMOc7Xx+nzlZNsdC5m7FAVRIEUPPjEucte+ZYKjSy+WOd +dAtQ07O3z9fi+JNplSjisKtBWaemfqc2TcYXOeIIgwJnkaCf2C7I2bsCgYEAw1vJ +9c5VLTisPj7ijMeGLWISG5E0aidOrb15E8xcnXuT9TEW1Dc1EgRK/D03tlnoxr1I +CISPx4EmdLTiEl2AVi33DOhCeFAt8TOd/y3chKsbewb+BYEMmBD93mhsKg+YmC5E +284SCV3fCcyFJfo9oy5Z2tIELerjT0cnpHgPCLkCgYEAij3OemRUeTUklol3jXgi +z+Y3P7gWreREAuBqSY4YujPNCRXcI43ORuu8MWvEohyxsYJKrO3hHrhdJNWBCMYd +ylXo5UN1vwIJXL6+bIXdY1X/aXQyhmVItzr/t6z0997/STFKRrRaVahNTWWYEHH7 +xEBL7scF7tjCrQAaafgo558CgYBkrrm3ZU+grsSWj/JSe8I7QX/zlTJeQ0PZZv0v +pvNUdowaoeISHSHM10mOFj7QTCYbxxGI0kkHmRgordCVhnrN74KTtGANgcUrul6D +VS+BcG4JSeFBFPFYreko5shYJRGP3MjAP8Qr76Uzd6RnnkCGCS1mCTb+M0BTa2iS +6w1UgQKBgQDQ2qV4s7xH3dixy4MWhDBFmrQlFpQkNNkrJ/ImHrxI2tFyNaq1fE+Q +PrXJi8mjwb/ETVN2C5iBTtIyVg1pZk3YAWAvIt9SPaRVYQWj8IJeOTTaNEZEvp5K +1LJBWO0ksgJK28f/z7FwejeGbBwg8ch9wVhtFwIV++rZ76smP7C+9Q== +-----END RSA PRIVATE KEY----- diff --git a/src/Renci.SshNet.IntegrationTests/resources/client/id_rsa.pub b/src/Renci.SshNet.IntegrationTests/resources/client/id_rsa.pub new file mode 100644 index 00000000..24ba2f7d --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/resources/client/id_rsa.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC4N0T6VopnwPSYQUw0waQNhBjz0DYFQwvkv4OwWYSf//fJF3M6bH42Tn2J+IlQ+4/fCFnE3m99seV5T1myEj7fsupNteY2sKFGXENLGtAD/76FM0iBmXx76xlSTyZSSmNDIRU4xUR23cfc+al84F5mO2lEk+5Zr3Qn5JUpucBfis4vtu0sMDgZ4w1d0tcuXkT/MEJn2iX2cnxbSy5qNAPHu7b+LEfXBv2OrMDqPrx/X6QREgi3w5RxL5kz7bvitWsIwIvb3ST2ARAArBwb8pEyp2A/w5p22rkQtL+3ibZ8fkmpgn33f31AZPgtM++iJPBmPKFjArcWEJ9fIVB/6DAj diff --git a/src/Renci.SshNet.IntegrationTests/resources/client/id_rsa_with_pass b/src/Renci.SshNet.IntegrationTests/resources/client/id_rsa_with_pass new file mode 100644 index 00000000..841532d1 --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/resources/client/id_rsa_with_pass @@ -0,0 +1,28 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABC9v0UCCP +T+1yNEu9m0w939AAAAEAAAAAEAAAEXAAAAB3NzaC1yc2EAAAADAQABAAABAQC4N0T6Vopn +wPSYQUw0waQNhBjz0DYFQwvkv4OwWYSf//fJF3M6bH42Tn2J+IlQ+4/fCFnE3m99seV5T1 +myEj7fsupNteY2sKFGXENLGtAD/76FM0iBmXx76xlSTyZSSmNDIRU4xUR23cfc+al84F5m +O2lEk+5Zr3Qn5JUpucBfis4vtu0sMDgZ4w1d0tcuXkT/MEJn2iX2cnxbSy5qNAPHu7b+LE +fXBv2OrMDqPrx/X6QREgi3w5RxL5kz7bvitWsIwIvb3ST2ARAArBwb8pEyp2A/w5p22rkQ +tL+3ibZ8fkmpgn33f31AZPgtM++iJPBmPKFjArcWEJ9fIVB/6DAjAAADwPBAapXaoQvm3O +2i9sBnmO+d8kCdm2nhGbEXNzswb0toARYyx7/rPON15BHv470NLK4GjtxWb8SbkUBjWIXJ +dpJR1feYAgJQ27yaU6SBEJvnQBFI3EvH+h9ykaikDP/SzgZuGup5NZIoB09PzCPk4SbAwn +skFT3s1v3ufaULYTiAO2xtWzABkjxfw8HOo+PF3UqGIF4145kGLUT1pUN7iy5EQZA8evRb +Yt/9+ChcyBgKONgXdpjLNf02XIM/jQofZkROBg7ZAKCjtL3yGpvtOpwzCKy1hWDmgjtkeK +Xn84/qwXWEobBa2wrDQ4Mjj7AIimRsCciO05bVB5KtNjT+WzCalpTzfj2nazukteNRKTD3 +bQR0gLFfFX4/YodXmtu2n+0R2AKkdPW5ZhoEEpT1FjfYUImAuElSEy5FEeR3bwE5uGQkF4 +uIMJWD+89QxO9PWKTloVI2hrOF9/z+UzUi7p16FQFDlB82qCQAiaIOHgotgDn9+OwMw0ew +Gu/D3T8ZpKXcTAxK1JeoDFh2h+CE37JDvftNIxIhTp7lrhCdioj1DSBorwfke/q4+OvLUH +8SZ6ZgppHjJ4jg6lB9TWCpD5PECDW+NuQQwUb5V4NKoBqnJrPaNUSbI7SL5Pq3mOXXNRmv +q1Va06CfcHL3JupICFifux5xBDlQY0foAt7DFOgA6qANaCnRl2H2oFsEdRhBbL6EPP0bAQ +7PBvWJlt5Aqr1V7QzcCHNZcyGpjiHeXsQxSWXzb1xQprlrDSnlGZpusrBTcZTLWUtOAgqQ +dNbUNeUq1E74rZ/RBiVliaLHNBKwTCE9KyZTl/DcfgInB7DDEd7Vlmujzar7xAXRJot7k2 +a12gT69eVsqiO5si23493JFl0JB5vbQvQWARAvcdNPDPiILoJVpbgyL9oMzVzTjJyqYS1x +cHXKE+rL4ZHeQhcfySXplZlUY9ADVY5QywAj2kl+5gT3Fohu/95axF7w9dAHyMntxbVnA3 +r0zOmqbAKOYEYEXxZ+Vq09YdEyJh33V48PUmvCusWWyeKIfjO4R1nD2+7iyiF7joF7bOBm +o0LXuJdr81BCMueryPUaqSRpGhg/P/nUqzxj7p0mAh9uUOr/CtOpbc6wNY70RgdiMGFWhz +zhO54p7iEg2zZNUZ5zRM1hTBikTeYyInpw8IQiMv9GH7/9gWwqPsh0qRVjj5kjqjSu069d +FeuGjsMGCnLLG6WvOQ9DgH9JR4cfinBL3JwtpHFBIHwhsh2EIuSseVHvQlFlEc1U+7Yoxc +0dn1VVtg== +-----END OPENSSH PRIVATE KEY----- diff --git a/src/Renci.SshNet.IntegrationTests/resources/client/key_ecdsa_256_openssh b/src/Renci.SshNet.IntegrationTests/resources/client/key_ecdsa_256_openssh new file mode 100644 index 00000000..29ecb907 --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/resources/client/key_ecdsa_256_openssh @@ -0,0 +1,9 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS +1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQT886z6SLRIzRu7VNA6SSeKZCNNRPXe +iutTik1T3RUEshgnTI/V3T/d5QurCQPvf2ob3+Rd4FhCsVCS9gilIhVsAAAAsH3KX4d9yl ++HAAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBPzzrPpItEjNG7tU +0DpJJ4pkI01E9d6K61OKTVPdFQSyGCdMj9XdP93lC6sJA+9/ahvf5F3gWEKxUJL2CKUiFW +wAAAAgYxeSyo7MVNup52COOCarcvARKlWhKIP2CKzj4qa5/6EAAAAYc3NobmV0QFVidW50 +dTE5MTBEZXNrdG9w +-----END OPENSSH PRIVATE KEY----- diff --git a/src/Renci.SshNet.IntegrationTests/resources/client/key_ecdsa_256_openssh.pub b/src/Renci.SshNet.IntegrationTests/resources/client/key_ecdsa_256_openssh.pub new file mode 100644 index 00000000..33524cea --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/resources/client/key_ecdsa_256_openssh.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBPzzrPpItEjNG7tU0DpJJ4pkI01E9d6K61OKTVPdFQSyGCdMj9XdP93lC6sJA+9/ahvf5F3gWEKxUJL2CKUiFWw= sshnet@Ubuntu1910Desktop diff --git a/src/Renci.SshNet.IntegrationTests/resources/client/key_ecdsa_384_openssh b/src/Renci.SshNet.IntegrationTests/resources/client/key_ecdsa_384_openssh new file mode 100644 index 00000000..e720d240 --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/resources/client/key_ecdsa_384_openssh @@ -0,0 +1,10 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAiAAAABNlY2RzYS +1zaGEyLW5pc3RwMzg0AAAACG5pc3RwMzg0AAAAYQS0rLvxzSomgC4UNulA7/jdABXTG9un +zFazinvOughrumo0n/R1DoSRXY4bHooQdq02pTD6/DK3FzS7n4ouJi/LuJfX1EFxYMJzP2 +aYlT0rOgvvIuLv2Q4OzcdjV8mzSIEAAADo1T5ZUtU+WVIAAAATZWNkc2Etc2hhMi1uaXN0 +cDM4NAAAAAhuaXN0cDM4NAAAAGEEtKy78c0qJoAuFDbpQO/43QAV0xvbp8xWs4p7zroIa7 +pqNJ/0dQ6EkV2OGx6KEHatNqUw+vwytxc0u5+KLiYvy7iX19RBcWDCcz9mmJU9KzoL7yLi +79kODs3HY1fJs0iBAAAAMQDgRb336Dk9e4VxOpSqnwBqHRsJ3QmSME9qMBvx5SXykHFAsK +wzVKEvIQizmg/+sWcAAAAYc3NobmV0QFVidW50dTE5MTBEZXNrdG9wAQIDBAUGBw== +-----END OPENSSH PRIVATE KEY----- diff --git a/src/Renci.SshNet.IntegrationTests/resources/client/key_ecdsa_384_openssh.pub b/src/Renci.SshNet.IntegrationTests/resources/client/key_ecdsa_384_openssh.pub new file mode 100644 index 00000000..99878d2c --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/resources/client/key_ecdsa_384_openssh.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBLSsu/HNKiaALhQ26UDv+N0AFdMb26fMVrOKe866CGu6ajSf9HUOhJFdjhseihB2rTalMPr8MrcXNLufii4mL8u4l9fUQXFgwnM/ZpiVPSs6C+8i4u/ZDg7Nx2NXybNIgQ== sshnet@Ubuntu1910Desktop diff --git a/src/Renci.SshNet.IntegrationTests/resources/client/key_ecdsa_521_openssh b/src/Renci.SshNet.IntegrationTests/resources/client/key_ecdsa_521_openssh new file mode 100644 index 00000000..47ee8ff8 --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/resources/client/key_ecdsa_521_openssh @@ -0,0 +1,12 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAArAAAABNlY2RzYS +1zaGEyLW5pc3RwNTIxAAAACG5pc3RwNTIxAAAAhQQAgeFoEYBgUaOlJPnEYIPLSTwmxLRl +EUVpVAOzww3q10fj/Tuppty/fRLcbMoeVzWKl8mjDbR+XOdaKDGo6xcHGsgByITz2/F9wr +E8BHyFEPemg8h0DKLW0X55J+rnn3lE0a0jXKngJ3VLVcgKgXam7KtpoCWFx689jVCpTxWI +GrkIvlkAAAEYr0LrEa9C6xEAAAATZWNkc2Etc2hhMi1uaXN0cDUyMQAAAAhuaXN0cDUyMQ +AAAIUEAIHhaBGAYFGjpST5xGCDy0k8JsS0ZRFFaVQDs8MN6tdH4/07qabcv30S3GzKHlc1 +ipfJow20flznWigxqOsXBxrIAciE89vxfcKxPAR8hRD3poPIdAyi1tF+eSfq5595RNGtI1 +yp4Cd1S1XICoF2puyraaAlhcevPY1QqU8ViBq5CL5ZAAAAQQ50pmBidmKTIknaRpdO5WIu +nYEGMUkLqdZ0egk9Ggg63mOHiLykf+XWcGbHbHM95CISXhqlvMtCYeGwOpP6FoMGAAAAGH +NzaG5ldEBVYnVudHUxOTEwRGVza3RvcAECAw== +-----END OPENSSH PRIVATE KEY----- diff --git a/src/Renci.SshNet.IntegrationTests/resources/client/key_ecdsa_521_openssh.pub b/src/Renci.SshNet.IntegrationTests/resources/client/key_ecdsa_521_openssh.pub new file mode 100644 index 00000000..085fa07b --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/resources/client/key_ecdsa_521_openssh.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBACB4WgRgGBRo6Uk+cRgg8tJPCbEtGURRWlUA7PDDerXR+P9O6mm3L99Etxsyh5XNYqXyaMNtH5c51ooMajrFwcayAHIhPPb8X3CsTwEfIUQ96aDyHQMotbRfnkn6uefeUTRrSNcqeAndUtVyAqBdqbsq2mgJYXHrz2NUKlPFYgauQi+WQ== sshnet@Ubuntu1910Desktop diff --git a/src/Renci.SshNet.IntegrationTests/resources/client/key_ed25519_openssh b/src/Renci.SshNet.IntegrationTests/resources/client/key_ed25519_openssh new file mode 100644 index 00000000..0bcb6e75 --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/resources/client/key_ed25519_openssh @@ -0,0 +1,7 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW +QyNTUxOQAAACAJDRj1Tk7s7ik4Bnx3L3tjEY+e8l4ZmYJFMovUaZia5QAAAKBKTcfHSk3H +xwAAAAtzc2gtZWQyNTUxOQAAACAJDRj1Tk7s7ik4Bnx3L3tjEY+e8l4ZmYJFMovUaZia5Q +AAAEBMMOaGa8RU8Vy0vLFlRT5iVSxl3ji9NBKaO/RS0aFL3QkNGPVOTuzuKTgGfHcve2MR +j57yXhmZgkUyi9RpmJrlAAAAGHNzaG5ldEBVYnVudHUxOTEwRGVza3RvcAECAwQF +-----END OPENSSH PRIVATE KEY----- diff --git a/src/Renci.SshNet.IntegrationTests/resources/issue #70.png b/src/Renci.SshNet.IntegrationTests/resources/issue #70.png new file mode 100644 index 00000000..8c972379 Binary files /dev/null and b/src/Renci.SshNet.IntegrationTests/resources/issue #70.png differ diff --git a/src/Renci.SshNet.IntegrationTests/server/script/start.sh b/src/Renci.SshNet.IntegrationTests/server/script/start.sh new file mode 100644 index 00000000..e7e9f758 --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/server/script/start.sh @@ -0,0 +1,10 @@ +#!/bin/ash +/usr/sbin/syslog-ng + +# allow us to make changes to /etc/hosts; we need this for the port forwarding tests +chmod 777 /etc/hosts + +# start PAM-enabled ssh daemon as we also want keyboard-interactive authentication to work +/usr/sbin/sshd.pam + +tail -f < /var/log/auth.log diff --git a/src/Renci.SshNet.IntegrationTests/server/ssh/ssh_host_dsa_key b/src/Renci.SshNet.IntegrationTests/server/ssh/ssh_host_dsa_key new file mode 100644 index 00000000..eedaafb0 --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/server/ssh/ssh_host_dsa_key @@ -0,0 +1,20 @@ +-----BEGIN DSA PRIVATE KEY----- +MIIDPgIBAAKCAQEAuXza5HoqeOTKgTBY0iTglJGVLmmGvp9mWbrx20Xj8V1ouy8u +0ceju7/4AR6m9BzYWm2sAMAwvQcDeUi6pD4C4oIRzQSOg/nuUJO6RkneLQjMYEzD +61FokmxcUzHXQiKtqRRGL97naxj5fFIOppQXfllRASuvHeiG+I6EiFJL4zL7Uwen +CshEkpZsLZ2Xj8nfaD8yPmviDT/QWRUsZgw8lte7MonYVdKd0yeRQwS3vgJwusZv +fFHP4X7aXSwDJTlTGagxFV7jCktwtSc6QFoLWv5LZ2OAJxmgBM1HJQKOnP8dvn56 +EZub3DQrx3IpAgtsxa/8bxt/xFbbfp4sDHwLLQIVAKTEaiNtqneHljGoEGhUWJrs ++kdpAoIBAQCdBG7aHBIV83/icpkELAZ87I/0XDA9pVG+Sgs/OFgUd24tXi9S+dwp +LsVMVaBnN9TaEwZYR6z7Zg12r2j2q8BDTrRwwYHYJvwjHtsZVqaHi35fgBT2RO4T +SqRKYjrjb4mtPodUEo7CzK5+rLpvLM1SiiHfeqmUJqbkDwxQ9xXkCjRP50huJ+tA +ccgQIUyOYioz9omszJGANZlF5ZabzbAiTcXews2p97OeFWNTGTbXebV3FPSV+KBO +c0A5jxzQhEo3Kk58GXuog8t3OksNISdZPIJxHn+th644ZOj0L1v6PrUbXshPL1hp +VNlbn9fO4/HbQzL4NThmgzaZkT2FqxPxAoIBADuwcLTKtLX2cy9cqFiraeEaBXT3 +lQiPTFSLQKVm/k+iumXuOy5Fh3Akzu35MpNLK2gsdoWN9ZRQ8eWODdcnFXSJrnqX +cMWV6ONQ+nZ9YHrRp47KHKWKe+2c0T++S8QZAimb3KCjSOyEwn+i4aAGIvoaYIoH ++tRKmeL+7z1Ff/zJEB1FYVDmcqxhUKd74En6O17EmUHPfiQvwwTYvP5NvlLB23Hz +9ZO4nwrUSUIyVsWYT01s0JThkjI06N0dqKS1we94Ht1mT7iNJ5x5DhVR6qSNOgQH +FMKxKdXHdSFopwwHUrzm3BpKzKW3NuQHazcdZEl1vHb6LpfTv6O6bZIANyACFGBZ +9othW6gmt8t4cI6IyoaLCtLp +-----END DSA PRIVATE KEY----- diff --git a/src/Renci.SshNet.IntegrationTests/server/ssh/ssh_host_ecdsa_key b/src/Renci.SshNet.IntegrationTests/server/ssh/ssh_host_ecdsa_key new file mode 100644 index 00000000..5739844c --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/server/ssh/ssh_host_ecdsa_key @@ -0,0 +1,9 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS +1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQTtDoci0CaEgyR+p+ersiYltKUSqZx/ +MffWpnEPfGgnFI81huQw0D9e/SqABbeHtrzcSWskSZc0f2jjFxyqVkliAAAAsDrEln06xJ +Z9AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBO0OhyLQJoSDJH6n +56uyJiW0pRKpnH8x99amcQ98aCcUjzWG5DDQP179KoAFt4e2vNxJayRJlzR/aOMXHKpWSW +IAAAAgYdRMomjDSquRMSYTvEIzX7cReJ2grVIWsxIOLyhJnw0AAAAWcm9vdEBVYnVudHUx +OTEwRGVza3RvcAEC +-----END OPENSSH PRIVATE KEY----- diff --git a/src/Renci.SshNet.IntegrationTests/server/ssh/ssh_host_ed25519_key b/src/Renci.SshNet.IntegrationTests/server/ssh/ssh_host_ed25519_key new file mode 100644 index 00000000..8e45178e --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/server/ssh/ssh_host_ed25519_key @@ -0,0 +1,7 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW +QyNTUxOQAAACCCMW3VnuKfj6AxBQ7gJ4qAfEgw/YJl9q3AXyelCw+OdwAAAKDKcLC0ynCw +tAAAAAtzc2gtZWQyNTUxOQAAACCCMW3VnuKfj6AxBQ7gJ4qAfEgw/YJl9q3AXyelCw+Odw +AAAED9TRnDkG0tzdZv5oPJCXwzqrkxut7y33A8Wi8AzusJL4IxbdWe4p+PoDEFDuAnioB8 +SDD9gmX2rcBfJ6ULD453AAAAFnJvb3RAVWJ1bnR1MTkxMERlc2t0b3ABAgMEBQYH +-----END OPENSSH PRIVATE KEY----- diff --git a/src/Renci.SshNet.IntegrationTests/server/ssh/ssh_host_rsa_key b/src/Renci.SshNet.IntegrationTests/server/ssh/ssh_host_rsa_key new file mode 100644 index 00000000..edb94f34 --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/server/ssh/ssh_host_rsa_key @@ -0,0 +1,38 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABlwAAAAdzc2gtcn +NhAAAAAwEAAQAAAYEAsG52bLGkFIU2I7mk7zC+VtZxE1JOJKNCTFy0DjZDsTvHaXFWHAyK +f26YIkuQofS2wb3S1/KOIv9UJIvdAK+J+URoUHLt7qGFmUm/YUf/9JjDTdHEvfMqkW5RPs +zShrJkkoz2tekAjDSNJzDs0PUS7MaOezh+8Odr88TH7kwVOdRM+NrKImVnxqN6/dEVJif0 +8NqRrJX6SOapZyOAN+2kTmb5AiS7fYg2W9mI3ulW4GUNN3zpnh2Ferp9pbQ5Afu1LQzQUv +sV6VzN2p3KqFeZ1cu3yAsMPGxpToUzTEiPp8GQs/RoIRV4Rt1uwQM65VkGJD2FMd2gfxe5 +bSHvAq+haOCE8oyT0aDMlR9B72Exfh7iaWIlv44xeQkl6pShbmq3mXkNMGOeW6cVEBO0tY +FSrOCGNPBMeVOPPE7IPAjlaToyYuV4s8FJG+OAjD5/d935tesqvibjF8cTgYdicn+2otcO +JG0h5wbYa7/hUf0wyDG51tQZ2PWKpNhoZjv1FChDAAAFkFijHj9Yox4/AAAAB3NzaC1yc2 +EAAAGBALBudmyxpBSFNiO5pO8wvlbWcRNSTiSjQkxctA42Q7E7x2lxVhwMin9umCJLkKH0 +tsG90tfyjiL/VCSL3QCviflEaFBy7e6hhZlJv2FH//SYw03RxL3zKpFuUT7M0oayZJKM9r +XpAIw0jScw7ND1EuzGjns4fvDna/PEx+5MFTnUTPjayiJlZ8ajev3RFSYn9PDakayV+kjm +qWcjgDftpE5m+QIku32INlvZiN7pVuBlDTd86Z4dhXq6faW0OQH7tS0M0FL7Felczdqdyq +hXmdXLt8gLDDxsaU6FM0xIj6fBkLP0aCEVeEbdbsEDOuVZBiQ9hTHdoH8XuW0h7wKvoWjg +hPKMk9GgzJUfQe9hMX4e4mliJb+OMXkJJeqUoW5qt5l5DTBjnlunFRATtLWBUqzghjTwTH +lTjzxOyDwI5Wk6MmLleLPBSRvjgIw+f3fd+bXrKr4m4xfHE4GHYnJ/tqLXDiRtIecG2Gu/ +4VH9MMgxudbUGdj1iqTYaGY79RQoQwAAAAMBAAEAAAGAQHm90W8BtXYRGPEo8zhu9rEbVa +JIaF85RUrDikYOauCbuU7v1wRGQNebxTy0OFuDxj2mpcBAbU295DUwqKV92JhFPtEhXoms +lx46UETNpweEqBW2vmv07HzSOA8GCK98zYmyRzxFNPendeENSjelmN3fB+zXhxYrf0Q0hE +NNpnqNPoxGPlesmwz3T3ZvMih7/OEDR3zvoGCbG9P/cXDpELXU3hGqau+yXdKbkErZstt6 +/wIpJd1IAFfSvxGjm7PuH81tf4na0IEL/qK6Iq4cMzWcPO06jCul8ZHrhlynR86WB2FkP5 +JMkg1LfkyYZOYu1Rc18WJEGne6qroYAWghdw6IiDAeQVOqDfhHY7dTnT62bgKREoWcFnC2 +lUZTY6KCHDu5NSF+bJa1KhRJzgxwjKEXm4dTxNC1qTMxjD7UqQvhXcJNbCWDDDvqEXjjn9 +Sj7EHWMN2/CnopyMJiXzT0JnXq8as5LAYkzT+B7DovGq233SNZ9xaP5LA89mEiP7UBAAAA +wE1+APSCfmyVrIcyredKJe5cEtc9VOPhYEQB+iTYvB5qeFZlLr1hq51rdUShqhxPEgiKD6 +e7NWO8omFwi6gauOdRQr5yHFt39JKtNqzjWh3WfiFOCq1HZllIMBKi147xqsprL9vmwyMs +IqdV/gcm2bbS7/IhymGPRgu9Gi5pdf/8XcEUuhhxNVRjcB0oLqU83jzi3+aUU5rj+jVu4D +HY1vXrT9jSFTdT4kGeJ5XaV0xjiA8MjMTsganOQ2Vum2BkfwAAAMEA6ykWOH/pIgdCK4NU +2KhYXzjhSmRLF1oJxyaisPtZ5fdA2DCP+WkSaLa25sXt4Jkn+Pm2w/ChpR3RHIx+7La2iA +uNnpk3o72Wnz/ikm+5sh536N+G+IquqSv4LJ9Dz4xSKZaBTO/nuoO7GoB0tGFeXX2nf+fe +EM8JNHMAXpmu7uFg880CNLGebHzsZXutLxVpKbl9reTCrCgpuWtErolSeYeVat+Pfunv/A +Rs9IQDMLxRYfxNUc5ZpVrBvbTrQOjBAAAAwQDAEQi+B14CD4cczo9+D66PzS8YqwOoZNkH +D5VUASa+q6hn7K4YKyrMBjURKIOff2qbpdsJNl6uPSZvGmv3GeplLqwMejpzbjzZGCizLI +VSrcBTkDR5mN8keBJg5BBcg07Ps8yMIyUcAYjEthNoE/nxQ2YxhxTP5Vw3b8AW5ONnorIT +lJdvUS1Zc41GOBE8BJ4iZcTkbzEzGy4S+Sw4pmxrY4JOeC9e/ewvIADn9UMsj3u1bxQNuJ +1T46YIINhu7gMAAAAWcm9vdEBVYnVudHUxOTEwRGVza3RvcAECAwQF +-----END OPENSSH PRIVATE KEY----- diff --git a/src/Renci.SshNet.IntegrationTests/user/sshnet/authorized_keys b/src/Renci.SshNet.IntegrationTests/user/sshnet/authorized_keys new file mode 100644 index 00000000..d91b4786 --- /dev/null +++ b/src/Renci.SshNet.IntegrationTests/user/sshnet/authorized_keys @@ -0,0 +1,6 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC4N0T6VopnwPSYQUw0waQNhBjz0DYFQwvkv4OwWYSf//fJF3M6bH42Tn2J+IlQ+4/fCFnE3m99seV5T1myEj7fsupNteY2sKFGXENLGtAD/76FM0iBmXx76xlSTyZSSmNDIRU4xUR23cfc+al84F5mO2lEk+5Zr3Qn5JUpucBfis4vtu0sMDgZ4w1d0tcuXkT/MEJn2iX2cnxbSy5qNAPHu7b+LEfXBv2OrMDqPrx/X6QREgi3w5RxL5kz7bvitWsIwIvb3ST2ARAArBwb8pEyp2A/w5p22rkQtL+3ibZ8fkmpgn33f31AZPgtM++iJPBmPKFjArcWEJ9fIVB/6DAj +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBPzzrPpItEjNG7tU0DpJJ4pkI01E9d6K61OKTVPdFQSyGCdMj9XdP93lC6sJA+9/ahvf5F3gWEKxUJL2CKUiFWw= +ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBLSsu/HNKiaALhQ26UDv+N0AFdMb26fMVrOKe866CGu6ajSf9HUOhJFdjhseihB2rTalMPr8MrcXNLufii4mL8u4l9fUQXFgwnM/ZpiVPSs6C+8i4u/ZDg7Nx2NXybNIgQ== +ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBACB4WgRgGBRo6Uk+cRgg8tJPCbEtGURRWlUA7PDDerXR+P9O6mm3L99Etxsyh5XNYqXyaMNtH5c51ooMajrFwcayAHIhPPb8X3CsTwEfIUQ96aDyHQMotbRfnkn6uefeUTRrSNcqeAndUtVyAqBdqbsq2mgJYXHrz2NUKlPFYgauQi+WQ== +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAkNGPVOTuzuKTgGfHcve2MRj57yXhmZgkUyi9RpmJrl +ssh-dss AAAAB3NzaC1kc3MAAACBALVl3fae2O4qwsAK95SUShX0KMUNP+yl/uT3lGH9T/ZptnHSlrTxnTWXCl0g91KEeCaEnDDhLxm4aCv1Ag4B/yvcM4u34qkmaNLy2LiAxiqdobZcNG61Pqwqd5IDkp38LBsn8tmb12xu9NalpUfOiSEB1cyCr4zFZMrm0wtdyJQVAAAAFQCu+iNkqf/YOAYjYrHSCHFmWAfEYQAAAIAOVJ434UAR3Hn6lA5nWNfFOuUVH3W7nJaP0FQJiIPx7GUbdxO9qtDNTbWkWL3c9qx5+B7Ole4xM7cvyXPrNQUYDHCFlS+Ue2x3IeJrkdfZkH9ePP25y5A0J4/c+8XXvQaj4zA5nfw13oy5Ptyd7d3Kq5tEDM8KiVdIhwkXjUA3PQAAAIEAm8IGZQatS7M6AfNITNWG4TI7Z2aRQjLb9/MWJIID7c/VQ4zdTZdG3kpk0Gj9n4xreopK5NmYAdj8rtFfPBgmXltsLqt+bBcXkpxW//7WC29WOXW3t90ySTh+cWuWfr9fV7mf4Ql/6u/ZIgpQNvnNYezazt3fK8EXjI1dAXEuQxE= diff --git a/src/Renci.SshNet.Silverlight/Properties/AssemblyInfo.cs b/src/Renci.SshNet.Silverlight/Properties/AssemblyInfo.cs deleted file mode 100644 index f9c8d324..00000000 --- a/src/Renci.SshNet.Silverlight/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,5 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -[assembly: AssemblyTitle("SSH.NET Silverlight 4")] -[assembly: Guid("2b3f6251-8079-48aa-a76b-df70e40092e2")] \ No newline at end of file diff --git a/src/Renci.SshNet.Silverlight/Renci.SshNet.Silverlight.csproj b/src/Renci.SshNet.Silverlight/Renci.SshNet.Silverlight.csproj deleted file mode 100644 index beeef21a..00000000 --- a/src/Renci.SshNet.Silverlight/Renci.SshNet.Silverlight.csproj +++ /dev/null @@ -1,1456 +0,0 @@ - - - - Debug - AnyCPU - 8.0.50727 - 2.0 - {77C294BB-1DC2-49DC-BE16-963F8F22794D} - {A1591282-1198-4647-A2B1-27E5FF5F6F3B};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} - Library - Properties - Renci.SshNet - Renci.SshNet - Silverlight - v4.0 - $(TargetFrameworkVersion) - false - true - true - - - - v3.5 - - - true - full - false - Bin\Debug - TRACE;DEBUG;FEATURE_DIRECTORYINFO_ENUMERATEFILES;FEATURE_RNG_CSP;FEATURE_SOCKET_EAP;FEATURE_SOCKET_DISPOSE;FEATURE_STREAM_APM;FEATURE_THREAD_THREADPOOL;FEATURE_THREAD_SLEEP;FEATURE_WAITHANDLE_DISPOSE;FEATURE_HASH_SHA1_MANAGED;FEATURE_HASH_SHA256_MANAGED;FEATURE_HMAC_SHA1;FEATURE_HMAC_SHA256;FEATURE_MEMORYSTREAM_GETBUFFER - true - true - prompt - 4 - Bin\Debug\Renci.SshNet.xml - - - none - true - Bin\Release - TRACE;FEATURE_DIRECTORYINFO_ENUMERATEFILES;FEATURE_RNG_CSP;FEATURE_SOCKET_EAP;FEATURE_SOCKET_DISPOSE;FEATURE_STREAM_APM;FEATURE_THREAD_THREADPOOL;FEATURE_THREAD_SLEEP;FEATURE_WAITHANDLE_DISPOSE;FEATURE_HASH_SHA1_MANAGED;FEATURE_HASH_SHA256_MANAGED;FEATURE_HMAC_SHA1;FEATURE_HMAC_SHA256;FEATURE_MEMORYSTREAM_GETBUFFER - true - true - prompt - 4 - Bin\Release\Renci.SshNet.xml - 1591 - - - true - - - ..\Renci.SshNet.snk - - - - - ..\..\packages\SshNet.Security.Cryptography.1.2.0\lib\sl4\SshNet.Security.Cryptography.dll - - - - - - - - - Abstractions\CryptoAbstraction.cs - - - Abstractions\DiagnosticAbstraction.cs - - - Abstractions\DnsAbstraction.cs - - - Abstractions\FileSystemAbstraction.cs - - - Abstractions\ReflectionAbstraction.cs - - - Abstractions\SocketAbstraction.cs - - - Abstractions\ThreadAbstraction.cs - - - AuthenticationMethod.cs - - - AuthenticationResult.cs - - - BaseClient.cs - - - Channels\Channel.cs - - - Channels\ChannelDirectTcpip.cs - - - Channels\ChannelForwardedTcpip.cs - - - Channels\ChannelSession.cs - - - Channels\ChannelTypes.cs - - - Channels\ClientChannel.cs - - - Channels\IChannel.cs - - - Channels\IChannelDirectTcpip.cs - - - Channels\IChannelForwardedTcpip.cs - - - Channels\IChannelSession.cs - - - Channels\ServerChannel.cs - - - CipherInfo.cs - - - ClientAuthentication.cs - - - CommandAsyncResult.cs - - - Common\Array.cs - - - Common\ASCIIEncoding.cs - - - Common\AsyncResult.cs - - - Common\AuthenticationBannerEventArgs.cs - - - Common\AuthenticationEventArgs.cs - - - Common\AuthenticationPasswordChangeEventArgs.cs - - - Common\AuthenticationPrompt.cs - - - Common\AuthenticationPromptEventArgs.cs - - - Common\BigInteger.cs - - - Common\ChannelDataEventArgs.cs - - - Common\ChannelEventArgs.cs - - - Common\ChannelExtendedDataEventArgs.cs - - - Common\ChannelOpenConfirmedEventArgs.cs - - - Common\ChannelOpenFailedEventArgs.cs - - - Common\ChannelRequestEventArgs.cs - - - Common\CountdownEvent.cs - - - Common\DerData.cs - - - Common\ExceptionEventArgs.cs - - - Common\Extensions.cs - - - Common\HostKeyEventArgs.cs - - - Common\ObjectIdentifier.cs - - - Common\Pack.cs - - - Common\PacketDump.cs - - - Common\PipeStream.cs - - - Common\PortForwardEventArgs.cs - - - Common\PosixPath.cs - - - Common\ProxyException.cs - - - Common\ScpDownloadEventArgs.cs - - - Common\ScpException.cs - - - Common\ScpUploadEventArgs.cs - - - Common\SemaphoreLight.cs - - - Common\SftpPathNotFoundException.cs - - - Common\SftpPermissionDeniedException.cs - - - Common\ShellDataEventArgs.cs - - - Common\SshAuthenticationException.cs - - - Common\SshConnectionException.cs - - - Common\SshData.cs - - - Common\SshDataStream.cs - - - Common\SshException.cs - - - Common\SshOperationTimeoutException.cs - - - Common\SshPassPhraseNullOrEmptyException.cs - - - Common\TerminalModes.cs - - - Compression\CompressionMode.cs - - - Compression\Compressor.cs - - - Compression\Zlib.cs - - - Compression\ZlibOpenSsh.cs - - - Compression\ZlibStream.cs - - - ConnectionInfo.cs - - - Connection\ConnectorBase.cs - - - Connection\DirectConnector.cs - - - Connection\HttpConnector.cs - - - Connection\IConnector.cs - - - Connection\IProtocolVersionExchange.cs - - - Connection\ISocketFactory.cs - - - Connection\ProtocolVersionExchange.cs - - - Connection\SocketFactory.cs - - - Connection\Socks4Connector.cs - - - Connection\Socks5Connector.cs - - - Connection\SshIdentification.cs - - - ExpectAction.cs - - - ExpectAsyncResult.cs - - - ForwardedPort.cs - - - ForwardedPortDynamic.cs - - - ForwardedPortLocal.cs - - - ForwardedPortRemote.cs - - - ForwardedPortStatus.cs - - - HashInfo.cs - - - IAuthenticationMethod.cs - - - IClientAuthentication.cs - - - IConnectionInfo.cs - - - IForwardedPort.cs - - - IRemotePathTransformation.cs - - - IServiceFactory.cs - - - ISession.cs - - - ISubsystemSession.cs - - - KeyboardInteractiveAuthenticationMethod.cs - - - KeyboardInteractiveConnectionInfo.cs - - - MessageEventArgs.cs - - - Messages\Authentication\BannerMessage.cs - - - Messages\Authentication\FailureMessage.cs - - - Messages\Authentication\InformationRequestMessage.cs - - - Messages\Authentication\InformationResponseMessage.cs - - - Messages\Authentication\PasswordChangeRequiredMessage.cs - - - Messages\Authentication\PublicKeyMessage.cs - - - Messages\Authentication\RequestMessage.cs - - - Messages\Authentication\RequestMessageHost.cs - - - Messages\Authentication\RequestMessageKeyboardInteractive.cs - - - Messages\Authentication\RequestMessageNone.cs - - - Messages\Authentication\RequestMessagePassword.cs - - - Messages\Authentication\RequestMessagePublicKey.cs - - - Messages\Authentication\SuccessMessage.cs - - - Messages\Connection\CancelTcpIpForwardGlobalRequestMessage.cs - - - Messages\Connection\ChannelCloseMessage.cs - - - Messages\Connection\ChannelDataMessage.cs - - - Messages\Connection\ChannelEofMessage.cs - - - Messages\Connection\ChannelExtendedDataMessage.cs - - - Messages\Connection\ChannelFailureMessage.cs - - - Messages\Connection\ChannelMessage.cs - - - Messages\Connection\ChannelOpenConfirmationMessage.cs - - - Messages\Connection\ChannelOpenFailureMessage.cs - - - Messages\Connection\ChannelOpenFailureReasons.cs - - - Messages\Connection\ChannelOpen\ChannelOpenInfo.cs - - - Messages\Connection\ChannelOpen\ChannelOpenMessage.cs - - - Messages\Connection\ChannelOpen\DirectTcpipChannelInfo.cs - - - Messages\Connection\ChannelOpen\ForwardedTcpipChannelInfo.cs - - - Messages\Connection\ChannelOpen\SessionChannelOpenInfo.cs - - - Messages\Connection\ChannelOpen\X11ChannelOpenInfo.cs - - - Messages\Connection\ChannelRequest\BreakRequestInfo.cs - - - Messages\Connection\ChannelRequest\ChannelRequestMessage.cs - - - Messages\Connection\ChannelRequest\EndOfWriteRequestInfo.cs - - - Messages\Connection\ChannelRequest\EnvironmentVariableRequestInfo.cs - - - Messages\Connection\ChannelRequest\ExecRequestInfo.cs - - - Messages\Connection\ChannelRequest\ExitSignalRequestInfo.cs - - - Messages\Connection\ChannelRequest\ExitStatusRequestInfo.cs - - - Messages\Connection\ChannelRequest\KeepAliveRequestInfo.cs - - - Messages\Connection\ChannelRequest\PseudoTerminalInfo.cs - - - Messages\Connection\ChannelRequest\RequestInfo.cs - - - Messages\Connection\ChannelRequest\ShellRequestInfo.cs - - - Messages\Connection\ChannelRequest\SignalRequestInfo.cs - - - Messages\Connection\ChannelRequest\SubsystemRequestInfo.cs - - - Messages\Connection\ChannelRequest\WindowChangeRequestInfo.cs - - - Messages\Connection\ChannelRequest\X11ForwardingRequestInfo.cs - - - Messages\Connection\ChannelRequest\XonXoffRequestInfo.cs - - - Messages\Connection\ChannelSuccessMessage.cs - - - Messages\Connection\ChannelWindowAdjustMessage.cs - - - Messages\Connection\GlobalRequestMessage.cs - - - Messages\Connection\GlobalRequestName.cs - - - Messages\Connection\RequestFailureMessage.cs - - - Messages\Connection\RequestSuccessMessage.cs - - - Messages\Connection\TcpIpForwardGlobalRequestMessage.cs - - - Messages\Message.cs - - - Messages\MessageAttribute.cs - - - Messages\ServiceName.cs - - - Messages\Transport\DebugMessage.cs - - - Messages\Transport\DisconnectMessage.cs - - - Messages\Transport\DisconnectReason.cs - - - Messages\Transport\IgnoreMessage.cs - - - Messages\Transport\IKeyExchangedAllowed.cs - - - Messages\Transport\KeyExchangeDhGroupExchangeGroup.cs - - - Messages\Transport\KeyExchangeDhGroupExchangeInit.cs - - - Messages\Transport\KeyExchangeDhGroupExchangeReply.cs - - - Messages\Transport\KeyExchangeDhGroupExchangeRequest.cs - - - Messages\Transport\KeyExchangeDhInitMessage.cs - - - Messages\Transport\KeyExchangeDhReplyMessage.cs - - - Messages\Transport\KeyExchangeEcdhInitMessage.cs - - - Messages\Transport\KeyExchangeEcdhReplyMessage.cs - - - Messages\Transport\KeyExchangeInitMessage.cs - - - Messages\Transport\NewKeysMessage.cs - - - Messages\Transport\ServiceAcceptMessage.cs - - - Messages\Transport\ServiceRequestMessage.cs - - - Messages\Transport\UnimplementedMessage.cs - - - NoneAuthenticationMethod.cs - - - PasswordAuthenticationMethod.cs - - - PasswordConnectionInfo.cs - - - PrivateKeyAuthenticationMethod.cs - - - PrivateKeyConnectionInfo.cs - - - PrivateKeyFile.cs - - - ProxyTypes.cs - - - RemotePathDoubleQuoteTransformation.cs - - - RemotePathNoneTransformation.cs - - - RemotePathShellQuoteTransformation.cs - - - RemotePathTransformation.cs - - - ScpClient.cs - - - Security\Algorithm.cs - - - Security\Cryptography\BouncyCastle\asn1\sec\SECNamedCurves.cs - - - Security\Cryptography\BouncyCastle\asn1\x9\X9Curve.cs - - - Security\Cryptography\BouncyCastle\asn1\x9\X9ECParameters.cs - - - Security\Cryptography\BouncyCastle\asn1\x9\X9ECParametersHolder.cs - - - Security\Cryptography\BouncyCastle\asn1\x9\X9ECPoint.cs - - - Security\Cryptography\BouncyCastle\crypto\agreement\ECDHCBasicAgreement.cs - - - Security\Cryptography\BouncyCastle\crypto\AsymmetricCipherKeyPair.cs - - - Security\Cryptography\BouncyCastle\crypto\AsymmetricKeyParameter.cs - - - Security\Cryptography\BouncyCastle\crypto\digests\GeneralDigest.cs - - - Security\Cryptography\BouncyCastle\crypto\digests\Sha256Digest.cs - - - Security\Cryptography\BouncyCastle\crypto\generators\ECKeyPairGenerator.cs - - - Security\Cryptography\BouncyCastle\crypto\IAsymmetricCipherKeyPairGenerator.cs - - - Security\Cryptography\BouncyCastle\crypto\IDigest.cs - - - Security\Cryptography\BouncyCastle\crypto\KeyGenerationParameters.cs - - - Security\Cryptography\BouncyCastle\crypto\parameters\ECDomainParameters.cs - - - Security\Cryptography\BouncyCastle\crypto\parameters\ECKeyGenerationParameters.cs - - - Security\Cryptography\BouncyCastle\crypto\parameters\ECKeyParameters.cs - - - Security\Cryptography\BouncyCastle\crypto\parameters\ECPrivateKeyParameters.cs - - - Security\Cryptography\BouncyCastle\crypto\parameters\ECPublicKeyParameters.cs - - - Security\Cryptography\BouncyCastle\crypto\prng\CryptoApiRandomGenerator.cs - - - Security\Cryptography\BouncyCastle\crypto\prng\DigestRandomGenerator.cs - - - Security\Cryptography\BouncyCastle\crypto\prng\IRandomGenerator.cs - - - Security\Cryptography\BouncyCastle\crypto\util\Pack.cs - - - Security\Cryptography\BouncyCastle\math\BigInteger.cs - - - Security\Cryptography\BouncyCastle\math\ec\abc\SimpleBigDecimal.cs - - - Security\Cryptography\BouncyCastle\math\ec\abc\Tnaf.cs - - - Security\Cryptography\BouncyCastle\math\ec\abc\ZTauElement.cs - - - Security\Cryptography\BouncyCastle\math\ec\ECAlgorithms.cs - - - Security\Cryptography\BouncyCastle\math\ec\ECCurve.cs - - - Security\Cryptography\BouncyCastle\math\ec\ECFieldElement.cs - - - Security\Cryptography\BouncyCastle\math\ec\ECLookupTable.cs - - - Security\Cryptography\BouncyCastle\math\ec\ECPoint.cs - - - Security\Cryptography\BouncyCastle\math\ec\ECPointMap.cs - - - Security\Cryptography\BouncyCastle\math\ec\endo\ECEndomorphism.cs - - - Security\Cryptography\BouncyCastle\math\ec\endo\GlvEndomorphism.cs - - - Security\Cryptography\BouncyCastle\math\ec\LongArray.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\AbstractECMultiplier.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\ECMultiplier.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\FixedPointCombMultiplier.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\FixedPointPreCompInfo.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\FixedPointUtilities.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\GlvMultiplier.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\IPreCompCallback.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\PreCompInfo.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\ValidityPreCompInfo.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\WNafL2RMultiplier.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\WNafPreCompInfo.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\WNafUtilities.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\WTauNafMultiplier.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\WTauNafPreCompInfo.cs - - - Security\Cryptography\BouncyCastle\math\field\FiniteFields.cs - - - Security\Cryptography\BouncyCastle\math\field\GenericPolynomialExtensionField.cs - - - Security\Cryptography\BouncyCastle\math\field\GF2Polynomial.cs - - - Security\Cryptography\BouncyCastle\math\field\IExtensionField.cs - - - Security\Cryptography\BouncyCastle\math\field\IFiniteField.cs - - - Security\Cryptography\BouncyCastle\math\field\IPolynomial.cs - - - Security\Cryptography\BouncyCastle\math\field\IPolynomialExtensionField.cs - - - Security\Cryptography\BouncyCastle\math\field\PrimeField.cs - - - Security\Cryptography\BouncyCastle\math\raw\Mod.cs - - - Security\Cryptography\BouncyCastle\math\raw\Nat.cs - - - Security\Cryptography\BouncyCastle\security\DigestUtilities.cs - - - Security\Cryptography\BouncyCastle\security\SecureRandom.cs - - - Security\Cryptography\BouncyCastle\security\SecurityUtilityException.cs - - - Security\Cryptography\BouncyCastle\util\Arrays.cs - - - Security\Cryptography\BouncyCastle\util\BigIntegers.cs - - - Security\Cryptography\BouncyCastle\util\encoders\Hex.cs - - - Security\Cryptography\BouncyCastle\util\encoders\HexEncoder.cs - - - Security\Cryptography\BouncyCastle\util\IMemoable.cs - - - Security\Cryptography\BouncyCastle\util\Integers.cs - - - Security\Cryptography\BouncyCastle\util\MemoableResetException.cs - - - Security\Cryptography\BouncyCastle\util\Times.cs - - - Security\CertificateHostAlgorithm.cs - - - Security\Cryptography\Chaos.NaCl\CryptoBytes.cs - - - Security\Cryptography\Chaos.NaCl\Ed25519.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Array16.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Array8.cs - - - Security\Cryptography\Chaos.NaCl\Internal\ByteIntegerConverter.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\base.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\base2.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\d.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\d2.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_0.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_1.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_add.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_cmov.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_cswap.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_frombytes.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_invert.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_isnegative.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_isnonzero.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_mul.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_mul121666.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_neg.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_pow22523.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_sq.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_sq2.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_sub.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_tobytes.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\FieldElement.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_add.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_double_scalarmult.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_frombytes.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_madd.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_msub.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p1p1_to_p2.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p1p1_to_p3.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p2_0.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p2_dbl.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p3_0.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p3_dbl.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p3_tobytes.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p3_to_cached.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p3_to_p2.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_precomp_0.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_scalarmult_base.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_sub.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_tobytes.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\GroupElement.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\keypair.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\open.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\scalarmult.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\sc_clamp.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\sc_mul_add.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\sc_reduce.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\sign.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\sqrtm1.cs - - - Security\Cryptography\Chaos.NaCl\Internal\InternalAssert.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Poly1305Donna.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Salsa\Salsa20.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Salsa\SalsaCore.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Sha512Internal.cs - - - Security\Cryptography\Chaos.NaCl\MontgomeryCurve25519.cs - - - Security\Cryptography\Chaos.NaCl\Sha512.cs - - - Security\Cryptography\AsymmetricCipher.cs - - - Security\Cryptography\Bcrypt.cs - - - Security\Cryptography\BlockCipher.cs - - - Security\Cryptography\Cipher.cs - - - Security\Cryptography\CipherDigitalSignature.cs - - - Security\Cryptography\Ciphers\AesCipher.cs - - - Security\Cryptography\Ciphers\Arc4Cipher.cs - - - Security\Cryptography\Ciphers\BlowfishCipher.cs - - - Security\Cryptography\Ciphers\CastCipher.cs - - - Security\Cryptography\Ciphers\CipherMode.cs - - - Security\Cryptography\Ciphers\CipherPadding.cs - - - Security\Cryptography\Ciphers\DesCipher.cs - - - Security\Cryptography\Ciphers\Modes\CbcCipherMode.cs - - - Security\Cryptography\Ciphers\Modes\CfbCipherMode.cs - - - Security\Cryptography\Ciphers\Modes\CtrCipherMode.cs - - - Security\Cryptography\Ciphers\Modes\OfbCipherMode.cs - - - Security\Cryptography\Ciphers\Paddings\PKCS7Padding.cs - - - Security\Cryptography\Ciphers\Paddings\PKCS5Padding.cs - - - Security\Cryptography\Ciphers\RsaCipher.cs - - - Security\Cryptography\Ciphers\SerpentCipher.cs - - - Security\Cryptography\Ciphers\TripleDesCipher.cs - - - Security\Cryptography\Ciphers\TwofishCipher.cs - - - Security\Cryptography\DigitalSignature.cs - - - Security\Cryptography\DsaDigitalSignature.cs - - - Security\Cryptography\DsaKey.cs - - - Security\Cryptography\ED25519DigitalSignature.cs - - - Security\Cryptography\ED25519Key.cs - - - Security\Cryptography\HMACMD5.cs - - - Security\Cryptography\HMACSHA1.cs - - - Security\Cryptography\HMACSHA256.cs - - - Security\Cryptography\HMACSHA384.cs - - - Security\Cryptography\HMACSHA512.cs - - - Security\Cryptography\Key.cs - - - Security\Cryptography\RsaDigitalSignature.cs - - - Security\Cryptography\RsaKey.cs - - - Security\Cryptography\StreamCipher.cs - - - Security\Cryptography\SymmetricCipher.cs - - - Security\GroupExchangeHashData.cs - - - Security\HostAlgorithm.cs - - - Security\IKeyExchange.cs - - - Security\KeyExchange.cs - - - Security\KeyExchangeDiffieHellman.cs - - - Security\KeyExchangeDiffieHellmanGroup14Sha1.cs - - - Security\KeyExchangeDiffieHellmanGroup14Sha256.cs - - - Security\KeyExchangeDiffieHellmanGroup16Sha512.cs - - - Security\KeyExchangeDiffieHellmanGroup1Sha1.cs - - - Security\KeyExchangeDiffieHellmanGroupExchangeSha1.cs - - - Security\KeyExchangeDiffieHellmanGroupExchangeSha256.cs - - - Security\KeyExchangeDiffieHellmanGroupExchangeShaBase.cs - - - Security\KeyExchangeDiffieHellmanGroupSha1.cs - - - Security\KeyExchangeDiffieHellmanGroupSha256.cs - - - Security\KeyExchangeDiffieHellmanGroupSha512.cs - - - Security\KeyExchangeDiffieHellmanGroupShaBase.cs - - - Security\KeyExchangeEC.cs - - - Security\KeyExchangeECCurve25519.cs - - - Security\KeyExchangeECDH.cs - - - Security\KeyExchangeECDH256.cs - - - Security\KeyExchangeECDH384.cs - - - Security\KeyExchangeECDH521.cs - - - Security\KeyExchangeHash.cs - - - Security\KeyHostAlgorithm.cs - - - ServiceFactory.cs - - - Session.cs - - - SftpClient.cs - - - ISftpClient.cs - - - Sftp\Flags.cs - - - Sftp\ISftpFileReader.cs - - - Sftp\ISftpResponseFactory.cs - - - Sftp\ISftpSession.cs - - - Sftp\Requests\ExtendedRequests\FStatVfsRequest.cs - - - Sftp\Requests\ExtendedRequests\HardLinkRequest.cs - - - Sftp\Requests\ExtendedRequests\PosixRenameRequest.cs - - - Sftp\Requests\ExtendedRequests\StatVfsRequest.cs - - - Sftp\Requests\SftpBlockRequest.cs - - - Sftp\Requests\SftpCloseRequest.cs - - - Sftp\Requests\SftpExtendedRequest.cs - - - Sftp\Requests\SftpFSetStatRequest.cs - - - Sftp\Requests\SftpFStatRequest.cs - - - Sftp\Requests\SftpInitRequest.cs - - - Sftp\Requests\SftpLinkRequest.cs - - - Sftp\Requests\SftpLStatRequest.cs - - - Sftp\Requests\SftpMkDirRequest.cs - - - Sftp\Requests\SftpOpenDirRequest.cs - - - Sftp\Requests\SftpOpenRequest.cs - - - Sftp\Requests\SftpReadDirRequest.cs - - - Sftp\Requests\SftpReadLinkRequest.cs - - - Sftp\Requests\SftpReadRequest.cs - - - Sftp\Requests\SftpRealPathRequest.cs - - - Sftp\Requests\SftpRemoveRequest.cs - - - Sftp\Requests\SftpRenameRequest.cs - - - Sftp\Requests\SftpRequest.cs - - - Sftp\Requests\SftpRmDirRequest.cs - - - Sftp\Requests\SftpSetStatRequest.cs - - - Sftp\Requests\SftpStatRequest.cs - - - Sftp\Requests\SftpSymLinkRequest.cs - - - Sftp\Requests\SftpUnblockRequest.cs - - - Sftp\Requests\SftpWriteRequest.cs - - - Sftp\Responses\ExtendedReplies\ExtendedReplyInfo.cs - - - Sftp\Responses\ExtendedReplies\StatVfsReplyInfo.cs - - - Sftp\Responses\SftpAttrsResponse.cs - - - Sftp\Responses\SftpDataResponse.cs - - - Sftp\Responses\SftpExtendedReplyResponse.cs - - - Sftp\Responses\SftpHandleResponse.cs - - - Sftp\Responses\SftpNameResponse.cs - - - Sftp\Responses\SftpResponse.cs - - - Sftp\Responses\SftpStatusResponse.cs - - - Sftp\Responses\SftpVersionResponse.cs - - - Sftp\SftpCloseAsyncResult.cs - - - Sftp\SftpDownloadAsyncResult.cs - - - Sftp\SftpFile.cs - - - Sftp\SftpFileAttributes.cs - - - Sftp\SftpFileReader.cs - - - Sftp\SftpFileStream.cs - - - Sftp\SftpFileSystemInformation.cs - - - Sftp\SftpListDirectoryAsyncResult.cs - - - Sftp\SftpMessage.cs - - - Sftp\SftpMessageTypes.cs - - - Sftp\SftpOpenAsyncResult.cs - - - Sftp\SftpReadAsyncResult.cs - - - Sftp\SftpRealPathAsyncResult.cs - - - Sftp\SftpResponseFactory.cs - - - Sftp\SftpSession.cs - - - Sftp\SFtpStatAsyncResult.cs - - - Sftp\SftpSynchronizeDirectoriesAsyncResult.cs - - - Sftp\SftpUploadAsyncResult.cs - - - Sftp\StatusCodes.cs - - - Shell.cs - - - ShellStream.cs - - - SshClient.cs - - - SshCommand.cs - - - SshMessageFactory.cs - - - SubsystemSession.cs - - - - Properties\CommonAssemblyInfo.cs - - - - - Renci.SshNet.snk - - - Designer - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Renci.SshNet.Silverlight/packages.config b/src/Renci.SshNet.Silverlight/packages.config deleted file mode 100644 index c0653dc3..00000000 --- a/src/Renci.SshNet.Silverlight/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/src/Renci.SshNet.Silverlight5/Properties/AssemblyInfo.cs b/src/Renci.SshNet.Silverlight5/Properties/AssemblyInfo.cs deleted file mode 100644 index 7266e154..00000000 --- a/src/Renci.SshNet.Silverlight5/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,5 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -[assembly: AssemblyTitle("SSH.NET Silverlight 5")] -[assembly: Guid("2b3f6251-8079-48aa-a76b-df70e40092e2")] \ No newline at end of file diff --git a/src/Renci.SshNet.Silverlight5/Renci.SshNet.Silverlight5.csproj b/src/Renci.SshNet.Silverlight5/Renci.SshNet.Silverlight5.csproj deleted file mode 100644 index 93b950ed..00000000 --- a/src/Renci.SshNet.Silverlight5/Renci.SshNet.Silverlight5.csproj +++ /dev/null @@ -1,1460 +0,0 @@ - - - - Debug - AnyCPU - 8.0.50727 - 2.0 - {E367F791-C1EC-4181-912A-2943CAC6B3BC} - {A1591282-1198-4647-A2B1-27E5FF5F6F3B};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} - Library - Properties - Renci.SshNet - Renci.SshNet - Silverlight - v5.0 - $(TargetFrameworkVersion) - false - true - true - - - - v3.5 - - - true - full - false - Bin\Debug - TRACE;DEBUG;FEATURE_DIRECTORYINFO_ENUMERATEFILES;FEATURE_RNG_CSP;FEATURE_SOCKET_EAP;FEATURE_SOCKET_DISPOSE;FEATURE_STREAM_APM;FEATURE_THREAD_THREADPOOL;FEATURE_THREAD_SLEEP;FEATURE_WAITHANDLE_DISPOSE;FEATURE_MEMORYSTREAM_GETBUFFER;FEATURE_HASH_SHA1_MANAGED;FEATURE_HASH_SHA256_MANAGED;FEATURE_HMAC_SHA1;FEATURE_HMAC_SHA256 - true - true - prompt - 4 - false - Bin\Debug\Renci.SshNet.xml - true - - - none - true - Bin\Release - TRACE;FEATURE_DIRECTORYINFO_ENUMERATEFILES;FEATURE_RNG_CSP;FEATURE_SOCKET_EAP;FEATURE_SOCKET_DISPOSE;FEATURE_STREAM_APM;FEATURE_THREAD_THREADPOOL;FEATURE_THREAD_SLEEP;FEATURE_WAITHANDLE_DISPOSE;FEATURE_MEMORYSTREAM_GETBUFFER;FEATURE_HASH_SHA1_MANAGED;FEATURE_HASH_SHA256_MANAGED;FEATURE_HMAC_SHA1;FEATURE_HMAC_SHA256 - true - true - prompt - 4 - false - Bin\Release\Renci.SshNet.xml - - - true - - - true - - - ..\Renci.SshNet.snk - - - - - ..\..\packages\SshNet.Security.Cryptography.1.2.0\lib\sl5\SshNet.Security.Cryptography.dll - True - - - - - - - - - Abstractions\CryptoAbstraction.cs - - - Abstractions\DiagnosticAbstraction.cs - - - Abstractions\DnsAbstraction.cs - - - Abstractions\FileSystemAbstraction.cs - - - Abstractions\ReflectionAbstraction.cs - - - Abstractions\SocketAbstraction.cs - - - Abstractions\ThreadAbstraction.cs - - - AuthenticationMethod.cs - - - AuthenticationResult.cs - - - BaseClient.cs - - - Channels\Channel.cs - - - Channels\ChannelDirectTcpip.cs - - - Channels\ChannelForwardedTcpip.cs - - - Channels\ChannelSession.cs - - - Channels\ChannelTypes.cs - - - Channels\ClientChannel.cs - - - Channels\IChannel.cs - - - Channels\IChannelDirectTcpip.cs - - - Channels\IChannelForwardedTcpip.cs - - - Channels\IChannelSession.cs - - - Channels\ServerChannel.cs - - - CipherInfo.cs - - - ClientAuthentication.cs - - - CommandAsyncResult.cs - - - Common\Array.cs - - - Common\ASCIIEncoding.cs - - - Common\AsyncResult.cs - - - Common\AuthenticationBannerEventArgs.cs - - - Common\AuthenticationEventArgs.cs - - - Common\AuthenticationPasswordChangeEventArgs.cs - - - Common\AuthenticationPrompt.cs - - - Common\AuthenticationPromptEventArgs.cs - - - Common\BigInteger.cs - - - Common\ChannelDataEventArgs.cs - - - Common\ChannelEventArgs.cs - - - Common\ChannelExtendedDataEventArgs.cs - - - Common\ChannelOpenConfirmedEventArgs.cs - - - Common\ChannelOpenFailedEventArgs.cs - - - Common\ChannelRequestEventArgs.cs - - - Common\CountdownEvent.cs - - - Common\DerData.cs - - - Common\ExceptionEventArgs.cs - - - Common\Extensions.cs - - - Common\HostKeyEventArgs.cs - - - Common\ObjectIdentifier.cs - - - Common\Pack.cs - - - Common\PacketDump.cs - - - Common\PipeStream.cs - - - Common\PortForwardEventArgs.cs - - - Common\PosixPath.cs - - - Common\ProxyException.cs - - - Common\ScpDownloadEventArgs.cs - - - Common\ScpException.cs - - - Common\ScpUploadEventArgs.cs - - - Common\SemaphoreLight.cs - - - Common\SftpPathNotFoundException.cs - - - Common\SftpPermissionDeniedException.cs - - - Common\ShellDataEventArgs.cs - - - Common\SshAuthenticationException.cs - - - Common\SshConnectionException.cs - - - Common\SshData.cs - - - Common\SshDataStream.cs - - - Common\SshException.cs - - - Common\SshOperationTimeoutException.cs - - - Common\SshPassPhraseNullOrEmptyException.cs - - - Common\TerminalModes.cs - - - Compression\CompressionMode.cs - - - Compression\Compressor.cs - - - Compression\Zlib.cs - - - Compression\ZlibOpenSsh.cs - - - Compression\ZlibStream.cs - - - ConnectionInfo.cs - - - Connection\ConnectorBase.cs - - - Connection\DirectConnector.cs - - - Connection\HttpConnector.cs - - - Connection\IConnector.cs - - - Connection\IProtocolVersionExchange.cs - - - Connection\ISocketFactory.cs - - - Connection\ProtocolVersionExchange.cs - - - Connection\SocketFactory.cs - - - Connection\Socks4Connector.cs - - - Connection\Socks5Connector.cs - - - Connection\SshIdentification.cs - - - ExpectAction.cs - - - ExpectAsyncResult.cs - - - ForwardedPort.cs - - - ForwardedPortDynamic.cs - - - ForwardedPortLocal.cs - - - ForwardedPortRemote.cs - - - ForwardedPortStatus.cs - - - HashInfo.cs - - - IAuthenticationMethod.cs - - - IClientAuthentication.cs - - - IConnectionInfo.cs - - - IForwardedPort.cs - - - IRemotePathTransformation.cs - - - IServiceFactory.cs - - - ISession.cs - - - ISubsystemSession.cs - - - KeyboardInteractiveAuthenticationMethod.cs - - - KeyboardInteractiveConnectionInfo.cs - - - MessageEventArgs.cs - - - Messages\Authentication\BannerMessage.cs - - - Messages\Authentication\FailureMessage.cs - - - Messages\Authentication\InformationRequestMessage.cs - - - Messages\Authentication\InformationResponseMessage.cs - - - Messages\Authentication\PasswordChangeRequiredMessage.cs - - - Messages\Authentication\PublicKeyMessage.cs - - - Messages\Authentication\RequestMessage.cs - - - Messages\Authentication\RequestMessageHost.cs - - - Messages\Authentication\RequestMessageKeyboardInteractive.cs - - - Messages\Authentication\RequestMessageNone.cs - - - Messages\Authentication\RequestMessagePassword.cs - - - Messages\Authentication\RequestMessagePublicKey.cs - - - Messages\Authentication\SuccessMessage.cs - - - Messages\Connection\CancelTcpIpForwardGlobalRequestMessage.cs - - - Messages\Connection\ChannelCloseMessage.cs - - - Messages\Connection\ChannelDataMessage.cs - - - Messages\Connection\ChannelEofMessage.cs - - - Messages\Connection\ChannelExtendedDataMessage.cs - - - Messages\Connection\ChannelFailureMessage.cs - - - Messages\Connection\ChannelMessage.cs - - - Messages\Connection\ChannelOpenConfirmationMessage.cs - - - Messages\Connection\ChannelOpenFailureMessage.cs - - - Messages\Connection\ChannelOpenFailureReasons.cs - - - Messages\Connection\ChannelOpen\ChannelOpenInfo.cs - - - Messages\Connection\ChannelOpen\ChannelOpenMessage.cs - - - Messages\Connection\ChannelOpen\DirectTcpipChannelInfo.cs - - - Messages\Connection\ChannelOpen\ForwardedTcpipChannelInfo.cs - - - Messages\Connection\ChannelOpen\SessionChannelOpenInfo.cs - - - Messages\Connection\ChannelOpen\X11ChannelOpenInfo.cs - - - Messages\Connection\ChannelRequest\BreakRequestInfo.cs - - - Messages\Connection\ChannelRequest\ChannelRequestMessage.cs - - - Messages\Connection\ChannelRequest\EndOfWriteRequestInfo.cs - - - Messages\Connection\ChannelRequest\EnvironmentVariableRequestInfo.cs - - - Messages\Connection\ChannelRequest\ExecRequestInfo.cs - - - Messages\Connection\ChannelRequest\ExitSignalRequestInfo.cs - - - Messages\Connection\ChannelRequest\ExitStatusRequestInfo.cs - - - Messages\Connection\ChannelRequest\KeepAliveRequestInfo.cs - - - Messages\Connection\ChannelRequest\PseudoTerminalInfo.cs - - - Messages\Connection\ChannelRequest\RequestInfo.cs - - - Messages\Connection\ChannelRequest\ShellRequestInfo.cs - - - Messages\Connection\ChannelRequest\SignalRequestInfo.cs - - - Messages\Connection\ChannelRequest\SubsystemRequestInfo.cs - - - Messages\Connection\ChannelRequest\WindowChangeRequestInfo.cs - - - Messages\Connection\ChannelRequest\X11ForwardingRequestInfo.cs - - - Messages\Connection\ChannelRequest\XonXoffRequestInfo.cs - - - Messages\Connection\ChannelSuccessMessage.cs - - - Messages\Connection\ChannelWindowAdjustMessage.cs - - - Messages\Connection\GlobalRequestMessage.cs - - - Messages\Connection\GlobalRequestName.cs - - - Messages\Connection\RequestFailureMessage.cs - - - Messages\Connection\RequestSuccessMessage.cs - - - Messages\Connection\TcpIpForwardGlobalRequestMessage.cs - - - Messages\Message.cs - - - Messages\MessageAttribute.cs - - - Messages\ServiceName.cs - - - Messages\Transport\DebugMessage.cs - - - Messages\Transport\DisconnectMessage.cs - - - Messages\Transport\DisconnectReason.cs - - - Messages\Transport\IgnoreMessage.cs - - - Messages\Transport\IKeyExchangedAllowed.cs - - - Messages\Transport\KeyExchangeDhGroupExchangeGroup.cs - - - Messages\Transport\KeyExchangeDhGroupExchangeInit.cs - - - Messages\Transport\KeyExchangeDhGroupExchangeReply.cs - - - Messages\Transport\KeyExchangeDhGroupExchangeRequest.cs - - - Messages\Transport\KeyExchangeDhInitMessage.cs - - - Messages\Transport\KeyExchangeDhReplyMessage.cs - - - Messages\Transport\KeyExchangeEcdhInitMessage.cs - - - Messages\Transport\KeyExchangeEcdhReplyMessage.cs - - - Messages\Transport\KeyExchangeInitMessage.cs - - - Messages\Transport\NewKeysMessage.cs - - - Messages\Transport\ServiceAcceptMessage.cs - - - Messages\Transport\ServiceRequestMessage.cs - - - Messages\Transport\UnimplementedMessage.cs - - - NoneAuthenticationMethod.cs - - - PasswordAuthenticationMethod.cs - - - PasswordConnectionInfo.cs - - - PrivateKeyAuthenticationMethod.cs - - - PrivateKeyConnectionInfo.cs - - - PrivateKeyFile.cs - - - ProxyTypes.cs - - - RemotePathDoubleQuoteTransformation.cs - - - RemotePathNoneTransformation.cs - - - RemotePathShellQuoteTransformation.cs - - - RemotePathTransformation.cs - - - ScpClient.cs - - - Security\Algorithm.cs - - - Security\Cryptography\BouncyCastle\asn1\sec\SECNamedCurves.cs - - - Security\Cryptography\BouncyCastle\asn1\x9\X9Curve.cs - - - Security\Cryptography\BouncyCastle\asn1\x9\X9ECParameters.cs - - - Security\Cryptography\BouncyCastle\asn1\x9\X9ECParametersHolder.cs - - - Security\Cryptography\BouncyCastle\asn1\x9\X9ECPoint.cs - - - Security\Cryptography\BouncyCastle\crypto\agreement\ECDHCBasicAgreement.cs - - - Security\Cryptography\BouncyCastle\crypto\AsymmetricCipherKeyPair.cs - - - Security\Cryptography\BouncyCastle\crypto\AsymmetricKeyParameter.cs - - - Security\Cryptography\BouncyCastle\crypto\digests\GeneralDigest.cs - - - Security\Cryptography\BouncyCastle\crypto\digests\Sha256Digest.cs - - - Security\Cryptography\BouncyCastle\crypto\generators\ECKeyPairGenerator.cs - - - Security\Cryptography\BouncyCastle\crypto\IAsymmetricCipherKeyPairGenerator.cs - - - Security\Cryptography\BouncyCastle\crypto\IDigest.cs - - - Security\Cryptography\BouncyCastle\crypto\KeyGenerationParameters.cs - - - Security\Cryptography\BouncyCastle\crypto\parameters\ECDomainParameters.cs - - - Security\Cryptography\BouncyCastle\crypto\parameters\ECKeyGenerationParameters.cs - - - Security\Cryptography\BouncyCastle\crypto\parameters\ECKeyParameters.cs - - - Security\Cryptography\BouncyCastle\crypto\parameters\ECPrivateKeyParameters.cs - - - Security\Cryptography\BouncyCastle\crypto\parameters\ECPublicKeyParameters.cs - - - Security\Cryptography\BouncyCastle\crypto\prng\CryptoApiRandomGenerator.cs - - - Security\Cryptography\BouncyCastle\crypto\prng\DigestRandomGenerator.cs - - - Security\Cryptography\BouncyCastle\crypto\prng\IRandomGenerator.cs - - - Security\Cryptography\BouncyCastle\crypto\util\Pack.cs - - - Security\Cryptography\BouncyCastle\math\BigInteger.cs - - - Security\Cryptography\BouncyCastle\math\ec\abc\SimpleBigDecimal.cs - - - Security\Cryptography\BouncyCastle\math\ec\abc\Tnaf.cs - - - Security\Cryptography\BouncyCastle\math\ec\abc\ZTauElement.cs - - - Security\Cryptography\BouncyCastle\math\ec\ECAlgorithms.cs - - - Security\Cryptography\BouncyCastle\math\ec\ECCurve.cs - - - Security\Cryptography\BouncyCastle\math\ec\ECFieldElement.cs - - - Security\Cryptography\BouncyCastle\math\ec\ECLookupTable.cs - - - Security\Cryptography\BouncyCastle\math\ec\ECPoint.cs - - - Security\Cryptography\BouncyCastle\math\ec\ECPointMap.cs - - - Security\Cryptography\BouncyCastle\math\ec\endo\ECEndomorphism.cs - - - Security\Cryptography\BouncyCastle\math\ec\endo\GlvEndomorphism.cs - - - Security\Cryptography\BouncyCastle\math\ec\LongArray.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\AbstractECMultiplier.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\ECMultiplier.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\FixedPointCombMultiplier.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\FixedPointPreCompInfo.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\FixedPointUtilities.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\GlvMultiplier.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\IPreCompCallback.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\PreCompInfo.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\ValidityPreCompInfo.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\WNafL2RMultiplier.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\WNafPreCompInfo.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\WNafUtilities.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\WTauNafMultiplier.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\WTauNafPreCompInfo.cs - - - Security\Cryptography\BouncyCastle\math\field\FiniteFields.cs - - - Security\Cryptography\BouncyCastle\math\field\GenericPolynomialExtensionField.cs - - - Security\Cryptography\BouncyCastle\math\field\GF2Polynomial.cs - - - Security\Cryptography\BouncyCastle\math\field\IExtensionField.cs - - - Security\Cryptography\BouncyCastle\math\field\IFiniteField.cs - - - Security\Cryptography\BouncyCastle\math\field\IPolynomial.cs - - - Security\Cryptography\BouncyCastle\math\field\IPolynomialExtensionField.cs - - - Security\Cryptography\BouncyCastle\math\field\PrimeField.cs - - - Security\Cryptography\BouncyCastle\math\raw\Mod.cs - - - Security\Cryptography\BouncyCastle\math\raw\Nat.cs - - - Security\Cryptography\BouncyCastle\security\DigestUtilities.cs - - - Security\Cryptography\BouncyCastle\security\SecureRandom.cs - - - Security\Cryptography\BouncyCastle\security\SecurityUtilityException.cs - - - Security\Cryptography\BouncyCastle\util\Arrays.cs - - - Security\Cryptography\BouncyCastle\util\BigIntegers.cs - - - Security\Cryptography\BouncyCastle\util\encoders\Hex.cs - - - Security\Cryptography\BouncyCastle\util\encoders\HexEncoder.cs - - - Security\Cryptography\BouncyCastle\util\IMemoable.cs - - - Security\Cryptography\BouncyCastle\util\Integers.cs - - - Security\Cryptography\BouncyCastle\util\MemoableResetException.cs - - - Security\Cryptography\BouncyCastle\util\Times.cs - - - Security\CertificateHostAlgorithm.cs - - - Security\Cryptography\Chaos.NaCl\CryptoBytes.cs - - - Security\Cryptography\Chaos.NaCl\Ed25519.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Array16.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Array8.cs - - - Security\Cryptography\Chaos.NaCl\Internal\ByteIntegerConverter.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\base.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\base2.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\d.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\d2.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_0.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_1.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_add.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_cmov.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_cswap.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_frombytes.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_invert.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_isnegative.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_isnonzero.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_mul.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_mul121666.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_neg.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_pow22523.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_sq.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_sq2.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_sub.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_tobytes.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\FieldElement.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_add.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_double_scalarmult.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_frombytes.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_madd.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_msub.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p1p1_to_p2.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p1p1_to_p3.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p2_0.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p2_dbl.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p3_0.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p3_dbl.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p3_tobytes.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p3_to_cached.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p3_to_p2.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_precomp_0.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_scalarmult_base.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_sub.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_tobytes.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\GroupElement.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\keypair.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\open.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\scalarmult.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\sc_clamp.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\sc_mul_add.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\sc_reduce.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\sign.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\sqrtm1.cs - - - Security\Cryptography\Chaos.NaCl\Internal\InternalAssert.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Poly1305Donna.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Salsa\Salsa20.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Salsa\SalsaCore.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Sha512Internal.cs - - - Security\Cryptography\Chaos.NaCl\MontgomeryCurve25519.cs - - - Security\Cryptography\Chaos.NaCl\Sha512.cs - - - Security\Cryptography\AsymmetricCipher.cs - - - Security\Cryptography\Bcrypt.cs - - - Security\Cryptography\BlockCipher.cs - - - Security\Cryptography\Cipher.cs - - - Security\Cryptography\CipherDigitalSignature.cs - - - Security\Cryptography\Ciphers\AesCipher.cs - - - Security\Cryptography\Ciphers\Arc4Cipher.cs - - - Security\Cryptography\Ciphers\BlowfishCipher.cs - - - Security\Cryptography\Ciphers\CastCipher.cs - - - Security\Cryptography\Ciphers\CipherMode.cs - - - Security\Cryptography\Ciphers\CipherPadding.cs - - - Security\Cryptography\Ciphers\DesCipher.cs - - - Security\Cryptography\Ciphers\Modes\CbcCipherMode.cs - - - Security\Cryptography\Ciphers\Modes\CfbCipherMode.cs - - - Security\Cryptography\Ciphers\Modes\CtrCipherMode.cs - - - Security\Cryptography\Ciphers\Modes\OfbCipherMode.cs - - - Security\Cryptography\Ciphers\Paddings\PKCS7Padding.cs - - - Security\Cryptography\Ciphers\Paddings\PKCS5Padding.cs - - - Security\Cryptography\Ciphers\RsaCipher.cs - - - Security\Cryptography\Ciphers\SerpentCipher.cs - - - Security\Cryptography\Ciphers\TripleDesCipher.cs - - - Security\Cryptography\Ciphers\TwofishCipher.cs - - - Security\Cryptography\DigitalSignature.cs - - - Security\Cryptography\DsaDigitalSignature.cs - - - Security\Cryptography\DsaKey.cs - - - Security\Cryptography\ED25519DigitalSignature.cs - - - Security\Cryptography\ED25519Key.cs - - - Security\Cryptography\HMACMD5.cs - - - Security\Cryptography\HMACSHA1.cs - - - Security\Cryptography\HMACSHA256.cs - - - Security\Cryptography\HMACSHA384.cs - - - Security\Cryptography\HMACSHA512.cs - - - Security\Cryptography\Key.cs - - - Security\Cryptography\RsaDigitalSignature.cs - - - Security\Cryptography\RsaKey.cs - - - Security\Cryptography\StreamCipher.cs - - - Security\Cryptography\SymmetricCipher.cs - - - Security\GroupExchangeHashData.cs - - - Security\HostAlgorithm.cs - - - Security\IKeyExchange.cs - - - Security\KeyExchange.cs - - - Security\KeyExchangeDiffieHellman.cs - - - Security\KeyExchangeDiffieHellmanGroup14Sha1.cs - - - Security\KeyExchangeDiffieHellmanGroup14Sha256.cs - - - Security\KeyExchangeDiffieHellmanGroup16Sha512.cs - - - Security\KeyExchangeDiffieHellmanGroup1Sha1.cs - - - Security\KeyExchangeDiffieHellmanGroupExchangeSha1.cs - - - Security\KeyExchangeDiffieHellmanGroupExchangeSha256.cs - - - Security\KeyExchangeDiffieHellmanGroupExchangeShaBase.cs - - - Security\KeyExchangeDiffieHellmanGroupSha1.cs - - - Security\KeyExchangeDiffieHellmanGroupSha256.cs - - - Security\KeyExchangeDiffieHellmanGroupSha512.cs - - - Security\KeyExchangeDiffieHellmanGroupShaBase.cs - - - Security\KeyExchangeEC.cs - - - Security\KeyExchangeECCurve25519.cs - - - Security\KeyExchangeECDH.cs - - - Security\KeyExchangeECDH256.cs - - - Security\KeyExchangeECDH384.cs - - - Security\KeyExchangeECDH521.cs - - - Security\KeyExchangeHash.cs - - - Security\KeyHostAlgorithm.cs - - - ServiceFactory.cs - - - Session.cs - - - SftpClient.cs - - - ISftpClient.cs - - - Sftp\Flags.cs - - - Sftp\ISftpFileReader.cs - - - Sftp\ISftpResponseFactory.cs - - - Sftp\ISftpSession.cs - - - Sftp\Requests\ExtendedRequests\FStatVfsRequest.cs - - - Sftp\Requests\ExtendedRequests\HardLinkRequest.cs - - - Sftp\Requests\ExtendedRequests\PosixRenameRequest.cs - - - Sftp\Requests\ExtendedRequests\StatVfsRequest.cs - - - Sftp\Requests\SftpBlockRequest.cs - - - Sftp\Requests\SftpCloseRequest.cs - - - Sftp\Requests\SftpExtendedRequest.cs - - - Sftp\Requests\SftpFSetStatRequest.cs - - - Sftp\Requests\SftpFStatRequest.cs - - - Sftp\Requests\SftpInitRequest.cs - - - Sftp\Requests\SftpLinkRequest.cs - - - Sftp\Requests\SftpLStatRequest.cs - - - Sftp\Requests\SftpMkDirRequest.cs - - - Sftp\Requests\SftpOpenDirRequest.cs - - - Sftp\Requests\SftpOpenRequest.cs - - - Sftp\Requests\SftpReadDirRequest.cs - - - Sftp\Requests\SftpReadLinkRequest.cs - - - Sftp\Requests\SftpReadRequest.cs - - - Sftp\Requests\SftpRealPathRequest.cs - - - Sftp\Requests\SftpRemoveRequest.cs - - - Sftp\Requests\SftpRenameRequest.cs - - - Sftp\Requests\SftpRequest.cs - - - Sftp\Requests\SftpRmDirRequest.cs - - - Sftp\Requests\SftpSetStatRequest.cs - - - Sftp\Requests\SftpStatRequest.cs - - - Sftp\Requests\SftpSymLinkRequest.cs - - - Sftp\Requests\SftpUnblockRequest.cs - - - Sftp\Requests\SftpWriteRequest.cs - - - Sftp\Responses\ExtendedReplies\ExtendedReplyInfo.cs - - - Sftp\Responses\ExtendedReplies\StatVfsReplyInfo.cs - - - Sftp\Responses\SftpAttrsResponse.cs - - - Sftp\Responses\SftpDataResponse.cs - - - Sftp\Responses\SftpExtendedReplyResponse.cs - - - Sftp\Responses\SftpHandleResponse.cs - - - Sftp\Responses\SftpNameResponse.cs - - - Sftp\Responses\SftpResponse.cs - - - Sftp\Responses\SftpStatusResponse.cs - - - Sftp\Responses\SftpVersionResponse.cs - - - Sftp\SftpCloseAsyncResult.cs - - - Sftp\SftpDownloadAsyncResult.cs - - - Sftp\SftpFile.cs - - - Sftp\SftpFileAttributes.cs - - - Sftp\SftpFileReader.cs - - - Sftp\SftpFileStream.cs - - - Sftp\SftpFileSystemInformation.cs - - - Sftp\SftpListDirectoryAsyncResult.cs - - - Sftp\SftpMessage.cs - - - Sftp\SftpMessageTypes.cs - - - Sftp\SftpOpenAsyncResult.cs - - - Sftp\SftpReadAsyncResult.cs - - - Sftp\SftpRealPathAsyncResult.cs - - - Sftp\SftpResponseFactory.cs - - - Sftp\SftpSession.cs - - - Sftp\SFtpStatAsyncResult.cs - - - Sftp\SftpSynchronizeDirectoriesAsyncResult.cs - - - Sftp\SftpUploadAsyncResult.cs - - - Sftp\StatusCodes.cs - - - Shell.cs - - - ShellStream.cs - - - SshClient.cs - - - SshCommand.cs - - - SshMessageFactory.cs - - - SubsystemSession.cs - - - - Properties\CommonAssemblyInfo.cs - - - - - Renci.SshNet.snk - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Renci.SshNet.Silverlight5/packages.config b/src/Renci.SshNet.Silverlight5/packages.config deleted file mode 100644 index d4c6bef0..00000000 --- a/src/Renci.SshNet.Silverlight5/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/src/Renci.SshNet.TestTools.OpenSSH/Cipher.cs b/src/Renci.SshNet.TestTools.OpenSSH/Cipher.cs new file mode 100644 index 00000000..f7d8d884 --- /dev/null +++ b/src/Renci.SshNet.TestTools.OpenSSH/Cipher.cs @@ -0,0 +1,54 @@ +namespace Renci.SshNet.TestTools.OpenSSH +{ + public class Cipher + { + public static readonly Cipher TripledesCbc = new Cipher("3des-cbc"); + public static readonly Cipher Aes128Cbc = new Cipher("aes128-cbc"); + public static readonly Cipher Aes192Cbc = new Cipher("aes192-cbc"); + public static readonly Cipher Aes256Cbc = new Cipher("aes256-cbc"); + public static readonly Cipher RijndaelCbc = new Cipher("rijndael-cbc@lysator.liu.se"); + public static readonly Cipher Aes128Ctr = new Cipher("aes128-ctr"); + public static readonly Cipher Aes192Ctr = new Cipher("aes192-ctr"); + public static readonly Cipher Aes256Ctr = new Cipher("aes256-ctr"); + public static readonly Cipher Aes128Gcm = new Cipher("aes128-gcm@openssh.com"); + public static readonly Cipher Aes256Gcm = new Cipher("aes256-gcm@openssh.com"); + public static readonly Cipher Arcfour = new Cipher("arcfour"); + public static readonly Cipher Arcfour128 = new Cipher("arcfour128"); + public static readonly Cipher Arcfour256 = new Cipher("arcfour256"); + public static readonly Cipher BlowfishCbc = new Cipher("blowfish-cbc"); + public static readonly Cipher Cast128Cbc = new Cipher("cast128-cbc"); + public static readonly Cipher Chacha20Poly1305 = new Cipher("chacha20-poly1305@openssh.com"); + + public Cipher(string name) + { + Name = name; + } + + public string Name { get; } + + public override bool Equals(object? obj) + { + if (obj == null) + { + return false; + } + + if (obj is Cipher otherCipher) + { + return otherCipher.Name == Name; + } + + return false; + } + + public override int GetHashCode() + { + return Name.GetHashCode(); + } + + public override string ToString() + { + return Name; + } + } +} diff --git a/src/Renci.SshNet.TestTools.OpenSSH/Formatters/BooleanFormatter.cs b/src/Renci.SshNet.TestTools.OpenSSH/Formatters/BooleanFormatter.cs new file mode 100644 index 00000000..3eab2b19 --- /dev/null +++ b/src/Renci.SshNet.TestTools.OpenSSH/Formatters/BooleanFormatter.cs @@ -0,0 +1,20 @@ +namespace Renci.SshNet.TestTools.OpenSSH.Formatters +{ + internal class BooleanFormatter + { + public string Format(bool value) + { + return value ? "yes" : "no"; + } + + public string Format(bool? value, bool defaultValue) + { + if (value.HasValue) + { + return Format(value.Value); + } + + return Format(defaultValue); + } + } +} diff --git a/src/Renci.SshNet.TestTools.OpenSSH/Formatters/Int32Formatter.cs b/src/Renci.SshNet.TestTools.OpenSSH/Formatters/Int32Formatter.cs new file mode 100644 index 00000000..2b823111 --- /dev/null +++ b/src/Renci.SshNet.TestTools.OpenSSH/Formatters/Int32Formatter.cs @@ -0,0 +1,12 @@ +using System.Globalization; + +namespace Renci.SshNet.TestTools.OpenSSH.Formatters +{ + internal class Int32Formatter + { + public string Format(int value) + { + return value.ToString(NumberFormatInfo.InvariantInfo); + } + } +} diff --git a/src/Renci.SshNet.TestTools.OpenSSH/Formatters/LogLevelFormatter.cs b/src/Renci.SshNet.TestTools.OpenSSH/Formatters/LogLevelFormatter.cs new file mode 100644 index 00000000..f9f4bbf6 --- /dev/null +++ b/src/Renci.SshNet.TestTools.OpenSSH/Formatters/LogLevelFormatter.cs @@ -0,0 +1,10 @@ +namespace Renci.SshNet.TestTools.OpenSSH.Formatters +{ + internal class LogLevelFormatter + { + public string Format(LogLevel logLevel) + { + return logLevel.ToString("G").ToUpperInvariant(); + } + } +} diff --git a/src/Renci.SshNet.TestTools.OpenSSH/Formatters/MatchFormatter.cs b/src/Renci.SshNet.TestTools.OpenSSH/Formatters/MatchFormatter.cs new file mode 100644 index 00000000..df2968be --- /dev/null +++ b/src/Renci.SshNet.TestTools.OpenSSH/Formatters/MatchFormatter.cs @@ -0,0 +1,54 @@ +namespace Renci.SshNet.TestTools.OpenSSH.Formatters +{ + internal class MatchFormatter + { + public string Format(Match match) + { + using (var writer = new StringWriter()) + { + Format(match, writer); + return writer.ToString(); + } + } + + public void Format(Match match, TextWriter writer) + { + writer.Write("Match "); + + if (match.Users.Length > 0) + { + writer.Write("User "); + for (var i = 0; i < match.Users.Length; i++) + { + if (i > 0) + { + writer.Write(','); + } + + writer.Write(match.Users[i]); + } + } + + if (match.Addresses.Length > 0) + { + writer.Write("Address "); + for (var i = 0; i < match.Addresses.Length; i++) + { + if (i > 0) + { + writer.Write(','); + } + + writer.Write(match.Addresses[i]); + } + } + + writer.WriteLine(); + + if (match.AuthenticationMethods != null) + { + writer.WriteLine(" AuthenticationMethods " + match.AuthenticationMethods); + } + } + } +} diff --git a/src/Renci.SshNet.TestTools.OpenSSH/Formatters/SubsystemFormatter.cs b/src/Renci.SshNet.TestTools.OpenSSH/Formatters/SubsystemFormatter.cs new file mode 100644 index 00000000..fcb74e26 --- /dev/null +++ b/src/Renci.SshNet.TestTools.OpenSSH/Formatters/SubsystemFormatter.cs @@ -0,0 +1,10 @@ +namespace Renci.SshNet.TestTools.OpenSSH.Formatters +{ + internal class SubsystemFormatter + { + public string Format(Subsystem subsystem) + { + return subsystem.Name + " " + subsystem.Command; + } + } +} diff --git a/src/Renci.SshNet.TestTools.OpenSSH/HostKeyAlgorithm.cs b/src/Renci.SshNet.TestTools.OpenSSH/HostKeyAlgorithm.cs new file mode 100644 index 00000000..65807f46 --- /dev/null +++ b/src/Renci.SshNet.TestTools.OpenSSH/HostKeyAlgorithm.cs @@ -0,0 +1,53 @@ +namespace Renci.SshNet.TestTools.OpenSSH +{ + public class HostKeyAlgorithm + { + public static readonly HostKeyAlgorithm EcdsaSha2Nistp256CertV01OpenSSH = new HostKeyAlgorithm("ecdsa-sha2-nistp256-cert-v01@openssh.com"); + public static readonly HostKeyAlgorithm EcdsaSha2Nistp384CertV01OpenSSH = new HostKeyAlgorithm("ecdsa-sha2-nistp384-cert-v01@openssh.com"); + public static readonly HostKeyAlgorithm EcdsaSha2Nistp521CertV01OpenSSH = new HostKeyAlgorithm("ecdsa-sha2-nistp521-cert-v01@openssh.com"); + public static readonly HostKeyAlgorithm SshEd25519CertV01OpenSSH = new HostKeyAlgorithm("ssh-ed25519-cert-v01@openssh.com"); + public static readonly HostKeyAlgorithm RsaSha2256CertV01OpenSSH = new HostKeyAlgorithm("rsa-sha2-256-cert-v01@openssh.com"); + public static readonly HostKeyAlgorithm RsaSha2512CertV01OpenSSH = new HostKeyAlgorithm("rsa-sha2-512-cert-v01@openssh.com"); + public static readonly HostKeyAlgorithm SshRsaCertV01OpenSSH = new HostKeyAlgorithm("ssh-rsa-cert-v01@openssh.com"); + public static readonly HostKeyAlgorithm EcdsaSha2Nistp256 = new HostKeyAlgorithm("ecdsa-sha2-nistp256"); + public static readonly HostKeyAlgorithm EcdsaSha2Nistp384 = new HostKeyAlgorithm("ecdsa-sha2-nistp384"); + public static readonly HostKeyAlgorithm EcdsaSha2Nistp521 = new HostKeyAlgorithm("ecdsa-sha2-nistp521"); + public static readonly HostKeyAlgorithm SshEd25519 = new HostKeyAlgorithm("ssh-ed25519"); + public static readonly HostKeyAlgorithm RsaSha2512 = new HostKeyAlgorithm("rsa-sha2-512"); + public static readonly HostKeyAlgorithm RsaSha2256 = new HostKeyAlgorithm("rsa-sha2-256"); + public static readonly HostKeyAlgorithm SshRsa = new HostKeyAlgorithm("ssh-rsa"); + public static readonly HostKeyAlgorithm SshDss = new HostKeyAlgorithm("ssh-dss"); + + public HostKeyAlgorithm(string name) + { + Name = name; + } + + public string Name { get; } + + public override bool Equals(object? obj) + { + if (obj == null) + { + return false; + } + + if (obj is HostKeyAlgorithm otherHka) + { + return otherHka.Name == Name; + } + + return false; + } + + public override int GetHashCode() + { + return Name.GetHashCode(); + } + + public override string ToString() + { + return Name; + } + } +} diff --git a/src/Renci.SshNet.TestTools.OpenSSH/KeyExchangeAlgorithm.cs b/src/Renci.SshNet.TestTools.OpenSSH/KeyExchangeAlgorithm.cs new file mode 100644 index 00000000..4701103f --- /dev/null +++ b/src/Renci.SshNet.TestTools.OpenSSH/KeyExchangeAlgorithm.cs @@ -0,0 +1,52 @@ +namespace Renci.SshNet.TestTools.OpenSSH +{ + public class KeyExchangeAlgorithm + { + public static readonly KeyExchangeAlgorithm DiffieHellmanGroup1Sha1 = new KeyExchangeAlgorithm("diffie-hellman-group1-sha1"); + public static readonly KeyExchangeAlgorithm DiffieHellmanGroup14Sha1 = new KeyExchangeAlgorithm("diffie-hellman-group14-sha1"); + public static readonly KeyExchangeAlgorithm DiffieHellmanGroup14Sha256 = new KeyExchangeAlgorithm("diffie-hellman-group14-sha256"); + public static readonly KeyExchangeAlgorithm DiffieHellmanGroup16Sha512 = new KeyExchangeAlgorithm("diffie-hellman-group16-sha512"); + public static readonly KeyExchangeAlgorithm DiffieHellmanGroup18Sha512 = new KeyExchangeAlgorithm("diffie-hellman-group18-sha512"); + public static readonly KeyExchangeAlgorithm DiffieHellmanGroupExchangeSha1 = new KeyExchangeAlgorithm("diffie-hellman-group-exchange-sha1"); + public static readonly KeyExchangeAlgorithm DiffieHellmanGroupExchangeSha256 = new KeyExchangeAlgorithm("diffie-hellman-group-exchange-sha256"); + public static readonly KeyExchangeAlgorithm EcdhSha2Nistp256 = new KeyExchangeAlgorithm("ecdh-sha2-nistp256"); + public static readonly KeyExchangeAlgorithm EcdhSha2Nistp384 = new KeyExchangeAlgorithm("ecdh-sha2-nistp384"); + public static readonly KeyExchangeAlgorithm EcdhSha2Nistp521 = new KeyExchangeAlgorithm("ecdh-sha2-nistp521"); + public static readonly KeyExchangeAlgorithm Curve25519Sha256 = new KeyExchangeAlgorithm("curve25519-sha256"); + public static readonly KeyExchangeAlgorithm Curve25519Sha256Libssh = new KeyExchangeAlgorithm("curve25519-sha256@libssh.org"); + public static readonly KeyExchangeAlgorithm Sntrup4591761x25519Sha512 = new KeyExchangeAlgorithm("sntrup4591761x25519-sha512@tinyssh.org"); + + + public KeyExchangeAlgorithm(string name) + { + Name = name; + } + + public string Name { get; } + + public override bool Equals(object? obj) + { + if (obj == null) + { + return false; + } + + if (obj is KeyExchangeAlgorithm otherKex) + { + return otherKex.Name == Name; + } + + return false; + } + + public override int GetHashCode() + { + return Name.GetHashCode(); + } + + public override string ToString() + { + return Name; + } + } +} diff --git a/src/Renci.SshNet.TestTools.OpenSSH/LogLevel.cs b/src/Renci.SshNet.TestTools.OpenSSH/LogLevel.cs new file mode 100644 index 00000000..8724ae06 --- /dev/null +++ b/src/Renci.SshNet.TestTools.OpenSSH/LogLevel.cs @@ -0,0 +1,15 @@ +namespace Renci.SshNet.TestTools.OpenSSH +{ + public enum LogLevel + { + Quiet = 1, + Fatal = 2, + Error = 3, + Info = 4, + Verbose = 5, + Debug = 6, + Debug1 = 7, + Debug2 = 8, + Debug3 = 9 + } +} diff --git a/src/Renci.SshNet.TestTools.OpenSSH/Match.cs b/src/Renci.SshNet.TestTools.OpenSSH/Match.cs new file mode 100644 index 00000000..16cd5073 --- /dev/null +++ b/src/Renci.SshNet.TestTools.OpenSSH/Match.cs @@ -0,0 +1,57 @@ +namespace Renci.SshNet.TestTools.OpenSSH +{ + public class Match + { + public Match(string[] users, string[] addresses) + { + Users = users; + Addresses = addresses; + } + + public string[] Users { get; } + + public string[] Addresses { get; } + + public string? AuthenticationMethods { get; set; } + + public void WriteTo(TextWriter writer) + { + writer.Write("Match "); + + if (Users.Length > 0) + { + writer.Write("User "); + for (var i = 0; i < Users.Length; i++) + { + if (i > 0) + { + writer.Write(','); + } + + writer.Write(Users[i]); + } + } + + if (Addresses.Length > 0) + { + writer.Write("Address "); + for (var i = 0; i < Addresses.Length; i++) + { + if (i > 0) + { + writer.Write(','); + } + + writer.Write(Addresses[i]); + } + } + + writer.WriteLine(); + + if (AuthenticationMethods != null) + { + writer.WriteLine(" AuthenticationMethods " + AuthenticationMethods); + } + } + } +} diff --git a/src/Renci.SshNet.TestTools.OpenSSH/MessageAuthenticationCodeAlgorithm.cs b/src/Renci.SshNet.TestTools.OpenSSH/MessageAuthenticationCodeAlgorithm.cs new file mode 100644 index 00000000..17bf0cf9 --- /dev/null +++ b/src/Renci.SshNet.TestTools.OpenSSH/MessageAuthenticationCodeAlgorithm.cs @@ -0,0 +1,56 @@ +namespace Renci.SshNet.TestTools.OpenSSH +{ + public class MessageAuthenticationCodeAlgorithm + { + public static readonly MessageAuthenticationCodeAlgorithm HmacMd5 = new MessageAuthenticationCodeAlgorithm("hmac-md5"); + public static readonly MessageAuthenticationCodeAlgorithm HmacMd5_96 = new MessageAuthenticationCodeAlgorithm("hmac-md5-96"); + public static readonly MessageAuthenticationCodeAlgorithm HmacRipemd160 = new MessageAuthenticationCodeAlgorithm("hmac-ripemd160"); + public static readonly MessageAuthenticationCodeAlgorithm HmacSha1 = new MessageAuthenticationCodeAlgorithm("hmac-sha1"); + public static readonly MessageAuthenticationCodeAlgorithm HmacSha1_96 = new MessageAuthenticationCodeAlgorithm("hmac-sha1-96"); + public static readonly MessageAuthenticationCodeAlgorithm HmacSha2_256 = new MessageAuthenticationCodeAlgorithm("hmac-sha2-256"); + public static readonly MessageAuthenticationCodeAlgorithm HmacSha2_512 = new MessageAuthenticationCodeAlgorithm("hmac-sha2-512"); + public static readonly MessageAuthenticationCodeAlgorithm Umac64 = new MessageAuthenticationCodeAlgorithm("umac-64@openssh.com"); + public static readonly MessageAuthenticationCodeAlgorithm Umac128 = new MessageAuthenticationCodeAlgorithm("umac-128@openssh.com"); + public static readonly MessageAuthenticationCodeAlgorithm HmacMd5Etm = new MessageAuthenticationCodeAlgorithm("hmac-md5-etm@openssh.com"); + public static readonly MessageAuthenticationCodeAlgorithm HmacMd5_96_Etm = new MessageAuthenticationCodeAlgorithm("hmac-md5-96-etm@openssh.com"); + public static readonly MessageAuthenticationCodeAlgorithm HmacRipemd160Etm = new MessageAuthenticationCodeAlgorithm("hmac-ripemd160-etm@openssh.com"); + public static readonly MessageAuthenticationCodeAlgorithm HmacSha1Etm = new MessageAuthenticationCodeAlgorithm("hmac-sha1-etm@openssh.com"); + public static readonly MessageAuthenticationCodeAlgorithm HmacSha1_96_Etm = new MessageAuthenticationCodeAlgorithm("hmac-sha1-96-etm@openssh.com"); + public static readonly MessageAuthenticationCodeAlgorithm HmacSha2_256_Etm = new MessageAuthenticationCodeAlgorithm("hmac-sha2-256-etm@openssh.com"); + public static readonly MessageAuthenticationCodeAlgorithm HmacSha2_512_Etm = new MessageAuthenticationCodeAlgorithm("hmac-sha2-512-etm@openssh.com"); + public static readonly MessageAuthenticationCodeAlgorithm Umac64_Etm = new MessageAuthenticationCodeAlgorithm("umac-64-etm@openssh.com"); + public static readonly MessageAuthenticationCodeAlgorithm Umac128_Etm = new MessageAuthenticationCodeAlgorithm("umac-128-etm@openssh.com"); + + public MessageAuthenticationCodeAlgorithm(string name) + { + Name = name; + } + + public string Name { get; } + + public override bool Equals(object? obj) + { + if (obj == null) + { + return false; + } + + if (obj is MessageAuthenticationCodeAlgorithm otherMac) + { + return otherMac.Name == Name; + } + + return false; + } + + public override int GetHashCode() + { + return Name.GetHashCode(); + } + + public override string ToString() + { + return Name; + } + } +} diff --git a/src/Renci.SshNet.TestTools.OpenSSH/PublicKeyAlgorithm.cs b/src/Renci.SshNet.TestTools.OpenSSH/PublicKeyAlgorithm.cs new file mode 100644 index 00000000..292ace7f --- /dev/null +++ b/src/Renci.SshNet.TestTools.OpenSSH/PublicKeyAlgorithm.cs @@ -0,0 +1,59 @@ +namespace Renci.SshNet.TestTools.OpenSSH +{ + public class PublicKeyAlgorithm + { + public static readonly PublicKeyAlgorithm SshEd25519 = new PublicKeyAlgorithm("ssh-ed25519"); + public static readonly PublicKeyAlgorithm SshEd25519CertV01OpenSSH = new PublicKeyAlgorithm("ssh-ed25519-cert-v01@openssh.com"); + public static readonly PublicKeyAlgorithm SkSshEd25519OpenSSH = new PublicKeyAlgorithm("sk-ssh-ed25519@openssh.com"); + public static readonly PublicKeyAlgorithm SkSshEd25519CertV01OpenSSH = new PublicKeyAlgorithm("sk-ssh-ed25519-cert-v01@openssh.com"); + public static readonly PublicKeyAlgorithm SshRsa = new PublicKeyAlgorithm("ssh-rsa"); + public static readonly PublicKeyAlgorithm RsaSha2256 = new PublicKeyAlgorithm("rsa-sha2-256"); + public static readonly PublicKeyAlgorithm RsaSha2512 = new PublicKeyAlgorithm("rsa-sha2-512"); + public static readonly PublicKeyAlgorithm SshDss = new PublicKeyAlgorithm("ssh-dss"); + public static readonly PublicKeyAlgorithm EcdsaSha2Nistp256 = new PublicKeyAlgorithm("ecdsa-sha2-nistp256"); + public static readonly PublicKeyAlgorithm EcdsaSha2Nistp384 = new PublicKeyAlgorithm("ecdsa-sha2-nistp384"); + public static readonly PublicKeyAlgorithm EcdsaSha2Nistp521 = new PublicKeyAlgorithm("ecdsa-sha2-nistp521"); + public static readonly PublicKeyAlgorithm SkEcdsaSha2Nistp256OpenSSH = new PublicKeyAlgorithm("sk-ecdsa-sha2-nistp256@openssh.com"); + public static readonly PublicKeyAlgorithm WebAuthnSkEcdsaSha2Nistp256OpenSSH = new PublicKeyAlgorithm("webauthn-sk-ecdsa-sha2-nistp256@openssh.com"); + public static readonly PublicKeyAlgorithm SshRsaCertV01OpenSSH = new PublicKeyAlgorithm("ssh-rsa-cert-v01@openssh.com"); + public static readonly PublicKeyAlgorithm RsaSha2256CertV01OpenSSH = new PublicKeyAlgorithm("rsa-sha2-256-cert-v01@openssh.com"); + public static readonly PublicKeyAlgorithm RsaSha2512CertV01OpenSSH = new PublicKeyAlgorithm("rsa-sha2-512-cert-v01@openssh.com"); + public static readonly PublicKeyAlgorithm SshDssCertV01OpenSSH = new PublicKeyAlgorithm("ssh-dss-cert-v01@openssh.com"); + public static readonly PublicKeyAlgorithm EcdsaSha2Nistp256CertV01OpenSSH = new PublicKeyAlgorithm("ecdsa-sha2-nistp256-cert-v01@openssh.com"); + public static readonly PublicKeyAlgorithm EcdsaSha2Nistp384CertV01OpenSSH = new PublicKeyAlgorithm("ecdsa-sha2-nistp384-cert-v01@openssh.com"); + public static readonly PublicKeyAlgorithm EcdsaSha2Nistp521CertV01OpenSSH = new PublicKeyAlgorithm("ecdsa-sha2-nistp521-cert-v01@openssh.com"); + public static readonly PublicKeyAlgorithm SkEcdsaSha2Nistp256CertV01OpenSSH = new PublicKeyAlgorithm("sk-ecdsa-sha2-nistp256-cert-v01@openssh.com"); + + public PublicKeyAlgorithm(string name) + { + Name = name; + } + + public string Name { get; } + + public override bool Equals(object? obj) + { + if (obj == null) + { + return false; + } + + if (obj is HostKeyAlgorithm otherHka) + { + return otherHka.Name == Name; + } + + return false; + } + + public override int GetHashCode() + { + return Name.GetHashCode(); + } + + public override string ToString() + { + return Name; + } + } +} diff --git a/src/Renci.SshNet.TestTools.OpenSSH/Renci.SshNet.TestTools.OpenSSH.csproj b/src/Renci.SshNet.TestTools.OpenSSH/Renci.SshNet.TestTools.OpenSSH.csproj new file mode 100644 index 00000000..bd59aa4e --- /dev/null +++ b/src/Renci.SshNet.TestTools.OpenSSH/Renci.SshNet.TestTools.OpenSSH.csproj @@ -0,0 +1,21 @@ + + + net7.0 + enable + enable + + + $(NoWarn);CS1591;SYSLIB0021;SYSLIB1045 + + + + + \ No newline at end of file diff --git a/src/Renci.SshNet.TestTools.OpenSSH/SshdConfig.cs b/src/Renci.SshNet.TestTools.OpenSSH/SshdConfig.cs new file mode 100644 index 00000000..7dc9f309 --- /dev/null +++ b/src/Renci.SshNet.TestTools.OpenSSH/SshdConfig.cs @@ -0,0 +1,504 @@ +using System.Globalization; +using System.Text; +using System.Text.RegularExpressions; + +using Renci.SshNet.TestTools.OpenSSH.Formatters; + +namespace Renci.SshNet.TestTools.OpenSSH +{ + public class SshdConfig + { + private static readonly Regex MatchRegex = new Regex($@"\s*Match\s+(User\s+(?[\S]+))?\s*(Address\s+(?[\S]+))?\s*", RegexOptions.Compiled); + + private readonly SubsystemFormatter _subsystemFormatter; + private readonly Int32Formatter _int32Formatter; + private readonly BooleanFormatter _booleanFormatter; + private readonly MatchFormatter _matchFormatter; + + private SshdConfig() + { + AcceptedEnvironmentVariables = new List(); + Ciphers = new List(); + HostKeyFiles = new List(); + HostKeyAlgorithms = new List(); + KeyExchangeAlgorithms = new List(); + PublicKeyAcceptedAlgorithms = new List(); + MessageAuthenticationCodeAlgorithms = new List(); + Subsystems = new List(); + Matches = new List(); + LogLevel = LogLevel.Info; + Port = 22; + Protocol = "2,1"; + + _booleanFormatter = new BooleanFormatter(); + _int32Formatter = new Int32Formatter(); + _matchFormatter = new MatchFormatter(); + _subsystemFormatter = new SubsystemFormatter(); + } + + /// + /// Gets or sets the port number that sshd listens on. + /// + /// + /// The port number that sshd listens on. The default is 22. + /// + public int Port { get; set; } + + /// + /// Gets or sets the list of private host key files used by sshd. + /// + /// + /// A list of private host key files used by sshd. + /// + public List HostKeyFiles { get; set; } + + /// + /// Gets or sets a value specifying whether challenge-response authentication is allowed. + /// + /// + /// A value specifying whether challenge-response authentication is allowed, or + /// if this option is not configured. + /// + public bool? ChallengeResponseAuthentication { get; set; } + + /// + /// Gets or sets a value indicating whether to allow keyboard-interactive authentication. + /// + /// + /// to allow and to disallow keyboard-interactive + /// authentication, or if this option is not configured. + /// + public bool? KeyboardInteractiveAuthentication { get; set; } + + /// + /// Gets or sets the verbosity when logging messages from sshd. + /// + /// + /// The verbosity when logging messages from sshd. The default is . + /// + public LogLevel LogLevel { get; set; } + + /// + /// Gets a sets a value indicating whether the Pluggable Authentication Module interface is enabled. + /// + /// + /// A value indicating whether the Pluggable Authentication Module interface is enabled. + /// + public bool? UsePAM { get; set; } + + public List Subsystems { get; } + + /// + /// Gets a list of conditional blocks. + /// + public List Matches { get; } + + public bool X11Forwarding { get; private set; } + public List AcceptedEnvironmentVariables { get; private set; } + public List Ciphers { get; private set; } + + /// + /// Gets the host key signature algorithms that the server offers. + /// + public List HostKeyAlgorithms { get; private set; } + + /// + /// Gets the available KEX (Key Exchange) algorithms. + /// + public List KeyExchangeAlgorithms { get; private set; } + + /// + /// Gets the signature algorithms that will be accepted for public key authentication. + /// + public List PublicKeyAcceptedAlgorithms { get; private set; } + + /// + /// Gets the available MAC (message authentication code) algorithms. + /// + public List MessageAuthenticationCodeAlgorithms { get; private set; } + + /// + /// Gets a value indicating whether sshd should print /etc/motd when a user logs in interactively. + /// + /// + /// if sshd should print /etc/motd when a user logs in interactively + /// and if it should not; if this option is not configured. + /// + public bool? PrintMotd { get; set; } + + /// + /// Gets or sets the protocol versions sshd supported. + /// + /// + /// The protocol versions sshd supported. The default is 2,1. + /// + public string Protocol { get; set; } + + /// + /// Gets or sets a value indicating whether TCP forwarding is allowed. + /// + /// + /// to allow and to disallow TCP forwarding, + /// or if this option is not configured. + /// + public bool? AllowTcpForwarding { get; set; } + + public void SaveTo(TextWriter writer) + { + writer.WriteLine("Protocol " + Protocol); + writer.WriteLine("Port " + _int32Formatter.Format(Port)); + if (HostKeyFiles.Count > 0) + { + writer.WriteLine("HostKey " + string.Join(",", HostKeyFiles.ToArray())); + } + + if (ChallengeResponseAuthentication is not null) + { + writer.WriteLine("ChallengeResponseAuthentication " + _booleanFormatter.Format(ChallengeResponseAuthentication.Value)); + } + + if (KeyboardInteractiveAuthentication is not null) + { + writer.WriteLine("KbdInteractiveAuthentication " + _booleanFormatter.Format(KeyboardInteractiveAuthentication.Value)); + } + + if (AllowTcpForwarding is not null) + { + writer.WriteLine("AllowTcpForwarding " + _booleanFormatter.Format(AllowTcpForwarding.Value)); + } + + if (PrintMotd is not null) + { + writer.WriteLine("PrintMotd " + _booleanFormatter.Format(PrintMotd.Value)); + } + + writer.WriteLine("LogLevel " + new LogLevelFormatter().Format(LogLevel)); + + foreach (var subsystem in Subsystems) + { + writer.WriteLine("Subsystem " + _subsystemFormatter.Format(subsystem)); + } + + if (UsePAM is not null) + { + writer.WriteLine("UsePAM " + _booleanFormatter.Format(UsePAM.Value)); + } + + writer.WriteLine("X11Forwarding " + _booleanFormatter.Format(X11Forwarding)); + + foreach (var acceptedEnvVar in AcceptedEnvironmentVariables) + { + writer.WriteLine("AcceptEnv " + acceptedEnvVar); + } + + if (Ciphers.Count > 0) + { + writer.WriteLine("Ciphers " + string.Join(",", Ciphers.Select(c => c.Name).ToArray())); + } + + if (HostKeyAlgorithms.Count > 0) + { + writer.WriteLine("HostKeyAlgorithms " + string.Join(",", HostKeyAlgorithms.Select(c => c.Name).ToArray())); + } + + if (KeyExchangeAlgorithms.Count > 0) + { + writer.WriteLine("KexAlgorithms " + string.Join(",", KeyExchangeAlgorithms.Select(c => c.Name).ToArray())); + } + + if (MessageAuthenticationCodeAlgorithms.Count > 0) + { + writer.WriteLine("MACs " + string.Join(",", MessageAuthenticationCodeAlgorithms.Select(c => c.Name).ToArray())); + } + + if (PublicKeyAcceptedAlgorithms.Count > 0) + { + writer.WriteLine("PubkeyAcceptedAlgorithms " + string.Join(",", PublicKeyAcceptedAlgorithms.Select(c => c.Name).ToArray())); + } + + foreach (var match in Matches) + { + _matchFormatter.Format(match, writer); + } + } + + public static SshdConfig LoadFrom(Stream stream, Encoding encoding) + { + using (var sr = new StreamReader(stream, encoding)) + { + var sshdConfig = new SshdConfig(); + + Match? currentMatchConfiguration = null; + + string? line; + while ((line = sr.ReadLine()) != null) + { + // Skip empty lines + if (line.Length == 0) + { + continue; + } + + // Skip comments + if (line[0] == '#') + { + continue; + } + + var match = MatchRegex.Match(line); + if (match.Success) + { + var usersGroup = match.Groups["users"]; + var addressesGroup = match.Groups["addresses"]; + var users = usersGroup.Success ? usersGroup.Value.Split(',') : Array.Empty(); + var addresses = addressesGroup.Success ? addressesGroup.Value.Split(',') : Array.Empty(); + + currentMatchConfiguration = new Match(users, addresses); + sshdConfig.Matches.Add(currentMatchConfiguration); + continue; + } + + if (currentMatchConfiguration != null) + { + ProcessMatchOption(currentMatchConfiguration, line); + } + else + { + ProcessGlobalOption(sshdConfig, line); + } + } + + if (sshdConfig.Ciphers == null) + { + // Obtain supported ciphers using ssh -Q cipher + } + + if (sshdConfig.KeyExchangeAlgorithms == null) + { + // Obtain supports key exchange algorithms using ssh -Q kex + } + + if (sshdConfig.HostKeyAlgorithms == null) + { + // Obtain supports host key algorithms using ssh -Q key + } + + if (sshdConfig.MessageAuthenticationCodeAlgorithms == null) + { + // Obtain supported MACs using ssh -Q mac + } + + + return sshdConfig; + } + } + + private static void ProcessGlobalOption(SshdConfig sshdConfig, string line) + { + var matchOptionRegex = new Regex(@"^\s*(?[\S]+)\s+(?.+?){1}\s*$"); + + var optionsMatch = matchOptionRegex.Match(line); + if (!optionsMatch.Success) + { + return; + } + + var nameGroup = optionsMatch.Groups["name"]; + var valueGroup = optionsMatch.Groups["value"]; + + var name = nameGroup.Value; + var value = valueGroup.Value; + + switch (name) + { + case "Port": + sshdConfig.Port = ToInt(value); + break; + case "HostKey": + sshdConfig.HostKeyFiles = ParseCommaSeparatedValue(value); + break; + case "ChallengeResponseAuthentication": + sshdConfig.ChallengeResponseAuthentication = ToBool(value); + break; + case "KbdInteractiveAuthentication": + sshdConfig.KeyboardInteractiveAuthentication = ToBool(value); + break; + case "LogLevel": + sshdConfig.LogLevel = (LogLevel) Enum.Parse(typeof(LogLevel), value, true); + break; + case "Subsystem": + sshdConfig.Subsystems.Add(Subsystem.FromConfig(value)); + break; + case "UsePAM": + sshdConfig.UsePAM = ToBool(value); + break; + case "X11Forwarding": + sshdConfig.X11Forwarding = ToBool(value); + break; + case "Ciphers": + sshdConfig.Ciphers = ParseCiphers(value); + break; + case "KexAlgorithms": + sshdConfig.KeyExchangeAlgorithms = ParseKeyExchangeAlgorithms(value); + break; + case "PubkeyAcceptedAlgorithms": + sshdConfig.PublicKeyAcceptedAlgorithms = ParsePublicKeyAcceptedAlgorithms(value); + break; + case "HostKeyAlgorithms": + sshdConfig.HostKeyAlgorithms = ParseHostKeyAlgorithms(value); + break; + case "MACs": + sshdConfig.MessageAuthenticationCodeAlgorithms = ParseMacs(value); + break; + case "PrintMotd": + sshdConfig.PrintMotd = ToBool(value); + break; + case "AcceptEnv": + ParseAcceptedEnvironmentVariable(sshdConfig, value); + break; + case "Protocol": + sshdConfig.Protocol = value; + break; + case "AllowTcpForwarding": + sshdConfig.AllowTcpForwarding = ToBool(value); + break; + case "KeyRegenerationInterval": + case "HostbasedAuthentication": + case "ServerKeyBits": + case "SyslogFacility": + case "LoginGraceTime": + case "PermitRootLogin": + case "StrictModes": + case "RSAAuthentication": + case "PubkeyAuthentication": + case "IgnoreRhosts": + case "RhostsRSAAuthentication": + case "PermitEmptyPasswords": + case "X11DisplayOffset": + case "PrintLastLog": + case "TCPKeepAlive": + case "AuthorizedKeysFile": + case "PasswordAuthentication": + case "GatewayPorts": + break; + default: + throw new Exception($"Global option '{name}' is not implemented."); + } + } + + private static void ParseAcceptedEnvironmentVariable(SshdConfig sshdConfig, string value) + { + var acceptedEnvironmentVariables = value.Split(' '); + foreach (var acceptedEnvironmentVariable in acceptedEnvironmentVariables) + { + sshdConfig.AcceptedEnvironmentVariables.Add(acceptedEnvironmentVariable); + } + } + + private static List ParseCiphers(string value) + { + var cipherNames = value.Split(','); + var ciphers = new List(cipherNames.Length); + foreach (var cipherName in cipherNames) + { + ciphers.Add(new Cipher(cipherName.Trim())); + } + return ciphers; + } + + private static List ParseKeyExchangeAlgorithms(string value) + { + var kexNames = value.Split(','); + var keyExchangeAlgorithms = new List(kexNames.Length); + foreach (var kexName in kexNames) + { + keyExchangeAlgorithms.Add(new KeyExchangeAlgorithm(kexName.Trim())); + } + return keyExchangeAlgorithms; + } + + public static List ParsePublicKeyAcceptedAlgorithms(string value) + { + var publicKeyAlgorithmNames = value.Split(','); + var publicKeyAlgorithms = new List(publicKeyAlgorithmNames.Length); + foreach (var publicKeyAlgorithmName in publicKeyAlgorithmNames) + { + publicKeyAlgorithms.Add(new PublicKeyAlgorithm(publicKeyAlgorithmName.Trim())); + } + return publicKeyAlgorithms; + } + + private static List ParseHostKeyAlgorithms(string value) + { + var algorithmNames = value.Split(','); + var hostKeyAlgorithms = new List(algorithmNames.Length); + foreach (var algorithmName in algorithmNames) + { + hostKeyAlgorithms.Add(new HostKeyAlgorithm(algorithmName.Trim())); + } + return hostKeyAlgorithms; + } + + private static List ParseMacs(string value) + { + var macNames = value.Split(','); + var macAlgorithms = new List(macNames.Length); + foreach (var algorithmName in macNames) + { + macAlgorithms.Add(new MessageAuthenticationCodeAlgorithm(algorithmName.Trim())); + } + return macAlgorithms; + } + + private static void ProcessMatchOption(Match matchConfiguration, string line) + { + var matchOptionRegex = new Regex(@"^\s+(?[\S]+)\s+(?.+?){1}\s*$"); + + var optionsMatch = matchOptionRegex.Match(line); + if (!optionsMatch.Success) + { + return; + } + + var nameGroup = optionsMatch.Groups["name"]; + var valueGroup = optionsMatch.Groups["value"]; + + var name = nameGroup.Value; + var value = valueGroup.Value; + + switch (name) + { + case "AuthenticationMethods": + matchConfiguration.AuthenticationMethods = value; + break; + default: + throw new Exception($"Match option '{name}' is not implemented."); + } + } + + + private static List ParseCommaSeparatedValue(string value) + { + var values = value.Split(','); + return new List(values); + } + + private static bool ToBool(string value) + { + switch (value) + { + case "yes": + return true; + case "no": + return false; + default: + throw new Exception($"Value '{value}' cannot be mapped to a boolean."); + } + } + + private static int ToInt(string value) + { + return int.Parse(value, NumberFormatInfo.InvariantInfo); + } + } +} diff --git a/src/Renci.SshNet.TestTools.OpenSSH/Subsystem.cs b/src/Renci.SshNet.TestTools.OpenSSH/Subsystem.cs new file mode 100644 index 00000000..4d115510 --- /dev/null +++ b/src/Renci.SshNet.TestTools.OpenSSH/Subsystem.cs @@ -0,0 +1,41 @@ +using System.Text.RegularExpressions; + +namespace Renci.SshNet.TestTools.OpenSSH +{ + public class Subsystem + { + public Subsystem(string name, string command) + { + Name = name; + Command = command; + } + + public string Name { get; } + + public string Command { get; set; } + + public void WriteTo(TextWriter writer) + { + writer.WriteLine(Name + "=" + Command); + } + + public static Subsystem FromConfig(string value) + { + var subSystemValueRegex = new Regex(@"^\s*(?[\S]+)\s+(?.+?){1}\s*$"); + + var match = subSystemValueRegex.Match(value); + if (match.Success) + { + var nameGroup = match.Groups["name"]; + var commandGroup = match.Groups["command"]; + + var name = nameGroup.Value; + var command = commandGroup.Value; + + return new Subsystem(name, command); + } + + throw new Exception($"'{value}' not recognized as value for Subsystem."); + } + } +} diff --git a/src/Renci.SshNet.Tests/.editorconfig b/src/Renci.SshNet.Tests/.editorconfig new file mode 100644 index 00000000..b94e2911 --- /dev/null +++ b/src/Renci.SshNet.Tests/.editorconfig @@ -0,0 +1,32 @@ +[*.cs] + +#### SYSLIB diagnostics #### + +# SYSLIB1045: Use 'GeneratedRegexAttribute' to generate the regular expression implementation at compile-time +# +# TODO: Remove this when https://github.com/sshnet/SSH.NET/issues/1131 is implemented. +dotnet_diagnostic.SYSLIB1045.severity = none + +### StyleCop Analyzers rules ### + +#### .NET Compiler Platform analysers rules #### + +# IDE0007: Use var instead of explicit type +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0007 +dotnet_diagnostic.IDE0007.severity = suggestion + +# IDE0028: Use collection initializers +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0028 +dotnet_diagnostic.IDE0028.severity = suggestion + +# IDE0058: Remove unnecessary expression value +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0058 +dotnet_diagnostic.IDE0058.severity = suggestion + +# IDE0059: Remove unnecessary value assignment +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0059 +dotnet_diagnostic.IDE0059.severity = suggestion + +# IDE0230: Use UTF-8 string literal +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0230 +dotnet_diagnostic.IDE0230.severity = suggestion diff --git a/src/Renci.SshNet.Tests/Classes/BaseClientTestBase.cs b/src/Renci.SshNet.Tests/Classes/BaseClientTestBase.cs index 2cce2e91..7c6c5838 100644 --- a/src/Renci.SshNet.Tests/Classes/BaseClientTestBase.cs +++ b/src/Renci.SshNet.Tests/Classes/BaseClientTestBase.cs @@ -6,15 +6,15 @@ namespace Renci.SshNet.Tests.Classes { public abstract class BaseClientTestBase : TripleATestBase { - internal Mock _serviceFactoryMock { get; private set; } - internal Mock _socketFactoryMock { get; private set; } - internal Mock _sessionMock { get; private set; } + internal Mock ServiceFactoryMock { get; private set; } + internal Mock SocketFactoryMock { get; private set; } + internal Mock SessionMock { get; private set; } protected virtual void CreateMocks() { - _serviceFactoryMock = new Mock(MockBehavior.Strict); - _socketFactoryMock = new Mock(MockBehavior.Strict); - _sessionMock = new Mock(MockBehavior.Strict); + ServiceFactoryMock = new Mock(MockBehavior.Strict); + SocketFactoryMock = new Mock(MockBehavior.Strict); + SessionMock = new Mock(MockBehavior.Strict); } protected virtual void SetupData() diff --git a/src/Renci.SshNet.Tests/Classes/BaseClientTest_Connect_OnConnectedThrowsException.cs b/src/Renci.SshNet.Tests/Classes/BaseClientTest_Connect_OnConnectedThrowsException.cs index e21f516d..806f6c30 100644 --- a/src/Renci.SshNet.Tests/Classes/BaseClientTest_Connect_OnConnectedThrowsException.cs +++ b/src/Renci.SshNet.Tests/Classes/BaseClientTest_Connect_OnConnectedThrowsException.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using System.Reflection; using System.Threading; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -24,20 +25,20 @@ namespace Renci.SshNet.Tests.Classes protected override void SetupMocks() { - _serviceFactoryMock.Setup(p => p.CreateSocketFactory()) - .Returns(_socketFactoryMock.Object); - _serviceFactoryMock.Setup(p => p.CreateSession(_connectionInfo, _socketFactoryMock.Object)) - .Returns(_sessionMock.Object); - _sessionMock.Setup(p => p.Connect()); - _sessionMock.Setup(p => p.Dispose()); + ServiceFactoryMock.Setup(p => p.CreateSocketFactory()) + .Returns(SocketFactoryMock.Object); + ServiceFactoryMock.Setup(p => p.CreateSession(_connectionInfo, SocketFactoryMock.Object)) + .Returns(SessionMock.Object); + SessionMock.Setup(p => p.Connect()); + SessionMock.Setup(p => p.Dispose()); } protected override void TearDown() { if (_client != null) { - _sessionMock.Setup(p => p.OnDisconnecting()); - _sessionMock.Setup(p => p.Dispose()); + SessionMock.Setup(p => p.OnDisconnecting()); + SessionMock.Setup(p => p.Dispose()); _client.Dispose(); } } @@ -46,7 +47,7 @@ namespace Renci.SshNet.Tests.Classes { base.Arrange(); - _client = new MyClient(_connectionInfo, false, _serviceFactoryMock.Object) + _client = new MyClient(_connectionInfo, false, ServiceFactoryMock.Object) { OnConnectedException = _onConnectException }; @@ -75,26 +76,26 @@ namespace Renci.SshNet.Tests.Classes [TestMethod] public void CreateSocketFactoryOnServiceFactoryShouldBeInvokedOnce() { - _serviceFactoryMock.Verify(p => p.CreateSocketFactory(), Times.Once); + ServiceFactoryMock.Verify(p => p.CreateSocketFactory(), Times.Once); } [TestMethod] public void CreateSessionOnServiceFactoryShouldBeInvokedOnce() { - _serviceFactoryMock.Verify(p => p.CreateSession(_connectionInfo, _socketFactoryMock.Object), + ServiceFactoryMock.Verify(p => p.CreateSession(_connectionInfo, SocketFactoryMock.Object), Times.Once); } [TestMethod] public void ConnectOnSessionShouldBeInvokedOnce() { - _sessionMock.Verify(p => p.Connect(), Times.Once); + SessionMock.Verify(p => p.Connect(), Times.Once); } [TestMethod] public void DisposeOnSessionShouldBeInvokedOnce() { - _sessionMock.Verify(p => p.Dispose(), Times.Once); + SessionMock.Verify(p => p.Dispose(), Times.Once); } [TestMethod] @@ -104,7 +105,7 @@ namespace Renci.SshNet.Tests.Classes _client.ErrorOccurred += (sender, args) => Interlocked.Increment(ref errorOccurredSignalCount); - _sessionMock.Raise(p => p.ErrorOccured += null, new ExceptionEventArgs(new Exception())); + SessionMock.Raise(p => p.ErrorOccured += null, new ExceptionEventArgs(new Exception())); Assert.AreEqual(0, errorOccurredSignalCount); } @@ -116,7 +117,7 @@ namespace Renci.SshNet.Tests.Classes _client.HostKeyReceived += (sender, args) => Interlocked.Increment(ref hostKeyReceivedSignalCount); - _sessionMock.Raise(p => p.HostKeyReceived += null, new HostKeyEventArgs(GetKeyHostAlgorithm())); + SessionMock.Raise(p => p.HostKeyReceived += null, new HostKeyEventArgs(GetKeyHostAlgorithm())); Assert.AreEqual(0, hostKeyReceivedSignalCount); } @@ -140,7 +141,7 @@ namespace Renci.SshNet.Tests.Classes using (var s = executingAssembly.GetManifestResourceStream(string.Format("Renci.SshNet.Tests.Data.{0}", "Key.RSA.txt"))) { var privateKey = new PrivateKeyFile(s); - return (KeyHostAlgorithm) privateKey.HostKey; + return (KeyHostAlgorithm) privateKey.HostKeyAlgorithms.First(); } } diff --git a/src/Renci.SshNet.Tests/Classes/BaseClientTest_Connected_KeepAliveInterval_NegativeOne.cs b/src/Renci.SshNet.Tests/Classes/BaseClientTest_Connected_KeepAliveInterval_NegativeOne.cs index e3b10c87..4f98bde8 100644 --- a/src/Renci.SshNet.Tests/Classes/BaseClientTest_Connected_KeepAliveInterval_NegativeOne.cs +++ b/src/Renci.SshNet.Tests/Classes/BaseClientTest_Connected_KeepAliveInterval_NegativeOne.cs @@ -1,8 +1,10 @@ using System; using System.Threading; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; -using Renci.SshNet.Connection; + using Renci.SshNet.Messages.Transport; namespace Renci.SshNet.Tests.Classes @@ -24,22 +26,23 @@ namespace Renci.SshNet.Tests.Classes protected override void SetupMocks() { - _serviceFactoryMock.Setup(p => p.CreateSocketFactory()) - .Returns(_socketFactoryMock.Object); - _serviceFactoryMock.Setup(p => p.CreateSession(_connectionInfo, _socketFactoryMock.Object)) - .Returns(_sessionMock.Object); - _sessionMock.Setup(p => p.Connect()); - _sessionMock.Setup(p => p.IsConnected).Returns(true); - _sessionMock.Setup(p => p.TrySendMessage(It.IsAny())) - .Returns(true) - .Callback(() => Interlocked.Increment(ref _keepAliveCount)); + _ = ServiceFactoryMock.Setup(p => p.CreateSocketFactory()) + .Returns(SocketFactoryMock.Object); + _ = ServiceFactoryMock.Setup(p => p.CreateSession(_connectionInfo, SocketFactoryMock.Object)) + .Returns(SessionMock.Object); + _ = SessionMock.Setup(p => p.Connect()); + _ = SessionMock.Setup(p => p.IsConnected) + .Returns(true); + _ = SessionMock.Setup(p => p.TrySendMessage(It.IsAny())) + .Returns(true) + .Callback(() => Interlocked.Increment(ref _keepAliveCount)); } protected override void Arrange() { base.Arrange(); - _client = new MyClient(_connectionInfo, false, _serviceFactoryMock.Object); + _client = new MyClient(_connectionInfo, false, ServiceFactoryMock.Object); _client.Connect(); _client.KeepAliveInterval = _keepAliveInterval; } @@ -48,8 +51,8 @@ namespace Renci.SshNet.Tests.Classes { if (_client != null) { - _sessionMock.Setup(p => p.OnDisconnecting()); - _sessionMock.Setup(p => p.Dispose()); + SessionMock.Setup(p => p.OnDisconnecting()); + SessionMock.Setup(p => p.Dispose()); _client.Dispose(); } } @@ -72,34 +75,34 @@ namespace Renci.SshNet.Tests.Classes [TestMethod] public void CreateSocketFactoryOnServiceFactoryShouldBeInvokedOnce() { - _serviceFactoryMock.Verify(p => p.CreateSocketFactory(), Times.Once); + ServiceFactoryMock.Verify(p => p.CreateSocketFactory(), Times.Once); } [TestMethod] public void CreateSessionOnServiceFactoryShouldBeInvokedOnce() { - _serviceFactoryMock.Verify(p => p.CreateSession(_connectionInfo, _socketFactoryMock.Object), Times.Once); + ServiceFactoryMock.Verify(p => p.CreateSession(_connectionInfo, SocketFactoryMock.Object), Times.Once); } [TestMethod] public void ConnectOnSessionShouldBeInvokedOnce() { - _sessionMock.Verify(p => p.Connect(), Times.Once); + SessionMock.Verify(p => p.Connect(), Times.Once); } [TestMethod] public void IsConnectedOnSessionShouldBeInvokedOnce() { - _sessionMock.Verify(p => p.IsConnected, Times.Once); + SessionMock.Verify(p => p.IsConnected, Times.Once); } [TestMethod] - public void SendMessageOnSessionShouldBeInvokedThreeTimes() + public void SendMessageOnSessionShouldBeInvokedOneTime() { // allow keep-alive to be sent once Thread.Sleep(100); - _sessionMock.Verify(p => p.TrySendMessage(It.IsAny()), Times.Exactly(1)); + SessionMock.Verify(p => p.TrySendMessage(It.IsAny()), Times.Exactly(1)); } private class MyClient : BaseClient diff --git a/src/Renci.SshNet.Tests/Classes/BaseClientTest_Connected_KeepAliveInterval_NotNegativeOne.cs b/src/Renci.SshNet.Tests/Classes/BaseClientTest_Connected_KeepAliveInterval_NotNegativeOne.cs index 26a8da00..2afb8460 100644 --- a/src/Renci.SshNet.Tests/Classes/BaseClientTest_Connected_KeepAliveInterval_NotNegativeOne.cs +++ b/src/Renci.SshNet.Tests/Classes/BaseClientTest_Connected_KeepAliveInterval_NotNegativeOne.cs @@ -23,13 +23,13 @@ namespace Renci.SshNet.Tests.Classes protected override void SetupMocks() { - _serviceFactoryMock.Setup(p => p.CreateSocketFactory()) - .Returns(_socketFactoryMock.Object); - _serviceFactoryMock.Setup(p => p.CreateSession(_connectionInfo, _socketFactoryMock.Object)) - .Returns(_sessionMock.Object); - _sessionMock.Setup(p => p.Connect()); - _sessionMock.Setup(p => p.IsConnected).Returns(true); - _sessionMock.Setup(p => p.TrySendMessage(It.IsAny())) + ServiceFactoryMock.Setup(p => p.CreateSocketFactory()) + .Returns(SocketFactoryMock.Object); + ServiceFactoryMock.Setup(p => p.CreateSession(_connectionInfo, SocketFactoryMock.Object)) + .Returns(SessionMock.Object); + SessionMock.Setup(p => p.Connect()); + SessionMock.Setup(p => p.IsConnected).Returns(true); + SessionMock.Setup(p => p.TrySendMessage(It.IsAny())) .Returns(true) .Callback(() => Interlocked.Increment(ref _keepAliveCount)); } @@ -38,7 +38,7 @@ namespace Renci.SshNet.Tests.Classes { base.Arrange(); - _client = new MyClient(_connectionInfo, false, _serviceFactoryMock.Object); + _client = new MyClient(_connectionInfo, false, ServiceFactoryMock.Object); _client.Connect(); } @@ -46,8 +46,8 @@ namespace Renci.SshNet.Tests.Classes { if (_client != null) { - _sessionMock.Setup(p => p.OnDisconnecting()); - _sessionMock.Setup(p => p.Dispose()); + SessionMock.Setup(p => p.OnDisconnecting()); + SessionMock.Setup(p => p.Dispose()); _client.Dispose(); } } @@ -56,8 +56,13 @@ namespace Renci.SshNet.Tests.Classes { _client.KeepAliveInterval = _keepAliveInterval; - // allow keep-alive to be sent a few times + // allow keep-alive to be sent a few times. .NET 7 is faster and + // we need to wait less because we want exactly three messages in a session. +#if NETFRAMEWORK Thread.Sleep(195); +#else + Thread.Sleep(180); +#endif } [TestMethod] @@ -69,32 +74,32 @@ namespace Renci.SshNet.Tests.Classes [TestMethod] public void CreateSocketFactoryOnServiceFactoryShouldBeInvokedOnce() { - _serviceFactoryMock.Verify(p => p.CreateSocketFactory(), Times.Once); + ServiceFactoryMock.Verify(p => p.CreateSocketFactory(), Times.Once); } [TestMethod] public void CreateSessionOnServiceFactoryShouldBeInvokedOnce() { - _serviceFactoryMock.Verify(p => p.CreateSession(_connectionInfo, _socketFactoryMock.Object), + ServiceFactoryMock.Verify(p => p.CreateSession(_connectionInfo, SocketFactoryMock.Object), Times.Once); } [TestMethod] public void ConnectOnSessionShouldBeInvokedOnce() { - _sessionMock.Verify(p => p.Connect(), Times.Once); + SessionMock.Verify(p => p.Connect(), Times.Once); } [TestMethod] public void IsConnectedOnSessionShouldBeInvokedOnce() { - _sessionMock.Verify(p => p.IsConnected, Times.Once); + SessionMock.Verify(p => p.IsConnected, Times.Once); } [TestMethod] public void SendMessageOnSessionShouldBeInvokedThreeTimes() { - _sessionMock.Verify(p => p.TrySendMessage(It.IsAny()), Times.Exactly(3)); + SessionMock.Verify(p => p.TrySendMessage(It.IsAny()), Times.Exactly(3)); } private class MyClient : BaseClient diff --git a/src/Renci.SshNet.Tests/Classes/BaseClientTest_Connected_KeepAlivesNotSentConcurrently.cs b/src/Renci.SshNet.Tests/Classes/BaseClientTest_Connected_KeepAlivesNotSentConcurrently.cs index 25d6ec7c..ac0539e9 100644 --- a/src/Renci.SshNet.Tests/Classes/BaseClientTest_Connected_KeepAlivesNotSentConcurrently.cs +++ b/src/Renci.SshNet.Tests/Classes/BaseClientTest_Connected_KeepAlivesNotSentConcurrently.cs @@ -24,15 +24,15 @@ namespace Renci.SshNet.Tests.Classes { _mockSequence = new MockSequence(); - _serviceFactoryMock.InSequence(_mockSequence) + ServiceFactoryMock.InSequence(_mockSequence) .Setup(p => p.CreateSocketFactory()) - .Returns(_socketFactoryMock.Object); - _serviceFactoryMock.InSequence(_mockSequence) - .Setup(p => p.CreateSession(_connectionInfo, _socketFactoryMock.Object)) - .Returns(_sessionMock.Object); - _sessionMock.InSequence(_mockSequence) + .Returns(SocketFactoryMock.Object); + ServiceFactoryMock.InSequence(_mockSequence) + .Setup(p => p.CreateSession(_connectionInfo, SocketFactoryMock.Object)) + .Returns(SessionMock.Object); + SessionMock.InSequence(_mockSequence) .Setup(p => p.Connect()); - _sessionMock.InSequence(_mockSequence) + SessionMock.InSequence(_mockSequence) .Setup(p => p.TrySendMessage(It.IsAny())) .Returns(true) .Callback(() => @@ -46,7 +46,7 @@ namespace Renci.SshNet.Tests.Classes { base.Arrange(); - _client = new MyClient(_connectionInfo, false, _serviceFactoryMock.Object) + _client = new MyClient(_connectionInfo, false, ServiceFactoryMock.Object) { KeepAliveInterval = TimeSpan.FromMilliseconds(50d) }; @@ -57,8 +57,8 @@ namespace Renci.SshNet.Tests.Classes { if (_client != null) { - _sessionMock.InSequence(_mockSequence).Setup(p => p.OnDisconnecting()); - _sessionMock.InSequence(_mockSequence).Setup(p => p.Dispose()); + SessionMock.InSequence(_mockSequence).Setup(p => p.OnDisconnecting()); + SessionMock.InSequence(_mockSequence).Setup(p => p.Dispose()); _client.Dispose(); } } @@ -79,7 +79,7 @@ namespace Renci.SshNet.Tests.Classes [TestMethod] public void SendMessageOnSessionShouldBeInvokedOnce() { - _sessionMock.Verify(p => p.TrySendMessage(It.IsAny()), Times.Once); + SessionMock.Verify(p => p.TrySendMessage(It.IsAny()), Times.Once); } private class MyClient : BaseClient diff --git a/src/Renci.SshNet.Tests/Classes/BaseClientTest_Disconnected_Connect.cs b/src/Renci.SshNet.Tests/Classes/BaseClientTest_Disconnected_Connect.cs index 51cd2d81..ebde0c20 100644 --- a/src/Renci.SshNet.Tests/Classes/BaseClientTest_Disconnected_Connect.cs +++ b/src/Renci.SshNet.Tests/Classes/BaseClientTest_Disconnected_Connect.cs @@ -29,22 +29,22 @@ namespace Renci.SshNet.Tests.Classes { var sequence = new MockSequence(); - _serviceFactoryMock.InSequence(sequence) + ServiceFactoryMock.InSequence(sequence) .Setup(p => p.CreateSocketFactory()) - .Returns(_socketFactoryMock.Object); - _serviceFactoryMock.InSequence(sequence) - .Setup(p => p.CreateSession(_connectionInfo, _socketFactoryMock.Object)) - .Returns(_sessionMock.Object); - _sessionMock.InSequence(sequence) + .Returns(SocketFactoryMock.Object); + ServiceFactoryMock.InSequence(sequence) + .Setup(p => p.CreateSession(_connectionInfo, SocketFactoryMock.Object)) + .Returns(SessionMock.Object); + SessionMock.InSequence(sequence) .Setup(p => p.Connect()); - _sessionMock.InSequence(sequence) + SessionMock.InSequence(sequence) .Setup(p => p.OnDisconnecting()); - _sessionMock.InSequence(sequence) + SessionMock.InSequence(sequence) .Setup(p => p.Dispose()); - _serviceFactoryMock.InSequence(sequence) + ServiceFactoryMock.InSequence(sequence) .Setup(p => p.CreateSocketFactory()) .Returns(_socketFactory2Mock.Object); - _serviceFactoryMock.InSequence(sequence) + ServiceFactoryMock.InSequence(sequence) .Setup(p => p.CreateSession(_connectionInfo, _socketFactory2Mock.Object)) .Returns(_session2Mock.Object); _session2Mock.InSequence(sequence) @@ -55,7 +55,7 @@ namespace Renci.SshNet.Tests.Classes { base.Arrange(); - _client = new MyClient(_connectionInfo, false, _serviceFactoryMock.Object); + _client = new MyClient(_connectionInfo, false, ServiceFactoryMock.Object); _client.Connect(); _client.Disconnect(); } @@ -78,22 +78,22 @@ namespace Renci.SshNet.Tests.Classes [TestMethod] public void CreateSocketFactoryOnServiceFactoryShouldBeInvokedTwic() { - _serviceFactoryMock.Verify(p => p.CreateSocketFactory(), Times.Exactly(2)); + ServiceFactoryMock.Verify(p => p.CreateSocketFactory(), Times.Exactly(2)); } [TestMethod] public void CreateSessionOnServiceFactoryShouldBeInvokedTwice() { - _serviceFactoryMock.Verify(p => p.CreateSession(_connectionInfo, _socketFactoryMock.Object), + ServiceFactoryMock.Verify(p => p.CreateSession(_connectionInfo, SocketFactoryMock.Object), Times.Once); - _serviceFactoryMock.Verify(p => p.CreateSession(_connectionInfo, _socketFactory2Mock.Object), + ServiceFactoryMock.Verify(p => p.CreateSession(_connectionInfo, _socketFactory2Mock.Object), Times.Once); } [TestMethod] public void ConnectOnSessionShouldBeInvokedTwice() { - _sessionMock.Verify(p => p.Connect(), Times.Once); + SessionMock.Verify(p => p.Connect(), Times.Once); _session2Mock.Verify(p => p.Connect(), Times.Once); } diff --git a/src/Renci.SshNet.Tests/Classes/BaseClientTest_Disconnected_KeepAliveInterval_NotNegativeOne.cs b/src/Renci.SshNet.Tests/Classes/BaseClientTest_Disconnected_KeepAliveInterval_NotNegativeOne.cs index 1ca4b19d..e026d09b 100644 --- a/src/Renci.SshNet.Tests/Classes/BaseClientTest_Disconnected_KeepAliveInterval_NotNegativeOne.cs +++ b/src/Renci.SshNet.Tests/Classes/BaseClientTest_Disconnected_KeepAliveInterval_NotNegativeOne.cs @@ -21,13 +21,13 @@ namespace Renci.SshNet.Tests.Classes protected override void SetupMocks() { - _serviceFactoryMock.Setup(p => p.CreateSocketFactory()) - .Returns(_socketFactoryMock.Object); - _serviceFactoryMock.Setup(p => p.CreateSession(_connectionInfo, _socketFactoryMock.Object)) - .Returns(_sessionMock.Object); - _sessionMock.Setup(p => p.Connect()); - _sessionMock.Setup(p => p.IsConnected).Returns(false); - _sessionMock.Setup(p => p.TrySendMessage(It.IsAny())) + ServiceFactoryMock.Setup(p => p.CreateSocketFactory()) + .Returns(SocketFactoryMock.Object); + ServiceFactoryMock.Setup(p => p.CreateSession(_connectionInfo, SocketFactoryMock.Object)) + .Returns(SessionMock.Object); + SessionMock.Setup(p => p.Connect()); + SessionMock.Setup(p => p.IsConnected).Returns(false); + SessionMock.Setup(p => p.TrySendMessage(It.IsAny())) .Returns(true); } @@ -35,7 +35,7 @@ namespace Renci.SshNet.Tests.Classes { base.Arrange(); - _client = new MyClient(_connectionInfo, false, _serviceFactoryMock.Object); + _client = new MyClient(_connectionInfo, false, ServiceFactoryMock.Object); _client.Connect(); } @@ -43,8 +43,8 @@ namespace Renci.SshNet.Tests.Classes { if (_client != null) { - _sessionMock.Setup(p => p.OnDisconnecting()); - _sessionMock.Setup(p => p.Dispose()); + SessionMock.Setup(p => p.OnDisconnecting()); + SessionMock.Setup(p => p.Dispose()); _client.Dispose(); } } @@ -66,32 +66,32 @@ namespace Renci.SshNet.Tests.Classes [TestMethod] public void CreateSocketFactoryOnServiceFactoryShouldBeInvokedOnce() { - _serviceFactoryMock.Verify(p => p.CreateSocketFactory(), Times.Once); + ServiceFactoryMock.Verify(p => p.CreateSocketFactory(), Times.Once); } [TestMethod] public void CreateSessionOnServiceFactoryShouldBeInvokedOnce() { - _serviceFactoryMock.Verify(p => p.CreateSession(_connectionInfo, _socketFactoryMock.Object), + ServiceFactoryMock.Verify(p => p.CreateSession(_connectionInfo, SocketFactoryMock.Object), Times.Once); } [TestMethod] public void ConnectOnSessionShouldBeInvokedOnce() { - _sessionMock.Verify(p => p.Connect(), Times.Once); + SessionMock.Verify(p => p.Connect(), Times.Once); } [TestMethod] public void IsConnectedOnSessionShouldBeInvokedOnce() { - _sessionMock.Verify(p => p.IsConnected, Times.Once); + SessionMock.Verify(p => p.IsConnected, Times.Once); } [TestMethod] public void SendMessageOnSessionShouldNeverBeInvoked() { - _sessionMock.Verify(p => p.TrySendMessage(It.IsAny()), Times.Never); + SessionMock.Verify(p => p.TrySendMessage(It.IsAny()), Times.Never); } private class MyClient : BaseClient diff --git a/src/Renci.SshNet.Tests/Classes/BaseClientTest_NotConnected_KeepAliveInterval_NotNegativeOne.cs b/src/Renci.SshNet.Tests/Classes/BaseClientTest_NotConnected_KeepAliveInterval_NotNegativeOne.cs index 0f449c82..e0720e42 100644 --- a/src/Renci.SshNet.Tests/Classes/BaseClientTest_NotConnected_KeepAliveInterval_NotNegativeOne.cs +++ b/src/Renci.SshNet.Tests/Classes/BaseClientTest_NotConnected_KeepAliveInterval_NotNegativeOne.cs @@ -25,15 +25,15 @@ namespace Renci.SshNet.Tests.Classes { base.Arrange(); - _client = new MyClient(_connectionInfo, false, _serviceFactoryMock.Object); + _client = new MyClient(_connectionInfo, false, ServiceFactoryMock.Object); } protected override void TearDown() { if (_client != null) { - _sessionMock.Setup(p => p.OnDisconnecting()); - _sessionMock.Setup(p => p.Dispose()); + SessionMock.Setup(p => p.OnDisconnecting()); + SessionMock.Setup(p => p.Dispose()); _client.Dispose(); } } @@ -55,12 +55,12 @@ namespace Renci.SshNet.Tests.Classes [TestMethod] public void ConnectShouldActivateKeepAliveIfSessionIs() { - _serviceFactoryMock.Setup(p => p.CreateSocketFactory()) - .Returns(_socketFactoryMock.Object); - _serviceFactoryMock.Setup(p => p.CreateSession(_connectionInfo, _socketFactoryMock.Object)) - .Returns(_sessionMock.Object); - _sessionMock.Setup(p => p.Connect()); - _sessionMock.Setup(p => p.TrySendMessage(It.IsAny())) + ServiceFactoryMock.Setup(p => p.CreateSocketFactory()) + .Returns(SocketFactoryMock.Object); + ServiceFactoryMock.Setup(p => p.CreateSession(_connectionInfo, SocketFactoryMock.Object)) + .Returns(SessionMock.Object); + SessionMock.Setup(p => p.Connect()); + SessionMock.Setup(p => p.TrySendMessage(It.IsAny())) .Returns(true) .Callback(() => Interlocked.Increment(ref _keepAliveCount)); @@ -70,7 +70,7 @@ namespace Renci.SshNet.Tests.Classes Thread.Sleep(250); // Exactly two keep-alives should be sent - _sessionMock.Verify(p => p.TrySendMessage(It.IsAny()), Times.Exactly(2)); + SessionMock.Verify(p => p.TrySendMessage(It.IsAny()), Times.Exactly(2)); } private class MyClient : BaseClient diff --git a/src/Renci.SshNet.Tests/Classes/Channels/ChannelDirectTcpipTest.cs b/src/Renci.SshNet.Tests/Classes/Channels/ChannelDirectTcpipTest.cs index 1dce239e..649f6a4f 100644 --- a/src/Renci.SshNet.Tests/Classes/Channels/ChannelDirectTcpipTest.cs +++ b/src/Renci.SshNet.Tests/Classes/Channels/ChannelDirectTcpipTest.cs @@ -3,8 +3,11 @@ using System.Globalization; using System.Net; using System.Net.Sockets; using System.Threading; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; + using Renci.SshNet.Channels; using Renci.SshNet.Common; using Renci.SshNet.Messages; @@ -53,16 +56,17 @@ namespace Renci.SshNet.Tests.Classes.Channels [TestMethod] public void SocketShouldBeClosedAndBindShouldEndWhenForwardedPortSignalsClosingEvent() { - _sessionMock.Setup(p => p.IsConnected).Returns(true); - _sessionMock.Setup(p => p.SendMessage(It.IsAny())) - .Callback(m => _sessionMock.Raise(p => p.ChannelOpenConfirmationReceived += null, + _ = _sessionMock.Setup(p => p.IsConnected) + .Returns(true); + _ = _sessionMock.Setup(p => p.SendMessage(It.IsAny())) + .Callback(m => _sessionMock.Raise(p => p.ChannelOpenConfirmationReceived += null, new MessageEventArgs( new ChannelOpenConfirmationMessage(((ChannelOpenMessage) m).LocalChannelNumber, _remoteWindowSize, _remotePacketSize, _remoteChannelNumber)))); - _sessionMock.Setup(p => p.WaitOnHandle(It.IsAny())) - .Callback(p => p.WaitOne(Session.Infinite)); + _ = _sessionMock.Setup(p => p.WaitOnHandle(It.IsAny())) + .Callback(p => p.WaitOne(Session.Infinite)); var localPortEndPoint = new IPEndPoint(IPAddress.Loopback, 8122); using (var localPortListener = new AsyncSocketListener(localPortEndPoint)) @@ -108,16 +112,17 @@ namespace Renci.SshNet.Tests.Classes.Channels [TestMethod] public void SocketShouldBeClosedAndBindShouldEndWhenOnErrorOccurredIsInvoked() { - _sessionMock.Setup(p => p.IsConnected).Returns(true); - _sessionMock.Setup(p => p.SendMessage(It.IsAny())) - .Callback(m => _sessionMock.Raise(p => p.ChannelOpenConfirmationReceived += null, + _ = _sessionMock.Setup(p => p.IsConnected) + .Returns(true); + _ = _sessionMock.Setup(p => p.SendMessage(It.IsAny())) + .Callback(m => _sessionMock.Raise(p => p.ChannelOpenConfirmationReceived += null, new MessageEventArgs( new ChannelOpenConfirmationMessage(((ChannelOpenMessage) m).LocalChannelNumber, _remoteWindowSize, _remotePacketSize, _remoteChannelNumber)))); - _sessionMock.Setup(p => p.WaitOnHandle(It.IsAny())) - .Callback(p => p.WaitOne(Session.Infinite)); + _ = _sessionMock.Setup(p => p.WaitOnHandle(It.IsAny())) + .Callback(p => p.WaitOne(Session.Infinite)); var localPortEndPoint = new IPEndPoint(IPAddress.Loopback, 8122); using (var localPortListener = new AsyncSocketListener(localPortEndPoint)) @@ -166,46 +171,53 @@ namespace Renci.SshNet.Tests.Classes.Channels { var sequence = new MockSequence(); - _sessionMock.InSequence(sequence).Setup(p => p.IsConnected).Returns(true); - _sessionMock.InSequence(sequence) - .Setup(p => p.SendMessage(It.IsAny())) - .Callback(m => _sessionMock.Raise(p => p.ChannelOpenConfirmationReceived += null, + _ = _sessionMock.InSequence(sequence).Setup(p => p.IsConnected) + .Returns(true); + _ = _sessionMock.InSequence(sequence) + .Setup(p => p.SendMessage(It.IsAny())) + .Callback(m => _sessionMock.Raise(p => p.ChannelOpenConfirmationReceived += null, new MessageEventArgs( new ChannelOpenConfirmationMessage(((ChannelOpenMessage) m).LocalChannelNumber, _remoteWindowSize, _remotePacketSize, _remoteChannelNumber)))); - _sessionMock.InSequence(sequence) - .Setup(p => p.WaitOnHandle(It.IsAny())) - .Callback(p => p.WaitOne(Session.Infinite)); - _sessionMock.InSequence(sequence).Setup(p => p.IsConnected).Returns(true); - _sessionMock.InSequence(sequence) - .Setup(p => p.TrySendMessage(It.IsAny())) - .Returns(true) - .Callback( - m => new Thread(() => - { - Thread.Sleep(50); - _sessionMock.Raise(s => s.ChannelEofReceived += null, - new MessageEventArgs(new ChannelEofMessage(_localChannelNumber))); - }).Start()); - _sessionMock.InSequence(sequence).Setup(p => p.IsConnected).Returns(true); - _sessionMock.InSequence(sequence) - .Setup(p => p.TrySendMessage(It.IsAny())) - .Returns(true) - .Callback( - m => new Thread(() => - { - Thread.Sleep(50); - _sessionMock.Raise(s => s.ChannelCloseReceived += null, - new MessageEventArgs(new ChannelCloseMessage(_localChannelNumber))); - }).Start()); - _sessionMock.InSequence(sequence).Setup(p => p.ConnectionInfo).Returns(_connectionInfoMock.Object); - _connectionInfoMock.InSequence(sequence).Setup(p => p.ChannelCloseTimeout).Returns(_channelCloseTimeout); - _sessionMock.InSequence(sequence) - .Setup(p => p.TryWait(It.IsAny(), _channelCloseTimeout)) - .Callback((waitHandle, channelCloseTimeout) => waitHandle.WaitOne()) - .Returns(WaitResult.Success); + _ = _sessionMock.InSequence(sequence) + .Setup(p => p.WaitOnHandle(It.IsAny())) + .Callback(p => p.WaitOne(Session.Infinite)); + _ = _sessionMock.InSequence(sequence) + .Setup(p => p.IsConnected) + .Returns(true); + _ = _sessionMock.InSequence(sequence) + .Setup(p => p.TrySendMessage(It.IsAny())) + .Returns(true) + .Callback(m => new Thread(() => + { + Thread.Sleep(50); + _sessionMock.Raise(s => s.ChannelEofReceived += null, + new MessageEventArgs(new ChannelEofMessage(_localChannelNumber))); + }).Start()); + _ = _sessionMock.InSequence(sequence) + .Setup(p => p.IsConnected) + .Returns(true); + _ = _sessionMock.InSequence(sequence) + .Setup(p => p.TrySendMessage(It.IsAny())) + .Returns(true) + .Callback(m => new Thread(() => + { + Thread.Sleep(50); + _sessionMock.Raise(s => s.ChannelCloseReceived += null, + new MessageEventArgs(new ChannelCloseMessage(_localChannelNumber))); + }).Start()); + _ = _sessionMock.InSequence(sequence) + .Setup(p => p.ConnectionInfo) + .Returns(_connectionInfoMock.Object); + _ = _connectionInfoMock.InSequence(sequence) + .Setup(p => p.ChannelCloseTimeout) + .Returns(_channelCloseTimeout); + _ = _sessionMock.InSequence(sequence) + .Setup(p => p.TryWait(It.IsAny(), _channelCloseTimeout)) + .Callback((waitHandle, channelCloseTimeout) => waitHandle.WaitOne()) + .Returns(WaitResult.Success); var channelBindFinishedWaitHandle = new ManualResetEvent(false); Socket handler = null; @@ -217,26 +229,26 @@ namespace Renci.SshNet.Tests.Classes.Channels localPortListener.Start(); localPortListener.Connected += socket => - { - channel = new ChannelDirectTcpip(_sessionMock.Object, - _localChannelNumber, - _localWindowSize, - _localPacketSize); - channel.Open(_remoteHost, _port, _forwardedPortMock.Object, socket); - channel.Bind(); - channel.Dispose(); + { + channel = new ChannelDirectTcpip(_sessionMock.Object, + _localChannelNumber, + _localWindowSize, + _localPacketSize); + channel.Open(_remoteHost, _port, _forwardedPortMock.Object, socket); + channel.Bind(); + channel.Dispose(); - handler = socket; + handler = socket; - channelBindFinishedWaitHandle.Set(); - }; + _ = channelBindFinishedWaitHandle.Set(); + }; var client = new Socket(localPortEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); client.Connect(localPortEndPoint); client.Shutdown(SocketShutdown.Send); Assert.IsFalse(client.Connected); - channelBindFinishedWaitHandle.WaitOne(); + _ = channelBindFinishedWaitHandle.WaitOne(); Assert.IsNotNull(handler); Assert.IsFalse(handler.Connected); @@ -251,4 +263,4 @@ namespace Renci.SshNet.Tests.Classes.Channels } } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.Tests/Classes/Channels/ChannelDirectTcpipTest_Dispose_SessionIsConnectedAndChannelIsOpen.cs b/src/Renci.SshNet.Tests/Classes/Channels/ChannelDirectTcpipTest_Dispose_SessionIsConnectedAndChannelIsOpen.cs index a75d407d..6ccda5c8 100644 --- a/src/Renci.SshNet.Tests/Classes/Channels/ChannelDirectTcpipTest_Dispose_SessionIsConnectedAndChannelIsOpen.cs +++ b/src/Renci.SshNet.Tests/Classes/Channels/ChannelDirectTcpipTest_Dispose_SessionIsConnectedAndChannelIsOpen.cs @@ -5,9 +5,6 @@ using System.Net.Sockets; using System.Threading; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; -#if !FEATURE_SOCKET_DISPOSE -using Renci.SshNet.Common; -#endif // !FEATURE_SOCKET_DISPOSE using Renci.SshNet.Channels; using Renci.SshNet.Messages.Connection; using Renci.SshNet.Tests.Common; @@ -82,45 +79,53 @@ namespace Renci.SshNet.Tests.Classes.Channels _forwardedPortMock = new Mock(MockBehavior.Strict); var sequence = new MockSequence(); - _sessionMock.InSequence(sequence).Setup(p => p.IsConnected).Returns(true); - _sessionMock.InSequence(sequence) - .Setup(p => p.SendMessage(It.Is(m => AssertExpectedMessage(m)))); - _sessionMock.InSequence(sequence) - .Setup(p => p.WaitOnHandle(It.IsNotNull())) - .Callback( - w => - { - _sessionMock.Raise( - s => s.ChannelOpenConfirmationReceived += null, - new MessageEventArgs( - new ChannelOpenConfirmationMessage( - _localChannelNumber, - _remoteWindowSize, - _remotePacketSize, - _remoteChannelNumber))); - w.WaitOne(); - }); - _sessionMock.InSequence(sequence).Setup(p => p.IsConnected).Returns(true); - _sessionMock.InSequence(sequence) - .Setup( - p => p.TrySendMessage(It.Is(m => m.LocalChannelNumber == _remoteChannelNumber))) - .Returns(true); - _sessionMock.InSequence(sequence).Setup(p => p.IsConnected).Returns(true); - _sessionMock.InSequence(sequence) - .Setup(p => p.TrySendMessage(It.Is(m => m.LocalChannelNumber == _remoteChannelNumber))) - .Returns(true); - _sessionMock.InSequence(sequence).Setup(p => p.ConnectionInfo).Returns(_connectionInfoMock.Object); - _connectionInfoMock.InSequence(sequence).Setup(p => p.ChannelCloseTimeout).Returns(_channelCloseTimeout); - _sessionMock.InSequence(sequence) - .Setup(p => p.TryWait(It.IsAny(), _channelCloseTimeout)) - .Callback((waitHandle, channelCloseTimeout) => - { - _sessionMock.Raise( - s => s.ChannelCloseReceived += null, - new MessageEventArgs(new ChannelCloseMessage(_localChannelNumber))); - waitHandle.WaitOne(); - }) - .Returns(WaitResult.Success); + + _ = _sessionMock.InSequence(sequence).Setup(p => p.IsConnected).Returns(true); + _ = _sessionMock.InSequence(sequence) + .Setup(p => p.SendMessage(It.Is(m => AssertExpectedMessage(m)))); + _ = _sessionMock.InSequence(sequence) + .Setup(p => p.WaitOnHandle(It.IsNotNull())) + .Callback( + w => + { + _sessionMock.Raise( + s => s.ChannelOpenConfirmationReceived += null, + new MessageEventArgs( + new ChannelOpenConfirmationMessage( + _localChannelNumber, + _remoteWindowSize, + _remotePacketSize, + _remoteChannelNumber))); + _ = w.WaitOne(); + }); + _ = _sessionMock.InSequence(sequence) + .Setup(p => p.IsConnected) + .Returns(true); + _ = _sessionMock.InSequence(sequence) + .Setup(p => p.TrySendMessage(It.Is(m => m.LocalChannelNumber == _remoteChannelNumber))) + .Returns(true); + _ = _sessionMock.InSequence(sequence) + .Setup(p => p.IsConnected) + .Returns(true); + _ = _sessionMock.InSequence(sequence) + .Setup(p => p.TrySendMessage(It.Is(m => m.LocalChannelNumber == _remoteChannelNumber))) + .Returns(true); + _ = _sessionMock.InSequence(sequence) + .Setup(p => p.ConnectionInfo) + .Returns(_connectionInfoMock.Object); + _ = _connectionInfoMock.InSequence(sequence) + .Setup(p => p.ChannelCloseTimeout) + .Returns(_channelCloseTimeout); + _ = _sessionMock.InSequence(sequence) + .Setup(p => p.TryWait(It.IsAny(), _channelCloseTimeout)) + .Callback((waitHandle, channelCloseTimeout) => + { + _sessionMock.Raise( + s => s.ChannelCloseReceived += null, + new MessageEventArgs(new ChannelCloseMessage(_localChannelNumber))); + _ = waitHandle.WaitOne(); + }) + .Returns(WaitResult.Success); var localEndpoint = new IPEndPoint(IPAddress.Loopback, 8122); _listener = new AsyncSocketListener(localEndpoint); @@ -141,7 +146,7 @@ namespace Renci.SshNet.Tests.Classes.Channels } finally { - _channelBindFinishedWaitHandle.Set(); + _ = _channelBindFinishedWaitHandle.Set(); } }; _listener.Start(); @@ -157,7 +162,7 @@ namespace Renci.SshNet.Tests.Classes.Channels if (bytesReceived == 0) { _client.Shutdown(SocketShutdown.Send); - _clientReceivedFinishedWaitHandle.Set(); + _ = _clientReceivedFinishedWaitHandle.Set(); } } ); @@ -169,17 +174,14 @@ namespace Renci.SshNet.Tests.Classes.Channels private void Act() { - if (_channel != null) - { - _channel.Dispose(); - } + _channel?.Dispose(); } [TestMethod] public void BindShouldHaveFinishedWithoutException() { Assert.IsTrue(_channelBindFinishedWaitHandle.WaitOne(0)); - Assert.IsNull(_channelException, _channelException != null ? _channelException.ToString() : null); + Assert.IsNull(_channelException, _channelException?.ToString()); } [TestMethod] @@ -209,29 +211,54 @@ namespace Renci.SshNet.Tests.Classes.Channels private bool AssertExpectedMessage(ChannelOpenMessage channelOpenMessage) { if (channelOpenMessage == null) + { return false; + } + if (channelOpenMessage.LocalChannelNumber != _localChannelNumber) + { return false; + } + if (channelOpenMessage.InitialWindowSize != _localWindowSize) + { return false; + } + if (channelOpenMessage.MaximumPacketSize != _localPacketSize) + { return false; + } - var directTcpipChannelInfo = channelOpenMessage.Info as DirectTcpipChannelInfo; - if (directTcpipChannelInfo == null) + if (channelOpenMessage.Info is not DirectTcpipChannelInfo directTcpipChannelInfo) + { return false; + } + if (directTcpipChannelInfo.HostToConnect != _remoteHost) + { return false; - if (directTcpipChannelInfo.PortToConnect != _port) - return false; + } - var clientEndpoint = _client.LocalEndPoint as IPEndPoint; - if (clientEndpoint == null) + if (directTcpipChannelInfo.PortToConnect != _port) + { return false; + } + + if (_client.LocalEndPoint is not IPEndPoint clientEndpoint) + { + return false; + } + if (directTcpipChannelInfo.OriginatorAddress != clientEndpoint.Address.ToString()) + { return false; + } + if (directTcpipChannelInfo.OriginatorPort != clientEndpoint.Port) + { return false; + } return true; } diff --git a/src/Renci.SshNet.Tests/Classes/Channels/ChannelForwardedTcpipTest_Dispose_SessionIsConnectedAndChannelIsOpen.cs b/src/Renci.SshNet.Tests/Classes/Channels/ChannelForwardedTcpipTest_Dispose_SessionIsConnectedAndChannelIsOpen.cs index bbbff6d3..393eae09 100644 --- a/src/Renci.SshNet.Tests/Classes/Channels/ChannelForwardedTcpipTest_Dispose_SessionIsConnectedAndChannelIsOpen.cs +++ b/src/Renci.SshNet.Tests/Classes/Channels/ChannelForwardedTcpipTest_Dispose_SessionIsConnectedAndChannelIsOpen.cs @@ -1,11 +1,13 @@ using System; -using System.Collections; using System.Collections.Generic; using System.Net; using System.Net.Sockets; using System.Threading; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; + using Renci.SshNet.Channels; using Renci.SshNet.Messages.Connection; using Renci.SshNet.Tests.Common; @@ -53,10 +55,10 @@ namespace Renci.SshNet.Tests.Classes.Channels if (_channelThread != null) { - if (_channelThread.IsAlive) - _channelThread.Abort(); + _channelThread.Join(); _channelThread = null; } + if (_channel != null) { _channel.Dispose(); @@ -87,56 +89,63 @@ namespace Renci.SshNet.Tests.Classes.Channels _forwardedPortMock = new Mock(MockBehavior.Strict); var sequence = new MockSequence(); - _sessionMock.InSequence(sequence).Setup(p => p.IsConnected).Returns(true); - _sessionMock.InSequence(sequence).Setup(p => p.ConnectionInfo).Returns(_connectionInfoMock.Object); - _connectionInfoMock.InSequence(sequence).Setup(p => p.Timeout).Returns(_connectionInfoTimeout); - _sessionMock.InSequence(sequence).Setup( - p => p.SendMessage( - It.Is( - m => m.LocalChannelNumber == _remoteChannelNumber - && - m.InitialWindowSize == _localWindowSize - && - m.MaximumPacketSize == _localPacketSize - && - m.RemoteChannelNumber == _localChannelNumber) - )); - _sessionMock.InSequence(sequence).Setup(p => p.IsConnected).Returns(true); - _sessionMock.InSequence(sequence) - .Setup( - p => p.TrySendMessage(It.Is(m => m.LocalChannelNumber == _remoteChannelNumber))) - .Returns(true); - _sessionMock.InSequence(sequence).Setup(p => p.IsConnected).Returns(true); - _sessionMock.InSequence(sequence) - .Setup( - p => p.TrySendMessage(It.Is(m => m.LocalChannelNumber == _remoteChannelNumber))) - .Returns(true); - _sessionMock.InSequence(sequence).Setup(p => p.ConnectionInfo).Returns(_connectionInfoMock.Object); - _connectionInfoMock.InSequence(sequence).Setup(p => p.ChannelCloseTimeout).Returns(_channelCloseTimeout); - _sessionMock.InSequence(sequence) - .Setup(p => p.TryWait(It.IsAny(), _channelCloseTimeout)) - .Callback((waitHandle, channelCloseTimeout) => - { - _sessionMock.Raise( - s => s.ChannelCloseReceived += null, - new MessageEventArgs(new ChannelCloseMessage(_localChannelNumber))); - waitHandle.WaitOne(); - }) - .Returns(WaitResult.Success); + + _ = _sessionMock.InSequence(sequence) + .Setup(p => p.IsConnected) + .Returns(true); + _ = _sessionMock.InSequence(sequence) + .Setup(p => p.ConnectionInfo) + .Returns(_connectionInfoMock.Object); + _ = _connectionInfoMock.InSequence(sequence) + .Setup(p => p.Timeout) + .Returns(_connectionInfoTimeout); + _ = _sessionMock.InSequence(sequence) + .Setup(p => p.SendMessage(It.Is(m => + m.LocalChannelNumber == _remoteChannelNumber && + m.InitialWindowSize == _localWindowSize && + m.MaximumPacketSize == _localPacketSize && + m.RemoteChannelNumber == _localChannelNumber))); + _ = _sessionMock.InSequence(sequence) + .Setup(p => p.IsConnected) + .Returns(true); + _ = _sessionMock.InSequence(sequence) + .Setup(p => p.TrySendMessage(It.Is(m => m.LocalChannelNumber == _remoteChannelNumber))) + .Returns(true); + _ = _sessionMock.InSequence(sequence) + .Setup(p => p.IsConnected) + .Returns(true); + _ = _sessionMock.InSequence(sequence) + .Setup(p => p.TrySendMessage(It.Is(m => m.LocalChannelNumber == _remoteChannelNumber))) + .Returns(true); + _ = _sessionMock.InSequence(sequence) + .Setup(p => p.ConnectionInfo) + .Returns(_connectionInfoMock.Object); + _ = _connectionInfoMock.InSequence(sequence) + .Setup(p => p.ChannelCloseTimeout) + .Returns(_channelCloseTimeout); + _ = _sessionMock.InSequence(sequence) + .Setup(p => p.TryWait(It.IsAny(), _channelCloseTimeout)) + .Callback((waitHandle, channelCloseTimeout) => + { + _sessionMock.Raise( + s => s.ChannelCloseReceived += null, + new MessageEventArgs(new ChannelCloseMessage(_localChannelNumber))); + _ = waitHandle.WaitOne(); + }) + .Returns(WaitResult.Success); _remoteListener = new AsyncSocketListener(_remoteEndpoint); - _remoteListener.Connected += socket => _connectedRegister.Add(socket); - _remoteListener.Disconnected += socket => _disconnectedRegister.Add(socket); + _remoteListener.Connected += _connectedRegister.Add; + _remoteListener.Disconnected += _disconnectedRegister.Add; _remoteListener.Start(); - _channel = new ChannelForwardedTcpip( - _sessionMock.Object, - _localChannelNumber, - _localWindowSize, - _localPacketSize, - _remoteChannelNumber, - _remoteWindowSize, - _remotePacketSize); + _channel = new ChannelForwardedTcpip(_sessionMock.Object, + _localChannelNumber, + _localWindowSize, + _localPacketSize, + _remoteChannelNumber, + _remoteWindowSize, + _remotePacketSize); _channelThread = new Thread(() => { @@ -150,7 +159,7 @@ namespace Renci.SshNet.Tests.Classes.Channels } finally { - _channelBindFinishedWaitHandle.Set(); + _ = _channelBindFinishedWaitHandle.Set(); } }); _channelThread.Start(); @@ -175,7 +184,7 @@ namespace Renci.SshNet.Tests.Classes.Channels [TestMethod] public void BindShouldHaveFinishedWithoutException() { - Assert.IsNull(_channelException, _channelException != null ? _channelException.ToString() : null); + Assert.IsNull(_channelException, _channelException?.ToString()); Assert.IsTrue(_channelBindFinishedWaitHandle.WaitOne(0)); } @@ -197,4 +206,4 @@ namespace Renci.SshNet.Tests.Classes.Channels Assert.IsFalse(_channel.IsOpen); } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.Tests/Classes/Channels/ChannelSessionTest_Dispose_Disposed.cs b/src/Renci.SshNet.Tests/Classes/Channels/ChannelSessionTest_Dispose_Disposed.cs index 379f7e18..07ba0d53 100644 --- a/src/Renci.SshNet.Tests/Classes/Channels/ChannelSessionTest_Dispose_Disposed.cs +++ b/src/Renci.SshNet.Tests/Classes/Channels/ChannelSessionTest_Dispose_Disposed.cs @@ -1,8 +1,11 @@ using System; using System.Collections.Generic; using System.Threading; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; + using Renci.SshNet.Channels; using Renci.SshNet.Common; using Renci.SshNet.Messages.Connection; @@ -144,4 +147,4 @@ namespace Renci.SshNet.Tests.Classes.Channels Assert.IsFalse(_channel.IsOpen); } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.Tests/Classes/Channels/ChannelStub.cs b/src/Renci.SshNet.Tests/Classes/Channels/ChannelStub.cs index ac141b9b..6476ec91 100644 --- a/src/Renci.SshNet.Tests/Classes/Channels/ChannelStub.cs +++ b/src/Renci.SshNet.Tests/Classes/Channels/ChannelStub.cs @@ -54,7 +54,7 @@ namespace Renci.SshNet.Tests.Classes.Channels public void InitializeRemoteChannelInfo(uint remoteChannelNumber, uint remoteWindowSize, uint remotePacketSize) { - base.InitializeRemoteInfo(remoteChannelNumber, remoteWindowSize, remotePacketSize); + InitializeRemoteInfo(remoteChannelNumber, remoteWindowSize, remotePacketSize); } protected override void OnClose() @@ -62,7 +62,9 @@ namespace Renci.SshNet.Tests.Classes.Channels base.OnClose(); if (OnCloseException != null) + { throw OnCloseException; + } } protected override void OnData(byte[] data) @@ -70,7 +72,9 @@ namespace Renci.SshNet.Tests.Classes.Channels base.OnData(data); if (OnDataException != null) + { throw OnDataException; + } } protected override void OnDisconnected() @@ -78,7 +82,9 @@ namespace Renci.SshNet.Tests.Classes.Channels base.OnDisconnected(); if (OnDisconnectedException != null) + { throw OnDisconnectedException; + } } protected override void OnEof() @@ -86,7 +92,9 @@ namespace Renci.SshNet.Tests.Classes.Channels base.OnEof(); if (OnEofException != null) + { throw OnEofException; + } } protected override void OnExtendedData(byte[] data, uint dataTypeCode) @@ -94,7 +102,9 @@ namespace Renci.SshNet.Tests.Classes.Channels base.OnExtendedData(data, dataTypeCode); if (OnExtendedDataException != null) + { throw OnExtendedDataException; + } } protected override void OnErrorOccured(Exception exp) @@ -102,7 +112,9 @@ namespace Renci.SshNet.Tests.Classes.Channels OnErrorOccurredInvocations.Add(exp); if (OnErrorOccurredException != null) + { throw OnErrorOccurredException; + } } protected override void OnFailure() @@ -110,7 +122,9 @@ namespace Renci.SshNet.Tests.Classes.Channels base.OnFailure(); if (OnFailureException != null) + { throw OnFailureException; + } } protected override void OnRequest(RequestInfo info) @@ -118,7 +132,9 @@ namespace Renci.SshNet.Tests.Classes.Channels base.OnRequest(info); if (OnRequestException != null) + { throw OnRequestException; + } } protected override void OnSuccess() @@ -126,7 +142,9 @@ namespace Renci.SshNet.Tests.Classes.Channels base.OnSuccess(); if (OnSuccessException != null) + { throw OnSuccessException; + } } protected override void OnWindowAdjust(uint bytesToAdd) @@ -134,7 +152,9 @@ namespace Renci.SshNet.Tests.Classes.Channels base.OnWindowAdjust(bytesToAdd); if (OnWindowAdjustException != null) + { throw OnWindowAdjustException; + } } } } diff --git a/src/Renci.SshNet.Tests/Classes/Channels/ChannelTest_Dispose_SessionIsConnectedAndChannelIsOpen_EofReceived.cs b/src/Renci.SshNet.Tests/Classes/Channels/ChannelTest_Dispose_SessionIsConnectedAndChannelIsOpen_EofReceived.cs index 0bb9e862..23a00ca8 100644 --- a/src/Renci.SshNet.Tests/Classes/Channels/ChannelTest_Dispose_SessionIsConnectedAndChannelIsOpen_EofReceived.cs +++ b/src/Renci.SshNet.Tests/Classes/Channels/ChannelTest_Dispose_SessionIsConnectedAndChannelIsOpen_EofReceived.cs @@ -94,12 +94,10 @@ namespace Renci.SshNet.Tests.Classes.Channels _channelClosedReceived = null; } - if (_raiseChannelCloseReceivedThread != null && _raiseChannelCloseReceivedThread.IsAlive) + if (_raiseChannelCloseReceivedThread != null) { - if (!_raiseChannelCloseReceivedThread.Join(1000)) - { - _raiseChannelCloseReceivedThread.Abort(); - } + _raiseChannelCloseReceivedThread.Join(); + _raiseChannelCloseReceivedThread = null; } if (_channelClosedEventHandlerCompleted != null) diff --git a/src/Renci.SshNet.Tests/Classes/Channels/ChannelTest_OnSessionChannelCloseReceived_OnClose_Exception.cs b/src/Renci.SshNet.Tests/Classes/Channels/ChannelTest_OnSessionChannelCloseReceived_OnClose_Exception.cs index 102a5769..8a736849 100644 --- a/src/Renci.SshNet.Tests/Classes/Channels/ChannelTest_OnSessionChannelCloseReceived_OnClose_Exception.cs +++ b/src/Renci.SshNet.Tests/Classes/Channels/ChannelTest_OnSessionChannelCloseReceived_OnClose_Exception.cs @@ -1,7 +1,8 @@ using System; using System.Collections.Generic; + using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; + using Renci.SshNet.Common; using Renci.SshNet.Messages.Connection; diff --git a/src/Renci.SshNet.Tests/Classes/Channels/ChannelTest_OnSessionChannelDataReceived_OnData_Exception.cs b/src/Renci.SshNet.Tests/Classes/Channels/ChannelTest_OnSessionChannelDataReceived_OnData_Exception.cs index 3c2884f2..bdb3cadf 100644 --- a/src/Renci.SshNet.Tests/Classes/Channels/ChannelTest_OnSessionChannelDataReceived_OnData_Exception.cs +++ b/src/Renci.SshNet.Tests/Classes/Channels/ChannelTest_OnSessionChannelDataReceived_OnData_Exception.cs @@ -1,7 +1,8 @@ using System; using System.Collections.Generic; + using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; + using Renci.SshNet.Common; using Renci.SshNet.Messages.Connection; @@ -61,4 +62,4 @@ namespace Renci.SshNet.Tests.Classes.Channels Assert.AreSame(_onDataException, _channel.OnErrorOccurredInvocations[0]); } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.Tests/Classes/Channels/ChannelTest_OnSessionChannelEofReceived_OnEof_Exception.cs b/src/Renci.SshNet.Tests/Classes/Channels/ChannelTest_OnSessionChannelEofReceived_OnEof_Exception.cs index ff4a38bb..69a5adb4 100644 --- a/src/Renci.SshNet.Tests/Classes/Channels/ChannelTest_OnSessionChannelEofReceived_OnEof_Exception.cs +++ b/src/Renci.SshNet.Tests/Classes/Channels/ChannelTest_OnSessionChannelEofReceived_OnEof_Exception.cs @@ -1,7 +1,8 @@ using System; using System.Collections.Generic; + using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; + using Renci.SshNet.Common; using Renci.SshNet.Messages.Connection; @@ -69,4 +70,4 @@ namespace Renci.SshNet.Tests.Classes.Channels Assert.AreSame(_onEofException, _channel.OnErrorOccurredInvocations[0]); } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.Tests/Classes/Channels/ChannelTest_OnSessionChannelExtendedDataReceived_OnExtendedData_Exception.cs b/src/Renci.SshNet.Tests/Classes/Channels/ChannelTest_OnSessionChannelExtendedDataReceived_OnExtendedData_Exception.cs index 9f582933..c27f7339 100644 --- a/src/Renci.SshNet.Tests/Classes/Channels/ChannelTest_OnSessionChannelExtendedDataReceived_OnExtendedData_Exception.cs +++ b/src/Renci.SshNet.Tests/Classes/Channels/ChannelTest_OnSessionChannelExtendedDataReceived_OnExtendedData_Exception.cs @@ -1,7 +1,8 @@ using System; using System.Collections.Generic; + using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; + using Renci.SshNet.Common; using Renci.SshNet.Messages.Connection; @@ -61,4 +62,4 @@ namespace Renci.SshNet.Tests.Classes.Channels Assert.AreSame(_onExtendedDataException, _channel.OnErrorOccurredInvocations[0]); } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.Tests/Classes/Channels/ChannelTest_OnSessionChannelFailureReceived_OnFailure_Exception.cs b/src/Renci.SshNet.Tests/Classes/Channels/ChannelTest_OnSessionChannelFailureReceived_OnFailure_Exception.cs index 935048a2..360628ca 100644 --- a/src/Renci.SshNet.Tests/Classes/Channels/ChannelTest_OnSessionChannelFailureReceived_OnFailure_Exception.cs +++ b/src/Renci.SshNet.Tests/Classes/Channels/ChannelTest_OnSessionChannelFailureReceived_OnFailure_Exception.cs @@ -1,7 +1,8 @@ using System; using System.Collections.Generic; + using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; + using Renci.SshNet.Common; using Renci.SshNet.Messages.Connection; @@ -61,4 +62,4 @@ namespace Renci.SshNet.Tests.Classes.Channels Assert.AreSame(_onFailureException, _channel.OnErrorOccurredInvocations[0]); } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.Tests/Classes/Channels/ChannelTest_OnSessionChannelRequestReceived_OnRequest_Exception.cs b/src/Renci.SshNet.Tests/Classes/Channels/ChannelTest_OnSessionChannelRequestReceived_OnRequest_Exception.cs index d5c82801..407480de 100644 --- a/src/Renci.SshNet.Tests/Classes/Channels/ChannelTest_OnSessionChannelRequestReceived_OnRequest_Exception.cs +++ b/src/Renci.SshNet.Tests/Classes/Channels/ChannelTest_OnSessionChannelRequestReceived_OnRequest_Exception.cs @@ -1,7 +1,8 @@ using System; using System.Collections.Generic; + using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; + using Renci.SshNet.Common; using Renci.SshNet.Messages.Connection; @@ -32,8 +33,8 @@ namespace Renci.SshNet.Tests.Classes.Channels protected override void SetupMocks() { - SessionMock.Setup(p => p.ConnectionInfo) - .Returns(new ConnectionInfo("host", "user", new PasswordAuthenticationMethod("user", "password"))); + _ = SessionMock.Setup(p => p.ConnectionInfo) + .Returns(new ConnectionInfo("host", "user", new PasswordAuthenticationMethod("user", "password"))); } protected override void Arrange() @@ -65,4 +66,4 @@ namespace Renci.SshNet.Tests.Classes.Channels Assert.AreSame(_onRequestException, _channel.OnErrorOccurredInvocations[0]); } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.Tests/Classes/Channels/ChannelTest_OnSessionChannelSuccessReceived_OnSuccess_Exception.cs b/src/Renci.SshNet.Tests/Classes/Channels/ChannelTest_OnSessionChannelSuccessReceived_OnSuccess_Exception.cs index 2caa91f1..09a9e1ea 100644 --- a/src/Renci.SshNet.Tests/Classes/Channels/ChannelTest_OnSessionChannelSuccessReceived_OnSuccess_Exception.cs +++ b/src/Renci.SshNet.Tests/Classes/Channels/ChannelTest_OnSessionChannelSuccessReceived_OnSuccess_Exception.cs @@ -1,7 +1,8 @@ using System; using System.Collections.Generic; + using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; + using Renci.SshNet.Common; using Renci.SshNet.Messages.Connection; @@ -61,4 +62,4 @@ namespace Renci.SshNet.Tests.Classes.Channels Assert.AreSame(_onSuccessException, _channel.OnErrorOccurredInvocations[0]); } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.Tests/Classes/Channels/ChannelTest_OnSessionChannelWindowAdjustReceived_OnWindowAdjust_Exception.cs b/src/Renci.SshNet.Tests/Classes/Channels/ChannelTest_OnSessionChannelWindowAdjustReceived_OnWindowAdjust_Exception.cs index ba223ad9..1019204b 100644 --- a/src/Renci.SshNet.Tests/Classes/Channels/ChannelTest_OnSessionChannelWindowAdjustReceived_OnWindowAdjust_Exception.cs +++ b/src/Renci.SshNet.Tests/Classes/Channels/ChannelTest_OnSessionChannelWindowAdjustReceived_OnWindowAdjust_Exception.cs @@ -1,7 +1,8 @@ using System; using System.Collections.Generic; + using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; + using Renci.SshNet.Common; using Renci.SshNet.Messages.Connection; @@ -70,4 +71,4 @@ namespace Renci.SshNet.Tests.Classes.Channels Assert.AreSame(_onWindowAdjustException, _channel.OnErrorOccurredInvocations[0]); } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.Tests/Classes/Channels/ChannelTest_OnSessionDisconnected_OnDisconnected_Exception.cs b/src/Renci.SshNet.Tests/Classes/Channels/ChannelTest_OnSessionDisconnected_OnDisconnected_Exception.cs index 4796c4e6..989ea952 100644 --- a/src/Renci.SshNet.Tests/Classes/Channels/ChannelTest_OnSessionDisconnected_OnDisconnected_Exception.cs +++ b/src/Renci.SshNet.Tests/Classes/Channels/ChannelTest_OnSessionDisconnected_OnDisconnected_Exception.cs @@ -1,7 +1,8 @@ using System; using System.Collections.Generic; + using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; + using Renci.SshNet.Common; namespace Renci.SshNet.Tests.Classes.Channels @@ -59,4 +60,4 @@ namespace Renci.SshNet.Tests.Classes.Channels Assert.AreSame(_onDisconnectedException, _channel.OnErrorOccurredInvocations[0]); } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.Tests/Classes/Channels/ChannelTest_OnSessionDisconnected_SessionIsConnectedAndChannelIsOpen.cs b/src/Renci.SshNet.Tests/Classes/Channels/ChannelTest_OnSessionDisconnected_SessionIsConnectedAndChannelIsOpen.cs index e228c36b..327aa126 100644 --- a/src/Renci.SshNet.Tests/Classes/Channels/ChannelTest_OnSessionDisconnected_SessionIsConnectedAndChannelIsOpen.cs +++ b/src/Renci.SshNet.Tests/Classes/Channels/ChannelTest_OnSessionDisconnected_SessionIsConnectedAndChannelIsOpen.cs @@ -1,7 +1,8 @@ using System; using System.Collections.Generic; + using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; + using Renci.SshNet.Common; namespace Renci.SshNet.Tests.Classes.Channels @@ -35,7 +36,8 @@ namespace Renci.SshNet.Tests.Classes.Channels protected override void SetupMocks() { - SessionMock.Setup(p => p.IsConnected).Returns(true); + _ = SessionMock.Setup(p => p.IsConnected) + .Returns(true); } protected override void Arrange() @@ -72,4 +74,4 @@ namespace Renci.SshNet.Tests.Classes.Channels Assert.AreEqual(0, _channelExceptionRegister.Count); } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.Tests/Classes/Channels/ClientChannelStub.cs b/src/Renci.SshNet.Tests/Classes/Channels/ClientChannelStub.cs index 621c4700..05c52f2a 100644 --- a/src/Renci.SshNet.Tests/Classes/Channels/ClientChannelStub.cs +++ b/src/Renci.SshNet.Tests/Classes/Channels/ClientChannelStub.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; + using Renci.SshNet.Channels; using Renci.SshNet.Messages.Connection; @@ -58,7 +59,7 @@ namespace Renci.SshNet.Tests.Classes.Channels public void InitializeRemoteChannelInfo(uint remoteChannelNumber, uint remoteWindowSize, uint remotePacketSize) { - base.InitializeRemoteInfo(remoteChannelNumber, remoteWindowSize, remotePacketSize); + InitializeRemoteInfo(remoteChannelNumber, remoteWindowSize, remotePacketSize); } protected override void OnClose() @@ -66,7 +67,9 @@ namespace Renci.SshNet.Tests.Classes.Channels base.OnClose(); if (OnCloseException != null) + { throw OnCloseException; + } } protected override void OnData(byte[] data) @@ -74,7 +77,9 @@ namespace Renci.SshNet.Tests.Classes.Channels base.OnData(data); if (OnDataException != null) + { throw OnDataException; + } } protected override void OnDisconnected() @@ -82,7 +87,9 @@ namespace Renci.SshNet.Tests.Classes.Channels base.OnDisconnected(); if (OnDisconnectedException != null) + { throw OnDisconnectedException; + } } protected override void OnEof() @@ -90,7 +97,9 @@ namespace Renci.SshNet.Tests.Classes.Channels base.OnEof(); if (OnEofException != null) + { throw OnEofException; + } } protected override void OnExtendedData(byte[] data, uint dataTypeCode) @@ -98,7 +107,9 @@ namespace Renci.SshNet.Tests.Classes.Channels base.OnExtendedData(data, dataTypeCode); if (OnExtendedDataException != null) + { throw OnExtendedDataException; + } } protected override void OnErrorOccured(Exception exp) @@ -106,7 +117,9 @@ namespace Renci.SshNet.Tests.Classes.Channels OnErrorOccurredInvocations.Add(exp); if (OnErrorOccurredException != null) + { throw OnErrorOccurredException; + } } protected override void OnFailure() @@ -114,7 +127,9 @@ namespace Renci.SshNet.Tests.Classes.Channels base.OnFailure(); if (OnFailureException != null) + { throw OnFailureException; + } } protected override void OnRequest(RequestInfo info) @@ -122,7 +137,9 @@ namespace Renci.SshNet.Tests.Classes.Channels base.OnRequest(info); if (OnRequestException != null) + { throw OnRequestException; + } } protected override void OnSuccess() @@ -130,7 +147,9 @@ namespace Renci.SshNet.Tests.Classes.Channels base.OnSuccess(); if (OnSuccessException != null) + { throw OnSuccessException; + } } protected override void OnWindowAdjust(uint bytesToAdd) @@ -138,7 +157,9 @@ namespace Renci.SshNet.Tests.Classes.Channels base.OnWindowAdjust(bytesToAdd); if (OnWindowAdjustException != null) + { throw OnWindowAdjustException; + } } protected override void OnOpenConfirmation(uint remoteChannelNumber, uint initialWindowSize, uint maximumPacketSize) @@ -146,7 +167,9 @@ namespace Renci.SshNet.Tests.Classes.Channels base.OnOpenConfirmation(remoteChannelNumber, initialWindowSize, maximumPacketSize); if (OnOpenConfirmationException != null) + { throw OnOpenConfirmationException; + } } protected override void OnOpenFailure(uint reasonCode, string description, string language) @@ -154,7 +177,9 @@ namespace Renci.SshNet.Tests.Classes.Channels base.OnOpenFailure(reasonCode, description, language); if (OnOpenFailureException != null) + { throw OnOpenFailureException; + } } } } diff --git a/src/Renci.SshNet.Tests/Classes/CipherInfoTest.cs b/src/Renci.SshNet.Tests/Classes/CipherInfoTest.cs index 4014fc3c..832c92f8 100644 --- a/src/Renci.SshNet.Tests/Classes/CipherInfoTest.cs +++ b/src/Renci.SshNet.Tests/Classes/CipherInfoTest.cs @@ -1,7 +1,8 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; -using Renci.SshNet.Common; + using Renci.SshNet.Security.Cryptography; using Renci.SshNet.Tests.Common; + using System; namespace Renci.SshNet.Tests.Classes @@ -19,10 +20,10 @@ namespace Renci.SshNet.Tests.Classes [Ignore] // placeholder public void CipherInfoConstructorTest() { - int keySize = 0; // TODO: Initialize to an appropriate value + var keySize = 0; // TODO: Initialize to an appropriate value Func cipher = null; // TODO: Initialize to an appropriate value - CipherInfo target = new CipherInfo(keySize, cipher); + var target = new CipherInfo(keySize, cipher); Assert.Inconclusive("TODO: Implement code to verify target"); } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.Tests/Classes/ClientAuthenticationTest.cs b/src/Renci.SshNet.Tests/Classes/ClientAuthenticationTest.cs index a5969d35..e2bb4d8c 100644 --- a/src/Renci.SshNet.Tests/Classes/ClientAuthenticationTest.cs +++ b/src/Renci.SshNet.Tests/Classes/ClientAuthenticationTest.cs @@ -1,6 +1,7 @@ using System; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; +using Renci.SshNet.Tests.Common; namespace Renci.SshNet.Tests.Classes { @@ -28,7 +29,7 @@ namespace Renci.SshNet.Tests.Classes catch (ArgumentOutOfRangeException ex) { Assert.IsNull(ex.InnerException); - Assert.AreEqual(string.Format("Cannot be less than one.{0}Parameter name: {1}", Environment.NewLine, ex.ParamName), ex.Message); + ArgumentExceptionAssert.MessageEquals("Cannot be less than one.", ex); Assert.AreEqual("partialSuccessLimit", ex.ParamName); } } @@ -46,7 +47,7 @@ namespace Renci.SshNet.Tests.Classes catch (ArgumentOutOfRangeException ex) { Assert.IsNull(ex.InnerException); - Assert.AreEqual(string.Format("Cannot be less than one.{0}Parameter name: {1}", Environment.NewLine, ex.ParamName), ex.Message); + ArgumentExceptionAssert.MessageEquals("Cannot be less than one.", ex); Assert.AreEqual("partialSuccessLimit", ex.ParamName); } } diff --git a/src/Renci.SshNet.Tests/Classes/ClientAuthenticationTest_Success_MultiList_PartialSuccessLimitReachedFollowedBySuccessInAlternateBranch.cs b/src/Renci.SshNet.Tests/Classes/ClientAuthenticationTest_Success_MultiList_PartialSuccessLimitReachedFollowedBySuccessInAlternateBranch.cs index 16f08ba9..87787414 100644 --- a/src/Renci.SshNet.Tests/Classes/ClientAuthenticationTest_Success_MultiList_PartialSuccessLimitReachedFollowedBySuccessInAlternateBranch.cs +++ b/src/Renci.SshNet.Tests/Classes/ClientAuthenticationTest_Success_MultiList_PartialSuccessLimitReachedFollowedBySuccessInAlternateBranch.cs @@ -1,7 +1,8 @@ using System.Collections.Generic; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; -using Renci.SshNet.Common; namespace Renci.SshNet.Tests.Classes { diff --git a/src/Renci.SshNet.Tests/Classes/ClientAuthenticationTest_Success_MultiList_PostponePartialAccessAuthenticationMethod.cs b/src/Renci.SshNet.Tests/Classes/ClientAuthenticationTest_Success_MultiList_PostponePartialAccessAuthenticationMethod.cs index dd7c14c3..4a0d5fc2 100644 --- a/src/Renci.SshNet.Tests/Classes/ClientAuthenticationTest_Success_MultiList_PostponePartialAccessAuthenticationMethod.cs +++ b/src/Renci.SshNet.Tests/Classes/ClientAuthenticationTest_Success_MultiList_PostponePartialAccessAuthenticationMethod.cs @@ -1,5 +1,7 @@ using System.Collections.Generic; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; namespace Renci.SshNet.Tests.Classes @@ -19,52 +21,68 @@ namespace Renci.SshNet.Tests.Classes { var seq = new MockSequence(); - SessionMock.InSequence(seq).Setup(p => p.RegisterMessage("SSH_MSG_USERAUTH_FAILURE")); - SessionMock.InSequence(seq).Setup(p => p.RegisterMessage("SSH_MSG_USERAUTH_SUCCESS")); - SessionMock.InSequence(seq).Setup(p => p.RegisterMessage("SSH_MSG_USERAUTH_BANNER")); - - ConnectionInfoMock.InSequence(seq).Setup(p => p.CreateNoneAuthenticationMethod()) - .Returns(NoneAuthenticationMethodMock.Object); - - NoneAuthenticationMethodMock.InSequence(seq) - .Setup(p => p.Authenticate(SessionMock.Object)) - .Returns(AuthenticationResult.Failure); - ConnectionInfoMock.InSequence(seq) - .Setup(p => p.AuthenticationMethods) - .Returns(new List - { - KeyboardInteractiveAuthenticationMethodMock.Object, - PasswordAuthenticationMethodMock.Object, - PublicKeyAuthenticationMethodMock.Object - }); - NoneAuthenticationMethodMock.InSequence(seq).Setup(p => p.AllowedAuthentications).Returns(new[] { "password" }); - KeyboardInteractiveAuthenticationMethodMock.InSequence(seq).Setup(p => p.Name).Returns("keyboard-interactive"); - PasswordAuthenticationMethodMock.InSequence(seq).Setup(p => p.Name).Returns("password"); - PublicKeyAuthenticationMethodMock.InSequence(seq).Setup(p => p.Name).Returns("publickey"); - - PasswordAuthenticationMethodMock.InSequence(seq) + _ = SessionMock.InSequence(seq) + .Setup(p => p.RegisterMessage("SSH_MSG_USERAUTH_FAILURE")); + _ = SessionMock.InSequence(seq) + .Setup(p => p.RegisterMessage("SSH_MSG_USERAUTH_SUCCESS")); + _ = SessionMock.InSequence(seq) + .Setup(p => p.RegisterMessage("SSH_MSG_USERAUTH_BANNER")); + _ = ConnectionInfoMock.InSequence(seq) + .Setup(p => p.CreateNoneAuthenticationMethod()) + .Returns(NoneAuthenticationMethodMock.Object); + _ = NoneAuthenticationMethodMock.InSequence(seq) .Setup(p => p.Authenticate(SessionMock.Object)) - .Returns(AuthenticationResult.PartialSuccess); - PasswordAuthenticationMethodMock.InSequence(seq) + .Returns(AuthenticationResult.Failure); + _ = ConnectionInfoMock.InSequence(seq) + .Setup(p => p.AuthenticationMethods) + .Returns(new List + { + KeyboardInteractiveAuthenticationMethodMock.Object, + PasswordAuthenticationMethodMock.Object, + PublicKeyAuthenticationMethodMock.Object + }); + _ = NoneAuthenticationMethodMock.InSequence(seq) .Setup(p => p.AllowedAuthentications) - .Returns(new[] {"password", "publickey"}); - KeyboardInteractiveAuthenticationMethodMock.InSequence(seq).Setup(p => p.Name).Returns("keyboard-interactive"); - PasswordAuthenticationMethodMock.InSequence(seq).Setup(p => p.Name).Returns("password"); - PublicKeyAuthenticationMethodMock.InSequence(seq).Setup(p => p.Name).Returns("publickey"); - - PublicKeyAuthenticationMethodMock.InSequence(seq) - .Setup(p => p.Authenticate(SessionMock.Object)) - .Returns(AuthenticationResult.Failure); - PublicKeyAuthenticationMethodMock.InSequence(seq) - .Setup(p => p.Name) - .Returns("publickey"); - PasswordAuthenticationMethodMock.InSequence(seq) - .Setup(p => p.Authenticate(SessionMock.Object)) - .Returns(AuthenticationResult.Success); - - SessionMock.InSequence(seq).Setup(p => p.UnRegisterMessage("SSH_MSG_USERAUTH_FAILURE")); - SessionMock.InSequence(seq).Setup(p => p.UnRegisterMessage("SSH_MSG_USERAUTH_SUCCESS")); - SessionMock.InSequence(seq).Setup(p => p.UnRegisterMessage("SSH_MSG_USERAUTH_BANNER")); + .Returns(new[] { "password" }); + _ = KeyboardInteractiveAuthenticationMethodMock.InSequence(seq) + .Setup(p => p.Name) + .Returns("keyboard-interactive"); + _ = PasswordAuthenticationMethodMock.InSequence(seq) + .Setup(p => p.Name) + .Returns("password"); + _ = PublicKeyAuthenticationMethodMock.InSequence(seq) + .Setup(p => p.Name) + .Returns("publickey"); + _ = PasswordAuthenticationMethodMock.InSequence(seq) + .Setup(p => p.Authenticate(SessionMock.Object)) + .Returns(AuthenticationResult.PartialSuccess); + _ = PasswordAuthenticationMethodMock.InSequence(seq) + .Setup(p => p.AllowedAuthentications) + .Returns(new[] {"password", "publickey"}); + _ = KeyboardInteractiveAuthenticationMethodMock.InSequence(seq) + .Setup(p => p.Name) + .Returns("keyboard-interactive"); + _ = PasswordAuthenticationMethodMock.InSequence(seq) + .Setup(p => p.Name) + .Returns("password"); + _ = PublicKeyAuthenticationMethodMock.InSequence(seq) + .Setup(p => p.Name) + .Returns("publickey"); + _ = PublicKeyAuthenticationMethodMock.InSequence(seq) + .Setup(p => p.Authenticate(SessionMock.Object)) + .Returns(AuthenticationResult.Failure); + _ = PublicKeyAuthenticationMethodMock.InSequence(seq) + .Setup(p => p.Name) + .Returns("publickey"); + _ = PasswordAuthenticationMethodMock.InSequence(seq) + .Setup(p => p.Authenticate(SessionMock.Object)) + .Returns(AuthenticationResult.Success); + _ = SessionMock.InSequence(seq) + .Setup(p => p.UnRegisterMessage("SSH_MSG_USERAUTH_FAILURE")); + _ = SessionMock.InSequence(seq) + .Setup(p => p.UnRegisterMessage("SSH_MSG_USERAUTH_SUCCESS")); + _ = SessionMock.InSequence(seq) + .Setup(p => p.UnRegisterMessage("SSH_MSG_USERAUTH_BANNER")); } protected override void Arrange() diff --git a/src/Renci.SshNet.Tests/Classes/CommandAsyncResultTest.cs b/src/Renci.SshNet.Tests/Classes/CommandAsyncResultTest.cs index c04b83d7..c340f40f 100644 --- a/src/Renci.SshNet.Tests/Classes/CommandAsyncResultTest.cs +++ b/src/Renci.SshNet.Tests/Classes/CommandAsyncResultTest.cs @@ -11,7 +11,7 @@ namespace Renci.SshNet.Tests.Classes public void BytesSentTest() { var target = new CommandAsyncResult(); - int expected = new Random().Next(); + var expected = new Random().Next(); target.BytesSent = expected; diff --git a/src/Renci.SshNet.Tests/Classes/Common/ASCIIEncodingTest.cs b/src/Renci.SshNet.Tests/Classes/Common/ASCIIEncodingTest.cs deleted file mode 100644 index 6e614b52..00000000 --- a/src/Renci.SshNet.Tests/Classes/Common/ASCIIEncodingTest.cs +++ /dev/null @@ -1,241 +0,0 @@ -using System; -using System.Diagnostics; -using System.Globalization; -using System.Text; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Renci.SshNet.Common; -using Renci.SshNet.Tests.Common; - -namespace Renci.SshNet.Tests.Classes.Common -{ - [TestClass] - public class ASCIIEncodingTest : TestBase - { - private Random _random; - private Encoding _ascii; - - [TestInitialize] - public void SetUp() - { - _random = new Random(); - _ascii = SshData.Ascii; - } - - [TestMethod] - public void GetByteCount_Chars() - { - var chars = new[] { 'B', 'e', 'l', 'g', 'i', 'u', 'm' }; - - var actual = _ascii.GetByteCount(chars); - - Assert.AreEqual(chars.Length, actual); - } - - [TestMethod] - public void GetBytes_CharArray() - { - var chars = new[] {'B', 'e', 'l', 'g', 'i', 'u', 'm'}; - - var actual = _ascii.GetBytes(chars); - - Assert.IsNotNull(actual); - Assert.AreEqual(7, actual.Length); - Assert.AreEqual(0x42, actual[0]); - Assert.AreEqual(0x65, actual[1]); - Assert.AreEqual(0x6c, actual[2]); - Assert.AreEqual(0x67, actual[3]); - Assert.AreEqual(0x69, actual[4]); - Assert.AreEqual(0x75, actual[5]); - Assert.AreEqual(0x6d ,actual[6]); - } - - [TestMethod] - public void GetCharCount_Bytes() - { - var bytes = new byte[] { 0x42, 0x65, 0x6c, 0x67, 0x69, 0x75, 0x6d }; - - var actual = _ascii.GetCharCount(bytes); - - Assert.AreEqual(bytes.Length, actual); - } - - [TestMethod] - public void GetChars_Bytes() - { - var bytes = new byte[] {0x42, 0x65, 0x6c, 0x67, 0x69, 0x75, 0x6d}; - - var actual = _ascii.GetChars(bytes); - - Assert.AreEqual("Belgium", new string(actual)); - } - - [TestMethod] - public void GetChars_Bytes_DefaultFallback() - { - var bytes = new byte[] { 0x42, 0x65, 0x6c, 0x80, 0x69, 0x75, 0x6d }; - - var actual = _ascii.GetChars(bytes); - - Assert.AreEqual("Bel?ium", new string(actual)); - } - - [TestMethod] - public void GetMaxByteCount_ShouldReturnCharCountPlusOneWhenCharCountIsNonNegative() - { - var charCount = _random.Next(0, 20000); - - var actual = _ascii.GetMaxByteCount(charCount); - - Assert.AreEqual(++charCount, actual); - } - - [TestMethod] - public void GetMaxByteCount_ShouldThrowArgumentOutOfRangeExceptionWhenCharCountIsNegative() - { - var charCount = _random.Next(-5000, -1); - - try - { - var actual = _ascii.GetMaxByteCount(charCount); - Assert.Fail(actual.ToString(CultureInfo.InvariantCulture)); - } - catch (ArgumentOutOfRangeException ex) - { - Assert.IsNull(ex.InnerException); - Assert.AreEqual("charCount", ex.ParamName); - } - } - - [TestMethod] - public void GetMaxCharCount_ShouldReturnByteCountWhenByteCountIsNonNegative() - { - var byteCount = _random.Next(0, 20000); - - var actual = _ascii.GetMaxCharCount(byteCount); - - Assert.AreEqual(byteCount, actual); - } - - [TestMethod] - public void GetMaxCharCount_ShouldThrowArgumentOutOfRangeExceptionWhenByteCountIsNegative() - { - var byteCount = _random.Next(-5000, -1); - - try - { - var actual = _ascii.GetMaxCharCount(byteCount); - Assert.Fail(actual.ToString(CultureInfo.InvariantCulture)); - } - catch (ArgumentOutOfRangeException ex) - { - Assert.IsNull(ex.InnerException); - Assert.AreEqual("byteCount", ex.ParamName); - } - } - - [TestMethod] - public void GetPreamble() - { - var actual = _ascii.GetPreamble(); - - Assert.AreEqual(0, actual.Length); - } - - [TestMethod] - public void IsSingleByte() - { - Assert.IsTrue(_ascii.IsSingleByte); - } - - [TestMethod] - [TestCategory("LongRunning")] - [TestCategory("Performance")] - public void GetBytes_Performance() - { - const string input = "eererzfdfdsfsfsfsqdqseererzfdfdsfsfsfsqdqseererzfdfdsfsfsfsqdqseererzfdfdsfsfsfsqdqseererzfdfdsfsfsfsqdqseererzfdfdsfsfsfsqdqseererzfdfdsfsfsfsqdqseererzfdfdsfsfsfsqdqseererzfdfdsfsfsfsqdqs"; - const int loopCount = 10000000; - var result = new byte[input.Length]; - - var corefxAscii = new System.Text.ASCIIEncoding(); - var sshAscii = _ascii; - - var stopWatch = new Stopwatch(); - - GC.Collect(); - GC.WaitForFullGCComplete(); - - stopWatch.Start(); - - for (var i = 0; i < loopCount; i++) - { - corefxAscii.GetBytes(input, 0, input.Length, result, 0); - } - - stopWatch.Stop(); - - Console.WriteLine(stopWatch.ElapsedMilliseconds); - - stopWatch.Reset(); - - GC.Collect(); - GC.WaitForFullGCComplete(); - - stopWatch.Start(); - - for (var i = 0; i < loopCount; i++) - { - sshAscii.GetBytes(input, 0, input.Length, result, 0); - } - - stopWatch.Stop(); - - Console.WriteLine(stopWatch.ElapsedMilliseconds); - } - - [TestMethod] - [TestCategory("LongRunning")] - [TestCategory("Performance")] - public void GetChars_Performance() - { - var input = new byte[2000]; - new Random().NextBytes(input); - const int loopCount = 100000; - - var corefxAscii = new System.Text.ASCIIEncoding(); - var sshAscii = _ascii; - - var stopWatch = new Stopwatch(); - - GC.Collect(); - GC.WaitForFullGCComplete(); - - stopWatch.Start(); - - for (var i = 0; i < loopCount; i++) - { - var actual = corefxAscii.GetChars(input); - } - - stopWatch.Stop(); - - Console.WriteLine(stopWatch.ElapsedMilliseconds); - - stopWatch.Reset(); - - GC.Collect(); - GC.WaitForFullGCComplete(); - - stopWatch.Start(); - - for (var i = 0; i < loopCount; i++) - { - var actual = sshAscii.GetChars(input); - } - - stopWatch.Stop(); - - Console.WriteLine(stopWatch.ElapsedMilliseconds); - } - - } -} \ No newline at end of file diff --git a/src/Renci.SshNet.Tests/Classes/Common/AsyncResultTest.cs b/src/Renci.SshNet.Tests/Classes/Common/AsyncResultTest.cs index e000fbe6..5a972e69 100644 --- a/src/Renci.SshNet.Tests/Classes/Common/AsyncResultTest.cs +++ b/src/Renci.SshNet.Tests/Classes/Common/AsyncResultTest.cs @@ -1,6 +1,7 @@ using System; -using System.Threading; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Renci.SshNet.Common; using Renci.SshNet.Tests.Common; @@ -19,10 +20,9 @@ namespace Renci.SshNet.Tests.Classes.Common /// public void EndInvokeTest1Helper() { - AsyncResult target = CreateAsyncResult(); // TODO: Initialize to an appropriate value - TResult expected = default(TResult); // TODO: Initialize to an appropriate value - TResult actual; - actual = target.EndInvoke(); + var target = CreateAsyncResult(); // TODO: Initialize to an appropriate value + var expected = default(TResult); // TODO: Initialize to an appropriate value + var actual = target.EndInvoke(); Assert.AreEqual(expected, actual); Assert.Inconclusive("Verify the correctness of this test method."); } @@ -45,9 +45,9 @@ namespace Renci.SshNet.Tests.Classes.Common /// public void SetAsCompletedTest1Helper() { - AsyncResult target = CreateAsyncResult(); // TODO: Initialize to an appropriate value - TResult result = default(TResult); // TODO: Initialize to an appropriate value - bool completedSynchronously = false; // TODO: Initialize to an appropriate value + var target = CreateAsyncResult(); // TODO: Initialize to an appropriate value + TResult result = default; // TODO: Initialize to an appropriate value + var completedSynchronously = false; // TODO: Initialize to an appropriate value target.SetAsCompleted(result, completedSynchronously); Assert.Inconclusive("A method that does not return a value cannot be verified."); } @@ -71,7 +71,7 @@ namespace Renci.SshNet.Tests.Classes.Common [TestMethod] public void EndInvokeTest() { - AsyncResult target = CreateAsyncResult(); // TODO: Initialize to an appropriate value + var target = CreateAsyncResult(); // TODO: Initialize to an appropriate value target.EndInvoke(); Assert.Inconclusive("A method that does not return a value cannot be verified."); } @@ -82,9 +82,9 @@ namespace Renci.SshNet.Tests.Classes.Common [TestMethod] public void SetAsCompletedTest() { - AsyncResult target = CreateAsyncResult(); // TODO: Initialize to an appropriate value + var target = CreateAsyncResult(); // TODO: Initialize to an appropriate value Exception exception = null; // TODO: Initialize to an appropriate value - bool completedSynchronously = false; // TODO: Initialize to an appropriate value + var completedSynchronously = false; // TODO: Initialize to an appropriate value target.SetAsCompleted(exception, completedSynchronously); Assert.Inconclusive("A method that does not return a value cannot be verified."); } @@ -95,9 +95,8 @@ namespace Renci.SshNet.Tests.Classes.Common [TestMethod] public void AsyncStateTest() { - AsyncResult target = CreateAsyncResult(); // TODO: Initialize to an appropriate value - object actual; - actual = target.AsyncState; + var target = CreateAsyncResult(); // TODO: Initialize to an appropriate value + var actual = target.AsyncState; Assert.Inconclusive("Verify the correctness of this test method."); } @@ -107,9 +106,8 @@ namespace Renci.SshNet.Tests.Classes.Common [TestMethod] public void AsyncWaitHandleTest() { - AsyncResult target = CreateAsyncResult(); // TODO: Initialize to an appropriate value - WaitHandle actual; - actual = target.AsyncWaitHandle; + var target = CreateAsyncResult(); // TODO: Initialize to an appropriate value + var actual = target.AsyncWaitHandle; Assert.Inconclusive("Verify the correctness of this test method."); } @@ -119,9 +117,8 @@ namespace Renci.SshNet.Tests.Classes.Common [TestMethod] public void CompletedSynchronouslyTest() { - AsyncResult target = CreateAsyncResult(); // TODO: Initialize to an appropriate value - bool actual; - actual = target.CompletedSynchronously; + var target = CreateAsyncResult(); // TODO: Initialize to an appropriate value + var actual = target.CompletedSynchronously; Assert.Inconclusive("Verify the correctness of this test method."); } @@ -131,9 +128,8 @@ namespace Renci.SshNet.Tests.Classes.Common [TestMethod] public void IsCompletedTest() { - AsyncResult target = CreateAsyncResult(); // TODO: Initialize to an appropriate value - bool actual; - actual = target.IsCompleted; + var target = CreateAsyncResult(); // TODO: Initialize to an appropriate value + var actual = target.IsCompleted; Assert.Inconclusive("Verify the correctness of this test method."); } } diff --git a/src/Renci.SshNet.Tests/Classes/Common/AuthenticationPasswordChangeEventArgsTest.cs b/src/Renci.SshNet.Tests/Classes/Common/AuthenticationPasswordChangeEventArgsTest.cs index e381cf1b..9200d2a4 100644 --- a/src/Renci.SshNet.Tests/Classes/Common/AuthenticationPasswordChangeEventArgsTest.cs +++ b/src/Renci.SshNet.Tests/Classes/Common/AuthenticationPasswordChangeEventArgsTest.cs @@ -19,8 +19,8 @@ namespace Renci.SshNet.Tests.Classes.Common [TestMethod] public void AuthenticationPasswordChangeEventArgsConstructorTest() { - string username = string.Empty; // TODO: Initialize to an appropriate value - AuthenticationPasswordChangeEventArgs target = new AuthenticationPasswordChangeEventArgs(username); + var username = string.Empty; // TODO: Initialize to an appropriate value + var target = new AuthenticationPasswordChangeEventArgs(username); Assert.Inconclusive("TODO: Implement code to verify target"); } @@ -31,7 +31,7 @@ namespace Renci.SshNet.Tests.Classes.Common public void NewPasswordTest() { string username = string.Empty; // TODO: Initialize to an appropriate value - AuthenticationPasswordChangeEventArgs target = new AuthenticationPasswordChangeEventArgs(username); // TODO: Initialize to an appropriate value + var target = new AuthenticationPasswordChangeEventArgs(username); // TODO: Initialize to an appropriate value byte[] expected = null; // TODO: Initialize to an appropriate value byte[] actual; target.NewPassword = expected; diff --git a/src/Renci.SshNet.Tests/Classes/Common/AuthenticationPromptEventArgsTest.cs b/src/Renci.SshNet.Tests/Classes/Common/AuthenticationPromptEventArgsTest.cs index 698d1ab2..47b83126 100644 --- a/src/Renci.SshNet.Tests/Classes/Common/AuthenticationPromptEventArgsTest.cs +++ b/src/Renci.SshNet.Tests/Classes/Common/AuthenticationPromptEventArgsTest.cs @@ -20,11 +20,11 @@ namespace Renci.SshNet.Tests.Classes.Common [TestMethod] public void AuthenticationPromptEventArgsConstructorTest() { - string username = string.Empty; // TODO: Initialize to an appropriate value - string instruction = string.Empty; // TODO: Initialize to an appropriate value - string language = string.Empty; // TODO: Initialize to an appropriate value + var username = string.Empty; // TODO: Initialize to an appropriate value + var instruction = string.Empty; // TODO: Initialize to an appropriate value + var language = string.Empty; // TODO: Initialize to an appropriate value IEnumerable prompts = null; // TODO: Initialize to an appropriate value - AuthenticationPromptEventArgs target = new AuthenticationPromptEventArgs(username, instruction, language, prompts); + var target = new AuthenticationPromptEventArgs(username, instruction, language, prompts); Assert.Inconclusive("TODO: Implement code to verify target"); } } diff --git a/src/Renci.SshNet.Tests/Classes/Common/AuthenticationPromptTest.cs b/src/Renci.SshNet.Tests/Classes/Common/AuthenticationPromptTest.cs index 763f7409..5fbf2582 100644 --- a/src/Renci.SshNet.Tests/Classes/Common/AuthenticationPromptTest.cs +++ b/src/Renci.SshNet.Tests/Classes/Common/AuthenticationPromptTest.cs @@ -17,10 +17,10 @@ namespace Renci.SshNet.Tests.Classes.Common [TestMethod] public void AuthenticationPromptConstructorTest() { - int id = 0; // TODO: Initialize to an appropriate value - bool isEchoed = false; // TODO: Initialize to an appropriate value - string request = string.Empty; // TODO: Initialize to an appropriate value - AuthenticationPrompt target = new AuthenticationPrompt(id, isEchoed, request); + var id = 0; // TODO: Initialize to an appropriate value + var isEchoed = false; // TODO: Initialize to an appropriate value + var request = string.Empty; // TODO: Initialize to an appropriate value + var target = new AuthenticationPrompt(id, isEchoed, request); Assert.Inconclusive("TODO: Implement code to verify target"); } @@ -30,14 +30,13 @@ namespace Renci.SshNet.Tests.Classes.Common [TestMethod] public void ResponseTest() { - int id = 0; // TODO: Initialize to an appropriate value - bool isEchoed = false; // TODO: Initialize to an appropriate value - string request = string.Empty; // TODO: Initialize to an appropriate value - AuthenticationPrompt target = new AuthenticationPrompt(id, isEchoed, request); // TODO: Initialize to an appropriate value - string expected = string.Empty; // TODO: Initialize to an appropriate value - string actual; + var id = 0; // TODO: Initialize to an appropriate value + var isEchoed = false; // TODO: Initialize to an appropriate value + var request = string.Empty; // TODO: Initialize to an appropriate value + var target = new AuthenticationPrompt(id, isEchoed, request); // TODO: Initialize to an appropriate value + var expected = string.Empty; // TODO: Initialize to an appropriate value target.Response = expected; - actual = target.Response; + var actual = target.Response; Assert.AreEqual(expected, actual); Assert.Inconclusive("Verify the correctness of this test method."); } diff --git a/src/Renci.SshNet.Tests/Classes/Common/BigIntegerTest.cs b/src/Renci.SshNet.Tests/Classes/Common/BigIntegerTest.cs index c99fb2a6..af5613e8 100644 --- a/src/Renci.SshNet.Tests/Classes/Common/BigIntegerTest.cs +++ b/src/Renci.SshNet.Tests/Classes/Common/BigIntegerTest.cs @@ -11,7 +11,6 @@ //#define FEATURE_NUMERICS_BIGINTEGER using System; -using System.Diagnostics; using System.Globalization; using System.Threading; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -28,7 +27,7 @@ namespace Renci.SshNet.Tests.Classes.Common [TestClass] public class BigIntegerTest { - private static readonly byte[] huge_a = + private static readonly byte[] Huge_a = { 0x1D, 0x33, 0xFB, 0xFE, 0xB1, 0x2, 0x85, 0x44, 0xCA, 0xDC, 0xFB, 0x70, 0xD, 0x39, 0xB1, 0x47, 0xB6, 0xE6, 0xA2, 0xD1, 0x19, 0x1E, 0x9F, 0xE4, 0x3C, 0x1E, 0x16, 0x56, 0x13, 0x9C, 0x4D, 0xD3, @@ -36,7 +35,7 @@ namespace Renci.SshNet.Tests.Classes.Common 0xF6, 0x8C }; - private static readonly byte[] huge_b = + private static readonly byte[] Huge_b = { 0x96, 0x5, 0xDA, 0xFE, 0x93, 0x17, 0xC1, 0x93, 0xEC, 0x2F, 0x30, 0x2D, 0x8F, 0x28, 0x13, 0x99, 0x70, 0xF4, 0x4C, 0x60, 0xA6, 0x49, 0x24, 0xF9, 0xB3, 0x4A, 0x41, 0x67, 0xDC, 0xDD, 0xB1, @@ -44,7 +43,7 @@ namespace Renci.SshNet.Tests.Classes.Common 0xA8, 0xC8, 0xB0, 0x20, 0x95, 0xE6, 0x4C, 0xE1, 0xE0, 0x4B, 0x49, 0xD5, 0x5A, 0xB7 }; - private static readonly byte[] huge_add = + private static readonly byte[] Huge_add = { 0xB3, 0x38, 0xD5, 0xFD, 0x45, 0x1A, 0x46, 0xD8, 0xB6, 0xC, 0x2C, 0x9E, 0x9C, 0x61, 0xC4, 0xE0, 0x26, 0xDB, 0xEF, 0x31, 0xC0, 0x67, 0xC3, 0xDD, 0xF0, 0x68, 0x57, 0xBD, 0xEF, 0x79, 0xFF, @@ -52,7 +51,7 @@ namespace Renci.SshNet.Tests.Classes.Common 0x16, 0xBF, 0x3D, 0x20, 0x95, 0xE6, 0x4C, 0xE1, 0xE0, 0x4B, 0x49, 0xD5, 0x5A, 0xB7 }; - private static readonly byte[] a_m_b = + private static readonly byte[] A_m_b = { 0x87, 0x2D, 0x21, 0x0, 0x1E, 0xEB, 0xC3, 0xB0, 0xDD, 0xAC, 0xCB, 0x43, 0x7E, 0x10, 0x9E, 0xAE, 0x45, 0xF2, 0x55, 0x71, 0x73, 0xD4, 0x7A, 0xEB, 0x88, 0xD3, 0xD4, 0xEE, 0x36, 0xBE, 0x9B, 0x2D, @@ -60,7 +59,7 @@ namespace Renci.SshNet.Tests.Classes.Common 0x2D, 0xDC, 0xDE, 0x6A, 0x19, 0xB3, 0x1E, 0x1F, 0xB4, 0xB6, 0x2A, 0xA5, 0x48 }; - private static readonly byte[] b_m_a = + private static readonly byte[] B_m_a = { 0x79, 0xD2, 0xDE, 0xFF, 0xE1, 0x14, 0x3C, 0x4F, 0x22, 0x53, 0x34, 0xBC, 0x81, 0xEF, 0x61, 0x51, 0xBA, 0xD, 0xAA, 0x8E, 0x8C, 0x2B, 0x85, 0x14, 0x77, 0x2C, 0x2B, 0x11, 0xC9, 0x41, 0x64, @@ -68,7 +67,7 @@ namespace Renci.SshNet.Tests.Classes.Common 0x3B, 0xD2, 0x23, 0x21, 0x95, 0xE6, 0x4C, 0xE1, 0xE0, 0x4B, 0x49, 0xD5, 0x5A, 0xB7 }; - private static readonly byte[] huge_mul = + private static readonly byte[] Huge_mul = { 0xFE, 0x83, 0xE1, 0x9B, 0x8D, 0x61, 0x40, 0xD1, 0x60, 0x19, 0xBD, 0x38, 0xF0, 0xFF, 0x90, 0xAE, 0xDD, 0xAE, 0x73, 0x2C, 0x20, 0x23, 0xCF, 0x6, 0x7A, 0xB4, 0x1C, 0xE7, 0xD9, 0x64, 0x96, @@ -79,18 +78,18 @@ namespace Renci.SshNet.Tests.Classes.Common 0x57, 0x40, 0x51, 0xB6, 0x5D, 0xC, 0x17, 0xD1, 0x86, 0xE9, 0xA4, 0x20 }; - private static readonly byte[] huge_div = {0x0}; + private static readonly byte[] Huge_div = {0x0}; - private static readonly byte[] huge_rem = + private static readonly byte[] Huge_rem = { 0x1D, 0x33, 0xFB, 0xFE, 0xB1, 0x2, 0x85, 0x44, 0xCA, 0xDC, 0xFB, 0x70, 0xD, 0x39, 0xB1, 0x47, 0xB6, 0xE6, 0xA2, 0xD1, 0x19, 0x1E, 0x9F, 0xE4, 0x3C, 0x1E, 0x16, 0x56, 0x13, 0x9C, 0x4D, 0xD3, 0x5C, 0x74, 0xC9, 0xBD, 0xFA, 0x56, 0x40, 0x58, 0xAC, 0x20, 0x6B, 0x55, 0xA2, 0xD5, 0x41, 0x38, 0xA4, 0x6D, 0xF6, 0x8C }; - private static readonly byte[][] add_a = {new byte[] {1}, new byte[] {0xFF}, huge_a}; - private static readonly byte[][] add_b = {new byte[] {1}, new byte[] {1}, huge_b}; - private static readonly byte[][] add_c = {new byte[] {2}, new byte[] {0}, huge_add}; + private static readonly byte[][] Add_a = { new byte[] { 1 }, new byte[] { 0xFF }, Huge_a }; + private static readonly byte[][] Add_b = { new byte[] { 1 }, new byte[] { 1 }, Huge_b }; + private static readonly byte[][] Add_c = { new byte[] { 2 }, new byte[] { 0 }, Huge_add }; private readonly NumberFormatInfo _nfi = NumberFormatInfo.InvariantInfo; private NumberFormatInfo _nfiUser; @@ -98,22 +97,24 @@ namespace Renci.SshNet.Tests.Classes.Common [TestInitialize] public void SetUpFixture() { - _nfiUser = new NumberFormatInfo(); - _nfiUser.CurrencyDecimalDigits = 3; - _nfiUser.CurrencyDecimalSeparator = ":"; - _nfiUser.CurrencyGroupSeparator = "/"; - _nfiUser.CurrencyGroupSizes = new[] { 2, 1, 0 }; - _nfiUser.CurrencyNegativePattern = 10; // n $- - _nfiUser.CurrencyPositivePattern = 3; // n $ - _nfiUser.CurrencySymbol = "XYZ"; - _nfiUser.PercentDecimalDigits = 1; - _nfiUser.PercentDecimalSeparator = ";"; - _nfiUser.PercentGroupSeparator = "~"; - _nfiUser.PercentGroupSizes = new[] { 1 }; - _nfiUser.PercentNegativePattern = 2; - _nfiUser.PercentPositivePattern = 2; - _nfiUser.PercentSymbol = "%%%"; - _nfiUser.NumberDecimalSeparator = "."; + _nfiUser = new NumberFormatInfo + { + CurrencyDecimalDigits = 3, + CurrencyDecimalSeparator = ":", + CurrencyGroupSeparator = "/", + CurrencyGroupSizes = new[] { 2, 1, 0 }, + CurrencyNegativePattern = 10, // n $- + CurrencyPositivePattern = 3, // n $ + CurrencySymbol = "XYZ", + PercentDecimalDigits = 1, + PercentDecimalSeparator = ";", + PercentGroupSeparator = "~", + PercentGroupSizes = new[] { 1 }, + PercentNegativePattern = 2, + PercentPositivePattern = 2, + PercentSymbol = "%%%", + NumberDecimalSeparator = "." + }; } [TestMethod] @@ -136,10 +137,10 @@ namespace Renci.SshNet.Tests.Classes.Common [TestMethod] public void TestHugeMul() { - var a = new BigInteger(huge_a); - var b = new BigInteger(huge_b); + var a = new BigInteger(Huge_a); + var b = new BigInteger(Huge_b); - Assert.IsTrue(huge_mul.IsEqualTo((a * b).ToByteArray())); + Assert.IsTrue(Huge_mul.IsEqualTo((a * b).ToByteArray())); } [TestMethod] @@ -152,11 +153,13 @@ namespace Renci.SshNet.Tests.Classes.Common for (var j = 0; j < values.Length; ++j) { if (values[j] == 0) + { continue; + } + var a = new BigInteger(values[i]); var b = new BigInteger(values[j]); - BigInteger d; - var c = BigInteger.DivRem(a, b, out d); + var c = BigInteger.DivRem(a, b, out var d); Assert.AreEqual(values[i] / values[j], (long)c, "#a_" + i + "_" + j); Assert.AreEqual(values[i] % values[j], (long)d, "#b_" + i + "_" + j); @@ -167,13 +170,12 @@ namespace Renci.SshNet.Tests.Classes.Common [TestMethod] public void TestHugeDivRem() { - var a = new BigInteger(huge_a); - var b = new BigInteger(huge_b); - BigInteger d; - var c = BigInteger.DivRem(a, b, out d); + var a = new BigInteger(Huge_a); + var b = new BigInteger(Huge_b); + var c = BigInteger.DivRem(a, b, out var d); - AssertEqual(huge_div, c.ToByteArray()); - AssertEqual(huge_rem, d.ToByteArray()); + AssertEqual(Huge_div, c.ToByteArray()); + AssertEqual(Huge_rem, d.ToByteArray()); } [TestMethod] @@ -181,7 +183,7 @@ namespace Renci.SshNet.Tests.Classes.Common { try { - BigInteger.Pow(1, -1); + _ = BigInteger.Pow(1, -1); Assert.Fail("#1"); } catch (ArgumentOutOfRangeException) { } @@ -198,14 +200,14 @@ namespace Renci.SshNet.Tests.Classes.Common { try { - BigInteger.ModPow(1, -1, 5); + _ = BigInteger.ModPow(1, -1, 5); Assert.Fail("#1"); } catch (ArgumentOutOfRangeException) { } try { - BigInteger.ModPow(1, 5, 0); + _ = BigInteger.ModPow(1, 5, 0); Assert.Fail("#2"); } catch (DivideByZeroException) { } @@ -281,8 +283,7 @@ namespace Renci.SshNet.Tests.Classes.Common { try { - BigInteger d; - BigInteger.DivRem(100, 0, out d); + _ = BigInteger.DivRem(100, 0, out var d); Assert.Fail("#1"); } catch (DivideByZeroException) @@ -293,16 +294,16 @@ namespace Renci.SshNet.Tests.Classes.Common [TestMethod] public void TestAdd() { - for (var i = 0; i < add_a.Length; ++i) + for (var i = 0; i < Add_a.Length; ++i) { - var a = new BigInteger(add_a[i]); - var b = new BigInteger(add_b[i]); - var c = new BigInteger(add_c[i]); + var a = new BigInteger(Add_a[i]); + var b = new BigInteger(Add_b[i]); + var c = new BigInteger(Add_c[i]); Assert.AreEqual(c, a + b, "#" + i + "a"); Assert.AreEqual(c, b + a, "#" + i + "b"); Assert.AreEqual(c, BigInteger.Add(a, b), "#" + i + "c"); - AssertEqual(add_c[i], (a + b).ToByteArray()); + AssertEqual(Add_c[i], (a + b).ToByteArray()); } } @@ -326,11 +327,11 @@ namespace Renci.SshNet.Tests.Classes.Common [TestMethod] public void TestHugeSub() { - var a = new BigInteger(huge_a); - var b = new BigInteger(huge_b); + var a = new BigInteger(Huge_a); + var b = new BigInteger(Huge_b); - AssertEqual(a_m_b, (a - b).ToByteArray()); - AssertEqual(b_m_a, (b - a).ToByteArray()); + AssertEqual(A_m_b, (a - b).ToByteArray()); + AssertEqual(B_m_a, (b - a).ToByteArray()); } [TestMethod] @@ -732,7 +733,7 @@ namespace Renci.SshNet.Tests.Classes.Common [TestMethod] public void TestIntCtorProperties() { - BigInteger a = new BigInteger(10); + var a = new BigInteger(10); Assert.IsTrue(a.IsEven, "#1"); Assert.IsFalse(a.IsOne, "#2"); Assert.IsFalse(a.IsPowerOfTwo, "#3"); @@ -784,11 +785,11 @@ namespace Renci.SshNet.Tests.Classes.Common [TestMethod] public void TestToStringFmtProvider() { - NumberFormatInfo info = new NumberFormatInfo - { - NegativeSign = ">", - PositiveSign = "%" - }; + var info = new NumberFormatInfo + { + NegativeSign = ">", + PositiveSign = "%" + }; Assert.AreEqual("10", new BigInteger(10).ToString(info), "#1"); Assert.AreEqual(">10", new BigInteger(-10).ToString(info), "#2"); @@ -801,12 +802,16 @@ namespace Renci.SshNet.Tests.Classes.Common Assert.AreEqual("10", new BigInteger(10).ToString("R", info), "#9"); Assert.AreEqual(">10", new BigInteger(-10).ToString("R", info), "#10"); - info = new NumberFormatInfo(); - info.NegativeSign = "#$%"; + info = new NumberFormatInfo + { + NegativeSign = "#$%" + }; + Assert.AreEqual("#$%10", new BigInteger(-10).ToString(info), "#2"); Assert.AreEqual("#$%10", new BigInteger(-10).ToString(null, info), "#2"); info = new NumberFormatInfo(); + Assert.AreEqual("-10", new BigInteger(-10).ToString(info), "#2"); } @@ -816,21 +821,21 @@ namespace Renci.SshNet.Tests.Classes.Common { try { - int v = (int)new BigInteger(huge_a); + _ = (int) new BigInteger(Huge_a); Assert.Fail("#1"); } catch (OverflowException) { } try { - int v = (int)new BigInteger(1L + int.MaxValue); + _ = (int) new BigInteger(1L + int.MaxValue); Assert.Fail("#2"); } catch (OverflowException) { } try { - int v = (int)new BigInteger(-1L + int.MinValue); + _ = (int) new BigInteger(-1L + int.MinValue); Assert.Fail("#3"); } catch (OverflowException) { } @@ -845,7 +850,7 @@ namespace Renci.SshNet.Tests.Classes.Common { try { - long v = (long)new BigInteger(huge_a); + _ = (long) new BigInteger(Huge_a); Assert.Fail("#1"); } catch (OverflowException) { } @@ -853,7 +858,7 @@ namespace Renci.SshNet.Tests.Classes.Common //long.MaxValue + 1 try { - long v = (long)new BigInteger(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00 }); + _ = (long) new BigInteger(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00 }); Assert.Fail("#2"); } catch (OverflowException) { } @@ -861,7 +866,7 @@ namespace Renci.SshNet.Tests.Classes.Common //TODO long.MinValue - 1 try { - long v = (long)new BigInteger(new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF }); + _ = (long) new BigInteger(new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF }); Assert.Fail("#3"); } catch (OverflowException) { } @@ -931,14 +936,14 @@ namespace Renci.SshNet.Tests.Classes.Common try { - short x = (short)new BigInteger(10000000); + _ = (short) new BigInteger(10000000); Assert.Fail("#3"); } catch (OverflowException) { } try { - short x = (short)new BigInteger(-10000000); + _ = (short) new BigInteger(-10000000); Assert.Fail("#4"); } catch (OverflowException) { } @@ -949,7 +954,7 @@ namespace Renci.SshNet.Tests.Classes.Common { try { - new BigInteger(double.NaN); + _ = new BigInteger(double.NaN); Assert.Fail(); } catch (OverflowException) @@ -962,7 +967,7 @@ namespace Renci.SshNet.Tests.Classes.Common { try { - new BigInteger(double.NegativeInfinity); + _ = new BigInteger(double.NegativeInfinity); Assert.Fail(); } catch (OverflowException) @@ -975,7 +980,7 @@ namespace Renci.SshNet.Tests.Classes.Common { try { - new BigInteger(double.PositiveInfinity); + _ = new BigInteger(double.PositiveInfinity); Assert.Fail(); } catch (OverflowException) @@ -1019,9 +1024,9 @@ namespace Renci.SshNet.Tests.Classes.Common Assert.AreEqual(result4, (double)new BigInteger(new byte[] { 0, 0, 0, 0, 48, 128, 208, 159, 60, 46, 59, 3 }), "#13"); Assert.AreEqual(result5, (double)new BigInteger(new byte[] { 0, 0, 0, 0, 64, 128, 208, 159, 60, 46, 59, 3 }), "#14"); - Assert.AreEqual(BitConverter.Int64BitsToDouble(-2748107935317889142), (double)new BigInteger(huge_a), "#15"); - Assert.AreEqual(BitConverter.Int64BitsToDouble(-2354774254443231289), (double)new BigInteger(huge_b), "#16"); - Assert.AreEqual(BitConverter.Int64BitsToDouble(8737073938546854790), (double)new BigInteger(huge_mul), "#17"); + Assert.AreEqual(BitConverter.Int64BitsToDouble(-2748107935317889142), (double)new BigInteger(Huge_a), "#15"); + Assert.AreEqual(BitConverter.Int64BitsToDouble(-2354774254443231289), (double)new BigInteger(Huge_b), "#16"); + Assert.AreEqual(BitConverter.Int64BitsToDouble(8737073938546854790), (double)new BigInteger(Huge_mul), "#17"); Assert.AreEqual(BitConverter.Int64BitsToDouble(6912920136897069886), (double)(2278888483353476799 * BigInteger.Pow(2, 451)), "#18"); Assert.AreEqual(double.PositiveInfinity, (double)(843942696292817306 * BigInteger.Pow(2, 965)), "#19"); @@ -1070,14 +1075,14 @@ namespace Renci.SshNet.Tests.Classes.Common { try { - BigInteger.Parse(null); + _ = BigInteger.Parse(null); Assert.Fail("#1"); } catch (ArgumentNullException) { } try { - BigInteger.Parse(""); + _ = BigInteger.Parse(""); Assert.Fail("#2"); } catch (FormatException) { } @@ -1085,28 +1090,28 @@ namespace Renci.SshNet.Tests.Classes.Common try { - BigInteger.Parse(" "); + _ = BigInteger.Parse(" "); Assert.Fail("#3"); } catch (FormatException) { } try { - BigInteger.Parse("hh"); + _ = BigInteger.Parse("hh"); Assert.Fail("#4"); } catch (FormatException) { } try { - BigInteger.Parse("-"); + _ = BigInteger.Parse("-"); Assert.Fail("#5"); } catch (FormatException) { } try { - BigInteger.Parse("-+"); + _ = BigInteger.Parse("-+"); Assert.Fail("#6"); } catch (FormatException) { } @@ -1137,7 +1142,7 @@ namespace Renci.SshNet.Tests.Classes.Common try { - BigInteger.Parse("2E3.0", NumberStyles.AllowExponent); // decimal notation for the exponent + _ = BigInteger.Parse("2E3.0", NumberStyles.AllowExponent); // decimal notation for the exponent Assert.Fail("#25"); } catch (FormatException) @@ -1146,7 +1151,7 @@ namespace Renci.SshNet.Tests.Classes.Common try { - Int32.Parse("2" + dsep + "09E1", NumberStyles.AllowDecimalPoint | NumberStyles.AllowExponent); + _ = int.Parse("2" + dsep + "09E1", NumberStyles.AllowDecimalPoint | NumberStyles.AllowExponent); Assert.Fail("#26"); } catch (OverflowException) @@ -1157,9 +1162,7 @@ namespace Renci.SshNet.Tests.Classes.Common [TestMethod] public void TryParse_Value_ShouldReturnFalseWhenValueIsNull() { - BigInteger x; - - var actual = BigInteger.TryParse(null, out x); + var actual = BigInteger.TryParse(null, out var x); Assert.IsFalse(actual); Assert.AreEqual(BigInteger.Zero, x); @@ -1168,9 +1171,7 @@ namespace Renci.SshNet.Tests.Classes.Common [TestMethod] public void TryParse_Value() { - BigInteger x; - - Assert.IsFalse(BigInteger.TryParse("", out x)); + Assert.IsFalse(BigInteger.TryParse("", out var x)); Assert.AreEqual(BigInteger.Zero, x); Assert.IsFalse(BigInteger.TryParse(" ", out x)); @@ -1198,9 +1199,7 @@ namespace Renci.SshNet.Tests.Classes.Common [TestMethod] public void TryParse_ValueAndStyleAndProvider() { - BigInteger x; - - Assert.IsFalse(BigInteger.TryParse("null", NumberStyles.None, null, out x)); + Assert.IsFalse(BigInteger.TryParse("null", NumberStyles.None, null, out var x)); Assert.AreEqual(BigInteger.Zero, x); Assert.IsFalse(BigInteger.TryParse("-10", NumberStyles.None, null, out x)); @@ -1252,9 +1251,7 @@ namespace Renci.SshNet.Tests.Classes.Common [TestMethod] public void TryParse_ValueAndStyleAndProvider_ShouldReturnFalseWhenValueIsNull() { - BigInteger x; - - var actual = BigInteger.TryParse(null, NumberStyles.Any, CultureInfo.InvariantCulture, out x); + var actual = BigInteger.TryParse(null, NumberStyles.Any, CultureInfo.InvariantCulture, out var x); Assert.IsFalse(actual); Assert.AreEqual(BigInteger.Zero, x); @@ -1284,20 +1281,17 @@ namespace Renci.SshNet.Tests.Classes.Common { var old = Thread.CurrentThread.CurrentCulture; var cur = (CultureInfo)old.Clone(); - - var ninfo = new NumberFormatInfo - { - NegativeSign = ">", - PositiveSign = "%" - }; - cur.NumberFormat = ninfo; + cur.NumberFormat = new NumberFormatInfo + { + NegativeSign = ">", + PositiveSign = "%" + }; Thread.CurrentThread.CurrentCulture = cur; try { - BigInteger x; - Assert.IsTrue(BigInteger.TryParse("%11", out x)); + Assert.IsTrue(BigInteger.TryParse("%11", out var x)); Assert.AreEqual(11, (int) x); Assert.IsTrue(BigInteger.TryParse(">11", out x)); @@ -1403,7 +1397,7 @@ namespace Renci.SshNet.Tests.Classes.Common public void RightShiftByInt() { var v = BigInteger.Parse("230794411440927908251127453634"); - v = v * BigInteger.Pow(2, 70); + v *= BigInteger.Pow(2, 70); Assert.AreEqual("272473948255566133040220955950698177909118065442816", (v >> 0).ToString(), "#0"); Assert.AreEqual("136236974127783066520110477975349088954559032721408", (v >> 1).ToString(), "#1"); @@ -1482,12 +1476,15 @@ namespace Renci.SshNet.Tests.Classes.Common { BigInteger b = 0; for (var i = 1; i <= 16; i++) - b = b * 256 + i; + { + b = (b * 256) + i; + } + var p = BigInteger.Pow(2, 32); Assert.AreEqual("1339673755198158349044581307228491536", b.ToString()); Assert.AreEqual("1339673755198158349044581307228491536", ((b << 32) / p).ToString()); - Assert.AreEqual("1339673755198158349044581307228491536", (b * p >> 32).ToString()); + Assert.AreEqual("1339673755198158349044581307228491536", ((b * p) >> 32).ToString()); } [TestMethod] @@ -1561,61 +1558,6 @@ namespace Renci.SshNet.Tests.Classes.Common } } - [TestMethod] - [TestCategory("LongRunning")] - [TestCategory("Performance")] - public void ToArray_Performance() - { - const int loopCount = 100000000; - var bigInteger = new BigInteger(huge_a); - - var stopWatch = new Stopwatch(); - - GC.Collect(); - GC.WaitForFullGCComplete(); - - stopWatch.Start(); - - for (var i = 0; i < loopCount; i++) - { - bigInteger.ToByteArray(); - } - - GC.Collect(); - GC.WaitForFullGCComplete(); - - stopWatch.Stop(); - - Console.WriteLine(stopWatch.ElapsedMilliseconds); - } - - [TestMethod] - [TestCategory("LongRunning")] - [TestCategory("Performance")] - public void Ctor_ByteArray_Performance() - { - const int loopCount = 100000000; - - var stopWatch = new Stopwatch(); - - GC.Collect(); - GC.WaitForFullGCComplete(); - - stopWatch.Start(); - - for (var i = 0; i < loopCount; i++) - { - new BigInteger(huge_a); - } - - GC.Collect(); - GC.WaitForFullGCComplete(); - - stopWatch.Stop(); - - Console.WriteLine(stopWatch.ElapsedMilliseconds); - } - [TestMethod] public void MinusOne() { @@ -1656,8 +1598,7 @@ namespace Renci.SshNet.Tests.Classes.Common public void Random() { var max = "26432534714839143538998938508341375449389492936207135611931371046236385860280414659368073862189301615603000443463893527273703804361856647266218472759410964268979057798543462774631912259980510080575520846081682603934587649566608158932346151315049355432937004801361578344502537300865702429436253728164365180058583916866804254965536833106467354901266304654706123552932560896874808786957654734387252964281680963136344135750381838556467139236094522411774117748615141352874979928570068255439327082539676660277104989857941859821396157749462154431239343148671646397611770487668571604363151098131876313773395912355145689712506"; - BigInteger maxBigInt; - Assert.IsTrue(BigInteger.TryParse(max, NumberStyles.Number, NumberFormatInfo.CurrentInfo, out maxBigInt)); + Assert.IsTrue(BigInteger.TryParse(max, NumberStyles.Number, NumberFormatInfo.CurrentInfo, out var maxBigInt)); var random = BigInteger.One; while (random <= BigInteger.One || random >= maxBigInt) @@ -1670,8 +1611,7 @@ namespace Renci.SshNet.Tests.Classes.Common public void TestClientExhcangeGenerationItem130() { var test = "1090748135619415929450294929359784500348155124953172211774101106966150168922785639028532473848836817769712164169076432969224698752674677662739994265785437233596157045970922338040698100507861033047312331823982435279475700199860971612732540528796554502867919746776983759391475987142521315878719577519148811830879919426939958487087540965716419167467499326156226529675209172277001377591248147563782880558861083327174154014975134893125116015776318890295960698011614157721282527539468816519319333337503114777192360412281721018955834377615480468479252748867320362385355596601795122806756217713579819870634321561907813255153703950795271232652404894983869492174481652303803498881366210508647263668376514131031102336837488999775744046733651827239395353540348414872854639719294694323450186884189822544540647226987292160693184734654941906936646576130260972193280317171696418971553954161446191759093719524951116705577362073481319296041201283516154269044389257727700289684119460283480452306204130024913879981135908026983868205969318167819680850998649694416907952712904962404937775789698917207356355227455066183815847669135530549755439819480321732925869069136146085326382334628745456398071603058051634209386708703306545903199608523824513729625136659128221100967735450519952404248198262813831097374261650380017277916975324134846574681307337017380830353680623216336949471306191686438249305686413380231046096450953594089375540285037292470929395114028305547452584962074309438151825437902976012891749355198678420603722034900311364893046495761404333938686140037848030916292543273684533640032637639100774502371542479302473698388692892420946478947733800387782741417786484770190108867879778991633218628640533982619322466154883011452291890252336487236086654396093853898628805813177559162076363154436494477507871294119841637867701722166609831201845484078070518041336869808398454625586921201308185638888082699408686536045192649569198110353659943111802300636106509865023943661829436426563007917282050894429388841748885398290707743052973605359277515749619730823773215894755121761467887865327707115573804264519206349215850195195364813387526811742474131549802130246506341207020335797706780705406945275438806265978516209706795702579244075380490231741030862614968783306207869687868108423639971983209077624758080499988275591392787267627182442892809646874228263172435642368588260139161962836121481966092745325488641054238839295138992979335446110090325230955276870524611359124918392740353154294858383359"; - BigInteger prime; - BigInteger.TryParse(test, NumberStyles.Number, NumberFormatInfo.CurrentInfo, out prime); + _ = BigInteger.TryParse(test, NumberStyles.Number, NumberFormatInfo.CurrentInfo, out var prime); BigInteger group = 2; var bitLength = prime.BitLength; @@ -1683,15 +1623,15 @@ namespace Renci.SshNet.Tests.Classes.Common //clientExchangeValue = BigInteger.ModPow(group, randomValue, prime); clientExchangeValue = (group ^ randomValue) % prime; - } while (clientExchangeValue < 1 || clientExchangeValue > (prime - 1)); + } + while (clientExchangeValue < 1 || clientExchangeValue > (prime - 1)); } [TestMethod] public void TestClientExhcangeGenerationGroup1() { var test = "00FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF"; - BigInteger prime; - BigInteger.TryParse(test, NumberStyles.AllowHexSpecifier, NumberFormatInfo.CurrentInfo, out prime); + _ = BigInteger.TryParse(test, NumberStyles.AllowHexSpecifier, NumberFormatInfo.CurrentInfo, out var prime); BigInteger group = 2; var bitLength = prime.BitLength; @@ -1703,15 +1643,15 @@ namespace Renci.SshNet.Tests.Classes.Common //clientExchangeValue = BigInteger.ModPow(group, randomValue, prime); clientExchangeValue = (group ^ randomValue) % prime; - } while (clientExchangeValue < 1 || clientExchangeValue > (prime - 1)); + } + while (clientExchangeValue < 1 || clientExchangeValue > (prime - 1)); } [TestMethod] public void TestClientExhcangeGenerationGroup14() { var test = "00FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF"; - BigInteger prime; - BigInteger.TryParse(test, NumberStyles.AllowHexSpecifier, NumberFormatInfo.CurrentInfo, out prime); + _ = BigInteger.TryParse(test, NumberStyles.AllowHexSpecifier, NumberFormatInfo.CurrentInfo, out var prime); BigInteger group = 2; var bitLength = prime.BitLength; @@ -1723,7 +1663,8 @@ namespace Renci.SshNet.Tests.Classes.Common //clientExchangeValue = BigInteger.ModPow(group, randomValue, prime); clientExchangeValue = (group ^ randomValue) % prime; - } while (clientExchangeValue < 1 || clientExchangeValue > (prime - 1)); + } + while (clientExchangeValue < 1 || clientExchangeValue > (prime - 1)); } private static void AssertEqual(byte[] a, byte[] b) diff --git a/src/Renci.SshNet.Tests/Classes/Common/ChannelOpenFailedEventArgsTest.cs b/src/Renci.SshNet.Tests/Classes/Common/ChannelOpenFailedEventArgsTest.cs index 8a8efdf2..2265a5f7 100644 --- a/src/Renci.SshNet.Tests/Classes/Common/ChannelOpenFailedEventArgsTest.cs +++ b/src/Renci.SshNet.Tests/Classes/Common/ChannelOpenFailedEventArgsTest.cs @@ -4,10 +4,10 @@ using Renci.SshNet.Tests.Common; namespace Renci.SshNet.Tests.Classes.Common { /// - /// Provides data for event. + /// Provides data for event. /// [TestClass] public class ChannelOpenFailedEventArgsTest : TestBase { } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.Tests/Classes/Common/CountdownEventTest.cs b/src/Renci.SshNet.Tests/Classes/Common/CountdownEventTest.cs index 742d9e1e..427e07cc 100644 --- a/src/Renci.SshNet.Tests/Classes/Common/CountdownEventTest.cs +++ b/src/Renci.SshNet.Tests/Classes/Common/CountdownEventTest.cs @@ -1,9 +1,8 @@ using System; +using System.Diagnostics; using System.Threading; + using Microsoft.VisualStudio.TestTools.UnitTesting; -#if !FEATURE_THREAD_COUNTDOWNEVENT -using CountdownEvent = Renci.SshNet.Common.CountdownEvent; -#endif namespace Renci.SshNet.Tests.Classes.Common { @@ -73,7 +72,7 @@ namespace Renci.SshNet.Tests.Classes.Common try { - countdownEvent.Signal(); + _ = countdownEvent.Signal(); Assert.Fail(); } catch (InvalidOperationException) @@ -97,7 +96,9 @@ namespace Renci.SshNet.Tests.Classes.Common var expectedSignalCount = _random.Next(5, 20); for (var i = 0; i < (expectedSignalCount - 1); i++) + { countdownEvent.AddCount(); + } var threads = new Thread[expectedSignalCount]; for (var i = 0; i < expectedSignalCount; i++) @@ -105,22 +106,23 @@ namespace Renci.SshNet.Tests.Classes.Common threads[i] = new Thread(() => { Thread.Sleep(sleep); - Interlocked.Increment(ref signalCount); - countdownEvent.Signal(); + _ = Interlocked.Increment(ref signalCount); + _ = countdownEvent.Signal(); }); threads[i].Start(); } - var start = DateTime.Now; + var watch = new Stopwatch(); + watch.Start(); var actual = countdownEvent.Wait(timeout); - var elapsedTime = DateTime.Now - start; + watch.Stop(); Assert.IsTrue(actual); Assert.AreEqual(expectedSignalCount, signalCount); Assert.IsTrue(countdownEvent.IsSet); Assert.IsTrue(countdownEvent.WaitHandle.WaitOne(0)); - Assert.IsTrue(elapsedTime >= sleep); - Assert.IsTrue(elapsedTime <= sleep.Add(TimeSpan.FromMilliseconds(100))); + Assert.IsTrue(watch.Elapsed >= sleep); + Assert.IsTrue(watch.Elapsed <= sleep.Add(TimeSpan.FromMilliseconds(100))); countdownEvent.Dispose(); } @@ -136,30 +138,33 @@ namespace Renci.SshNet.Tests.Classes.Common var expectedSignalCount = _random.Next(5, 20); for (var i = 0; i < (expectedSignalCount - 1); i++) + { countdownEvent.AddCount(); + } var threads = new Thread[expectedSignalCount]; for (var i = 0; i < expectedSignalCount; i++) { threads[i] = new Thread(() => - { - Thread.Sleep(sleep); - Interlocked.Increment(ref signalCount); - countdownEvent.Signal(); - }); + { + Thread.Sleep(sleep); + _ = Interlocked.Increment(ref signalCount); + _ = countdownEvent.Signal(); + }); threads[i].Start(); } - var start = DateTime.Now; + var watch = new Stopwatch(); + watch.Start(); var actual = countdownEvent.Wait(timeout); - var elapsedTime = DateTime.Now - start; + watch.Stop(); Assert.IsTrue(actual); Assert.AreEqual(expectedSignalCount, signalCount); Assert.IsTrue(countdownEvent.IsSet); Assert.IsTrue(countdownEvent.WaitHandle.WaitOne(0)); - Assert.IsTrue(elapsedTime >= sleep); - Assert.IsTrue(elapsedTime <= timeout); + Assert.IsTrue(watch.Elapsed >= sleep); + Assert.IsTrue(watch.Elapsed <= timeout); countdownEvent.Dispose(); } @@ -175,30 +180,32 @@ namespace Renci.SshNet.Tests.Classes.Common var expectedSignalCount = _random.Next(5, 20); for (var i = 0; i < (expectedSignalCount - 1); i++) + { countdownEvent.AddCount(); + } var threads = new Thread[expectedSignalCount]; for (var i = 0; i < expectedSignalCount; i++) { threads[i] = new Thread(() => - { - Thread.Sleep(sleep); - countdownEvent.Signal(); - Interlocked.Increment(ref signalCount); - }); + { + Thread.Sleep(sleep); + _ = countdownEvent.Signal(); + _ = Interlocked.Increment(ref signalCount); + }); threads[i].Start(); } - var start = DateTime.Now; + var watch = new Stopwatch(); + watch.Start(); var actual = countdownEvent.Wait(timeout); - var elapsedTime = DateTime.Now - start; + watch.Stop(); Assert.IsFalse(actual); Assert.IsFalse(countdownEvent.IsSet); Assert.IsFalse(countdownEvent.WaitHandle.WaitOne(0)); - Assert.IsTrue(elapsedTime >= timeout); - countdownEvent.Wait(Session.InfiniteTimeSpan); + _ = countdownEvent.Wait(Session.InfiniteTimeSpan); countdownEvent.Dispose(); } @@ -225,30 +232,33 @@ namespace Renci.SshNet.Tests.Classes.Common var expectedSignalCount = _random.Next(5, 20); for (var i = 0; i < (expectedSignalCount - 1); i++) + { countdownEvent.AddCount(); + } var threads = new Thread[expectedSignalCount]; for (var i = 0; i < expectedSignalCount; i++) { threads[i] = new Thread(() => - { - Thread.Sleep(sleep); - Interlocked.Increment(ref signalCount); - countdownEvent.Signal(); - }); + { + Thread.Sleep(sleep); + _ = Interlocked.Increment(ref signalCount); + _ = countdownEvent.Signal(); + }); threads[i].Start(); } - var start = DateTime.Now; + var watch = new Stopwatch(); + watch.Start(); var actual = countdownEvent.WaitHandle.WaitOne(timeout); - var elapsedTime = DateTime.Now - start; + watch.Stop(); Assert.IsTrue(actual); Assert.AreEqual(expectedSignalCount, signalCount); Assert.IsTrue(countdownEvent.IsSet); Assert.IsTrue(countdownEvent.WaitHandle.WaitOne(0)); - Assert.IsTrue(elapsedTime >= sleep); - Assert.IsTrue(elapsedTime <= sleep.Add(TimeSpan.FromMilliseconds(100))); + Assert.IsTrue(watch.Elapsed >= sleep); + Assert.IsTrue(watch.Elapsed <= sleep.Add(TimeSpan.FromMilliseconds(100))); countdownEvent.Dispose(); } @@ -264,30 +274,33 @@ namespace Renci.SshNet.Tests.Classes.Common var expectedSignalCount = _random.Next(5, 20); for (var i = 0; i < (expectedSignalCount - 1); i++) + { countdownEvent.AddCount(); + } var threads = new Thread[expectedSignalCount]; for (var i = 0; i < expectedSignalCount; i++) { threads[i] = new Thread(() => - { - Thread.Sleep(sleep); - Interlocked.Increment(ref signalCount); - countdownEvent.Signal(); - }); + { + Thread.Sleep(sleep); + _ = Interlocked.Increment(ref signalCount); + _ = countdownEvent.Signal(); + }); threads[i].Start(); } - var start = DateTime.Now; + var watch = new Stopwatch(); + watch.Start(); var actual = countdownEvent.Wait(timeout); - var elapsedTime = DateTime.Now - start; + watch.Stop(); Assert.IsTrue(actual); Assert.AreEqual(expectedSignalCount, signalCount); Assert.IsTrue(countdownEvent.IsSet); Assert.IsTrue(countdownEvent.WaitHandle.WaitOne(0)); - Assert.IsTrue(elapsedTime >= sleep); - Assert.IsTrue(elapsedTime <= timeout); + Assert.IsTrue(watch.Elapsed >= sleep); + Assert.IsTrue(watch.Elapsed <= timeout); countdownEvent.Dispose(); } @@ -303,30 +316,32 @@ namespace Renci.SshNet.Tests.Classes.Common var expectedSignalCount = _random.Next(5, 20); for (var i = 0; i < (expectedSignalCount - 1); i++) + { countdownEvent.AddCount(); + } var threads = new Thread[expectedSignalCount]; for (var i = 0; i < expectedSignalCount; i++) { threads[i] = new Thread(() => - { - Thread.Sleep(sleep); - countdownEvent.Signal(); - Interlocked.Increment(ref signalCount); - }); + { + Thread.Sleep(sleep); + _ = countdownEvent.Signal(); + _ = Interlocked.Increment(ref signalCount); + }); threads[i].Start(); } - var start = DateTime.Now; + var watch = new Stopwatch(); + watch.Start(); var actual = countdownEvent.WaitHandle.WaitOne(timeout); - var elapsedTime = DateTime.Now - start; + watch.Stop(); Assert.IsFalse(actual); Assert.IsFalse(countdownEvent.IsSet); Assert.IsFalse(countdownEvent.WaitHandle.WaitOne(0)); - Assert.IsTrue(elapsedTime >= timeout); - countdownEvent.Wait(Session.InfiniteTimeSpan); + _ = countdownEvent.Wait(Session.InfiniteTimeSpan); countdownEvent.Dispose(); } diff --git a/src/Renci.SshNet.Tests/Classes/Common/CountdownEventTest_Dispose_NotSet.cs b/src/Renci.SshNet.Tests/Classes/Common/CountdownEventTest_Dispose_NotSet.cs index 3d5a5d17..a2badf97 100644 --- a/src/Renci.SshNet.Tests/Classes/Common/CountdownEventTest_Dispose_NotSet.cs +++ b/src/Renci.SshNet.Tests/Classes/Common/CountdownEventTest_Dispose_NotSet.cs @@ -1,10 +1,6 @@ using System; using Microsoft.VisualStudio.TestTools.UnitTesting; -#if !FEATURE_THREAD_COUNTDOWNEVENT -using CountdownEvent = Renci.SshNet.Common.CountdownEvent; -#else using System.Threading; -#endif namespace Renci.SshNet.Tests.Classes.Common { diff --git a/src/Renci.SshNet.Tests/Classes/Common/CountdownEventTest_Dispose_Set.cs b/src/Renci.SshNet.Tests/Classes/Common/CountdownEventTest_Dispose_Set.cs index 60f13c30..df5f5b97 100644 --- a/src/Renci.SshNet.Tests/Classes/Common/CountdownEventTest_Dispose_Set.cs +++ b/src/Renci.SshNet.Tests/Classes/Common/CountdownEventTest_Dispose_Set.cs @@ -1,10 +1,6 @@ using System; using Microsoft.VisualStudio.TestTools.UnitTesting; -#if !FEATURE_THREAD_COUNTDOWNEVENT -using CountdownEvent = Renci.SshNet.Common.CountdownEvent; -#else using System.Threading; -#endif namespace Renci.SshNet.Tests.Classes.Common { diff --git a/src/Renci.SshNet.Tests/Classes/Common/ExtensionsTest_Concat.cs b/src/Renci.SshNet.Tests/Classes/Common/ExtensionsTest_Concat.cs index ab15e010..ad961bfc 100644 --- a/src/Renci.SshNet.Tests/Classes/Common/ExtensionsTest_Concat.cs +++ b/src/Renci.SshNet.Tests/Classes/Common/ExtensionsTest_Concat.cs @@ -1,7 +1,5 @@ using System; -using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using System.Linq; using Microsoft.VisualStudio.TestTools.UnitTesting; using Renci.SshNet.Common; @@ -22,7 +20,7 @@ namespace Renci.SshNet.Tests.Classes.Common [TestMethod] public void ShouldReturnSecondWhenFirstIsEmpty() { - var first = Array.Empty; + var first = Array.Empty(); var second = CreateBuffer(16); var actual = Extensions.Concat(first, second); @@ -47,7 +45,7 @@ namespace Renci.SshNet.Tests.Classes.Common public void ShouldReturnFirstWhenSecondIsEmpty() { var first = CreateBuffer(16); - var second = Array.Empty; + var second = Array.Empty(); var actual = Extensions.Concat(first, second); @@ -96,85 +94,6 @@ namespace Renci.SshNet.Tests.Classes.Common Assert.AreEqual(second[1], actual[5]); } - [TestMethod] - [TestCategory("LongRunning")] - [TestCategory("Performance")] - public void Performance_LargeArray_FirstEmpty() - { - var first = Array.Empty; - var second = CreateBuffer(50000); - const int runs = 10000; - - Performance(first, second, runs); - } - - - [TestMethod] - [TestCategory("LongRunning")] - [TestCategory("Performance")] - public void Performance_LargeArray_SecondEmpty() - { - var first = CreateBuffer(50000); - var second = Array.Empty; - const int runs = 10000; - - Performance(first, second, runs); - } - - [TestMethod] - [TestCategory("LongRunning")] - [TestCategory("Performance")] - public void Performance_LargeArray_BothNotEmpty() - { - var first = CreateBuffer(50000); - var second = CreateBuffer(20000); - const int runs = 10000; - - Performance(first, second, runs); - } - - private static void Performance(byte[] first, byte[] second, int runs) - { - var stopWatch = new Stopwatch(); - - GC.Collect(); - GC.WaitForPendingFinalizers(); - GC.Collect(); - - stopWatch.Start(); - - for (var i = 0; i < runs; i++) - { - var result = Extensions.Concat(first, second); - var resultLength = result.Length; - } - - GC.Collect(); - GC.WaitForPendingFinalizers(); - GC.Collect(); - - stopWatch.Stop(); - - Console.WriteLine(stopWatch.ElapsedMilliseconds); - - stopWatch.Reset(); - stopWatch.Start(); - - for (var i = 0; i < runs; i++) - { - var result = Enumerable.Concat(first, second); - var resultLength = result.ToArray().Length; - } - - GC.Collect(); - GC.WaitForPendingFinalizers(); - GC.Collect(); - - stopWatch.Stop(); - - Console.WriteLine(stopWatch.ElapsedMilliseconds); - } - private byte[] CreateBuffer(int length) { var buffer = new byte[length]; diff --git a/src/Renci.SshNet.Tests/Classes/Common/ExtensionsTest_IsEqualTo_ByteArray.cs b/src/Renci.SshNet.Tests/Classes/Common/ExtensionsTest_IsEqualTo_ByteArray.cs index 42fe8fd5..8d1d1807 100644 --- a/src/Renci.SshNet.Tests/Classes/Common/ExtensionsTest_IsEqualTo_ByteArray.cs +++ b/src/Renci.SshNet.Tests/Classes/Common/ExtensionsTest_IsEqualTo_ByteArray.cs @@ -1,5 +1,4 @@ using System; -using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using Microsoft.VisualStudio.TestTools.UnitTesting; using Renci.SshNet.Common; @@ -26,7 +25,7 @@ namespace Renci.SshNet.Tests.Classes.Common try { - Extensions.IsEqualTo(left, right); + _ = Extensions.IsEqualTo(left, right); Assert.Fail(); } catch (ArgumentNullException ex) @@ -44,7 +43,7 @@ namespace Renci.SshNet.Tests.Classes.Common try { - Extensions.IsEqualTo(left, right); + _ = Extensions.IsEqualTo(left, right); Assert.Fail(); } catch (ArgumentNullException ex) @@ -62,7 +61,7 @@ namespace Renci.SshNet.Tests.Classes.Common try { - Extensions.IsEqualTo(left, right); + _ = Extensions.IsEqualTo(left, right); Assert.Fail(); } catch (ArgumentNullException ex) @@ -96,95 +95,6 @@ namespace Renci.SshNet.Tests.Classes.Common Assert.IsTrue(Extensions.IsEqualTo(left, left)); } - [TestMethod] - [TestCategory("LongRunning")] - [TestCategory("Performance")] - public void Performance_LargeArray_Equal() - { - var buffer = CreateBuffer(50000); - var left = buffer.Concat(new byte[] {0x0a}); - var right = buffer.Concat(new byte[] {0x0a}); - const int runs = 10000; - - Performance(left, right, runs); - } - [TestMethod] - [TestCategory("LongRunning")] - [TestCategory("Performance")] - public void Performance_LargeArray_NotEqual_DifferentLength() - { - var left = CreateBuffer(50000); - var right = left.Concat(new byte[] {0x0a}); - const int runs = 10000; - - Performance(left, right, runs); - } - - [TestMethod] - [TestCategory("LongRunning")] - [TestCategory("Performance")] - public void Performance_LargeArray_NotEqual_SameLength() - { - var buffer = CreateBuffer(50000); - var left = buffer.Concat(new byte[] {0x0a}); - var right = buffer.Concat(new byte[] {0x0b}); - const int runs = 10000; - - Performance(left, right, runs); - } - - [TestMethod] - [TestCategory("LongRunning")] - [TestCategory("Performance")] - public void Performance_LargeArray_Same() - { - var left = CreateBuffer(50000); - var right = left.Concat(new byte[] {0x0a}); - const int runs = 10000; - - Performance(left, right, runs); - } - - private static void Performance(byte[] left, byte[] right, int runs) - { - var stopWatch = new Stopwatch(); - - GC.Collect(); - GC.WaitForPendingFinalizers(); - GC.Collect(); - - stopWatch.Start(); - - for (var i = 0; i < runs; i++) - { - Extensions.IsEqualTo(left, right); - } - - GC.Collect(); - GC.WaitForPendingFinalizers(); - GC.Collect(); - - stopWatch.Stop(); - - Console.WriteLine(stopWatch.ElapsedMilliseconds); - - stopWatch.Reset(); - stopWatch.Start(); - - for (var i = 0; i < runs; i++) - { - var result = System.Linq.Enumerable.SequenceEqual(left, right); - } - - GC.Collect(); - GC.WaitForPendingFinalizers(); - GC.Collect(); - - stopWatch.Stop(); - - Console.WriteLine(stopWatch.ElapsedMilliseconds); - } - private byte[] CreateBuffer(int length) { var buffer = new byte[length]; diff --git a/src/Renci.SshNet.Tests/Classes/Common/ExtensionsTest_Pad.cs b/src/Renci.SshNet.Tests/Classes/Common/ExtensionsTest_Pad.cs index f39c45de..ba41dfb3 100644 --- a/src/Renci.SshNet.Tests/Classes/Common/ExtensionsTest_Pad.cs +++ b/src/Renci.SshNet.Tests/Classes/Common/ExtensionsTest_Pad.cs @@ -1,6 +1,7 @@ -using System; -using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.CodeAnalysis; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Renci.SshNet.Common; namespace Renci.SshNet.Tests.Classes.Common @@ -13,7 +14,7 @@ namespace Renci.SshNet.Tests.Classes.Common public void ShouldReturnNotPadded() { byte[] value = {0x0a, 0x0d}; - byte[] padded = value.Pad(2); + var padded = value.Pad(2); Assert.AreEqual(value, padded); Assert.AreEqual(value.Length, padded.Length); } @@ -22,7 +23,7 @@ namespace Renci.SshNet.Tests.Classes.Common public void ShouldReturnPadded() { byte[] value = { 0x0a, 0x0d }; - byte[] padded = value.Pad(3); + var padded = value.Pad(3); Assert.AreEqual(value.Length + 1, padded.Length); Assert.AreEqual(0x00, padded[0]); Assert.AreEqual(0x0a, padded[1]); diff --git a/src/Renci.SshNet.Tests/Classes/Common/ExtensionsTest_Take_Count.cs b/src/Renci.SshNet.Tests/Classes/Common/ExtensionsTest_Take_Count.cs index 3d734da1..05a09061 100644 --- a/src/Renci.SshNet.Tests/Classes/Common/ExtensionsTest_Take_Count.cs +++ b/src/Renci.SshNet.Tests/Classes/Common/ExtensionsTest_Take_Count.cs @@ -1,7 +1,5 @@ using System; -using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using System.Linq; using Microsoft.VisualStudio.TestTools.UnitTesting; using Renci.SshNet.Common; @@ -94,95 +92,6 @@ namespace Renci.SshNet.Tests.Classes.Common } } - [TestMethod] - [TestCategory("LongRunning")] - [TestCategory("Performance")] - public void Performance_LargeArray_All() - { - var value = CreateBuffer(50000); - var count = value.Length; - const int runs = 10000; - - Performance(value, count, runs); - } - - [TestMethod] - [TestCategory("LongRunning")] - [TestCategory("Performance")] - public void Performance_LargeArray_LargeCount() - { - var value = CreateBuffer(50000); - const int count = 40000; - const int runs = 1000000; - - Performance(value, count, runs); - } - - [TestMethod] - [TestCategory("LongRunning")] - [TestCategory("Performance")] - public void Performance_LargeArray_SmallCount() - { - var value = CreateBuffer(50000); - const int count = 50; - const int runs = 1000000; - - Performance(value, count, runs); - } - - [TestMethod] - [TestCategory("Performance")] - public void Performance_LargeArray_ZeroCount() - { - var value = CreateBuffer(50000); - const int count = 0; - const int runs = 1000000; - - Performance(value, count, runs); - } - - private static void Performance(byte[] value, int count, int runs) - { - var stopWatch = new Stopwatch(); - - GC.Collect(); - GC.WaitForPendingFinalizers(); - GC.Collect(); - - stopWatch.Start(); - - for (var i = 0; i < runs; i++) - { - var result = Extensions.Take(value, count); - var resultLength = result.Length; - } - - GC.Collect(); - GC.WaitForPendingFinalizers(); - GC.Collect(); - - stopWatch.Stop(); - - Console.WriteLine(stopWatch.ElapsedMilliseconds); - - stopWatch.Reset(); - stopWatch.Start(); - - for (var i = 0; i < runs; i++) - { - var result = Enumerable.Take(value, count); - var resultLength = result.ToArray().Length; - } - - GC.Collect(); - GC.WaitForPendingFinalizers(); - GC.Collect(); - - stopWatch.Stop(); - - Console.WriteLine(stopWatch.ElapsedMilliseconds); - } - private byte[] CreateBuffer(int length) { var buffer = new byte[length]; diff --git a/src/Renci.SshNet.Tests/Classes/Common/ExtensionsTest_ToBigInteger2.cs b/src/Renci.SshNet.Tests/Classes/Common/ExtensionsTest_ToBigInteger2.cs index 1299f17a..f9299165 100644 --- a/src/Renci.SshNet.Tests/Classes/Common/ExtensionsTest_ToBigInteger2.cs +++ b/src/Renci.SshNet.Tests/Classes/Common/ExtensionsTest_ToBigInteger2.cs @@ -1,12 +1,10 @@ -using System; -using System.Diagnostics.CodeAnalysis; -using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.VisualStudio.TestTools.UnitTesting; + using Renci.SshNet.Common; namespace Renci.SshNet.Tests.Classes.Common { [TestClass] - [SuppressMessage("ReSharper", "InvokeAsExtensionMethod")] public class ExtensionsTest_ToBigInteger2 { [TestMethod] diff --git a/src/Renci.SshNet.Tests/Classes/Common/HostKeyEventArgsTest.cs b/src/Renci.SshNet.Tests/Classes/Common/HostKeyEventArgsTest.cs index 93055c6e..39ff85d7 100644 --- a/src/Renci.SshNet.Tests/Classes/Common/HostKeyEventArgsTest.cs +++ b/src/Renci.SshNet.Tests/Classes/Common/HostKeyEventArgsTest.cs @@ -2,6 +2,8 @@ using Renci.SshNet.Common; using Renci.SshNet.Security; using Renci.SshNet.Tests.Common; +using System.Linq; +using System.Reflection; namespace Renci.SshNet.Tests.Classes.Common { @@ -10,7 +12,6 @@ namespace Renci.SshNet.Tests.Classes.Common ///to contain all HostKeyEventArgsTest Unit Tests /// [TestClass] - [Ignore] // placeholder for actual test public class HostKeyEventArgsTest : TestBase { /// @@ -19,9 +20,54 @@ namespace Renci.SshNet.Tests.Classes.Common [TestMethod] public void HostKeyEventArgsConstructorTest() { - KeyHostAlgorithm host = null; // TODO: Initialize to an appropriate value - HostKeyEventArgs target = new HostKeyEventArgs(host); - Assert.Inconclusive("TODO: Implement code to verify target"); + HostKeyEventArgs target = new HostKeyEventArgs(GetKeyHostAlgorithm()); + Assert.IsTrue(target.CanTrust); + Assert.IsTrue(new byte[] { + 0x00, 0x00, 0x00, 0x07, 0x73, 0x73, 0x68, 0x2d, 0x72, 0x73, 0x61, 0x00, 0x00, 0x00, 0x01, 0x23, + 0x00, 0x00, 0x01, 0x01, 0x00, 0xb9, 0x3b, 0x57, 0x9f, 0xe0, 0x5a, 0xb5, 0x7d, 0x68, 0x26, 0xeb, + 0xe1, 0xa9, 0xf2, 0x59, 0xc3, 0x98, 0xdc, 0xfe, 0x97, 0x08, 0xc4, 0x95, 0x0f, 0x9a, 0xea, 0x05, + 0x08, 0x7d, 0xfe, 0x6d, 0x77, 0xca, 0x04, 0x9f, 0xfd, 0xe2, 0x2c, 0x4d, 0x11, 0x3c, 0xd9, 0x05, + 0xab, 0x32, 0xbd, 0x3f, 0xe8, 0xcd, 0xba, 0x00, 0x6c, 0x21, 0xb7, 0xa9, 0xc2, 0x4e, 0x63, 0x17, + 0xf6, 0x04, 0x47, 0x93, 0x00, 0x85, 0xde, 0xd6, 0x32, 0xc0, 0xa1, 0x37, 0x75, 0x18, 0xa0, 0xb0, + 0x32, 0xf6, 0x4e, 0xca, 0x39, 0xec, 0x3c, 0xdf, 0x79, 0xfe, 0x50, 0xa1, 0xc1, 0xf7, 0x67, 0x05, + 0xb3, 0x33, 0xa5, 0x96, 0x13, 0x19, 0xfa, 0x14, 0xca, 0x55, 0xe6, 0x7b, 0xf9, 0xb3, 0x8e, 0x32, + 0xee, 0xfc, 0x9d, 0x2a, 0x5e, 0x04, 0x79, 0x97, 0x29, 0x3d, 0x1c, 0x54, 0xfe, 0xc7, 0x96, 0x04, + 0xb5, 0x19, 0x7c, 0x55, 0x21, 0xe2, 0x0e, 0x42, 0xca, 0x4d, 0x9d, 0xfb, 0x77, 0x08, 0x6c, 0xaa, + 0x07, 0x2c, 0xf8, 0xf9, 0x1f, 0xbd, 0x83, 0x14, 0x2b, 0xe0, 0xbc, 0x7a, 0xf9, 0xdf, 0x13, 0x4b, + 0x60, 0x5a, 0x02, 0x99, 0x93, 0x41, 0x1a, 0xb6, 0x5f, 0x3b, 0x9c, 0xb5, 0xb2, 0x55, 0x70, 0x78, + 0x2f, 0x38, 0x52, 0x0e, 0xd1, 0x8a, 0x2c, 0x23, 0xc0, 0x3a, 0x0a, 0xd7, 0xed, 0xf6, 0x1f, 0xa6, + 0x50, 0xf0, 0x27, 0x65, 0x8a, 0xd4, 0xde, 0xa7, 0x1b, 0x41, 0x67, 0xc5, 0x6d, 0x47, 0x84, 0x37, + 0x92, 0x2b, 0xb7, 0xb6, 0x4d, 0xb0, 0x1a, 0xda, 0xf6, 0x50, 0x82, 0xf1, 0x57, 0x31, 0x69, 0xce, + 0xe0, 0xef, 0xcd, 0x64, 0xaa, 0x78, 0x08, 0xea, 0x4e, 0x45, 0xec, 0xa5, 0x89, 0x68, 0x5d, 0xb4, + 0xa0, 0x23, 0xaf, 0xff, 0x9c, 0x0f, 0x8c, 0x83, 0x7c, 0xf8, 0xe1, 0x8e, 0x32, 0x8e, 0x61, 0xfc, + 0x5b, 0xbd, 0xd4, 0x46, 0xe1 + }.SequenceEqual(target.HostKey)); + Assert.AreEqual("rsa-sha2-512", target.HostKeyName); + Assert.AreEqual(2048, target.KeyLength); + } + + /// + ///A test for MD5 calculation in HostKeyEventArgs Constructor + /// + [TestMethod] + public void HostKeyEventArgsConstructorTest_VerifyMD5() + { + HostKeyEventArgs target = new HostKeyEventArgs(GetKeyHostAlgorithm()); + Assert.IsTrue(new byte[] { + 0x92, 0xea, 0x54, 0xa1, 0x01, 0xf9, 0x95, 0x9c, 0x71, 0xd9, 0xbb, 0x51, 0xb2, 0x55, 0xf8, 0xd9 + }.SequenceEqual(target.FingerPrint)); + Assert.AreEqual("92:ea:54:a1:01:f9:95:9c:71:d9:bb:51:b2:55:f8:d9", target.FingerPrintMD5); + + } + + /// + ///A test for SHA256 calculation in HostKeyEventArgs Constructor + /// + [TestMethod] + public void HostKeyEventArgsConstructorTest_VerifySHA256() + { + HostKeyEventArgs target = new HostKeyEventArgs(GetKeyHostAlgorithm()); + Assert.AreEqual("93LkmoWksp9ytNVZIPXi9KJU1uvlC9clZ/CkUHf6uEE", target.FingerPrintSHA256); } /// @@ -30,14 +76,24 @@ namespace Renci.SshNet.Tests.Classes.Common [TestMethod] public void CanTrustTest() { - KeyHostAlgorithm host = null; // TODO: Initialize to an appropriate value - HostKeyEventArgs target = new HostKeyEventArgs(host); // TODO: Initialize to an appropriate value - bool expected = false; // TODO: Initialize to an appropriate value + HostKeyEventArgs target = new HostKeyEventArgs(GetKeyHostAlgorithm()); + bool expected = false; bool actual; target.CanTrust = expected; actual = target.CanTrust; Assert.AreEqual(expected, actual); - Assert.Inconclusive("Verify the correctness of this test method."); } + + private static KeyHostAlgorithm GetKeyHostAlgorithm() + { + var executingAssembly = Assembly.GetExecutingAssembly(); + + using (var s = executingAssembly.GetManifestResourceStream(string.Format("Renci.SshNet.Tests.Data.{0}", "Key.RSA.txt"))) + { + var privateKey = new PrivateKeyFile(s); + return (KeyHostAlgorithm)privateKey.HostKeyAlgorithms.First(); + } + } + } } diff --git a/src/Renci.SshNet.Tests/Classes/Common/ObjectIdentifierTest.cs b/src/Renci.SshNet.Tests/Classes/Common/ObjectIdentifierTest.cs index 5bfbfc29..60815942 100644 --- a/src/Renci.SshNet.Tests/Classes/Common/ObjectIdentifierTest.cs +++ b/src/Renci.SshNet.Tests/Classes/Common/ObjectIdentifierTest.cs @@ -1,26 +1,37 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; + +using Microsoft.VisualStudio.TestTools.UnitTesting; using Renci.SshNet.Common; using Renci.SshNet.Tests.Common; namespace Renci.SshNet.Tests.Classes.Common -{ - /// - ///This is a test class for ObjectIdentifierTest and is intended - ///to contain all ObjectIdentifierTest Unit Tests - /// +{ [TestClass] - [Ignore] // placeholder for actual test public class ObjectIdentifierTest : TestBase { - /// - ///A test for ObjectIdentifier Constructor - /// [TestMethod] - public void ObjectIdentifierConstructorTest() + public void Constructor_IdentifiersIsNull() { - ulong[] identifiers = null; // TODO: Initialize to an appropriate value - ObjectIdentifier target = new ObjectIdentifier(identifiers); - Assert.Inconclusive("TODO: Implement code to verify target"); + const ulong[] identifiers = null; + + var actualException = Assert.ThrowsException(() => new ObjectIdentifier(identifiers)); + + Assert.AreEqual(typeof(ArgumentNullException), actualException.GetType()); + Assert.IsNull(actualException.InnerException); + Assert.AreEqual(nameof(identifiers), actualException.ParamName); + } + + [TestMethod] + public void Constructor_LengthOfIdentifiersIsLessThanTwo() + { + var identifiers = new[] { 5UL }; + + var actualException = Assert.ThrowsException(() => new ObjectIdentifier(identifiers)); + + Assert.AreEqual(typeof(ArgumentException), actualException.GetType()); + Assert.IsNull(actualException.InnerException); + ArgumentExceptionAssert.MessageEquals("Must contain at least two elements.", actualException); + Assert.AreEqual(nameof(identifiers), actualException.ParamName); } } } diff --git a/src/Renci.SshNet.Tests/Classes/Common/PacketDumpTest.cs b/src/Renci.SshNet.Tests/Classes/Common/PacketDumpTest.cs index 00401915..13c649bd 100644 --- a/src/Renci.SshNet.Tests/Classes/Common/PacketDumpTest.cs +++ b/src/Renci.SshNet.Tests/Classes/Common/PacketDumpTest.cs @@ -1,6 +1,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Renci.SshNet.Common; using System; +using Renci.SshNet.Tests.Common; namespace Renci.SshNet.Tests.Classes.Common { @@ -14,7 +15,7 @@ namespace Renci.SshNet.Tests.Classes.Common try { - PacketDump.Create(data, 0); + _ = PacketDump.Create(data, 0); Assert.Fail(); } catch (ArgumentNullException ex) @@ -31,13 +32,15 @@ namespace Renci.SshNet.Tests.Classes.Common try { - PacketDump.Create(data, -1); + _ =PacketDump.Create(data, -1); Assert.Fail(); } catch (ArgumentOutOfRangeException ex) { Assert.IsNull(ex.InnerException); - Assert.AreEqual(string.Format("Cannot be less than zero.{0}Parameter name: {1}", Environment.NewLine, ex.ParamName), ex.Message); + + ArgumentExceptionAssert.MessageEquals("Cannot be less than zero.", ex); + Assert.AreEqual("indentLevel", ex.ParamName); } } diff --git a/src/Renci.SshNet.Tests/Classes/Common/PipeStreamTest.cs b/src/Renci.SshNet.Tests/Classes/Common/PipeStreamTest.cs index 4c11b432..6d56807f 100644 --- a/src/Renci.SshNet.Tests/Classes/Common/PipeStreamTest.cs +++ b/src/Renci.SshNet.Tests/Classes/Common/PipeStreamTest.cs @@ -1,7 +1,9 @@ using System; using System.IO; using System.Threading; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Renci.SshNet.Common; using Renci.SshNet.Tests.Common; @@ -25,7 +27,7 @@ namespace Renci.SshNet.Tests.Classes.Common Assert.AreEqual(stream.Length, testBuffer.Length); - stream.Read(outputBuffer, 0, outputBuffer.Length); + _ = stream.Read(outputBuffer, 0, outputBuffer.Length); Assert.AreEqual(stream.Length, 0); @@ -44,9 +46,9 @@ namespace Renci.SshNet.Tests.Classes.Common { stream.Write(testBuffer, 0, testBuffer.Length); Assert.AreEqual(stream.Length, testBuffer.Length); - stream.ReadByte(); + _ = stream.ReadByte(); Assert.AreEqual(stream.Length, testBuffer.Length - 1); - stream.ReadByte(); + _ = stream.ReadByte(); Assert.AreEqual(stream.Length, testBuffer.Length - 2); } } @@ -92,7 +94,7 @@ namespace Renci.SshNet.Tests.Classes.Common try { - target.Seek(offset, origin); + _ = target.Seek(offset, origin); Assert.Fail(); } catch (NotSupportedException) @@ -169,9 +171,9 @@ namespace Renci.SshNet.Tests.Classes.Common Assert.AreEqual(2L, target.Length); target.WriteByte(0x0a); Assert.AreEqual(3L, target.Length); - target.Read(new byte[2], 0, 2); + _ = target.Read(new byte[2], 0, 2); Assert.AreEqual(1L, target.Length); - target.ReadByte(); + _ = target.ReadByte(); Assert.AreEqual(0L, target.Length); } @@ -195,7 +197,7 @@ namespace Renci.SshNet.Tests.Classes.Common Assert.AreEqual(0, target.Position); target.WriteByte(0x0a); Assert.AreEqual(0, target.Position); - target.ReadByte(); + _ = target.ReadByte(); Assert.AreEqual(0, target.Position); } @@ -214,4 +216,4 @@ namespace Renci.SshNet.Tests.Classes.Common } } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.Tests/Classes/Common/PipeStream_Close_BlockingRead.cs b/src/Renci.SshNet.Tests/Classes/Common/PipeStream_Close_BlockingRead.cs index fb010c14..ed25b260 100644 --- a/src/Renci.SshNet.Tests/Classes/Common/PipeStream_Close_BlockingRead.cs +++ b/src/Renci.SshNet.Tests/Classes/Common/PipeStream_Close_BlockingRead.cs @@ -34,7 +34,7 @@ namespace Renci.SshNet.Tests.Classes.Common _pipeStream.Close(); // give async read time to complete - _readThread.Join(100); + _ = _readThread.Join(100); } [TestMethod] diff --git a/src/Renci.SshNet.Tests/Classes/Common/PipeStream_Close_BlockingWrite.cs b/src/Renci.SshNet.Tests/Classes/Common/PipeStream_Close_BlockingWrite.cs index 1fbd2d15..eb314d4d 100644 --- a/src/Renci.SshNet.Tests/Classes/Common/PipeStream_Close_BlockingWrite.cs +++ b/src/Renci.SshNet.Tests/Classes/Common/PipeStream_Close_BlockingWrite.cs @@ -32,7 +32,6 @@ namespace Renci.SshNet.Tests.Classes.Common catch (Exception ex) { _writeException = ex; - throw; } }); _writehread.Start(); @@ -46,7 +45,7 @@ namespace Renci.SshNet.Tests.Classes.Common _pipeStream.Close(); // give write time to complete - _writehread.Join(100); + _ = _writehread.Join(100); } [TestMethod] diff --git a/src/Renci.SshNet.Tests/Classes/Common/PipeStream_Flush_BytesRemainingAfterRead.cs b/src/Renci.SshNet.Tests/Classes/Common/PipeStream_Flush_BytesRemainingAfterRead.cs index 3af0a3de..415466af 100644 --- a/src/Renci.SshNet.Tests/Classes/Common/PipeStream_Flush_BytesRemainingAfterRead.cs +++ b/src/Renci.SshNet.Tests/Classes/Common/PipeStream_Flush_BytesRemainingAfterRead.cs @@ -30,7 +30,7 @@ namespace Renci.SshNet.Tests.Classes.Common _readThread.Start(); // ensure we've started reading - _readThread.Join(50); + _ = _readThread.Join(50); } protected override void Act() @@ -38,7 +38,7 @@ namespace Renci.SshNet.Tests.Classes.Common _pipeStream.Flush(); // give async read time to complete - _readThread.Join(100); + _ = _readThread.Join(100); } [TestMethod] @@ -88,7 +88,7 @@ namespace Renci.SshNet.Tests.Classes.Common Assert.AreEqual(0, buffer[2]); Assert.AreEqual(0, buffer[3]); } - +#if NETFRAMEWORK [TestMethod] public void WriteCausesSubsequentReadToBlockUntilRequestedNumberOfBytesAreAvailable() { @@ -104,7 +104,10 @@ namespace Renci.SshNet.Tests.Classes.Common readThread.Start(); Assert.IsFalse(readThread.Join(500)); + + // Thread Abort method is obsolete: https://learn.microsoft.com/en-us/dotnet/core/compatibility/core-libraries/5.0/thread-abort-obsolete readThread.Abort(); + Assert.AreEqual(int.MaxValue, bytesRead); Assert.AreEqual(0, buffer[0]); @@ -112,5 +115,6 @@ namespace Renci.SshNet.Tests.Classes.Common Assert.AreEqual(0, buffer[2]); Assert.AreEqual(0, buffer[3]); } +#endif } } diff --git a/src/Renci.SshNet.Tests/Classes/Common/PipeStream_Flush_NoBytesRemainingAfterRead.cs b/src/Renci.SshNet.Tests/Classes/Common/PipeStream_Flush_NoBytesRemainingAfterRead.cs index 44c7f3f7..f306edf1 100644 --- a/src/Renci.SshNet.Tests/Classes/Common/PipeStream_Flush_NoBytesRemainingAfterRead.cs +++ b/src/Renci.SshNet.Tests/Classes/Common/PipeStream_Flush_NoBytesRemainingAfterRead.cs @@ -34,7 +34,7 @@ namespace Renci.SshNet.Tests.Classes.Common _pipeStream.Flush(); // give async read time to complete - _readThread.Join(100); + _ = _readThread.Join(100); } [TestMethod] diff --git a/src/Renci.SshNet.Tests/Classes/Common/PortForwardEventArgsTest.cs b/src/Renci.SshNet.Tests/Classes/Common/PortForwardEventArgsTest.cs index d0b9860c..c1f41dd7 100644 --- a/src/Renci.SshNet.Tests/Classes/Common/PortForwardEventArgsTest.cs +++ b/src/Renci.SshNet.Tests/Classes/Common/PortForwardEventArgsTest.cs @@ -18,7 +18,7 @@ namespace Renci.SshNet.Tests.Classes.Common { try { - new PortForwardEventArgs(null, 80); + _ = new PortForwardEventArgs(null, 80); } catch (ArgumentNullException ex) { @@ -54,7 +54,7 @@ namespace Renci.SshNet.Tests.Classes.Common try { - new PortForwardEventArgs(Resources.HOST, port); + _ = new PortForwardEventArgs(Resources.HOST, port); Assert.Fail(); } catch (ArgumentOutOfRangeException ex) @@ -64,4 +64,4 @@ namespace Renci.SshNet.Tests.Classes.Common } } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.Tests/Classes/Common/PosixPathTest_CreateAbsoluteOrRelativeFilePath.cs b/src/Renci.SshNet.Tests/Classes/Common/PosixPathTest_CreateAbsoluteOrRelativeFilePath.cs index 986ccdea..519c9543 100644 --- a/src/Renci.SshNet.Tests/Classes/Common/PosixPathTest_CreateAbsoluteOrRelativeFilePath.cs +++ b/src/Renci.SshNet.Tests/Classes/Common/PosixPathTest_CreateAbsoluteOrRelativeFilePath.cs @@ -1,6 +1,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Renci.SshNet.Common; using System; +using Renci.SshNet.Tests.Common; namespace Renci.SshNet.Tests.Classes.Common { @@ -14,7 +15,7 @@ namespace Renci.SshNet.Tests.Classes.Common try { - PosixPath.CreateAbsoluteOrRelativeFilePath(path); + _ = PosixPath.CreateAbsoluteOrRelativeFilePath(path); Assert.Fail(); } catch (ArgumentNullException ex) @@ -31,13 +32,13 @@ namespace Renci.SshNet.Tests.Classes.Common try { - PosixPath.CreateAbsoluteOrRelativeFilePath(path); + _ = PosixPath.CreateAbsoluteOrRelativeFilePath(path); Assert.Fail(); } catch (ArgumentException ex) { Assert.IsNull(ex.InnerException); - Assert.AreEqual(string.Format("The path is a zero-length string.{0}Parameter name: {1}", Environment.NewLine, ex.ParamName), ex.Message); + ArgumentExceptionAssert.MessageEquals("The path is a zero-length string.", ex); Assert.AreEqual("path", ex.ParamName); } } diff --git a/src/Renci.SshNet.Tests/Classes/Common/PosixPathTest_GetDirectoryName.cs b/src/Renci.SshNet.Tests/Classes/Common/PosixPathTest_GetDirectoryName.cs index db1a38e7..0767c11e 100644 --- a/src/Renci.SshNet.Tests/Classes/Common/PosixPathTest_GetDirectoryName.cs +++ b/src/Renci.SshNet.Tests/Classes/Common/PosixPathTest_GetDirectoryName.cs @@ -14,7 +14,7 @@ namespace Renci.SshNet.Tests.Classes.Common try { - PosixPath.GetDirectoryName(path); + _ = PosixPath.GetDirectoryName(path); Assert.Fail(); } catch (ArgumentNullException ex) diff --git a/src/Renci.SshNet.Tests/Classes/Common/PosixPathTest_GetFileName.cs b/src/Renci.SshNet.Tests/Classes/Common/PosixPathTest_GetFileName.cs index 3c7d74e2..d81a13d2 100644 --- a/src/Renci.SshNet.Tests/Classes/Common/PosixPathTest_GetFileName.cs +++ b/src/Renci.SshNet.Tests/Classes/Common/PosixPathTest_GetFileName.cs @@ -14,7 +14,7 @@ namespace Renci.SshNet.Tests.Classes.Common try { - PosixPath.GetFileName(path); + _ = PosixPath.GetFileName(path); Assert.Fail(); } catch (ArgumentNullException ex) diff --git a/src/Renci.SshNet.Tests/Classes/Common/SemaphoreLightTest.cs b/src/Renci.SshNet.Tests/Classes/Common/SemaphoreLightTest.cs index b1d3bfeb..17d5e72f 100644 --- a/src/Renci.SshNet.Tests/Classes/Common/SemaphoreLightTest.cs +++ b/src/Renci.SshNet.Tests/Classes/Common/SemaphoreLightTest.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics; using System.Threading; using Microsoft.VisualStudio.TestTools.UnitTesting; using Renci.SshNet.Common; @@ -58,28 +59,27 @@ namespace Renci.SshNet.Tests.Classes.Common const int initialCount = 2; var target = new SemaphoreLight(initialCount); - var start = DateTime.Now; + var watch = new Stopwatch(); + watch.Start(); target.Wait(); target.Wait(); + + Assert.IsTrue(watch.ElapsedMilliseconds < 50); - Assert.IsTrue((DateTime.Now - start).TotalMilliseconds < 50); - - var releaseThread = new Thread( - () => - { - Thread.Sleep(sleepTime); - target.Release(); - }); + var releaseThread = new Thread(() => + { + Thread.Sleep(sleepTime); + _ = target.Release(); + }); releaseThread.Start(); target.Wait(); - var end = DateTime.Now; - var elapsed = end - start; + watch.Stop(); - Assert.IsTrue(elapsed.TotalMilliseconds > 200); - Assert.IsTrue(elapsed.TotalMilliseconds < 250); + Assert.IsTrue(watch.ElapsedMilliseconds > 200); + Assert.IsTrue(watch.ElapsedMilliseconds < 250); } [TestMethod] diff --git a/src/Renci.SshNet.Tests/Classes/Common/SshConnectionExceptionTest.cs b/src/Renci.SshNet.Tests/Classes/Common/SshConnectionExceptionTest.cs index b960e4f4..2a305fcb 100644 --- a/src/Renci.SshNet.Tests/Classes/Common/SshConnectionExceptionTest.cs +++ b/src/Renci.SshNet.Tests/Classes/Common/SshConnectionExceptionTest.cs @@ -1,6 +1,8 @@ using System; using System.Runtime.Serialization; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Renci.SshNet.Common; using Renci.SshNet.Messages.Transport; using Renci.SshNet.Tests.Common; @@ -20,7 +22,7 @@ namespace Renci.SshNet.Tests.Classes.Common [TestMethod] public void SshConnectionExceptionConstructorTest() { - SshConnectionException target = new SshConnectionException(); + var target = new SshConnectionException(); Assert.Inconclusive("TODO: Implement code to verify target"); } @@ -30,8 +32,8 @@ namespace Renci.SshNet.Tests.Classes.Common [TestMethod] public void SshConnectionExceptionConstructorTest1() { - string message = string.Empty; // TODO: Initialize to an appropriate value - SshConnectionException target = new SshConnectionException(message); + var message = string.Empty; // TODO: Initialize to an appropriate value + var target = new SshConnectionException(message); Assert.Inconclusive("TODO: Implement code to verify target"); } @@ -41,9 +43,9 @@ namespace Renci.SshNet.Tests.Classes.Common [TestMethod] public void SshConnectionExceptionConstructorTest2() { - string message = string.Empty; // TODO: Initialize to an appropriate value - DisconnectReason disconnectReasonCode = new DisconnectReason(); // TODO: Initialize to an appropriate value - SshConnectionException target = new SshConnectionException(message, disconnectReasonCode); + var message = string.Empty; // TODO: Initialize to an appropriate value + var disconnectReasonCode = new DisconnectReason(); // TODO: Initialize to an appropriate value + var target = new SshConnectionException(message, disconnectReasonCode); Assert.Inconclusive("TODO: Implement code to verify target"); } @@ -53,10 +55,10 @@ namespace Renci.SshNet.Tests.Classes.Common [TestMethod] public void SshConnectionExceptionConstructorTest3() { - string message = string.Empty; // TODO: Initialize to an appropriate value - DisconnectReason disconnectReasonCode = new DisconnectReason(); // TODO: Initialize to an appropriate value + var message = string.Empty; // TODO: Initialize to an appropriate value + var disconnectReasonCode = new DisconnectReason(); // TODO: Initialize to an appropriate value Exception inner = null; // TODO: Initialize to an appropriate value - SshConnectionException target = new SshConnectionException(message, disconnectReasonCode, inner); + var target = new SshConnectionException(message, disconnectReasonCode, inner); Assert.Inconclusive("TODO: Implement code to verify target"); } @@ -67,9 +69,9 @@ namespace Renci.SshNet.Tests.Classes.Common [Ignore] // placeholder for actual test public void GetObjectDataTest() { - SshConnectionException target = new SshConnectionException(); // TODO: Initialize to an appropriate value + var target = new SshConnectionException(); // TODO: Initialize to an appropriate value SerializationInfo info = null; // TODO: Initialize to an appropriate value - StreamingContext context = new StreamingContext(); // TODO: Initialize to an appropriate value + var context = new StreamingContext(); // TODO: Initialize to an appropriate value target.GetObjectData(info, context); Assert.Inconclusive("A method that does not return a value cannot be verified."); } diff --git a/src/Renci.SshNet.Tests/Classes/Compression/ZlibOpenSshTest.cs b/src/Renci.SshNet.Tests/Classes/Compression/ZlibOpenSshTest.cs index c11e13dc..b3e16650 100644 --- a/src/Renci.SshNet.Tests/Classes/Compression/ZlibOpenSshTest.cs +++ b/src/Renci.SshNet.Tests/Classes/Compression/ZlibOpenSshTest.cs @@ -1,7 +1,6 @@ -using Renci.SshNet.Compression; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; -using Renci.SshNet; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Renci.SshNet.Compression; using Renci.SshNet.Tests.Common; namespace Renci.SshNet.Tests.Classes.Compression @@ -20,7 +19,7 @@ namespace Renci.SshNet.Tests.Classes.Compression [TestMethod()] public void ZlibOpenSshConstructorTest() { - ZlibOpenSsh target = new ZlibOpenSsh(); + var target = new ZlibOpenSsh(); Assert.Inconclusive("TODO: Implement code to verify target"); } @@ -30,7 +29,7 @@ namespace Renci.SshNet.Tests.Classes.Compression [TestMethod()] public void InitTest() { - ZlibOpenSsh target = new ZlibOpenSsh(); // TODO: Initialize to an appropriate value + var target = new ZlibOpenSsh(); // TODO: Initialize to an appropriate value Session session = null; // TODO: Initialize to an appropriate value target.Init(session); Assert.Inconclusive("A method that does not return a value cannot be verified."); @@ -42,9 +41,8 @@ namespace Renci.SshNet.Tests.Classes.Compression [TestMethod()] public void NameTest() { - ZlibOpenSsh target = new ZlibOpenSsh(); // TODO: Initialize to an appropriate value - string actual; - actual = target.Name; + var target = new ZlibOpenSsh(); // TODO: Initialize to an appropriate value + var actual = target.Name; Assert.Inconclusive("Verify the correctness of this test method."); } } diff --git a/src/Renci.SshNet.Tests/Classes/Compression/ZlibStreamTest.cs b/src/Renci.SshNet.Tests/Classes/Compression/ZlibStreamTest.cs index dd1c93bf..6ee6236c 100644 --- a/src/Renci.SshNet.Tests/Classes/Compression/ZlibStreamTest.cs +++ b/src/Renci.SshNet.Tests/Classes/Compression/ZlibStreamTest.cs @@ -1,11 +1,12 @@ -using Renci.SshNet.Compression; +using System.IO; + using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; -using System.IO; + +using Renci.SshNet.Compression; using Renci.SshNet.Tests.Common; namespace Renci.SshNet.Tests.Classes.Compression -{ +{ /// ///This is a test class for ZlibStreamTest and is intended ///to contain all ZlibStreamTest Unit Tests @@ -21,8 +22,8 @@ namespace Renci.SshNet.Tests.Classes.Compression public void ZlibStreamConstructorTest() { Stream stream = null; // TODO: Initialize to an appropriate value - CompressionMode mode = new CompressionMode(); // TODO: Initialize to an appropriate value - ZlibStream target = new ZlibStream(stream, mode); + var mode = new CompressionMode(); // TODO: Initialize to an appropriate value + var target = new ZlibStream(stream, mode); Assert.Inconclusive("TODO: Implement code to verify target"); } @@ -34,11 +35,11 @@ namespace Renci.SshNet.Tests.Classes.Compression public void WriteTest() { Stream stream = null; // TODO: Initialize to an appropriate value - CompressionMode mode = new CompressionMode(); // TODO: Initialize to an appropriate value - ZlibStream target = new ZlibStream(stream, mode); // TODO: Initialize to an appropriate value + var mode = new CompressionMode(); // TODO: Initialize to an appropriate value + var target = new ZlibStream(stream, mode); // TODO: Initialize to an appropriate value byte[] buffer = null; // TODO: Initialize to an appropriate value - int offset = 0; // TODO: Initialize to an appropriate value - int count = 0; // TODO: Initialize to an appropriate value + var offset = 0; // TODO: Initialize to an appropriate value + var count = 0; // TODO: Initialize to an appropriate value target.Write(buffer, offset, count); Assert.Inconclusive("A method that does not return a value cannot be verified."); } diff --git a/src/Renci.SshNet.Tests/Classes/Connection/DirectConnectorTestBase.cs b/src/Renci.SshNet.Tests/Classes/Connection/DirectConnectorTestBase.cs index edb14680..d1989c3d 100644 --- a/src/Renci.SshNet.Tests/Classes/Connection/DirectConnectorTestBase.cs +++ b/src/Renci.SshNet.Tests/Classes/Connection/DirectConnectorTestBase.cs @@ -1,7 +1,7 @@ using Moq; + using Renci.SshNet.Connection; using Renci.SshNet.Tests.Common; -using System.Net; namespace Renci.SshNet.Tests.Classes.Connection { diff --git a/src/Renci.SshNet.Tests/Classes/Connection/DirectConnectorTest_Connect_ConnectionRefusedByServer.cs b/src/Renci.SshNet.Tests/Classes/Connection/DirectConnectorTest_Connect_ConnectionRefusedByServer.cs index 0583729a..2a097619 100644 --- a/src/Renci.SshNet.Tests/Classes/Connection/DirectConnectorTest_Connect_ConnectionRefusedByServer.cs +++ b/src/Renci.SshNet.Tests/Classes/Connection/DirectConnectorTest_Connect_ConnectionRefusedByServer.cs @@ -1,11 +1,12 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; -using Renci.SshNet.Common; -using System; +using System; using System.Diagnostics; using System.Net; using System.Net.Sockets; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Moq; + namespace Renci.SshNet.Tests.Classes.Connection { [TestClass] @@ -30,18 +31,15 @@ namespace Renci.SshNet.Tests.Classes.Connection protected override void SetupMocks() { - SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) - .Returns(_clientSocket); + _ = SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) + .Returns(_clientSocket); } protected override void TearDown() { base.TearDown(); - if (_clientSocket != null) - { - _clientSocket.Dispose(); - } + _clientSocket?.Dispose(); } protected override void Act() @@ -50,7 +48,7 @@ namespace Renci.SshNet.Tests.Classes.Connection try { - Connector.Connect(_connectionInfo); + _ = Connector.Connect(_connectionInfo); Assert.Fail(); } catch (SocketException ex) @@ -86,7 +84,7 @@ namespace Renci.SshNet.Tests.Classes.Connection { try { - _clientSocket.Receive(new byte[0]); + _ = _clientSocket.Receive(new byte[0]); Assert.Fail(); } catch (ObjectDisposedException) diff --git a/src/Renci.SshNet.Tests/Classes/Connection/DirectConnectorTest_Connect_ConnectionSucceeded.cs b/src/Renci.SshNet.Tests/Classes/Connection/DirectConnectorTest_Connect_ConnectionSucceeded.cs index d2abc774..433adff4 100644 --- a/src/Renci.SshNet.Tests/Classes/Connection/DirectConnectorTest_Connect_ConnectionSucceeded.cs +++ b/src/Renci.SshNet.Tests/Classes/Connection/DirectConnectorTest_Connect_ConnectionSucceeded.cs @@ -1,12 +1,13 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; -using Renci.SshNet.Common; -using Renci.SshNet.Tests.Common; -using System; +using System; using System.Diagnostics; using System.Net; using System.Net.Sockets; -using System.Threading; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Moq; + +using Renci.SshNet.Tests.Common; namespace Renci.SshNet.Tests.Classes.Connection { @@ -41,23 +42,16 @@ namespace Renci.SshNet.Tests.Classes.Connection protected override void SetupMocks() { - SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) - .Returns(_clientSocket); + _ = SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) + .Returns(_clientSocket); } protected override void TearDown() { base.TearDown(); - if (_server != null) - { - _server.Dispose(); - } - - if (_clientSocket != null) - { - _clientSocket.Dispose(); - } + _server?.Dispose(); + _clientSocket?.Dispose(); } protected override void Act() diff --git a/src/Renci.SshNet.Tests/Classes/Connection/DirectConnectorTest_Connect_HostNameInvalid.cs b/src/Renci.SshNet.Tests/Classes/Connection/DirectConnectorTest_Connect_HostNameInvalid.cs index 05a69e94..7cd0f6ec 100644 --- a/src/Renci.SshNet.Tests/Classes/Connection/DirectConnectorTest_Connect_HostNameInvalid.cs +++ b/src/Renci.SshNet.Tests/Classes/Connection/DirectConnectorTest_Connect_HostNameInvalid.cs @@ -1,6 +1,6 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; -using System.Net.Sockets; +using System.Net.Sockets; + +using Microsoft.VisualStudio.TestTools.UnitTesting; namespace Renci.SshNet.Tests.Classes.Connection { @@ -22,7 +22,7 @@ namespace Renci.SshNet.Tests.Classes.Connection { try { - Connector.Connect(_connectionInfo); + _ = Connector.Connect(_connectionInfo); Assert.Fail(); } catch (SocketException ex) @@ -36,7 +36,7 @@ namespace Renci.SshNet.Tests.Classes.Connection { Assert.IsNotNull(_actualException); Assert.IsNull(_actualException.InnerException); - Assert.AreEqual(SocketError.HostNotFound, _actualException.SocketErrorCode); + Assert.IsTrue(_actualException.SocketErrorCode is SocketError.HostNotFound or SocketError.TryAgain); } } } diff --git a/src/Renci.SshNet.Tests/Classes/Connection/DirectConnectorTest_Connect_TimeoutConnectingToServer.cs b/src/Renci.SshNet.Tests/Classes/Connection/DirectConnectorTest_Connect_TimeoutConnectingToServer.cs index 44cfe62c..661b286a 100644 --- a/src/Renci.SshNet.Tests/Classes/Connection/DirectConnectorTest_Connect_TimeoutConnectingToServer.cs +++ b/src/Renci.SshNet.Tests/Classes/Connection/DirectConnectorTest_Connect_TimeoutConnectingToServer.cs @@ -1,13 +1,15 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; -using Renci.SshNet.Common; -using Renci.SshNet.Tests.Common; -using System; +using System; using System.Diagnostics; using System.Globalization; using System.Net; using System.Net.Sockets; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Moq; + +using Renci.SshNet.Common; + namespace Renci.SshNet.Tests.Classes.Connection { [TestClass] @@ -34,18 +36,15 @@ namespace Renci.SshNet.Tests.Classes.Connection protected override void SetupMocks() { - SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) - .Returns(_clientSocket); + _ = SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) + .Returns(_clientSocket); } protected override void TearDown() { base.TearDown(); - if (_clientSocket != null) - { - _clientSocket.Dispose(); - } + _clientSocket?.Dispose(); } protected override void Act() @@ -54,7 +53,7 @@ namespace Renci.SshNet.Tests.Classes.Connection try { - Connector.Connect(_connectionInfo); + _ = Connector.Connect(_connectionInfo); Assert.Fail(); } catch (SshOperationTimeoutException ex) @@ -91,7 +90,7 @@ namespace Renci.SshNet.Tests.Classes.Connection { try { - _clientSocket.Receive(new byte[0]); + _ = _clientSocket.Receive(new byte[0]); Assert.Fail(); } catch (ObjectDisposedException) diff --git a/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTestBase.cs b/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTestBase.cs index 82cfe0c2..1ed56ecd 100644 --- a/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTestBase.cs +++ b/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTestBase.cs @@ -24,7 +24,7 @@ namespace Renci.SshNet.Tests.Classes.Connection protected virtual void SetupMocks() { } - + protected sealed override void Arrange() { CreateMocks(); diff --git a/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_ConnectionToProxyRefused.cs b/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_ConnectionToProxyRefused.cs index e507b92b..51830332 100644 --- a/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_ConnectionToProxyRefused.cs +++ b/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_ConnectionToProxyRefused.cs @@ -1,6 +1,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; -using Renci.SshNet.Common; + using System; using System.Diagnostics; using System.Net; @@ -28,8 +29,10 @@ namespace Renci.SshNet.Tests.Classes.Connection 8122, "proxyUser", "proxyPwd", - new KeyboardInteractiveAuthenticationMethod("user")); - _connectionInfo.Timeout = TimeSpan.FromMilliseconds(5000); + new KeyboardInteractiveAuthenticationMethod("user")) + { + Timeout = TimeSpan.FromMilliseconds(5000) + }; _stopWatch = new Stopwatch(); _actualException = null; @@ -38,18 +41,15 @@ namespace Renci.SshNet.Tests.Classes.Connection protected override void SetupMocks() { - SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) - .Returns(_clientSocket); + _ = SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) + .Returns(_clientSocket); } protected override void TearDown() { base.TearDown(); - if (_clientSocket != null) - { - _clientSocket.Dispose(); - } + _clientSocket?.Dispose(); } protected override void Act() @@ -58,7 +58,7 @@ namespace Renci.SshNet.Tests.Classes.Connection try { - Connector.Connect(_connectionInfo); + _ = Connector.Connect(_connectionInfo); Assert.Fail(); } catch (SocketException ex) @@ -94,7 +94,7 @@ namespace Renci.SshNet.Tests.Classes.Connection { try { - _clientSocket.Receive(new byte[0]); + _ = _clientSocket.Receive(new byte[0]); Assert.Fail(); } catch (ObjectDisposedException) diff --git a/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_ProxyClosesConnectionBeforeStatusLineIsSent.cs b/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_ProxyClosesConnectionBeforeStatusLineIsSent.cs index 72bdca90..ece0aa9e 100644 --- a/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_ProxyClosesConnectionBeforeStatusLineIsSent.cs +++ b/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_ProxyClosesConnectionBeforeStatusLineIsSent.cs @@ -5,6 +5,7 @@ using Renci.SshNet.Tests.Common; using System; using System.Net; using System.Net.Sockets; +using System.Threading; namespace Renci.SshNet.Tests.Classes.Connection { @@ -29,8 +30,10 @@ namespace Renci.SshNet.Tests.Classes.Connection 8122, "proxyUser", "proxyPwd", - new KeyboardInteractiveAuthenticationMethod("user")); - _connectionInfo.Timeout = TimeSpan.FromMilliseconds(100); + new KeyboardInteractiveAuthenticationMethod("user")) + { + Timeout = TimeSpan.FromMilliseconds(100) + }; _actualException = null; _clientSocket = SocketFactory.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); @@ -46,36 +49,32 @@ namespace Renci.SshNet.Tests.Classes.Connection protected override void SetupMocks() { - SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) - .Returns(_clientSocket); + _ = SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) + .Returns(_clientSocket); } protected override void TearDown() { base.TearDown(); - if (_proxyServer != null) - { - _proxyServer.Dispose(); - } - - if (_clientSocket != null) - { - _clientSocket.Dispose(); - } + _proxyServer?.Dispose(); + _clientSocket?.Dispose(); } protected override void Act() { try { - Connector.Connect(_connectionInfo); + _ = Connector.Connect(_connectionInfo); Assert.Fail(); } catch (ProxyException ex) { _actualException = ex; } + + // Give some time to process all messages + Thread.Sleep(200); } [TestMethod] @@ -97,7 +96,7 @@ namespace Renci.SshNet.Tests.Classes.Connection { try { - _clientSocket.Receive(new byte[0]); + _ = _clientSocket.Receive(new byte[0]); Assert.Fail(); } catch (ObjectDisposedException) diff --git a/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_ProxyHostInvalid.cs b/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_ProxyHostInvalid.cs index 5843947d..7a87e9d6 100644 --- a/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_ProxyHostInvalid.cs +++ b/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_ProxyHostInvalid.cs @@ -29,7 +29,7 @@ namespace Renci.SshNet.Tests.Classes.Connection { try { - Connector.Connect(_connectionInfo); + _ = Connector.Connect(_connectionInfo); Assert.Fail(); } catch (SocketException ex) @@ -43,7 +43,7 @@ namespace Renci.SshNet.Tests.Classes.Connection { Assert.IsNotNull(_actualException); Assert.IsNull(_actualException.InnerException); - Assert.AreEqual(SocketError.HostNotFound, _actualException.SocketErrorCode); + Assert.IsTrue(_actualException.SocketErrorCode is SocketError.HostNotFound or SocketError.TryAgain); } } } diff --git a/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_ProxyPasswordIsEmpty.cs b/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_ProxyPasswordIsEmpty.cs index c94933b2..9a7e876a 100644 --- a/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_ProxyPasswordIsEmpty.cs +++ b/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_ProxyPasswordIsEmpty.cs @@ -1,12 +1,16 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; -using Renci.SshNet.Tests.Common; -using System; +using System; using System.Collections.Generic; using System.Globalization; using System.Net; using System.Net.Sockets; using System.Text; +using System.Threading; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Moq; + +using Renci.SshNet.Tests.Common; namespace Renci.SshNet.Tests.Classes.Connection { @@ -33,8 +37,10 @@ namespace Renci.SshNet.Tests.Classes.Connection 8122, "proxyUser", string.Empty, - new KeyboardInteractiveAuthenticationMethod("user")); - _connectionInfo.Timeout = TimeSpan.FromMilliseconds(20); + new KeyboardInteractiveAuthenticationMethod("user")) + { + Timeout = TimeSpan.FromMilliseconds(20) + }; _expectedHttpRequest = string.Format("CONNECT {0}:{1} HTTP/1.0{2}" + "Proxy-Authorization: Basic cHJveHlVc2VyOg=={2}{2}", _connectionInfo.Host, @@ -48,13 +54,14 @@ namespace Renci.SshNet.Tests.Classes.Connection _proxyServer.Disconnected += (socket) => _disconnected = true; _proxyServer.Connected += socket => { - socket.Send(Encoding.ASCII.GetBytes("\r\n")); - socket.Send(Encoding.ASCII.GetBytes("SSH.NET\r\n")); - socket.Send(Encoding.ASCII.GetBytes("HTTP/1.0 200 OK\r\n")); - socket.Send(Encoding.ASCII.GetBytes("Content-Type: application/octet-stream\r\n")); - socket.Send(Encoding.ASCII.GetBytes("\r\n")); - socket.Send(Encoding.ASCII.GetBytes("SSH4EVER")); - socket.Shutdown(SocketShutdown.Send); + _ = socket.Send(Encoding.ASCII.GetBytes("\r\n")); + _ = socket.Send(Encoding.ASCII.GetBytes("SSH.NET\r\n")); + _ = socket.Send(Encoding.ASCII.GetBytes("HTTP/1.0 200 OK\r\n")); + _ = socket.Send(Encoding.ASCII.GetBytes("Content-Type: application/octet-stream\r\n")); + _ = socket.Send(Encoding.ASCII.GetBytes("\r\n")); + _ = socket.Send(Encoding.ASCII.GetBytes("SSH4EVER")); + + socket.Shutdown(SocketShutdown.Send); }; _proxyServer.BytesReceived += (bytesReceived, socket) => { @@ -65,18 +72,15 @@ namespace Renci.SshNet.Tests.Classes.Connection protected override void SetupMocks() { - SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) - .Returns(_clientSocket); + _ = SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) + .Returns(_clientSocket); } protected override void TearDown() { base.TearDown(); - if (_proxyServer != null) - { - _proxyServer.Dispose(); - } + _proxyServer?.Dispose(); if (_clientSocket != null) { @@ -88,6 +92,9 @@ namespace Renci.SshNet.Tests.Classes.Connection protected override void Act() { _actual = Connector.Connect(_connectionInfo); + + // Give some time to process all messages + Thread.Sleep(200); } [TestMethod] diff --git a/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_ProxyPasswordIsNull.cs b/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_ProxyPasswordIsNull.cs index 277a6f29..ccbf2b75 100644 --- a/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_ProxyPasswordIsNull.cs +++ b/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_ProxyPasswordIsNull.cs @@ -7,6 +7,7 @@ using System.Globalization; using System.Net; using System.Net.Sockets; using System.Text; +using System.Threading; namespace Renci.SshNet.Tests.Classes.Connection { @@ -33,8 +34,10 @@ namespace Renci.SshNet.Tests.Classes.Connection 8122, "proxyUser", null, - new KeyboardInteractiveAuthenticationMethod("user")); - _connectionInfo.Timeout = TimeSpan.FromMilliseconds(20); + new KeyboardInteractiveAuthenticationMethod("user")) + { + Timeout = TimeSpan.FromMilliseconds(20) + }; _expectedHttpRequest = string.Format("CONNECT {0}:{1} HTTP/1.0{2}" + "Proxy-Authorization: Basic cHJveHlVc2VyOg=={2}{2}", _connectionInfo.Host, @@ -48,12 +51,13 @@ namespace Renci.SshNet.Tests.Classes.Connection _proxyServer.Disconnected += (socket) => _disconnected = true; _proxyServer.Connected += socket => { - socket.Send(Encoding.ASCII.GetBytes("\r\n")); - socket.Send(Encoding.ASCII.GetBytes("SSH.NET\r\n")); - socket.Send(Encoding.ASCII.GetBytes("HTTP/1.0 200 OK\r\n")); - socket.Send(Encoding.ASCII.GetBytes("Content-Type: application/octet-stream\r\n")); - socket.Send(Encoding.ASCII.GetBytes("\r\n")); - socket.Send(Encoding.ASCII.GetBytes("SSH4EVER")); + _ = socket.Send(Encoding.ASCII.GetBytes("\r\n")); + _ = socket.Send(Encoding.ASCII.GetBytes("SSH.NET\r\n")); + _ = socket.Send(Encoding.ASCII.GetBytes("HTTP/1.0 200 OK\r\n")); + _ = socket.Send(Encoding.ASCII.GetBytes("Content-Type: application/octet-stream\r\n")); + _ = socket.Send(Encoding.ASCII.GetBytes("\r\n")); + _ = socket.Send(Encoding.ASCII.GetBytes("SSH4EVER")); + socket.Shutdown(SocketShutdown.Send); }; _proxyServer.BytesReceived += (bytesReceived, socket) => @@ -65,18 +69,15 @@ namespace Renci.SshNet.Tests.Classes.Connection protected override void SetupMocks() { - SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) - .Returns(_clientSocket); + _ = SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) + .Returns(_clientSocket); } protected override void TearDown() { base.TearDown(); - if (_proxyServer != null) - { - _proxyServer.Dispose(); - } + _proxyServer?.Dispose(); if (_clientSocket != null) { @@ -88,6 +89,9 @@ namespace Renci.SshNet.Tests.Classes.Connection protected override void Act() { _actual = Connector.Connect(_connectionInfo); + + // Give some time to process all messages + Thread.Sleep(400); } [TestMethod] diff --git a/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_ProxyResponseDoesNotContainHttpStatusLine.cs b/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_ProxyResponseDoesNotContainHttpStatusLine.cs index b9f90019..eebb70fa 100644 --- a/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_ProxyResponseDoesNotContainHttpStatusLine.cs +++ b/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_ProxyResponseDoesNotContainHttpStatusLine.cs @@ -1,12 +1,16 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; -using Renci.SshNet.Common; -using Renci.SshNet.Tests.Common; -using System; +using System; using System.Collections.Generic; using System.Net; using System.Net.Sockets; using System.Text; +using System.Threading; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Moq; + +using Renci.SshNet.Common; +using Renci.SshNet.Tests.Common; namespace Renci.SshNet.Tests.Classes.Connection { @@ -32,8 +36,10 @@ namespace Renci.SshNet.Tests.Classes.Connection 8122, "proxyUser", "proxyPwd", - new KeyboardInteractiveAuthenticationMethod("user")); - _connectionInfo.Timeout = TimeSpan.FromMilliseconds(100); + new KeyboardInteractiveAuthenticationMethod("user")) + { + Timeout = TimeSpan.FromMilliseconds(100) + }; _bytesReceivedByProxy = new List(); _actualException = null; @@ -45,7 +51,8 @@ namespace Renci.SshNet.Tests.Classes.Connection { if (_bytesReceivedByProxy.Count == 0) { - socket.Send(Encoding.ASCII.GetBytes("Whatever\r\n")); + _ = socket.Send(Encoding.ASCII.GetBytes("Whatever\r\n")); + socket.Shutdown(SocketShutdown.Send); } @@ -56,31 +63,31 @@ namespace Renci.SshNet.Tests.Classes.Connection protected override void SetupMocks() { - SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) - .Returns(_clientSocket); + _ = SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) + .Returns(_clientSocket); } protected override void TearDown() { base.TearDown(); - if (_proxyServer != null) - { - _proxyServer.Dispose(); - } + _proxyServer?.Dispose(); } protected override void Act() { try { - Connector.Connect(_connectionInfo); + _ = Connector.Connect(_connectionInfo); Assert.Fail(); } catch (ProxyException ex) { _actualException = ex; } + + // Give some time to process all messages + Thread.Sleep(200); } [TestMethod] @@ -102,7 +109,7 @@ namespace Renci.SshNet.Tests.Classes.Connection { try { - _clientSocket.Receive(new byte[0]); + _ = _clientSocket.Receive(new byte[0]); Assert.Fail(); } catch (ObjectDisposedException) diff --git a/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_ProxyResponseStatusIs200_ExtraTextBeforeStatusLine.cs b/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_ProxyResponseStatusIs200_ExtraTextBeforeStatusLine.cs index 70491330..f2287807 100644 --- a/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_ProxyResponseStatusIs200_ExtraTextBeforeStatusLine.cs +++ b/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_ProxyResponseStatusIs200_ExtraTextBeforeStatusLine.cs @@ -1,12 +1,16 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; -using Renci.SshNet.Tests.Common; -using System; +using System; using System.Collections.Generic; using System.Globalization; using System.Net; using System.Net.Sockets; using System.Text; +using System.Threading; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Moq; + +using Renci.SshNet.Tests.Common; namespace Renci.SshNet.Tests.Classes.Connection { @@ -33,8 +37,10 @@ namespace Renci.SshNet.Tests.Classes.Connection 8122, "proxyUser", "proxyPwd", - new KeyboardInteractiveAuthenticationMethod("user")); - _connectionInfo.Timeout = TimeSpan.FromMilliseconds(20); + new KeyboardInteractiveAuthenticationMethod("user")) + { + Timeout = TimeSpan.FromMilliseconds(20) + }; _expectedHttpRequest = string.Format("CONNECT {0}:{1} HTTP/1.0{2}" + "Proxy-Authorization: Basic cHJveHlVc2VyOnByb3h5UHdk{2}{2}", _connectionInfo.Host, @@ -55,12 +61,13 @@ namespace Renci.SshNet.Tests.Classes.Connection // it sends the CONNECT request if (_bytesReceivedByProxy.Count == _expectedHttpRequest.Length) { - socket.Send(Encoding.ASCII.GetBytes("\r\n")); - socket.Send(Encoding.ASCII.GetBytes("SSH.NET\r\n")); - socket.Send(Encoding.ASCII.GetBytes("HTTP/1.0 200 OK\r\n")); - socket.Send(Encoding.ASCII.GetBytes("Content-Type: application/octet-stream\r\n")); - socket.Send(Encoding.ASCII.GetBytes("\r\n")); - socket.Send(Encoding.ASCII.GetBytes("SSH4EVER")); + _ = socket.Send(Encoding.ASCII.GetBytes("\r\n")); + _ = socket.Send(Encoding.ASCII.GetBytes("SSH.NET\r\n")); + _ = socket.Send(Encoding.ASCII.GetBytes("HTTP/1.0 200 OK\r\n")); + _ = socket.Send(Encoding.ASCII.GetBytes("Content-Type: application/octet-stream\r\n")); + _ = socket.Send(Encoding.ASCII.GetBytes("\r\n")); + _ = socket.Send(Encoding.ASCII.GetBytes("SSH4EVER")); + socket.Shutdown(SocketShutdown.Send); } }; @@ -69,18 +76,15 @@ namespace Renci.SshNet.Tests.Classes.Connection protected override void SetupMocks() { - SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) - .Returns(_clientSocket); + _ = SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) + .Returns(_clientSocket); } protected override void TearDown() { base.TearDown(); - if (_proxyServer != null) - { - _proxyServer.Dispose(); - } + _proxyServer?.Dispose(); if (_clientSocket != null) { @@ -92,6 +96,9 @@ namespace Renci.SshNet.Tests.Classes.Connection protected override void Act() { _actual = Connector.Connect(_connectionInfo); + + // Give some time to process all messages + Thread.Sleep(200); } [TestMethod] diff --git a/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_ProxyResponseStatusIs200_HeadersAndContent.cs b/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_ProxyResponseStatusIs200_HeadersAndContent.cs index dc0bba59..765e7f81 100644 --- a/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_ProxyResponseStatusIs200_HeadersAndContent.cs +++ b/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_ProxyResponseStatusIs200_HeadersAndContent.cs @@ -1,12 +1,16 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; -using Renci.SshNet.Tests.Common; -using System; +using System; using System.Collections.Generic; using System.Globalization; using System.Net; using System.Net.Sockets; using System.Text; +using System.Threading; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Moq; + +using Renci.SshNet.Tests.Common; namespace Renci.SshNet.Tests.Classes.Connection { @@ -33,8 +37,10 @@ namespace Renci.SshNet.Tests.Classes.Connection 8122, "proxyUser", "proxyPwd", - new KeyboardInteractiveAuthenticationMethod("user")); - _connectionInfo.Timeout = TimeSpan.FromMilliseconds(20); + new KeyboardInteractiveAuthenticationMethod("user")) + { + Timeout = TimeSpan.FromMilliseconds(20) + }; _expectedHttpRequest = string.Format("CONNECT {0}:{1} HTTP/1.0{2}" + "Proxy-Authorization: Basic cHJveHlVc2VyOnByb3h5UHdk{2}{2}", _connectionInfo.Host, @@ -55,12 +61,13 @@ namespace Renci.SshNet.Tests.Classes.Connection // it sends the CONNECT request if (_bytesReceivedByProxy.Count == _expectedHttpRequest.Length) { - socket.Send(Encoding.ASCII.GetBytes("HTTP/1.0 200 OK\r\n")); - socket.Send(Encoding.ASCII.GetBytes("Content-Length: 10\r\n")); - socket.Send(Encoding.ASCII.GetBytes("Content-Type: application/octet-stream\r\n")); - socket.Send(Encoding.ASCII.GetBytes("\r\n")); - socket.Send(Encoding.ASCII.GetBytes("TEEN_BYTES")); - socket.Send(Encoding.ASCII.GetBytes("!666!")); + _ = socket.Send(Encoding.ASCII.GetBytes("HTTP/1.0 200 OK\r\n")); + _ = socket.Send(Encoding.ASCII.GetBytes("Content-Length: 10\r\n")); + _ = socket.Send(Encoding.ASCII.GetBytes("Content-Type: application/octet-stream\r\n")); + _ = socket.Send(Encoding.ASCII.GetBytes("\r\n")); + _ = socket.Send(Encoding.ASCII.GetBytes("TEEN_BYTES")); + _ = socket.Send(Encoding.ASCII.GetBytes("!666!")); + socket.Shutdown(SocketShutdown.Send); } }; @@ -69,18 +76,15 @@ namespace Renci.SshNet.Tests.Classes.Connection protected override void SetupMocks() { - SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) - .Returns(_clientSocket); + _ = SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) + .Returns(_clientSocket); } protected override void TearDown() { base.TearDown(); - if (_proxyServer != null) - { - _proxyServer.Dispose(); - } + _proxyServer?.Dispose(); if (_clientSocket != null) { @@ -92,6 +96,9 @@ namespace Renci.SshNet.Tests.Classes.Connection protected override void Act() { _actual = Connector.Connect(_connectionInfo); + + // Give some time to process all messages + Thread.Sleep(200); } [TestMethod] diff --git a/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_ProxyResponseStatusIs200_OnlyHeaders.cs b/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_ProxyResponseStatusIs200_OnlyHeaders.cs index 235b4dc0..17da268c 100644 --- a/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_ProxyResponseStatusIs200_OnlyHeaders.cs +++ b/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_ProxyResponseStatusIs200_OnlyHeaders.cs @@ -7,6 +7,7 @@ using System.Globalization; using System.Net; using System.Net.Sockets; using System.Text; +using System.Threading; namespace Renci.SshNet.Tests.Classes.Connection { @@ -33,8 +34,10 @@ namespace Renci.SshNet.Tests.Classes.Connection 8122, "proxyUser", "proxyPwd", - new KeyboardInteractiveAuthenticationMethod("user")); - _connectionInfo.Timeout = TimeSpan.FromMilliseconds(20); + new KeyboardInteractiveAuthenticationMethod("user")) + { + Timeout = TimeSpan.FromMilliseconds(20) + }; _expectedHttpRequest = string.Format("CONNECT {0}:{1} HTTP/1.0{2}" + "Proxy-Authorization: Basic cHJveHlVc2VyOnByb3h5UHdk{2}{2}", _connectionInfo.Host, @@ -55,10 +58,11 @@ namespace Renci.SshNet.Tests.Classes.Connection // it sends the CONNECT request if (_bytesReceivedByProxy.Count == _expectedHttpRequest.Length) { - socket.Send(Encoding.ASCII.GetBytes("HTTP/1.0 200 OK\r\n")); - socket.Send(Encoding.ASCII.GetBytes("Content-Type: application/octet-stream\r\n")); - socket.Send(Encoding.ASCII.GetBytes("\r\n")); - socket.Send(Encoding.ASCII.GetBytes("SSH4EVER")); + _ = socket.Send(Encoding.ASCII.GetBytes("HTTP/1.0 200 OK\r\n")); + _ = socket.Send(Encoding.ASCII.GetBytes("Content-Type: application/octet-stream\r\n")); + _ = socket.Send(Encoding.ASCII.GetBytes("\r\n")); + _ = socket.Send(Encoding.ASCII.GetBytes("SSH4EVER")); + socket.Shutdown(SocketShutdown.Send); } }; @@ -67,18 +71,15 @@ namespace Renci.SshNet.Tests.Classes.Connection protected override void SetupMocks() { - SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) - .Returns(_clientSocket); + _ = SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) + .Returns(_clientSocket); } protected override void TearDown() { base.TearDown(); - if (_proxyServer != null) - { - _proxyServer.Dispose(); - } + _proxyServer?.Dispose(); if (_clientSocket != null) { @@ -90,6 +91,9 @@ namespace Renci.SshNet.Tests.Classes.Connection protected override void Act() { _actual = Connector.Connect(_connectionInfo); + + // Give some time to process all messages + Thread.Sleep(200); } [TestMethod] diff --git a/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_ProxyResponseStatusIsNot200.cs b/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_ProxyResponseStatusIsNot200.cs index 9eb0094b..b2cdc5fd 100644 --- a/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_ProxyResponseStatusIsNot200.cs +++ b/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_ProxyResponseStatusIsNot200.cs @@ -1,12 +1,16 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; -using Renci.SshNet.Common; -using Renci.SshNet.Tests.Common; -using System; +using System; using System.Collections.Generic; using System.Net; using System.Net.Sockets; using System.Text; +using System.Threading; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Moq; + +using Renci.SshNet.Common; +using Renci.SshNet.Tests.Common; namespace Renci.SshNet.Tests.Classes.Connection { @@ -32,8 +36,10 @@ namespace Renci.SshNet.Tests.Classes.Connection 8122, "proxyUser", "proxyPwd", - new KeyboardInteractiveAuthenticationMethod("user")); - _connectionInfo.Timeout = TimeSpan.FromMilliseconds(100); + new KeyboardInteractiveAuthenticationMethod("user")) + { + Timeout = TimeSpan.FromMilliseconds(100) + }; _bytesReceivedByProxy = new List(); _actualException = null; @@ -45,7 +51,8 @@ namespace Renci.SshNet.Tests.Classes.Connection { if (_bytesReceivedByProxy.Count == 0) { - socket.Send(Encoding.ASCII.GetBytes("HTTP/1.0 404 I searched everywhere, really...\r\n")); + _ = socket.Send(Encoding.ASCII.GetBytes("HTTP/1.0 404 I searched everywhere, really...\r\n")); + socket.Shutdown(SocketShutdown.Send); } @@ -56,31 +63,31 @@ namespace Renci.SshNet.Tests.Classes.Connection protected override void SetupMocks() { - SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) - .Returns(_clientSocket); + _ = SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) + .Returns(_clientSocket); } protected override void TearDown() { base.TearDown(); - if (_proxyServer != null) - { - _proxyServer.Dispose(); - } + _proxyServer?.Dispose(); } protected override void Act() { try { - Connector.Connect(_connectionInfo); + _ = Connector.Connect(_connectionInfo); Assert.Fail(); } catch (ProxyException ex) { _actualException = ex; } + + // Give some time to process all messages + Thread.Sleep(200); } [TestMethod] @@ -102,7 +109,7 @@ namespace Renci.SshNet.Tests.Classes.Connection { try { - _clientSocket.Receive(new byte[0]); + _ = _clientSocket.Receive(new byte[0]); Assert.Fail(); } catch (ObjectDisposedException) diff --git a/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_ProxyUserNameIsEmpty.cs b/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_ProxyUserNameIsEmpty.cs index 33c22253..455a41a9 100644 --- a/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_ProxyUserNameIsEmpty.cs +++ b/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_ProxyUserNameIsEmpty.cs @@ -1,12 +1,16 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; -using Renci.SshNet.Tests.Common; -using System; +using System; using System.Collections.Generic; using System.Globalization; using System.Net; using System.Net.Sockets; using System.Text; +using System.Threading; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Moq; + +using Renci.SshNet.Tests.Common; namespace Renci.SshNet.Tests.Classes.Connection { @@ -33,8 +37,10 @@ namespace Renci.SshNet.Tests.Classes.Connection 8122, string.Empty, "proxyPwd", - new KeyboardInteractiveAuthenticationMethod("user")); - _connectionInfo.Timeout = TimeSpan.FromMilliseconds(20); + new KeyboardInteractiveAuthenticationMethod("user")) + { + Timeout = TimeSpan.FromMilliseconds(20) + }; _expectedHttpRequest = string.Format("CONNECT {0}:{1} HTTP/1.0{2}{2}", _connectionInfo.Host, _connectionInfo.Port.ToString(CultureInfo.InvariantCulture), @@ -54,12 +60,12 @@ namespace Renci.SshNet.Tests.Classes.Connection // it sends the CONNECT request if (_bytesReceivedByProxy.Count == _expectedHttpRequest.Length) { - socket.Send(Encoding.ASCII.GetBytes("\r\n")); - socket.Send(Encoding.ASCII.GetBytes("SSH.NET\r\n")); - socket.Send(Encoding.ASCII.GetBytes("HTTP/1.0 200 OK\r\n")); - socket.Send(Encoding.ASCII.GetBytes("Content-Type: application/octet-stream\r\n")); - socket.Send(Encoding.ASCII.GetBytes("\r\n")); - socket.Send(Encoding.ASCII.GetBytes("SSH4EVER")); + _ = socket.Send(Encoding.ASCII.GetBytes("\r\n")); + _ = socket.Send(Encoding.ASCII.GetBytes("SSH.NET\r\n")); + _ = socket.Send(Encoding.ASCII.GetBytes("HTTP/1.0 200 OK\r\n")); + _ = socket.Send(Encoding.ASCII.GetBytes("Content-Type: application/octet-stream\r\n")); + _ = socket.Send(Encoding.ASCII.GetBytes("\r\n")); + _ = socket.Send(Encoding.ASCII.GetBytes("SSH4EVER")); } }; _proxyServer.Start(); @@ -67,28 +73,24 @@ namespace Renci.SshNet.Tests.Classes.Connection protected override void SetupMocks() { - SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) - .Returns(_clientSocket); + _ = SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) + .Returns(_clientSocket); } protected override void TearDown() { base.TearDown(); - if (_proxyServer != null) - { - _proxyServer.Dispose(); - } - - if (_clientSocket != null) - { - _clientSocket.Close(); - } + _proxyServer?.Dispose(); + _clientSocket?.Close(); } protected override void Act() { _actual = Connector.Connect(_connectionInfo); + + // Give some time to process all messages + Thread.Sleep(200); } [TestMethod] diff --git a/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_ProxyUserNameIsNotNullAndNotEmpty.cs b/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_ProxyUserNameIsNotNullAndNotEmpty.cs index dad0e74b..b49d1b12 100644 --- a/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_ProxyUserNameIsNotNullAndNotEmpty.cs +++ b/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_ProxyUserNameIsNotNullAndNotEmpty.cs @@ -1,12 +1,16 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; -using Renci.SshNet.Tests.Common; -using System; +using System; using System.Collections.Generic; using System.Globalization; using System.Net; using System.Net.Sockets; using System.Text; +using System.Threading; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Moq; + +using Renci.SshNet.Tests.Common; namespace Renci.SshNet.Tests.Classes.Connection { @@ -33,8 +37,10 @@ namespace Renci.SshNet.Tests.Classes.Connection 8122, "user", "pwd", - new KeyboardInteractiveAuthenticationMethod("user")); - _connectionInfo.Timeout = TimeSpan.FromMilliseconds(20); + new KeyboardInteractiveAuthenticationMethod("user")) + { + Timeout = TimeSpan.FromMilliseconds(20) + }; _expectedHttpRequest = string.Format("CONNECT {0}:{1} HTTP/1.0{2}" + "Proxy-Authorization: Basic dXNlcjpwd2Q={2}{2}", _connectionInfo.Host, @@ -47,10 +53,10 @@ namespace Renci.SshNet.Tests.Classes.Connection _proxyServer = new AsyncSocketListener(new IPEndPoint(IPAddress.Loopback, _connectionInfo.ProxyPort)); _proxyServer.Connected += (socket) => { - socket.Send(Encoding.ASCII.GetBytes("HTTP/1.0 200 OK\r\n")); - socket.Send(Encoding.ASCII.GetBytes("Content-Type: application/octet-stream\r\n")); - socket.Send(Encoding.ASCII.GetBytes("\r\n")); - socket.Send(Encoding.ASCII.GetBytes("SSH4EVER")); + _ = socket.Send(Encoding.ASCII.GetBytes("HTTP/1.0 200 OK\r\n")); + _ = socket.Send(Encoding.ASCII.GetBytes("Content-Type: application/octet-stream\r\n")); + _ = socket.Send(Encoding.ASCII.GetBytes("\r\n")); + _ = socket.Send(Encoding.ASCII.GetBytes("SSH4EVER")); }; _proxyServer.Disconnected += (socket) => _disconnected = true; _proxyServer.BytesReceived += (bytesReceived, socket) => @@ -62,18 +68,15 @@ namespace Renci.SshNet.Tests.Classes.Connection protected override void SetupMocks() { - SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) - .Returns(_clientSocket); + _ = SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) + .Returns(_clientSocket); } protected override void TearDown() { base.TearDown(); - if (_proxyServer != null) - { - _proxyServer.Dispose(); - } + _proxyServer?.Dispose(); if (_clientSocket != null) { @@ -85,6 +88,9 @@ namespace Renci.SshNet.Tests.Classes.Connection protected override void Act() { _actual = Connector.Connect(_connectionInfo); + + // Give some time to process all messages + Thread.Sleep(200); } [TestMethod] diff --git a/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_ProxyUserNameIsNull.cs b/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_ProxyUserNameIsNull.cs index 3de1bddb..59a022bc 100644 --- a/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_ProxyUserNameIsNull.cs +++ b/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_ProxyUserNameIsNull.cs @@ -1,12 +1,16 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; -using Renci.SshNet.Tests.Common; -using System; +using System; using System.Collections.Generic; using System.Globalization; using System.Net; using System.Net.Sockets; using System.Text; +using System.Threading; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Moq; + +using Renci.SshNet.Tests.Common; namespace Renci.SshNet.Tests.Classes.Connection { @@ -33,8 +37,10 @@ namespace Renci.SshNet.Tests.Classes.Connection 8122, null, "proxyPwd", - new KeyboardInteractiveAuthenticationMethod("user")); - _connectionInfo.Timeout = TimeSpan.FromMilliseconds(20); + new KeyboardInteractiveAuthenticationMethod("user")) + { + Timeout = TimeSpan.FromMilliseconds(20) + }; _expectedHttpRequest = string.Format("CONNECT {0}:{1} HTTP/1.0{2}{2}", _connectionInfo.Host, _connectionInfo.Port.ToString(CultureInfo.InvariantCulture), @@ -54,12 +60,12 @@ namespace Renci.SshNet.Tests.Classes.Connection // it sends the CONNECT request if (_bytesReceivedByProxy.Count == _expectedHttpRequest.Length) { - socket.Send(Encoding.ASCII.GetBytes("\r\n")); - socket.Send(Encoding.ASCII.GetBytes("SSH.NET\r\n")); - socket.Send(Encoding.ASCII.GetBytes("HTTP/1.0 200 OK\r\n")); - socket.Send(Encoding.ASCII.GetBytes("Content-Type: application/octet-stream\r\n")); - socket.Send(Encoding.ASCII.GetBytes("\r\n")); - socket.Send(Encoding.ASCII.GetBytes("SSH4EVER")); + _ = socket.Send(Encoding.ASCII.GetBytes("\r\n")); + _ = socket.Send(Encoding.ASCII.GetBytes("SSH.NET\r\n")); + _ = socket.Send(Encoding.ASCII.GetBytes("HTTP/1.0 200 OK\r\n")); + _ = socket.Send(Encoding.ASCII.GetBytes("Content-Type: application/octet-stream\r\n")); + _ = socket.Send(Encoding.ASCII.GetBytes("\r\n")); + _ = socket.Send(Encoding.ASCII.GetBytes("SSH4EVER")); } }; _proxyServer.Start(); @@ -67,8 +73,8 @@ namespace Renci.SshNet.Tests.Classes.Connection protected override void SetupMocks() { - SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) - .Returns(_clientSocket); + _ = SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) + .Returns(_clientSocket); } protected override void TearDown() @@ -81,15 +87,15 @@ namespace Renci.SshNet.Tests.Classes.Connection _clientSocket.Close(); } - if (_proxyServer != null) - { - _proxyServer.Dispose(); - } + _proxyServer?.Dispose(); } protected override void Act() { _actual = Connector.Connect(_connectionInfo); + + // Give some time to process all messages + Thread.Sleep(200); } [TestMethod] diff --git a/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_TimeoutConnectingToProxy.cs b/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_TimeoutConnectingToProxy.cs index 61e35cf3..bb8041c8 100644 --- a/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_TimeoutConnectingToProxy.cs +++ b/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_TimeoutConnectingToProxy.cs @@ -1,12 +1,15 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; -using Renci.SshNet.Common; -using System; +using System; using System.Diagnostics; using System.Globalization; using System.Net; using System.Net.Sockets; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Moq; + +using Renci.SshNet.Common; + namespace Renci.SshNet.Tests.Classes.Connection { [TestClass] @@ -31,8 +34,10 @@ namespace Renci.SshNet.Tests.Classes.Connection 8122, "proxyUser", "proxyPwd", - new KeyboardInteractiveAuthenticationMethod("user")); - _connectionInfo.Timeout = TimeSpan.FromMilliseconds(random.Next(50, 200)); + new KeyboardInteractiveAuthenticationMethod("user")) + { + Timeout = TimeSpan.FromMilliseconds(random.Next(50, 200)) + }; _stopWatch = new Stopwatch(); _actualException = null; @@ -41,18 +46,15 @@ namespace Renci.SshNet.Tests.Classes.Connection protected override void SetupMocks() { - SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) - .Returns(_clientSocket); + _ = SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) + .Returns(_clientSocket); } protected override void TearDown() { base.TearDown(); - if (_clientSocket != null) - { - _clientSocket.Dispose(); - } + _clientSocket?.Dispose(); } protected override void Act() @@ -61,7 +63,7 @@ namespace Renci.SshNet.Tests.Classes.Connection try { - Connector.Connect(_connectionInfo); + _ = Connector.Connect(_connectionInfo); Assert.Fail(); } catch (SshOperationTimeoutException ex) @@ -98,7 +100,7 @@ namespace Renci.SshNet.Tests.Classes.Connection { try { - _clientSocket.Receive(new byte[0]); + _ = _clientSocket.Receive(new byte[0]); Assert.Fail(); } catch (ObjectDisposedException) diff --git a/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_TimeoutReadingHttpContent.cs b/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_TimeoutReadingHttpContent.cs index b1c24fc6..98ffde6a 100644 --- a/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_TimeoutReadingHttpContent.cs +++ b/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_TimeoutReadingHttpContent.cs @@ -1,15 +1,18 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; -using Renci.SshNet.Common; -using Renci.SshNet.Connection; -using Renci.SshNet.Tests.Common; -using System; +using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Net; using System.Net.Sockets; using System.Text; +using System.Threading; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Moq; + +using Renci.SshNet.Common; +using Renci.SshNet.Tests.Common; namespace Renci.SshNet.Tests.Classes.Connection { @@ -40,8 +43,10 @@ namespace Renci.SshNet.Tests.Classes.Connection 8122, "proxyUser", "proxyPwd", - new KeyboardInteractiveAuthenticationMethod("user")); - _connectionInfo.Timeout = TimeSpan.FromMilliseconds(random.Next(50, 200)); + new KeyboardInteractiveAuthenticationMethod("user")) + { + Timeout = TimeSpan.FromMilliseconds(random.Next(50, 200)) + }; _expectedHttpRequest = string.Format("CONNECT {0}:{1} HTTP/1.0{2}" + "Proxy-Authorization: Basic cHJveHlVc2VyOnByb3h5UHdk{2}{2}", _connectionInfo.Host, @@ -62,11 +67,11 @@ namespace Renci.SshNet.Tests.Classes.Connection // Force a timeout by sending less content than indicated by Content-Length header if (_bytesReceivedByProxy.Count == _expectedHttpRequest.Length) { - socket.Send(Encoding.ASCII.GetBytes("HTTP/1.0 200 OK\r\n")); - socket.Send(Encoding.ASCII.GetBytes("Content-Length: 10\r\n")); - socket.Send(Encoding.ASCII.GetBytes("Content-Type: application/octet-stream\r\n")); - socket.Send(Encoding.ASCII.GetBytes("\r\n")); - socket.Send(Encoding.ASCII.GetBytes("TOO_FEW")); + _ = socket.Send(Encoding.ASCII.GetBytes("HTTP/1.0 200 OK\r\n")); + _ = socket.Send(Encoding.ASCII.GetBytes("Content-Length: 10\r\n")); + _ = socket.Send(Encoding.ASCII.GetBytes("Content-Type: application/octet-stream\r\n")); + _ = socket.Send(Encoding.ASCII.GetBytes("\r\n")); + _ = socket.Send(Encoding.ASCII.GetBytes("TOO_FEW")); } }; _proxyServer.Start(); @@ -77,23 +82,16 @@ namespace Renci.SshNet.Tests.Classes.Connection protected override void SetupMocks() { - SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) - .Returns(_clientSocket); + _ = SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) + .Returns(_clientSocket); } protected override void TearDown() { base.TearDown(); - if (_server != null) - { - _server.Dispose(); - } - - if (_proxyServer != null) - { - _proxyServer.Dispose(); - } + _server?.Dispose(); + _proxyServer?.Dispose(); } protected override void Act() @@ -102,7 +100,7 @@ namespace Renci.SshNet.Tests.Classes.Connection try { - Connector.Connect(_connectionInfo); + _ = Connector.Connect(_connectionInfo); Assert.Fail(); } catch (SshOperationTimeoutException ex) @@ -113,6 +111,9 @@ namespace Renci.SshNet.Tests.Classes.Connection { _stopWatch.Stop(); } + + // Give some time to process all messages + Thread.Sleep(200); } [TestMethod] @@ -152,7 +153,7 @@ namespace Renci.SshNet.Tests.Classes.Connection { try { - _clientSocket.Receive(new byte[0]); + _ = _clientSocket.Receive(new byte[0]); Assert.Fail(); } catch (ObjectDisposedException) diff --git a/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_TimeoutReadingStatusLine.cs b/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_TimeoutReadingStatusLine.cs index b4d77ec5..bec205f1 100644 --- a/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_TimeoutReadingStatusLine.cs +++ b/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_TimeoutReadingStatusLine.cs @@ -1,13 +1,16 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; -using Renci.SshNet.Common; -using Renci.SshNet.Connection; -using Renci.SshNet.Tests.Common; -using System; +using System; using System.Diagnostics; using System.Globalization; using System.Net; using System.Net.Sockets; +using System.Threading; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Moq; + +using Renci.SshNet.Common; +using Renci.SshNet.Tests.Common; namespace Renci.SshNet.Tests.Classes.Connection { @@ -36,8 +39,10 @@ namespace Renci.SshNet.Tests.Classes.Connection 8122, "proxyUser", "proxyPwd", - new KeyboardInteractiveAuthenticationMethod("user")); - _connectionInfo.Timeout = TimeSpan.FromMilliseconds(random.Next(50, 200)); + new KeyboardInteractiveAuthenticationMethod("user")) + { + Timeout = TimeSpan.FromMilliseconds(random.Next(50, 200)) + }; _stopWatch = new Stopwatch(); _actualException = null; @@ -53,23 +58,16 @@ namespace Renci.SshNet.Tests.Classes.Connection protected override void SetupMocks() { - SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) - .Returns(_clientSocket); + _ = SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) + .Returns(_clientSocket); } protected override void TearDown() { base.TearDown(); - if (_server != null) - { - _server.Dispose(); - } - - if (_proxyServer != null) - { - _proxyServer.Dispose(); - } + _server?.Dispose(); + _proxyServer?.Dispose(); } protected override void Act() @@ -78,7 +76,7 @@ namespace Renci.SshNet.Tests.Classes.Connection try { - Connector.Connect(_connectionInfo); + _ = Connector.Connect(_connectionInfo); Assert.Fail(); } catch (SshOperationTimeoutException ex) @@ -89,6 +87,9 @@ namespace Renci.SshNet.Tests.Classes.Connection { _stopWatch.Stop(); } + + // Give some time to process all messages + Thread.Sleep(200); } [TestMethod] @@ -122,7 +123,7 @@ namespace Renci.SshNet.Tests.Classes.Connection { try { - _clientSocket.Receive(new byte[0]); + _ = _clientSocket.Receive(new byte[0]); Assert.Fail(); } catch (ObjectDisposedException) diff --git a/src/Renci.SshNet.Tests/Classes/Connection/ProtocolVersionExchangeTest_ConnectionClosedByServer_NoDataSentByServer.cs b/src/Renci.SshNet.Tests/Classes/Connection/ProtocolVersionExchangeTest_ConnectionClosedByServer_NoDataSentByServer.cs index c01aacc1..43aa9a3b 100644 --- a/src/Renci.SshNet.Tests/Classes/Connection/ProtocolVersionExchangeTest_ConnectionClosedByServer_NoDataSentByServer.cs +++ b/src/Renci.SshNet.Tests/Classes/Connection/ProtocolVersionExchangeTest_ConnectionClosedByServer_NoDataSentByServer.cs @@ -1,11 +1,14 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Renci.SshNet.Common; -using Renci.SshNet.Connection; -using Renci.SshNet.Tests.Common; -using System; +using System; using System.Collections.Generic; using System.Net; using System.Net.Sockets; +using System.Threading; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Renci.SshNet.Common; +using Renci.SshNet.Connection; +using Renci.SshNet.Tests.Common; namespace Renci.SshNet.Tests.Classes.Connection { @@ -79,6 +82,9 @@ namespace Renci.SshNet.Tests.Classes.Connection { _actualException = ex; } + + // Give some time to process all messages + Thread.Sleep(200); } [TestMethod] diff --git a/src/Renci.SshNet.Tests/Classes/Connection/ProtocolVersionExchangeTest_ServerResponseContainsNullCharacter.cs b/src/Renci.SshNet.Tests/Classes/Connection/ProtocolVersionExchangeTest_ServerResponseContainsNullCharacter.cs index 29792888..2b3245ee 100644 --- a/src/Renci.SshNet.Tests/Classes/Connection/ProtocolVersionExchangeTest_ServerResponseContainsNullCharacter.cs +++ b/src/Renci.SshNet.Tests/Classes/Connection/ProtocolVersionExchangeTest_ServerResponseContainsNullCharacter.cs @@ -8,6 +8,7 @@ using System.Linq; using System.Net; using System.Net.Sockets; using System.Text; +using System.Threading; namespace Renci.SshNet.Tests.Classes.Connection { @@ -83,6 +84,9 @@ namespace Renci.SshNet.Tests.Classes.Connection { _actualException = ex; } + + // Give some time to process all messages + Thread.Sleep(200); } [TestMethod] diff --git a/src/Renci.SshNet.Tests/Classes/Connection/ProtocolVersionExchangeTest_ServerResponseInvalid_SshIdentificationOnlyContainsProtocolVersion.cs b/src/Renci.SshNet.Tests/Classes/Connection/ProtocolVersionExchangeTest_ServerResponseInvalid_SshIdentificationOnlyContainsProtocolVersion.cs index ff2bc5e5..3b064951 100644 --- a/src/Renci.SshNet.Tests/Classes/Connection/ProtocolVersionExchangeTest_ServerResponseInvalid_SshIdentificationOnlyContainsProtocolVersion.cs +++ b/src/Renci.SshNet.Tests/Classes/Connection/ProtocolVersionExchangeTest_ServerResponseInvalid_SshIdentificationOnlyContainsProtocolVersion.cs @@ -1,13 +1,16 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Renci.SshNet.Common; -using Renci.SshNet.Connection; -using Renci.SshNet.Tests.Common; -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Sockets; using System.Text; +using System.Threading; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Renci.SshNet.Common; +using Renci.SshNet.Connection; +using Renci.SshNet.Tests.Common; namespace Renci.SshNet.Tests.Classes.Connection { @@ -62,7 +65,9 @@ namespace Renci.SshNet.Tests.Classes.Connection _server.BytesReceived += (bytes, socket) => { _dataReceivedByServer.AddRange(bytes); - socket.Send(_serverIdentification); + + _ = socket.Send(_serverIdentification); + socket.Shutdown(SocketShutdown.Send); }; _server.Disconnected += (socket) => _clientDisconnected = true; @@ -77,13 +82,16 @@ namespace Renci.SshNet.Tests.Classes.Connection { try { - _protocolVersionExchange.Start(_clientVersion, _client, _timeout); + _ = _protocolVersionExchange.Start(_clientVersion, _client, _timeout); Assert.Fail(); } catch (SshConnectionException ex) { _actualException = ex; } + + // Give some time to process all messages + Thread.Sleep(200); } [TestMethod] diff --git a/src/Renci.SshNet.Tests/Classes/Connection/ProtocolVersionExchangeTest_ServerResponseValid_Comments.cs b/src/Renci.SshNet.Tests/Classes/Connection/ProtocolVersionExchangeTest_ServerResponseValid_Comments.cs index df533ebe..4108c7c0 100644 --- a/src/Renci.SshNet.Tests/Classes/Connection/ProtocolVersionExchangeTest_ServerResponseValid_Comments.cs +++ b/src/Renci.SshNet.Tests/Classes/Connection/ProtocolVersionExchangeTest_ServerResponseValid_Comments.cs @@ -1,5 +1,5 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; -using Renci.SshNet.Common; + using Renci.SshNet.Connection; using Renci.SshNet.Tests.Common; using System; @@ -8,6 +8,7 @@ using System.Linq; using System.Net; using System.Net.Sockets; using System.Text; +using System.Threading; namespace Renci.SshNet.Tests.Classes.Connection { @@ -76,6 +77,9 @@ namespace Renci.SshNet.Tests.Classes.Connection protected void Act() { _actual = _protocolVersionExchange.Start(_clientVersion, _client, _timeout); + + // Give some time to process all messages + Thread.Sleep(200); } [TestMethod] diff --git a/src/Renci.SshNet.Tests/Classes/Connection/ProtocolVersionExchangeTest_ServerResponseValid_NoComments.cs b/src/Renci.SshNet.Tests/Classes/Connection/ProtocolVersionExchangeTest_ServerResponseValid_NoComments.cs index 8b4f5997..f3ce9ccf 100644 --- a/src/Renci.SshNet.Tests/Classes/Connection/ProtocolVersionExchangeTest_ServerResponseValid_NoComments.cs +++ b/src/Renci.SshNet.Tests/Classes/Connection/ProtocolVersionExchangeTest_ServerResponseValid_NoComments.cs @@ -1,5 +1,5 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; -using Renci.SshNet.Common; + using Renci.SshNet.Connection; using Renci.SshNet.Tests.Common; using System; @@ -8,6 +8,7 @@ using System.Linq; using System.Net; using System.Net.Sockets; using System.Text; +using System.Threading; namespace Renci.SshNet.Tests.Classes.Connection { @@ -76,6 +77,9 @@ namespace Renci.SshNet.Tests.Classes.Connection protected void Act() { _actual = _protocolVersionExchange.Start(_clientVersion, _client, _timeout); + + // Give some time to process all messages + Thread.Sleep(200); } [TestMethod] diff --git a/src/Renci.SshNet.Tests/Classes/Connection/ProtocolVersionExchangeTest_ServerResponseValid_TerminatedByLineFeedWithoutCarriageReturn.cs b/src/Renci.SshNet.Tests/Classes/Connection/ProtocolVersionExchangeTest_ServerResponseValid_TerminatedByLineFeedWithoutCarriageReturn.cs index 98bbf6fe..2c35bce5 100644 --- a/src/Renci.SshNet.Tests/Classes/Connection/ProtocolVersionExchangeTest_ServerResponseValid_TerminatedByLineFeedWithoutCarriageReturn.cs +++ b/src/Renci.SshNet.Tests/Classes/Connection/ProtocolVersionExchangeTest_ServerResponseValid_TerminatedByLineFeedWithoutCarriageReturn.cs @@ -1,13 +1,15 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Renci.SshNet.Common; -using Renci.SshNet.Connection; -using Renci.SshNet.Tests.Common; -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Sockets; using System.Text; +using System.Threading; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Renci.SshNet.Connection; +using Renci.SshNet.Tests.Common; namespace Renci.SshNet.Tests.Classes.Connection { @@ -60,11 +62,13 @@ namespace Renci.SshNet.Tests.Classes.Connection _server = new AsyncSocketListener(_serverEndPoint); _server.Start(); _server.BytesReceived += (bytes, socket) => - { - _dataReceivedByServer.AddRange(bytes); - socket.Send(_serverIdentification); - socket.Shutdown(SocketShutdown.Send); - }; + { + _dataReceivedByServer.AddRange(bytes); + + _ = socket.Send(_serverIdentification); + + socket.Shutdown(SocketShutdown.Send); + }; _server.Disconnected += (socket) => _clientDisconnected = true; _client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); @@ -76,6 +80,9 @@ namespace Renci.SshNet.Tests.Classes.Connection protected void Act() { _actual = _protocolVersionExchange.Start(_clientVersion, _client, _timeout); + + // Give some time to process all messages + Thread.Sleep(200); } [TestMethod] diff --git a/src/Renci.SshNet.Tests/Classes/Connection/ProtocolVersionExchangeTest_TimeoutReadingIdentificationString.cs b/src/Renci.SshNet.Tests/Classes/Connection/ProtocolVersionExchangeTest_TimeoutReadingIdentificationString.cs index 6ef1668f..3710e206 100644 --- a/src/Renci.SshNet.Tests/Classes/Connection/ProtocolVersionExchangeTest_TimeoutReadingIdentificationString.cs +++ b/src/Renci.SshNet.Tests/Classes/Connection/ProtocolVersionExchangeTest_TimeoutReadingIdentificationString.cs @@ -1,13 +1,16 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Renci.SshNet.Common; -using Renci.SshNet.Connection; -using Renci.SshNet.Tests.Common; -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Sockets; using System.Text; +using System.Threading; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Renci.SshNet.Common; +using Renci.SshNet.Connection; +using Renci.SshNet.Tests.Common; namespace Renci.SshNet.Tests.Classes.Connection { @@ -60,7 +63,8 @@ namespace Renci.SshNet.Tests.Classes.Connection _server.BytesReceived += (bytes, socket) => { _dataReceivedByServer.AddRange(bytes); - socket.Send(Encoding.UTF8.GetBytes("Welcome!\r\n")); + + _ = socket.Send(Encoding.UTF8.GetBytes("Welcome!\r\n")); }; _server.Disconnected += (socket) => _clientDisconnected = true; @@ -74,13 +78,16 @@ namespace Renci.SshNet.Tests.Classes.Connection { try { - _protocolVersionExchange.Start(_clientVersion, _client, _timeout); + _ = _protocolVersionExchange.Start(_clientVersion, _client, _timeout); Assert.Fail(); } catch (SshOperationTimeoutException ex) { _actualException = ex; } + + // Give some time to process all messages + Thread.Sleep(200); } [TestMethod] diff --git a/src/Renci.SshNet.Tests/Classes/Connection/Socks4ConnectorTestBase.cs b/src/Renci.SshNet.Tests/Classes/Connection/Socks4ConnectorTestBase.cs index 50f62256..0c3497f1 100644 --- a/src/Renci.SshNet.Tests/Classes/Connection/Socks4ConnectorTestBase.cs +++ b/src/Renci.SshNet.Tests/Classes/Connection/Socks4ConnectorTestBase.cs @@ -1,7 +1,9 @@ -using Moq; +using System.Net; + +using Moq; + using Renci.SshNet.Connection; using Renci.SshNet.Tests.Common; -using System.Net; namespace Renci.SshNet.Tests.Classes.Connection { @@ -32,7 +34,7 @@ namespace Renci.SshNet.Tests.Classes.Connection SetupData(); SetupMocks(); } - + protected ConnectionInfo CreateConnectionInfo(string proxyUser, string proxyPassword) { return new ConnectionInfo(IPAddress.Loopback.ToString(), diff --git a/src/Renci.SshNet.Tests/Classes/Connection/Socks4ConnectorTest_Connect_ConnectionRejectedByProxy.cs b/src/Renci.SshNet.Tests/Classes/Connection/Socks4ConnectorTest_Connect_ConnectionRejectedByProxy.cs index 3ef993f2..a220b942 100644 --- a/src/Renci.SshNet.Tests/Classes/Connection/Socks4ConnectorTest_Connect_ConnectionRejectedByProxy.cs +++ b/src/Renci.SshNet.Tests/Classes/Connection/Socks4ConnectorTest_Connect_ConnectionRejectedByProxy.cs @@ -1,11 +1,15 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; -using Renci.SshNet.Common; -using Renci.SshNet.Tests.Common; -using System; +using System; using System.Collections.Generic; using System.Net; using System.Net.Sockets; +using System.Threading; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Moq; + +using Renci.SshNet.Common; +using Renci.SshNet.Tests.Common; namespace Renci.SshNet.Tests.Classes.Connection { @@ -35,7 +39,7 @@ namespace Renci.SshNet.Tests.Classes.Connection { if (_bytesReceivedByProxy.Count == 0) { - socket.Send(new byte[] + _ = socket.Send(new byte[] { // Reply version (null byte) 0x00, @@ -51,36 +55,32 @@ namespace Renci.SshNet.Tests.Classes.Connection protected override void SetupMocks() { - SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) - .Returns(_clientSocket); + _ = SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) + .Returns(_clientSocket); } protected override void TearDown() { base.TearDown(); - if (_proxyServer != null) - { - _proxyServer.Dispose(); - } - - if (_clientSocket != null) - { - _clientSocket.Dispose(); - } + _proxyServer?.Dispose(); + _clientSocket?.Dispose(); } protected override void Act() { try { - Connector.Connect(_connectionInfo); + _ = Connector.Connect(_connectionInfo); Assert.Fail(); } catch (ProxyException ex) { _actualException = ex; } + + // Give some time to process all messages + Thread.Sleep(200); } [TestMethod] @@ -102,7 +102,7 @@ namespace Renci.SshNet.Tests.Classes.Connection { try { - _clientSocket.Receive(new byte[0]); + _ = _clientSocket.Receive(new byte[0]); Assert.Fail(); } catch (ObjectDisposedException) @@ -116,19 +116,5 @@ namespace Renci.SshNet.Tests.Classes.Connection SocketFactoryMock.Verify(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp), Times.Once()); } - - private static byte GetNotSupportedSocksVersion() - { - var random = new Random(); - - while (true) - { - var socksVersion = random.Next(1, 255); - if (socksVersion != 4) - { - return (byte) socksVersion; - } - } - } } } diff --git a/src/Renci.SshNet.Tests/Classes/Connection/Socks4ConnectorTest_Connect_ConnectionSucceeded.cs b/src/Renci.SshNet.Tests/Classes/Connection/Socks4ConnectorTest_Connect_ConnectionSucceeded.cs index 9f184942..5aabb816 100644 --- a/src/Renci.SshNet.Tests/Classes/Connection/Socks4ConnectorTest_Connect_ConnectionSucceeded.cs +++ b/src/Renci.SshNet.Tests/Classes/Connection/Socks4ConnectorTest_Connect_ConnectionSucceeded.cs @@ -1,13 +1,16 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; -using Renci.SshNet.Common; -using Renci.SshNet.Connection; -using Renci.SshNet.Tests.Common; -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Sockets; +using System.Threading; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Moq; + +using Renci.SshNet.Common; +using Renci.SshNet.Tests.Common; namespace Renci.SshNet.Tests.Classes.Connection { @@ -42,7 +45,7 @@ namespace Renci.SshNet.Tests.Classes.Connection if (_bytesReceivedByProxy.Count == bytesReceived.Length) { // Send SOCKS response - socket.Send(new byte[] + _ = socket.Send(new byte[] { // Reply version (null byte) 0x00, @@ -59,7 +62,7 @@ namespace Renci.SshNet.Tests.Classes.Connection }); // Send extra byte to allow us to verify that connector did not consume too much - socket.Send(new byte[] + _ = socket.Send(new byte[] { 0xfe }); @@ -70,28 +73,24 @@ namespace Renci.SshNet.Tests.Classes.Connection protected override void SetupMocks() { - SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) - .Returns(_clientSocket); + _ = SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) + .Returns(_clientSocket); } protected override void TearDown() { base.TearDown(); - if (_proxyServer != null) - { - _proxyServer.Dispose(); - } - - if (_clientSocket != null) - { - _clientSocket.Dispose(); - } + _proxyServer?.Dispose(); + _clientSocket?.Dispose(); } protected override void Act() { _actual = Connector.Connect(_connectionInfo); + + // Give some time to process all messages + Thread.Sleep(200); } [TestMethod] diff --git a/src/Renci.SshNet.Tests/Classes/Connection/Socks4ConnectorTest_Connect_ConnectionToProxyRefused.cs b/src/Renci.SshNet.Tests/Classes/Connection/Socks4ConnectorTest_Connect_ConnectionToProxyRefused.cs index 176a5a57..7c41d93d 100644 --- a/src/Renci.SshNet.Tests/Classes/Connection/Socks4ConnectorTest_Connect_ConnectionToProxyRefused.cs +++ b/src/Renci.SshNet.Tests/Classes/Connection/Socks4ConnectorTest_Connect_ConnectionToProxyRefused.cs @@ -1,10 +1,11 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; -using Renci.SshNet.Common; -using System; +using System; using System.Diagnostics; using System.Net.Sockets; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Moq; + namespace Renci.SshNet.Tests.Classes.Connection { [TestClass] @@ -29,18 +30,15 @@ namespace Renci.SshNet.Tests.Classes.Connection protected override void SetupMocks() { - SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) - .Returns(_clientSocket); + _ = SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) + .Returns(_clientSocket); } protected override void TearDown() { base.TearDown(); - if (_clientSocket != null) - { - _clientSocket.Dispose(); - } + _clientSocket?.Dispose(); } protected override void Act() @@ -49,7 +47,7 @@ namespace Renci.SshNet.Tests.Classes.Connection try { - Connector.Connect(_connectionInfo); + _ = Connector.Connect(_connectionInfo); Assert.Fail(); } catch (SocketException ex) @@ -85,7 +83,7 @@ namespace Renci.SshNet.Tests.Classes.Connection { try { - _clientSocket.Receive(new byte[0]); + _ = _clientSocket.Receive(new byte[0]); Assert.Fail(); } catch (ObjectDisposedException) diff --git a/src/Renci.SshNet.Tests/Classes/Connection/Socks4ConnectorTest_Connect_TimeoutConnectingToProxy.cs b/src/Renci.SshNet.Tests/Classes/Connection/Socks4ConnectorTest_Connect_TimeoutConnectingToProxy.cs index 7c7ad08f..d7ad4215 100644 --- a/src/Renci.SshNet.Tests/Classes/Connection/Socks4ConnectorTest_Connect_TimeoutConnectingToProxy.cs +++ b/src/Renci.SshNet.Tests/Classes/Connection/Socks4ConnectorTest_Connect_TimeoutConnectingToProxy.cs @@ -31,18 +31,15 @@ namespace Renci.SshNet.Tests.Classes.Connection protected override void SetupMocks() { - SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) - .Returns(_clientSocket); + _ = SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) + .Returns(_clientSocket); } protected override void TearDown() { base.TearDown(); - if (_clientSocket != null) - { - _clientSocket.Dispose(); - } + _clientSocket?.Dispose(); } protected override void Act() @@ -51,7 +48,7 @@ namespace Renci.SshNet.Tests.Classes.Connection try { - Connector.Connect(_connectionInfo); + _ = Connector.Connect(_connectionInfo); Assert.Fail(); } catch (SshOperationTimeoutException ex) @@ -88,7 +85,7 @@ namespace Renci.SshNet.Tests.Classes.Connection { try { - _clientSocket.Receive(new byte[0]); + _ = _clientSocket.Receive(new byte[0]); Assert.Fail(); } catch (ObjectDisposedException) diff --git a/src/Renci.SshNet.Tests/Classes/Connection/Socks4ConnectorTest_Connect_TimeoutReadingDestinationAddress.cs b/src/Renci.SshNet.Tests/Classes/Connection/Socks4ConnectorTest_Connect_TimeoutReadingDestinationAddress.cs index df8cc365..d87969ce 100644 --- a/src/Renci.SshNet.Tests/Classes/Connection/Socks4ConnectorTest_Connect_TimeoutReadingDestinationAddress.cs +++ b/src/Renci.SshNet.Tests/Classes/Connection/Socks4ConnectorTest_Connect_TimeoutReadingDestinationAddress.cs @@ -1,13 +1,16 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; -using Renci.SshNet.Common; -using Renci.SshNet.Connection; -using Renci.SshNet.Tests.Common; -using System; +using System; using System.Diagnostics; using System.Globalization; using System.Net; using System.Net.Sockets; +using System.Threading; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Moq; + +using Renci.SshNet.Common; +using Renci.SshNet.Tests.Common; namespace Renci.SshNet.Tests.Classes.Connection { @@ -39,7 +42,7 @@ namespace Renci.SshNet.Tests.Classes.Connection _proxyServer.Disconnected += socket => _disconnected = true; _proxyServer.Connected += socket => { - socket.Send(new byte[] + _ = socket.Send(new byte[] { // Reply version (null byte) 0x00, @@ -57,23 +60,16 @@ namespace Renci.SshNet.Tests.Classes.Connection protected override void SetupMocks() { - SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) - .Returns(_clientSocket); + _ = SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) + .Returns(_clientSocket); } protected override void TearDown() { base.TearDown(); - if (_server != null) - { - _server.Dispose(); - } - - if (_proxyServer != null) - { - _proxyServer.Dispose(); - } + _server?.Dispose(); + _proxyServer?.Dispose(); } protected override void Act() @@ -82,7 +78,7 @@ namespace Renci.SshNet.Tests.Classes.Connection try { - Connector.Connect(_connectionInfo); + _ = Connector.Connect(_connectionInfo); Assert.Fail(); } catch (SshOperationTimeoutException ex) @@ -93,6 +89,9 @@ namespace Renci.SshNet.Tests.Classes.Connection { _stopWatch.Stop(); } + + // Give some time to process all messages + Thread.Sleep(200); } [TestMethod] @@ -126,7 +125,7 @@ namespace Renci.SshNet.Tests.Classes.Connection { try { - _clientSocket.Receive(new byte[0]); + _ = _clientSocket.Receive(new byte[0]); Assert.Fail(); } catch (ObjectDisposedException) diff --git a/src/Renci.SshNet.Tests/Classes/Connection/Socks4ConnectorTest_Connect_TimeoutReadingReplyCode.cs b/src/Renci.SshNet.Tests/Classes/Connection/Socks4ConnectorTest_Connect_TimeoutReadingReplyCode.cs index 501ff8fc..8f6ee901 100644 --- a/src/Renci.SshNet.Tests/Classes/Connection/Socks4ConnectorTest_Connect_TimeoutReadingReplyCode.cs +++ b/src/Renci.SshNet.Tests/Classes/Connection/Socks4ConnectorTest_Connect_TimeoutReadingReplyCode.cs @@ -1,13 +1,16 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; -using Renci.SshNet.Common; -using Renci.SshNet.Connection; -using Renci.SshNet.Tests.Common; -using System; +using System; using System.Diagnostics; using System.Globalization; using System.Net; using System.Net.Sockets; +using System.Threading; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Moq; + +using Renci.SshNet.Common; +using Renci.SshNet.Tests.Common; namespace Renci.SshNet.Tests.Classes.Connection { @@ -39,7 +42,7 @@ namespace Renci.SshNet.Tests.Classes.Connection _proxyServer.Disconnected += socket => _disconnected = true; _proxyServer.Connected += socket => { - socket.Send(new byte[] + _ = socket.Send(new byte[] { // Reply version (null byte) 0x00 @@ -53,23 +56,16 @@ namespace Renci.SshNet.Tests.Classes.Connection protected override void SetupMocks() { - SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) - .Returns(_clientSocket); + _ = SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) + .Returns(_clientSocket); } protected override void TearDown() { base.TearDown(); - if (_server != null) - { - _server.Dispose(); - } - - if (_proxyServer != null) - { - _proxyServer.Dispose(); - } + _server?.Dispose(); + _proxyServer?.Dispose(); } protected override void Act() @@ -78,7 +74,7 @@ namespace Renci.SshNet.Tests.Classes.Connection try { - Connector.Connect(_connectionInfo); + _ = Connector.Connect(_connectionInfo); Assert.Fail(); } catch (SshOperationTimeoutException ex) @@ -89,6 +85,9 @@ namespace Renci.SshNet.Tests.Classes.Connection { _stopWatch.Stop(); } + + // Give some time to process all messages + Thread.Sleep(200); } [TestMethod] diff --git a/src/Renci.SshNet.Tests/Classes/Connection/Socks4ConnectorTest_Connect_TimeoutReadingReplyVersion.cs b/src/Renci.SshNet.Tests/Classes/Connection/Socks4ConnectorTest_Connect_TimeoutReadingReplyVersion.cs index 679d213a..4ca6e0c5 100644 --- a/src/Renci.SshNet.Tests/Classes/Connection/Socks4ConnectorTest_Connect_TimeoutReadingReplyVersion.cs +++ b/src/Renci.SshNet.Tests/Classes/Connection/Socks4ConnectorTest_Connect_TimeoutReadingReplyVersion.cs @@ -1,13 +1,13 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using Renci.SshNet.Common; -using Renci.SshNet.Connection; using Renci.SshNet.Tests.Common; using System; using System.Diagnostics; using System.Globalization; using System.Net; using System.Net.Sockets; +using System.Threading; namespace Renci.SshNet.Tests.Classes.Connection { @@ -45,23 +45,16 @@ namespace Renci.SshNet.Tests.Classes.Connection protected override void SetupMocks() { - SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) - .Returns(_clientSocket); + _ = SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) + .Returns(_clientSocket); } protected override void TearDown() { base.TearDown(); - if (_server != null) - { - _server.Dispose(); - } - - if (_proxyServer != null) - { - _proxyServer.Dispose(); - } + _server?.Dispose(); + _proxyServer?.Dispose(); } protected override void Act() @@ -70,7 +63,7 @@ namespace Renci.SshNet.Tests.Classes.Connection try { - Connector.Connect(_connectionInfo); + _ = Connector.Connect(_connectionInfo); Assert.Fail(); } catch (SshOperationTimeoutException ex) @@ -81,6 +74,9 @@ namespace Renci.SshNet.Tests.Classes.Connection { _stopWatch.Stop(); } + + // Give some time to process all messages + Thread.Sleep(200); } [TestMethod] @@ -114,7 +110,7 @@ namespace Renci.SshNet.Tests.Classes.Connection { try { - _clientSocket.Receive(new byte[0]); + _ = _clientSocket.Receive(new byte[0]); Assert.Fail(); } catch (ObjectDisposedException) diff --git a/src/Renci.SshNet.Tests/Classes/Connection/Socks5ConnectorTestBase.cs b/src/Renci.SshNet.Tests/Classes/Connection/Socks5ConnectorTestBase.cs index 2d2f2cb9..fefe5728 100644 --- a/src/Renci.SshNet.Tests/Classes/Connection/Socks5ConnectorTestBase.cs +++ b/src/Renci.SshNet.Tests/Classes/Connection/Socks5ConnectorTestBase.cs @@ -1,6 +1,8 @@ using Moq; + using Renci.SshNet.Connection; using Renci.SshNet.Tests.Common; + using System; using System.Net; using System.Text; @@ -58,8 +60,8 @@ namespace Renci.SshNet.Tests.Classes.Connection for (var i = 0; i < length; i++) { - var @char = (char) random.Next(offset, offset + 26); - sb.Append(@char); + var c = (char) random.Next(offset, offset + 26); + _ = sb.Append(c); } return sb.ToString(); diff --git a/src/Renci.SshNet.Tests/Classes/Connection/Socks5ConnectorTest_Connect_ConnectionToProxyRefused.cs b/src/Renci.SshNet.Tests/Classes/Connection/Socks5ConnectorTest_Connect_ConnectionToProxyRefused.cs index 82e195fb..05bd850d 100644 --- a/src/Renci.SshNet.Tests/Classes/Connection/Socks5ConnectorTest_Connect_ConnectionToProxyRefused.cs +++ b/src/Renci.SshNet.Tests/Classes/Connection/Socks5ConnectorTest_Connect_ConnectionToProxyRefused.cs @@ -1,11 +1,11 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; -using Renci.SshNet.Common; -using System; +using System; using System.Diagnostics; -using System.Net; using System.Net.Sockets; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Moq; + namespace Renci.SshNet.Tests.Classes.Connection { [TestClass] @@ -30,18 +30,15 @@ namespace Renci.SshNet.Tests.Classes.Connection protected override void SetupMocks() { - SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) - .Returns(_clientSocket); + _ = SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) + .Returns(_clientSocket); } protected override void TearDown() { base.TearDown(); - if (_clientSocket != null) - { - _clientSocket.Dispose(); - } + _clientSocket?.Dispose(); } protected override void Act() @@ -50,7 +47,7 @@ namespace Renci.SshNet.Tests.Classes.Connection try { - Connector.Connect(_connectionInfo); + _ = Connector.Connect(_connectionInfo); Assert.Fail(); } catch (SocketException ex) @@ -86,7 +83,7 @@ namespace Renci.SshNet.Tests.Classes.Connection { try { - _clientSocket.Receive(new byte[0]); + _ = _clientSocket.Receive(new byte[0]); Assert.Fail(); } catch (ObjectDisposedException) diff --git a/src/Renci.SshNet.Tests/Classes/Connection/Socks5ConnectorTest_Connect_NoAuthentication_ConnectionSucceeded.cs b/src/Renci.SshNet.Tests/Classes/Connection/Socks5ConnectorTest_Connect_NoAuthentication_ConnectionSucceeded.cs index 7f4b108c..e5154884 100644 --- a/src/Renci.SshNet.Tests/Classes/Connection/Socks5ConnectorTest_Connect_NoAuthentication_ConnectionSucceeded.cs +++ b/src/Renci.SshNet.Tests/Classes/Connection/Socks5ConnectorTest_Connect_NoAuthentication_ConnectionSucceeded.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Sockets; +using System.Threading; namespace Renci.SshNet.Tests.Classes.Connection { @@ -38,7 +39,7 @@ namespace Renci.SshNet.Tests.Classes.Connection { // We received the greeting - socket.Send(new byte[] + _ = socket.Send(new byte[] { // SOCKS version 0x05, @@ -50,7 +51,7 @@ namespace Renci.SshNet.Tests.Classes.Connection { // We received the connection request - socket.Send(new byte[] + _ = socket.Send(new byte[] { // SOCKS version 0x05, @@ -61,7 +62,7 @@ namespace Renci.SshNet.Tests.Classes.Connection }); // Send server bound address - socket.Send(new byte[] + _ = socket.Send(new byte[] { // IPv6 0x04, @@ -88,7 +89,7 @@ namespace Renci.SshNet.Tests.Classes.Connection }); // Send extra byte to allow us to verify that connector did not consume too much - socket.Send(new byte[] + _ = socket.Send(new byte[] { 0xff }); @@ -99,18 +100,15 @@ namespace Renci.SshNet.Tests.Classes.Connection protected override void SetupMocks() { - SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) - .Returns(_clientSocket); + _ = SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) + .Returns(_clientSocket); } protected override void TearDown() { base.TearDown(); - if (_proxyServer != null) - { - _proxyServer.Dispose(); - } + _proxyServer?.Dispose(); if (_clientSocket != null) { @@ -122,6 +120,9 @@ namespace Renci.SshNet.Tests.Classes.Connection protected override void Act() { _actual = Connector.Connect(_connectionInfo); + + // Give some time to process all messages + Thread.Sleep(200); } [TestMethod] diff --git a/src/Renci.SshNet.Tests/Classes/Connection/Socks5ConnectorTest_Connect_ProxySocksVersionIsNotSupported.cs b/src/Renci.SshNet.Tests/Classes/Connection/Socks5ConnectorTest_Connect_ProxySocksVersionIsNotSupported.cs index dc9a3a9e..76cd2568 100644 --- a/src/Renci.SshNet.Tests/Classes/Connection/Socks5ConnectorTest_Connect_ProxySocksVersionIsNotSupported.cs +++ b/src/Renci.SshNet.Tests/Classes/Connection/Socks5ConnectorTest_Connect_ProxySocksVersionIsNotSupported.cs @@ -1,10 +1,14 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; -using Renci.SshNet.Common; -using Renci.SshNet.Tests.Common; -using System; +using System; using System.Net; using System.Net.Sockets; +using System.Threading; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Moq; + +using Renci.SshNet.Common; +using Renci.SshNet.Tests.Common; namespace Renci.SshNet.Tests.Classes.Connection { @@ -33,43 +37,39 @@ namespace Renci.SshNet.Tests.Classes.Connection _proxyServer.Disconnected += socket => _disconnected = true; _proxyServer.BytesReceived += (bytesReceived, socket) => { - socket.Send(new byte[] { _proxySocksVersion }); + _ = socket.Send(new byte[] { _proxySocksVersion }); }; _proxyServer.Start(); } protected override void SetupMocks() { - SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) - .Returns(_clientSocket); + _ = SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) + .Returns(_clientSocket); } protected override void TearDown() { base.TearDown(); - if (_proxyServer != null) - { - _proxyServer.Dispose(); - } - - if (_clientSocket != null) - { - _clientSocket.Dispose(); - } + _proxyServer?.Dispose(); + _clientSocket?.Dispose(); } protected override void Act() { try { - Connector.Connect(_connectionInfo); + _ = Connector.Connect(_connectionInfo); Assert.Fail(); } catch (ProxyException ex) { _actualException = ex; } + + // Give some time to process all messages + Thread.Sleep(200); } [TestMethod] @@ -91,7 +91,7 @@ namespace Renci.SshNet.Tests.Classes.Connection { try { - _clientSocket.Receive(new byte[0]); + _ = _clientSocket.Receive(new byte[0]); Assert.Fail(); } catch (ObjectDisposedException) diff --git a/src/Renci.SshNet.Tests/Classes/Connection/Socks5ConnectorTest_Connect_TimeoutConnectingToProxy.cs b/src/Renci.SshNet.Tests/Classes/Connection/Socks5ConnectorTest_Connect_TimeoutConnectingToProxy.cs index e93d8dc5..720c24ff 100644 --- a/src/Renci.SshNet.Tests/Classes/Connection/Socks5ConnectorTest_Connect_TimeoutConnectingToProxy.cs +++ b/src/Renci.SshNet.Tests/Classes/Connection/Socks5ConnectorTest_Connect_TimeoutConnectingToProxy.cs @@ -1,12 +1,14 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; -using Renci.SshNet.Common; -using System; +using System; using System.Diagnostics; using System.Globalization; -using System.Net; using System.Net.Sockets; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Moq; + +using Renci.SshNet.Common; + namespace Renci.SshNet.Tests.Classes.Connection { [TestClass] @@ -33,18 +35,15 @@ namespace Renci.SshNet.Tests.Classes.Connection protected override void SetupMocks() { - SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) - .Returns(_clientSocket); + _ = SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) + .Returns(_clientSocket); } protected override void TearDown() { base.TearDown(); - if (_clientSocket != null) - { - _clientSocket.Dispose(); - } + _clientSocket?.Dispose(); } protected override void Act() @@ -53,7 +52,7 @@ namespace Renci.SshNet.Tests.Classes.Connection try { - Connector.Connect(_connectionInfo); + _ = Connector.Connect(_connectionInfo); Assert.Fail(); } catch (SshOperationTimeoutException ex) @@ -90,7 +89,7 @@ namespace Renci.SshNet.Tests.Classes.Connection { try { - _clientSocket.Receive(new byte[0]); + _ = _clientSocket.Receive(new byte[0]); Assert.Fail(); } catch (ObjectDisposedException) diff --git a/src/Renci.SshNet.Tests/Classes/Connection/Socks5ConnectorTest_Connect_UserNamePasswordAuthentication_AuthenticationFailed.cs b/src/Renci.SshNet.Tests/Classes/Connection/Socks5ConnectorTest_Connect_UserNamePasswordAuthentication_AuthenticationFailed.cs index 878636ba..e6bcf5ab 100644 --- a/src/Renci.SshNet.Tests/Classes/Connection/Socks5ConnectorTest_Connect_UserNamePasswordAuthentication_AuthenticationFailed.cs +++ b/src/Renci.SshNet.Tests/Classes/Connection/Socks5ConnectorTest_Connect_UserNamePasswordAuthentication_AuthenticationFailed.cs @@ -8,6 +8,7 @@ using System.Linq; using System.Net; using System.Net.Sockets; using System.Text; +using System.Threading; namespace Renci.SshNet.Tests.Classes.Connection { @@ -41,7 +42,7 @@ namespace Renci.SshNet.Tests.Classes.Connection { // We received the greeting - socket.Send(new byte[] + _ = socket.Send(new byte[] { // SOCKS version 0x05, @@ -53,7 +54,7 @@ namespace Renci.SshNet.Tests.Classes.Connection { // We received the username/password authentication request - socket.Send(new byte[] + _ = socket.Send(new byte[] { // Authentication version 0x01, @@ -67,36 +68,32 @@ namespace Renci.SshNet.Tests.Classes.Connection protected override void SetupMocks() { - SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) - .Returns(_clientSocket); + _ = SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) + .Returns(_clientSocket); } protected override void TearDown() { base.TearDown(); - if (_proxyServer != null) - { - _proxyServer.Dispose(); - } - - if (_clientSocket != null) - { - _clientSocket.Dispose(); - } + _proxyServer?.Dispose(); + _clientSocket?.Dispose(); } protected override void Act() { try { - Connector.Connect(_connectionInfo); + _ = Connector.Connect(_connectionInfo); Assert.Fail(); } catch (ProxyException ex) { _actualException = ex; } + + // Give some time to process all messages + Thread.Sleep(200); } [TestMethod] @@ -159,7 +156,7 @@ namespace Renci.SshNet.Tests.Classes.Connection { try { - _clientSocket.Receive(new byte[0]); + _ = _clientSocket.Receive(new byte[0]); Assert.Fail(); } catch (ObjectDisposedException) diff --git a/src/Renci.SshNet.Tests/Classes/Connection/Socks5ConnectorTest_Connect_UserNamePasswordAuthentication_ConnectionSucceeded.cs b/src/Renci.SshNet.Tests/Classes/Connection/Socks5ConnectorTest_Connect_UserNamePasswordAuthentication_ConnectionSucceeded.cs index b94c40e8..0c13af50 100644 --- a/src/Renci.SshNet.Tests/Classes/Connection/Socks5ConnectorTest_Connect_UserNamePasswordAuthentication_ConnectionSucceeded.cs +++ b/src/Renci.SshNet.Tests/Classes/Connection/Socks5ConnectorTest_Connect_UserNamePasswordAuthentication_ConnectionSucceeded.cs @@ -1,13 +1,17 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; -using Renci.SshNet.Common; -using Renci.SshNet.Tests.Common; -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Sockets; using System.Text; +using System.Threading; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Moq; + +using Renci.SshNet.Common; +using Renci.SshNet.Tests.Common; namespace Renci.SshNet.Tests.Classes.Connection { @@ -39,7 +43,7 @@ namespace Renci.SshNet.Tests.Classes.Connection { // We received the greeting - socket.Send(new byte[] + _ = socket.Send(new byte[] { // SOCKS version 0x05, @@ -51,7 +55,7 @@ namespace Renci.SshNet.Tests.Classes.Connection { // We received the username/password authentication request - socket.Send(new byte[] + _ = socket.Send(new byte[] { // Authentication version 0x01, @@ -63,7 +67,7 @@ namespace Renci.SshNet.Tests.Classes.Connection { // We received the connection request - socket.Send(new byte[] + _ = socket.Send(new byte[] { // SOCKS version 0x05, @@ -74,7 +78,7 @@ namespace Renci.SshNet.Tests.Classes.Connection }); // Send server bound address - socket.Send(new byte[] + _ = socket.Send(new byte[] { // IPv4 0x01, @@ -89,7 +93,7 @@ namespace Renci.SshNet.Tests.Classes.Connection }); // Send extra byte to allow us to verify that connector did not consume too much - socket.Send(new byte[] + _ = socket.Send(new byte[] { 0xfe }); @@ -100,18 +104,15 @@ namespace Renci.SshNet.Tests.Classes.Connection protected override void SetupMocks() { - SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) - .Returns(_clientSocket); + _ = SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) + .Returns(_clientSocket); } protected override void TearDown() { base.TearDown(); - if (_proxyServer != null) - { - _proxyServer.Dispose(); - } + _proxyServer?.Dispose(); if (_clientSocket != null) { @@ -123,6 +124,9 @@ namespace Renci.SshNet.Tests.Classes.Connection protected override void Act() { _actual = Connector.Connect(_connectionInfo); + + // Give some time to process all messages + Thread.Sleep(200); } [TestMethod] diff --git a/src/Renci.SshNet.Tests/Classes/Connection/Socks5ConnectorTest_Connect_UserNamePasswordAuthentication_PasswordExceedsMaximumLength.cs b/src/Renci.SshNet.Tests/Classes/Connection/Socks5ConnectorTest_Connect_UserNamePasswordAuthentication_PasswordExceedsMaximumLength.cs index 61db86a4..b36a7eff 100644 --- a/src/Renci.SshNet.Tests/Classes/Connection/Socks5ConnectorTest_Connect_UserNamePasswordAuthentication_PasswordExceedsMaximumLength.cs +++ b/src/Renci.SshNet.Tests/Classes/Connection/Socks5ConnectorTest_Connect_UserNamePasswordAuthentication_PasswordExceedsMaximumLength.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Sockets; +using System.Threading; namespace Renci.SshNet.Tests.Classes.Connection { @@ -40,7 +41,7 @@ namespace Renci.SshNet.Tests.Classes.Connection // Wait until we received the greeting if (_bytesReceivedByProxy.Count == 4) { - socket.Send(new byte[] + _ = socket.Send(new byte[] { // SOCKS version 0x05, @@ -54,36 +55,32 @@ namespace Renci.SshNet.Tests.Classes.Connection protected override void SetupMocks() { - SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) - .Returns(_clientSocket); + _ = SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) + .Returns(_clientSocket); } protected override void TearDown() { base.TearDown(); - if (_proxyServer != null) - { - _proxyServer.Dispose(); - } - - if (_clientSocket != null) - { - _clientSocket.Dispose(); - } + _proxyServer?.Dispose(); + _clientSocket?.Dispose(); } protected override void Act() { try { - Connector.Connect(_connectionInfo); + _ = Connector.Connect(_connectionInfo); Assert.Fail(); } catch (ProxyException ex) { _actualException = ex; } + + // Give some time to process all messages + Thread.Sleep(200); } [TestMethod] @@ -131,7 +128,7 @@ namespace Renci.SshNet.Tests.Classes.Connection { try { - _clientSocket.Receive(new byte[0]); + _ = _clientSocket.Receive(new byte[0]); Assert.Fail(); } catch (ObjectDisposedException) diff --git a/src/Renci.SshNet.Tests/Classes/Connection/Socks5ConnectorTest_Connect_UserNamePasswordAuthentication_UserNameExceedsMaximumLength.cs b/src/Renci.SshNet.Tests/Classes/Connection/Socks5ConnectorTest_Connect_UserNamePasswordAuthentication_UserNameExceedsMaximumLength.cs index 7a0ef5d4..83a58f89 100644 --- a/src/Renci.SshNet.Tests/Classes/Connection/Socks5ConnectorTest_Connect_UserNamePasswordAuthentication_UserNameExceedsMaximumLength.cs +++ b/src/Renci.SshNet.Tests/Classes/Connection/Socks5ConnectorTest_Connect_UserNamePasswordAuthentication_UserNameExceedsMaximumLength.cs @@ -1,12 +1,16 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; -using Renci.SshNet.Common; -using Renci.SshNet.Tests.Common; -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Sockets; +using System.Threading; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Moq; + +using Renci.SshNet.Common; +using Renci.SshNet.Tests.Common; namespace Renci.SshNet.Tests.Classes.Connection { @@ -40,7 +44,7 @@ namespace Renci.SshNet.Tests.Classes.Connection // Wait until we received the greeting if (_bytesReceivedByProxy.Count == 4) { - socket.Send(new byte[] + _ = socket.Send(new byte[] { // SOCKS version 0x05, @@ -54,36 +58,32 @@ namespace Renci.SshNet.Tests.Classes.Connection protected override void SetupMocks() { - SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) - .Returns(_clientSocket); + _ = SocketFactoryMock.Setup(p => p.Create(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) + .Returns(_clientSocket); } protected override void TearDown() { base.TearDown(); - if (_proxyServer != null) - { - _proxyServer.Dispose(); - } - - if (_clientSocket != null) - { - _clientSocket.Dispose(); - } + _proxyServer?.Dispose(); + _clientSocket?.Dispose(); } protected override void Act() { try { - Connector.Connect(_connectionInfo); + _ = Connector.Connect(_connectionInfo); Assert.Fail(); } catch (ProxyException ex) { _actualException = ex; } + + // Give some time to process all messages + Thread.Sleep(200); } [TestMethod] @@ -97,20 +97,18 @@ namespace Renci.SshNet.Tests.Classes.Connection [TestMethod] public void ProxyShouldHaveReceivedExpectedSocksRequest() { - var expectedSocksRequest = new List(); - - // // Client greeting - // - - // SOCKS version - expectedSocksRequest.Add(0x05); - // Number of authentication methods supported - expectedSocksRequest.Add(0x02); - // No authentication - expectedSocksRequest.Add(0x00); - // Username/password - expectedSocksRequest.Add(0x02); + var expectedSocksRequest = new List + { + // SOCKS version + 0x05, + // Number of authentication methods supported + 0x02, + // No authentication + 0x00, + // Username/password + 0x02 + }; var errorText = string.Format("Expected:{0}{1}{0}but was:{0}{2}", Environment.NewLine, @@ -131,7 +129,7 @@ namespace Renci.SshNet.Tests.Classes.Connection { try { - _clientSocket.Receive(new byte[0]); + _ = _clientSocket.Receive(new byte[0]); Assert.Fail(); } catch (ObjectDisposedException) diff --git a/src/Renci.SshNet.Tests/Classes/Connection/SshIdentificationTest.cs b/src/Renci.SshNet.Tests/Classes/Connection/SshIdentificationTest.cs index 2a7137ae..3d18cfa5 100644 --- a/src/Renci.SshNet.Tests/Classes/Connection/SshIdentificationTest.cs +++ b/src/Renci.SshNet.Tests/Classes/Connection/SshIdentificationTest.cs @@ -27,7 +27,7 @@ namespace Renci.SshNet.Tests.Classes.Connection try { - new SshIdentification(protocolVersion, softwareVersion); + _ = new SshIdentification(protocolVersion, softwareVersion); Assert.Fail(); } catch (ArgumentNullException ex) @@ -45,7 +45,7 @@ namespace Renci.SshNet.Tests.Classes.Connection try { - new SshIdentification(protocolVersion, softwareVersion); + _ = new SshIdentification(protocolVersion, softwareVersion); Assert.Fail(); } catch (ArgumentNullException ex) @@ -90,7 +90,7 @@ namespace Renci.SshNet.Tests.Classes.Connection try { - new SshIdentification(protocolVersion, softwareVersion, comments); + _ = new SshIdentification(protocolVersion, softwareVersion, comments); Assert.Fail(); } catch (ArgumentNullException ex) @@ -109,7 +109,7 @@ namespace Renci.SshNet.Tests.Classes.Connection try { - new SshIdentification(protocolVersion, softwareVersion, comments); + _ = new SshIdentification(protocolVersion, softwareVersion, comments); Assert.Fail(); } catch (ArgumentNullException ex) diff --git a/src/Renci.SshNet.Tests/Classes/ConnectionInfoTest.cs b/src/Renci.SshNet.Tests/Classes/ConnectionInfoTest.cs index 5fc1cd83..c6b19ca4 100644 --- a/src/Renci.SshNet.Tests/Classes/ConnectionInfoTest.cs +++ b/src/Renci.SshNet.Tests/Classes/ConnectionInfoTest.cs @@ -33,9 +33,15 @@ namespace Renci.SshNet.Tests.Classes { try { - new ConnectionInfo(Resources.HOST, int.Parse(Resources.PORT), Resources.USERNAME, ProxyTypes.Http, null, - int.Parse(Resources.PORT), Resources.USERNAME, Resources.PASSWORD, - new KeyboardInteractiveAuthenticationMethod(Resources.USERNAME)); + _ = new ConnectionInfo(Resources.HOST, + int.Parse(Resources.PORT), + Resources.USERNAME, + ProxyTypes.Http, + null, + int.Parse(Resources.PORT), + Resources.USERNAME, + Resources.PASSWORD, + new KeyboardInteractiveAuthenticationMethod(Resources.USERNAME)); Assert.Fail(); } catch (ArgumentNullException ex) @@ -51,8 +57,14 @@ namespace Renci.SshNet.Tests.Classes { var proxyHost = string.Empty; - var connectionInfo = new ConnectionInfo(Resources.HOST, int.Parse(Resources.PORT), Resources.USERNAME, - ProxyTypes.Http, string.Empty, int.Parse(Resources.PORT), Resources.USERNAME, Resources.PASSWORD, + var connectionInfo = new ConnectionInfo(Resources.HOST, + int.Parse(Resources.PORT), + Resources.USERNAME, + ProxyTypes.Http, + string.Empty, + int.Parse(Resources.PORT), + Resources.USERNAME, + Resources.PASSWORD, new KeyboardInteractiveAuthenticationMethod(Resources.USERNAME)); Assert.AreSame(proxyHost, connectionInfo.ProxyHost); @@ -66,7 +78,15 @@ namespace Renci.SshNet.Tests.Classes try { - new ConnectionInfo(Resources.HOST, int.Parse(Resources.PORT), Resources.USERNAME, ProxyTypes.Http, Resources.HOST, ++maxPort, Resources.USERNAME, Resources.PASSWORD, null); + _ = new ConnectionInfo(Resources.HOST, + int.Parse(Resources.PORT), + Resources.USERNAME, + ProxyTypes.Http, + Resources.HOST, + ++maxPort, + Resources.USERNAME, + Resources.PASSWORD, + null); Assert.Fail(); } catch (ArgumentOutOfRangeException ex) @@ -84,7 +104,15 @@ namespace Renci.SshNet.Tests.Classes try { - new ConnectionInfo(Resources.HOST, int.Parse(Resources.PORT), Resources.USERNAME, ProxyTypes.Http, Resources.HOST, --minPort, Resources.USERNAME, Resources.PASSWORD, null); + _ = new ConnectionInfo(Resources.HOST, + int.Parse(Resources.PORT), + Resources.USERNAME, + ProxyTypes.Http, + Resources.HOST, + --minPort, + Resources.USERNAME, + Resources.PASSWORD, + null); Assert.Fail(); } catch (ArgumentOutOfRangeException ex) @@ -126,8 +154,15 @@ namespace Renci.SshNet.Tests.Classes { try { - new ConnectionInfo(null, int.Parse(Resources.PORT), Resources.USERNAME, ProxyTypes.None, Resources.HOST, - int.Parse(Resources.PORT), Resources.USERNAME, Resources.PASSWORD, null); + _ = new ConnectionInfo(null, + int.Parse(Resources.PORT), + Resources.USERNAME, + ProxyTypes.None, + Resources.HOST, + int.Parse(Resources.PORT), + Resources.USERNAME, + Resources.PASSWORD, + null); } catch (ArgumentNullException ex) { @@ -183,8 +218,15 @@ namespace Renci.SshNet.Tests.Classes try { - new ConnectionInfo(Resources.HOST, port, Resources.USERNAME, ProxyTypes.None, Resources.HOST, - int.Parse(Resources.PORT), Resources.USERNAME, Resources.PASSWORD, null); + _ = new ConnectionInfo(Resources.HOST, + port, + Resources.USERNAME, + ProxyTypes.None, + Resources.HOST, + int.Parse(Resources.PORT), + Resources.USERNAME, + Resources.PASSWORD, + null); Assert.Fail(); } catch (ArgumentOutOfRangeException ex) @@ -202,8 +244,15 @@ namespace Renci.SshNet.Tests.Classes try { - new ConnectionInfo(Resources.HOST, port, Resources.USERNAME, ProxyTypes.None, Resources.HOST, - int.Parse(Resources.PORT), Resources.USERNAME, Resources.PASSWORD, null); + _ = new ConnectionInfo(Resources.HOST, + port, + Resources.USERNAME, + ProxyTypes.None, + Resources.HOST, + int.Parse(Resources.PORT), + Resources.USERNAME, + Resources.PASSWORD, + null); Assert.Fail(); } catch (ArgumentOutOfRangeException ex) @@ -234,9 +283,15 @@ namespace Renci.SshNet.Tests.Classes try { - new ConnectionInfo(Resources.HOST, int.Parse(Resources.PORT), username, ProxyTypes.Http, Resources.USERNAME, - int.Parse(Resources.PORT), Resources.USERNAME, Resources.PASSWORD, - new KeyboardInteractiveAuthenticationMethod(Resources.USERNAME)); + _ = new ConnectionInfo(Resources.HOST, + int.Parse(Resources.PORT), + username, + ProxyTypes.Http, + Resources.USERNAME, + int.Parse(Resources.PORT), + Resources.USERNAME, + Resources.PASSWORD, + new KeyboardInteractiveAuthenticationMethod(Resources.USERNAME)); Assert.Fail(); } catch (ArgumentNullException ex) @@ -254,9 +309,15 @@ namespace Renci.SshNet.Tests.Classes try { - new ConnectionInfo(Resources.HOST, int.Parse(Resources.PORT), username, ProxyTypes.Http, Resources.USERNAME, - int.Parse(Resources.PORT), Resources.USERNAME, Resources.PASSWORD, - new KeyboardInteractiveAuthenticationMethod(Resources.USERNAME)); + _ = new ConnectionInfo(Resources.HOST, + int.Parse(Resources.PORT), + username, + ProxyTypes.Http, + Resources.USERNAME, + int.Parse(Resources.PORT), + Resources.USERNAME, + Resources.PASSWORD, + new KeyboardInteractiveAuthenticationMethod(Resources.USERNAME)); Assert.Fail(); } catch (ArgumentException ex) @@ -275,9 +336,15 @@ namespace Renci.SshNet.Tests.Classes try { - new ConnectionInfo(Resources.HOST, int.Parse(Resources.PORT), username, ProxyTypes.Http, Resources.USERNAME, - int.Parse(Resources.PORT), Resources.USERNAME, Resources.PASSWORD, - new KeyboardInteractiveAuthenticationMethod(Resources.USERNAME)); + _ = new ConnectionInfo(Resources.HOST, + int.Parse(Resources.PORT), + username, + ProxyTypes.Http, + Resources.USERNAME, + int.Parse(Resources.PORT), + Resources.USERNAME, + Resources.PASSWORD, + new KeyboardInteractiveAuthenticationMethod(Resources.USERNAME)); Assert.Fail(); } catch (ArgumentException ex) @@ -294,8 +361,15 @@ namespace Renci.SshNet.Tests.Classes { try { - new ConnectionInfo(Resources.HOST, int.Parse(Resources.PORT), Resources.USERNAME, ProxyTypes.None, Resources.HOST, - int.Parse(Resources.PORT), Resources.USERNAME, Resources.PASSWORD, null); + _ = new ConnectionInfo(Resources.HOST, + int.Parse(Resources.PORT), + Resources.USERNAME, + ProxyTypes.None, + Resources.HOST, + int.Parse(Resources.PORT), + Resources.USERNAME, + Resources.PASSWORD, + null); Assert.Fail(); } catch (ArgumentNullException ex) @@ -311,8 +385,15 @@ namespace Renci.SshNet.Tests.Classes { try { - new ConnectionInfo(Resources.HOST, int.Parse(Resources.PORT), Resources.USERNAME, ProxyTypes.None, Resources.HOST, - int.Parse(Resources.PORT), Resources.USERNAME, Resources.PASSWORD, new AuthenticationMethod[0]); + _ = new ConnectionInfo(Resources.HOST, + int.Parse(Resources.PORT), + Resources.USERNAME, + ProxyTypes.None, + Resources.HOST, + int.Parse(Resources.PORT), + Resources.USERNAME, + Resources.PASSWORD, + new AuthenticationMethod[0]); Assert.Fail(); } catch (ArgumentException ex) @@ -326,11 +407,18 @@ namespace Renci.SshNet.Tests.Classes [TestCategory("ConnectionInfo")] public void AuthenticateShouldThrowArgumentNullExceptionWhenServiceFactoryIsNull() { - var connectionInfo = new ConnectionInfo(Resources.HOST, int.Parse(Resources.PORT), Resources.USERNAME, ProxyTypes.None, - Resources.HOST, int.Parse(Resources.PORT), Resources.USERNAME, Resources.PASSWORD, - new KeyboardInteractiveAuthenticationMethod(Resources.USERNAME)); + var connectionInfo = new ConnectionInfo(Resources.HOST, + int.Parse(Resources.PORT), + Resources.USERNAME, + ProxyTypes.None, + Resources.HOST, + int.Parse(Resources.PORT), + Resources.USERNAME, + Resources.PASSWORD, + new KeyboardInteractiveAuthenticationMethod(Resources.USERNAME)); + var session = new Mock(MockBehavior.Strict).Object; - IServiceFactory serviceFactory = null; + const IServiceFactory serviceFactory = null; try { @@ -344,4 +432,4 @@ namespace Renci.SshNet.Tests.Classes } } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_Dispose_PortNeverStarted.cs b/src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_Dispose_PortNeverStarted.cs index afa281d8..dfd07a27 100644 --- a/src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_Dispose_PortNeverStarted.cs +++ b/src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_Dispose_PortNeverStarted.cs @@ -2,9 +2,11 @@ using System.Collections.Generic; using System.Net; using System.Net.Sockets; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; -using Renci.SshNet.Channels; + using Renci.SshNet.Common; namespace Renci.SshNet.Tests.Classes diff --git a/src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_Dispose_PortStarted_ChannelNotBound.cs b/src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_Dispose_PortStarted_ChannelNotBound.cs index 6c443944..aa995738 100644 --- a/src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_Dispose_PortStarted_ChannelNotBound.cs +++ b/src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_Dispose_PortStarted_ChannelNotBound.cs @@ -2,10 +2,12 @@ using System.Collections.Generic; using System.Net; using System.Net.Sockets; -using System.Runtime.InteropServices; using System.Threading; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; + using Renci.SshNet.Channels; using Renci.SshNet.Common; diff --git a/src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_Started_SocketSendShutdownImmediately.cs b/src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_Started_SocketSendShutdownImmediately.cs index ffc104f1..9859be49 100644 --- a/src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_Started_SocketSendShutdownImmediately.cs +++ b/src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_Started_SocketSendShutdownImmediately.cs @@ -2,10 +2,12 @@ using System.Collections.Generic; using System.Net; using System.Net.Sockets; -using System.Text; using System.Threading; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; + using Renci.SshNet.Channels; using Renci.SshNet.Common; using Renci.SshNet.Tests.Common; @@ -38,8 +40,11 @@ namespace Renci.SshNet.Tests.Classes { if (_forwardedPort != null && _forwardedPort.IsStarted) { - _sessionMock.Setup(p => p.ConnectionInfo).Returns(_connectionInfoMock.Object); - _connectionInfoMock.Setup(p => p.Timeout).Returns(TimeSpan.FromSeconds(5)); + _ = _sessionMock.Setup(p => p.ConnectionInfo) + .Returns(_connectionInfoMock.Object); + _ = _connectionInfoMock.Setup(p => p.Timeout) + .Returns(TimeSpan.FromSeconds(5)); + _forwardedPort.Stop(); } @@ -87,11 +92,21 @@ namespace Renci.SshNet.Tests.Classes { var seq = new MockSequence(); - _sessionMock.InSequence(seq).Setup(p => p.IsConnected).Returns(true); - _sessionMock.InSequence(seq).Setup(p => p.CreateChannelDirectTcpip()).Returns(_channelMock.Object); - _sessionMock.InSequence(seq).Setup(p => p.ConnectionInfo).Returns(_connectionInfoMock.Object); - _connectionInfoMock.InSequence(seq).Setup(p => p.Timeout).Returns(_connectionTimeout); - _channelMock.InSequence(seq).Setup(p => p.Dispose()).Callback(() => _channelDisposed.Set()); + _ = _sessionMock.InSequence(seq) + .Setup(p => p.IsConnected) + .Returns(true); + _ = _sessionMock.InSequence(seq) + .Setup(p => p.CreateChannelDirectTcpip()) + .Returns(_channelMock.Object); + _ = _sessionMock.InSequence(seq) + .Setup(p => p.ConnectionInfo) + .Returns(_connectionInfoMock.Object); + _ = _connectionInfoMock.InSequence(seq) + .Setup(p => p.Timeout) + .Returns(_connectionTimeout); + _ = _channelMock.InSequence(seq) + .Setup(p => p.Dispose()) + .Callback(() => _channelDisposed.Set()); } private void Arrange() @@ -110,7 +125,7 @@ namespace Renci.SshNet.Tests.Classes _client.Shutdown(SocketShutdown.Send); // wait for channel to be disposed - _channelDisposed.WaitOne(TimeSpan.FromMilliseconds(200)); + _ = _channelDisposed.WaitOne(TimeSpan.FromMilliseconds(200)); } [TestMethod] diff --git a/src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_Started_SocketVersionNotSupported.cs b/src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_Started_SocketVersionNotSupported.cs index 8eef3061..e3dc264b 100644 --- a/src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_Started_SocketVersionNotSupported.cs +++ b/src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_Started_SocketVersionNotSupported.cs @@ -2,10 +2,12 @@ using System.Collections.Generic; using System.Net; using System.Net.Sockets; -using System.Text; using System.Threading; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; + using Renci.SshNet.Channels; using Renci.SshNet.Common; using Renci.SshNet.Tests.Common; diff --git a/src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_Stop_PortStarted_ChannelNotBound.cs b/src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_Stop_PortStarted_ChannelNotBound.cs index b84d947d..34db4f9d 100644 --- a/src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_Stop_PortStarted_ChannelNotBound.cs +++ b/src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_Stop_PortStarted_ChannelNotBound.cs @@ -2,10 +2,12 @@ using System.Collections.Generic; using System.Net; using System.Net.Sockets; -using System.Runtime.InteropServices; using System.Threading; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; + using Renci.SshNet.Channels; using Renci.SshNet.Common; @@ -55,11 +57,15 @@ namespace Renci.SshNet.Tests.Classes _sessionMock = new Mock(MockBehavior.Strict); _channelMock = new Mock(MockBehavior.Strict); - _connectionInfoMock.Setup(p => p.Timeout).Returns(TimeSpan.FromSeconds(15)); - _sessionMock.Setup(p => p.IsConnected).Returns(true); - _sessionMock.Setup(p => p.ConnectionInfo).Returns(_connectionInfoMock.Object); - _sessionMock.Setup(p => p.CreateChannelDirectTcpip()).Returns(_channelMock.Object); - _channelMock.Setup(p => p.Dispose()); + _ = _connectionInfoMock.Setup(p => p.Timeout) + .Returns(TimeSpan.FromSeconds(15)); + _ = _sessionMock.Setup(p => p.IsConnected) + .Returns(true); + _ = _sessionMock.Setup(p => p.ConnectionInfo) + .Returns(_connectionInfoMock.Object); + _ = _sessionMock.Setup(p => p.CreateChannelDirectTcpip()) + .Returns(_channelMock.Object); + _ = _channelMock.Setup(p => p.Dispose()); _forwardedPort = new ForwardedPortDynamic(_endpoint.Address.ToString(), (uint) _endpoint.Port); _forwardedPort.Closing += (sender, args) => _closingRegister.Add(args); @@ -112,7 +118,7 @@ namespace Renci.SshNet.Tests.Classes { try { - _client.Send(new byte[] { 0x0a }, 0, 1, SocketFlags.None); + _ = _client.Send(new byte[] { 0x0a }, 0, 1, SocketFlags.None); Assert.Fail(); } catch (SocketException ex) diff --git a/src/Renci.SshNet.Tests/Classes/ForwardedPortLocalTest.cs b/src/Renci.SshNet.Tests/Classes/ForwardedPortLocalTest.cs index 13b2c81b..766dd23a 100644 --- a/src/Renci.SshNet.Tests/Classes/ForwardedPortLocalTest.cs +++ b/src/Renci.SshNet.Tests/Classes/ForwardedPortLocalTest.cs @@ -1,11 +1,9 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Renci.SshNet.Common; +using System; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + using Renci.SshNet.Tests.Common; using Renci.SshNet.Tests.Properties; -using System; -using System.Diagnostics; -using System.Net; -using System.Threading; namespace Renci.SshNet.Tests.Classes { @@ -15,88 +13,12 @@ namespace Renci.SshNet.Tests.Classes [TestClass] public partial class ForwardedPortLocalTest : TestBase { - [TestMethod] - [WorkItem(713)] - [Owner("Kenneth_aa")] - [TestCategory("PortForwarding")] - [TestCategory("integration")] - [Description("Test if calling Stop on ForwardedPortLocal instance causes wait.")] - public void Test_PortForwarding_Local_Stop_Hangs_On_Wait() - { - using (var client = new SshClient(Resources.HOST, Int32.Parse(Resources.PORT), Resources.USERNAME, Resources.PASSWORD)) - { - client.Connect(); - - var port1 = new ForwardedPortLocal("localhost", 8084, "www.google.com", 80); - client.AddForwardedPort(port1); - port1.Exception += delegate(object sender, ExceptionEventArgs e) - { - Assert.Fail(e.Exception.ToString()); - }; - - port1.Start(); - - bool hasTestedTunnel = false; - System.Threading.ThreadPool.QueueUserWorkItem(delegate(object state) - { - try - { - var url = "http://www.google.com/"; - Debug.WriteLine("Starting web request to \"" + url + "\""); - HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url); - HttpWebResponse response = (HttpWebResponse)request.GetResponse(); - - Assert.IsNotNull(response); - - Debug.WriteLine("Http Response status code: " + response.StatusCode.ToString()); - - response.Close(); - - hasTestedTunnel = true; - } - catch (Exception ex) - { - Assert.Fail(ex.ToString()); - } - }); - - // Wait for the web request to complete. - while (!hasTestedTunnel) - { - System.Threading.Thread.Sleep(1000); - } - - try - { - // Try stop the port forwarding, wait 3 seconds and fail if it is still started. - System.Threading.ThreadPool.QueueUserWorkItem(delegate(object state) - { - Debug.WriteLine("Trying to stop port forward."); - port1.Stop(); - Debug.WriteLine("Port forwarding stopped."); - }); - - System.Threading.Thread.Sleep(3000); - if (port1.IsStarted) - { - Assert.Fail("Port forwarding not stopped."); - } - } - catch (Exception ex) - { - Assert.Fail(ex.ToString()); - } - client.Disconnect(); - Debug.WriteLine("Success."); - } - } - [TestMethod] public void ConstructorShouldThrowArgumentNullExceptionWhenBoundHostIsNull() { try { - new ForwardedPortLocal(null, 8080, Resources.HOST, 80); + _ = new ForwardedPortLocal(null, 8080, Resources.HOST, 80); Assert.Fail(); } catch (ArgumentNullException ex) @@ -131,7 +53,7 @@ namespace Renci.SshNet.Tests.Classes { try { - new ForwardedPortLocal(Resources.HOST, 8080, null, 80); + _ = new ForwardedPortLocal(Resources.HOST, 8080, null, 80); Assert.Fail(); } catch (ArgumentNullException ex) @@ -160,124 +82,5 @@ namespace Renci.SshNet.Tests.Classes Assert.AreSame(host, forwardedPort.Host); } - - /// - ///A test for ForwardedPortRemote Constructor - /// - [TestMethod] - [TestCategory("integration")] - public void Test_ForwardedPortRemote() - { - using (var client = new SshClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) - { - #region Example SshClient AddForwardedPort Start Stop ForwardedPortLocal - client.Connect(); - var port = new ForwardedPortLocal(8082, "www.cnn.com", 80); - client.AddForwardedPort(port); - port.Exception += delegate(object sender, ExceptionEventArgs e) - { - Console.WriteLine(e.Exception.ToString()); - }; - port.Start(); - - Thread.Sleep(1000 * 60 * 20); // Wait 20 minutes for port to be forwarded - - port.Stop(); - #endregion - } - Assert.Inconclusive("TODO: Implement code to verify target"); - } - -#if FEATURE_TPL - [TestMethod] - [TestCategory("integration")] - [ExpectedException(typeof(SshConnectionException))] - public void Test_PortForwarding_Local_Without_Connecting() - { - using (var client = new SshClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) - { - var port1 = new ForwardedPortLocal("localhost", 8084, "www.renci.org", 80); - client.AddForwardedPort(port1); - port1.Exception += delegate (object sender, ExceptionEventArgs e) - { - Assert.Fail(e.Exception.ToString()); - }; - port1.Start(); - - System.Threading.Tasks.Parallel.For(0, 100, - - //new ParallelOptions - //{ - // MaxDegreeOfParallelism = 20, - //}, - (counter) => - { - var start = DateTime.Now; - var req = HttpWebRequest.Create("http://localhost:8084"); - using (var response = req.GetResponse()) - { - var data = ReadStream(response.GetResponseStream()); - var end = DateTime.Now; - - Debug.WriteLine(string.Format("Request# {2}: Lenght: {0} Time: {1}", data.Length, (end - start), counter)); - } - } - ); - } - } - - [TestMethod] - [TestCategory("integration")] - public void Test_PortForwarding_Local() - { - using (var client = new SshClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) - { - client.Connect(); - var port1 = new ForwardedPortLocal("localhost", 8084, "www.renci.org", 80); - client.AddForwardedPort(port1); - port1.Exception += delegate (object sender, ExceptionEventArgs e) - { - Assert.Fail(e.Exception.ToString()); - }; - port1.Start(); - - System.Threading.Tasks.Parallel.For(0, 100, - - //new ParallelOptions - //{ - // MaxDegreeOfParallelism = 20, - //}, - (counter) => - { - var start = DateTime.Now; - var req = HttpWebRequest.Create("http://localhost:8084"); - using (var response = req.GetResponse()) - { - var data = ReadStream(response.GetResponseStream()); - var end = DateTime.Now; - - Debug.WriteLine(string.Format("Request# {2}: Length: {0} Time: {1}", data.Length, (end - start), counter)); - } - } - ); - } - } - - private static byte[] ReadStream(System.IO.Stream stream) - { - byte[] buffer = new byte[1024]; - using (var ms = new System.IO.MemoryStream()) - { - while (true) - { - int read = stream.Read(buffer, 0, buffer.Length); - if (read > 0) - ms.Write(buffer, 0, read); - else - return ms.ToArray(); - } - } - } -#endif // FEATURE_TPL } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.Tests/Classes/ForwardedPortLocalTest_Start_PortNeverStarted.cs b/src/Renci.SshNet.Tests/Classes/ForwardedPortLocalTest_Start_PortNeverStarted.cs index 6bb32b28..c286bd88 100644 --- a/src/Renci.SshNet.Tests/Classes/ForwardedPortLocalTest_Start_PortNeverStarted.cs +++ b/src/Renci.SshNet.Tests/Classes/ForwardedPortLocalTest_Start_PortNeverStarted.cs @@ -51,9 +51,12 @@ namespace Renci.SshNet.Tests.Classes _sessionMock = new Mock(MockBehavior.Strict); _channelMock = new Mock(MockBehavior.Strict); - _connectionInfoMock.Setup(p => p.Timeout).Returns(TimeSpan.FromSeconds(15)); - _sessionMock.Setup(p => p.IsConnected).Returns(true); - _sessionMock.Setup(p => p.ConnectionInfo).Returns(_connectionInfoMock.Object); + _ = _connectionInfoMock.Setup(p => p.Timeout) + .Returns(TimeSpan.FromSeconds(15)); + _ = _sessionMock.Setup(p => p.IsConnected) + .Returns(true); + _ = _sessionMock.Setup(p => p.ConnectionInfo) + .Returns(_connectionInfoMock.Object); _forwardedPort = new ForwardedPortLocal(_localEndpoint.Address.ToString(), (uint) _localEndpoint.Port, _remoteEndpoint.Address.ToString(), (uint) _remoteEndpoint.Port); @@ -78,14 +81,19 @@ namespace Renci.SshNet.Tests.Classes { Socket handlerSocket = null; - _sessionMock.Setup(p => p.CreateChannelDirectTcpip()).Returns(_channelMock.Object); - _channelMock.Setup(p => p.Open(_forwardedPort.Host, _forwardedPort.Port, _forwardedPort, It.IsAny())).Callback((address, port, forwardedPort, socket) => handlerSocket = socket); - _channelMock.Setup(p => p.Bind()).Callback(() => - { - if (handlerSocket != null && handlerSocket.Connected) - handlerSocket.Shutdown(SocketShutdown.Both); - }); - _channelMock.Setup(p => p.Dispose()); + _ = _sessionMock.Setup(p => p.CreateChannelDirectTcpip()) + .Returns(_channelMock.Object); + _ = _channelMock.Setup(p => p.Open(_forwardedPort.Host, _forwardedPort.Port, _forwardedPort, It.IsAny())) + .Callback((address, port, forwardedPort, socket) => handlerSocket = socket); + _ = _channelMock.Setup(p => p.Bind()) + .Callback(() => + { + if (handlerSocket != null && handlerSocket.Connected) + { + handlerSocket.Shutdown(SocketShutdown.Both); + } + }); + _ = _channelMock.Setup(p => p.Dispose()); using (var client = new Socket(_localEndpoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp)) { diff --git a/src/Renci.SshNet.Tests/Classes/ForwardedPortLocalTest_Start_PortStarted.cs b/src/Renci.SshNet.Tests/Classes/ForwardedPortLocalTest_Start_PortStarted.cs index 5980aad9..e9e07e39 100644 --- a/src/Renci.SshNet.Tests/Classes/ForwardedPortLocalTest_Start_PortStarted.cs +++ b/src/Renci.SshNet.Tests/Classes/ForwardedPortLocalTest_Start_PortStarted.cs @@ -2,8 +2,11 @@ using System.Collections.Generic; using System.Net; using System.Net.Sockets; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; + using Renci.SshNet.Channels; using Renci.SshNet.Common; @@ -45,16 +48,18 @@ namespace Renci.SshNet.Tests.Classes _closingRegister = new List(); _exceptionRegister = new List(); _localEndpoint = new IPEndPoint(IPAddress.Loopback, 8122); - _remoteEndpoint = new IPEndPoint(IPAddress.Parse("193.168.1.5"), - random.Next(IPEndPoint.MinPort, IPEndPoint.MaxPort)); + _remoteEndpoint = new IPEndPoint(IPAddress.Parse("193.168.1.5"), random.Next(IPEndPoint.MinPort, IPEndPoint.MaxPort)); _connectionInfoMock = new Mock(MockBehavior.Strict); _sessionMock = new Mock(MockBehavior.Strict); _channelMock = new Mock(MockBehavior.Strict); - _connectionInfoMock.Setup(p => p.Timeout).Returns(TimeSpan.FromSeconds(15)); - _sessionMock.Setup(p => p.IsConnected).Returns(true); - _sessionMock.Setup(p => p.ConnectionInfo).Returns(_connectionInfoMock.Object); + _ = _connectionInfoMock.Setup(p => p.Timeout) + .Returns(TimeSpan.FromSeconds(15)); + _ = _sessionMock.Setup(p => p.IsConnected) + .Returns(true); + _ = _sessionMock.Setup(p => p.ConnectionInfo) + .Returns(_connectionInfoMock.Object); _forwardedPort = new ForwardedPortLocal(_localEndpoint.Address.ToString(), (uint)_localEndpoint.Port, _remoteEndpoint.Address.ToString(), (uint)_remoteEndpoint.Port); @@ -95,14 +100,19 @@ namespace Renci.SshNet.Tests.Classes { Socket handlerSocket = null; - _sessionMock.Setup(p => p.CreateChannelDirectTcpip()).Returns(_channelMock.Object); - _channelMock.Setup(p => p.Open(_forwardedPort.Host, _forwardedPort.Port, _forwardedPort, It.IsAny())).Callback((address, port, forwardedPort, socket) => handlerSocket = socket); - _channelMock.Setup(p => p.Bind()).Callback(() => - { - if (handlerSocket != null && handlerSocket.Connected) - handlerSocket.Shutdown(SocketShutdown.Both); - }); - _channelMock.Setup(p => p.Dispose()); + _ = _sessionMock.Setup(p => p.CreateChannelDirectTcpip()) + .Returns(_channelMock.Object); + _ = _channelMock.Setup(p => p.Open(_forwardedPort.Host, _forwardedPort.Port, _forwardedPort, It.IsAny())) + .Callback((address, port, forwardedPort, socket) => handlerSocket = socket); + _ = _channelMock.Setup(p => p.Bind()) + .Callback(() => + { + if (handlerSocket != null && handlerSocket.Connected) + { + handlerSocket.Shutdown(SocketShutdown.Both); + } + }); + _ = _channelMock.Setup(p => p.Dispose()); using (var client = new Socket(_localEndpoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp)) { diff --git a/src/Renci.SshNet.Tests/Classes/ForwardedPortLocalTest_Start_PortStopped.cs b/src/Renci.SshNet.Tests/Classes/ForwardedPortLocalTest_Start_PortStopped.cs index 9889af6a..7998ea99 100644 --- a/src/Renci.SshNet.Tests/Classes/ForwardedPortLocalTest_Start_PortStopped.cs +++ b/src/Renci.SshNet.Tests/Classes/ForwardedPortLocalTest_Start_PortStopped.cs @@ -2,8 +2,11 @@ using System.Collections.Generic; using System.Net; using System.Net.Sockets; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; + using Renci.SshNet.Channels; using Renci.SshNet.Common; @@ -45,18 +48,22 @@ namespace Renci.SshNet.Tests.Classes _exceptionRegister = new List(); _localEndpoint = new IPEndPoint(IPAddress.Loopback, 8122); _remoteEndpoint = new IPEndPoint(IPAddress.Parse("193.168.1.5"), - random.Next(IPEndPoint.MinPort, IPEndPoint.MaxPort)); + random.Next(IPEndPoint.MinPort, IPEndPoint.MaxPort)); _connectionInfoMock = new Mock(MockBehavior.Strict); _sessionMock = new Mock(MockBehavior.Strict); _channelMock = new Mock(MockBehavior.Strict); - _connectionInfoMock.Setup(p => p.Timeout).Returns(TimeSpan.FromSeconds(15)); - _sessionMock.Setup(p => p.IsConnected).Returns(true); - _sessionMock.Setup(p => p.ConnectionInfo).Returns(_connectionInfoMock.Object); + _ = _connectionInfoMock.Setup(p => p.Timeout) + .Returns(TimeSpan.FromSeconds(15)); + _ = _sessionMock.Setup(p => p.IsConnected) + .Returns(true); + _ = _sessionMock.Setup(p => p.ConnectionInfo) + .Returns(_connectionInfoMock.Object); - _forwardedPort = new ForwardedPortLocal(_localEndpoint.Address.ToString(), (uint)_localEndpoint.Port, - _remoteEndpoint.Address.ToString(), (uint)_remoteEndpoint.Port); + _forwardedPort = new ForwardedPortLocal(_localEndpoint.Address.ToString(), + (uint) _localEndpoint.Port, + _remoteEndpoint.Address.ToString(), (uint) _remoteEndpoint.Port); _forwardedPort.Closing += (sender, args) => _closingRegister.Add(args); _forwardedPort.Exception += (sender, args) => _exceptionRegister.Add(args); _forwardedPort.Session = _sessionMock.Object; @@ -82,14 +89,19 @@ namespace Renci.SshNet.Tests.Classes { Socket handlerSocket = null; - _sessionMock.Setup(p => p.CreateChannelDirectTcpip()).Returns(_channelMock.Object); - _channelMock.Setup(p => p.Open(_forwardedPort.Host, _forwardedPort.Port, _forwardedPort, It.IsAny())).Callback((address, port, forwardedPort, socket) => handlerSocket = socket); - _channelMock.Setup(p => p.Bind()).Callback(() => - { - if (handlerSocket != null && handlerSocket.Connected) - handlerSocket.Shutdown(SocketShutdown.Both); - }); - _channelMock.Setup(p => p.Dispose()); + _ = _sessionMock.Setup(p => p.CreateChannelDirectTcpip()) + .Returns(_channelMock.Object); + _ = _channelMock.Setup(p => p.Open(_forwardedPort.Host, _forwardedPort.Port, _forwardedPort, It.IsAny())) + .Callback((address, port, forwardedPort, socket) => handlerSocket = socket); + _ = _channelMock.Setup(p => p.Bind()) + .Callback(() => + { + if (handlerSocket != null && handlerSocket.Connected) + { + handlerSocket.Shutdown(SocketShutdown.Both); + } + }); + _ = _channelMock.Setup(p => p.Dispose()); using (var client = new Socket(_localEndpoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp)) { diff --git a/src/Renci.SshNet.Tests/Classes/ForwardedPortRemoteTest.cs b/src/Renci.SshNet.Tests/Classes/ForwardedPortRemoteTest.cs index 3a99441d..be6adef7 100644 --- a/src/Renci.SshNet.Tests/Classes/ForwardedPortRemoteTest.cs +++ b/src/Renci.SshNet.Tests/Classes/ForwardedPortRemoteTest.cs @@ -1,10 +1,8 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Renci.SshNet.Common; +using System; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + using Renci.SshNet.Tests.Common; -using Renci.SshNet.Tests.Properties; -using System; -using System.Net; -using System.Threading; namespace Renci.SshNet.Tests.Classes { @@ -14,64 +12,6 @@ namespace Renci.SshNet.Tests.Classes [TestClass] public partial class ForwardedPortRemoteTest : TestBase { - [TestMethod] - [Description("Test passing null to AddForwardedPort hosts (remote).")] - [ExpectedException(typeof(ArgumentNullException))] - [TestCategory("integration")] - public void Test_AddForwardedPort_Remote_Hosts_Are_Null() - { - using (var client = new SshClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) - { - client.Connect(); - var port1 = new ForwardedPortRemote((string)null, 8080, (string)null, 80); - client.AddForwardedPort(port1); - client.Disconnect(); - } - } - - [TestMethod] - [Description("Test passing invalid port numbers to AddForwardedPort.")] - [ExpectedException(typeof(ArgumentOutOfRangeException))] - [TestCategory("integration")] - public void Test_AddForwardedPort_Invalid_PortNumber() - { - using (var client = new SshClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) - { - client.Connect(); - var port1 = new ForwardedPortRemote("localhost", IPEndPoint.MaxPort + 1, "www.renci.org", IPEndPoint.MaxPort + 1); - client.AddForwardedPort(port1); - client.Disconnect(); - } - } - - /// - ///A test for ForwardedPortRemote Constructor - /// - [TestMethod] - [TestCategory("integration")] - public void Test_ForwardedPortRemote() - { - using (var client = new SshClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) - { - #region Example SshClient AddForwardedPort Start Stop ForwardedPortRemote - client.Connect(); - var port = new ForwardedPortRemote(8082, "www.cnn.com", 80); - client.AddForwardedPort(port); - port.Exception += delegate(object sender, ExceptionEventArgs e) - { - Console.WriteLine(e.Exception.ToString()); - }; - port.Start(); - - Thread.Sleep(1000 * 60 * 20); // Wait 20 minutes for port to be forwarded - - port.Stop(); - #endregion - } - Assert.Inconclusive("TODO: Implement code to verify target"); - } - - /// ///A test for Stop /// @@ -80,9 +20,9 @@ namespace Renci.SshNet.Tests.Classes public void StopTest() { uint boundPort = 0; // TODO: Initialize to an appropriate value - string host = string.Empty; // TODO: Initialize to an appropriate value + var host = string.Empty; // TODO: Initialize to an appropriate value uint port = 0; // TODO: Initialize to an appropriate value - ForwardedPortRemote target = new ForwardedPortRemote(boundPort, host, port); // TODO: Initialize to an appropriate value + var target = new ForwardedPortRemote(boundPort, host, port); // TODO: Initialize to an appropriate value target.Stop(); Assert.Inconclusive("A method that does not return a value cannot be verified."); } @@ -91,7 +31,7 @@ namespace Renci.SshNet.Tests.Classes public void Start_NotAddedToClient() { const int boundPort = 80; - string host = string.Empty; + var host = string.Empty; const uint port = 22; var target = new ForwardedPortRemote(boundPort, host, port); @@ -115,9 +55,9 @@ namespace Renci.SshNet.Tests.Classes public void DisposeTest() { uint boundPort = 0; // TODO: Initialize to an appropriate value - string host = string.Empty; // TODO: Initialize to an appropriate value + var host = string.Empty; // TODO: Initialize to an appropriate value uint port = 0; // TODO: Initialize to an appropriate value - ForwardedPortRemote target = new ForwardedPortRemote(boundPort, host, port); // TODO: Initialize to an appropriate value + var target = new ForwardedPortRemote(boundPort, host, port); // TODO: Initialize to an appropriate value target.Dispose(); Assert.Inconclusive("A method that does not return a value cannot be verified."); } @@ -129,11 +69,11 @@ namespace Renci.SshNet.Tests.Classes [Ignore] // placeholder public void ForwardedPortRemoteConstructorTest() { - string boundHost = string.Empty; // TODO: Initialize to an appropriate value + var boundHost = string.Empty; // TODO: Initialize to an appropriate value uint boundPort = 0; // TODO: Initialize to an appropriate value - string host = string.Empty; // TODO: Initialize to an appropriate value + var host = string.Empty; // TODO: Initialize to an appropriate value uint port = 0; // TODO: Initialize to an appropriate value - ForwardedPortRemote target = new ForwardedPortRemote(boundHost, boundPort, host, port); + var target = new ForwardedPortRemote(boundHost, boundPort, host, port); Assert.Inconclusive("TODO: Implement code to verify target"); } @@ -145,51 +85,10 @@ namespace Renci.SshNet.Tests.Classes public void ForwardedPortRemoteConstructorTest1() { uint boundPort = 0; // TODO: Initialize to an appropriate value - string host = string.Empty; // TODO: Initialize to an appropriate value + var host = string.Empty; // TODO: Initialize to an appropriate value uint port = 0; // TODO: Initialize to an appropriate value - ForwardedPortRemote target = new ForwardedPortRemote(boundPort, host, port); + var target = new ForwardedPortRemote(boundPort, host, port); Assert.Inconclusive("TODO: Implement code to verify target"); } - -#if FEATURE_TPL - [TestMethod] - [TestCategory("integration")] - public void Test_PortForwarding_Remote() - { - // ****************************************************************** - // ************* Tests are still in not finished ******************** - // ****************************************************************** - - using (var client = new SshClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) - { - client.Connect(); - var port1 = new ForwardedPortRemote(8082, "www.renci.org", 80); - client.AddForwardedPort(port1); - port1.Exception += delegate (object sender, ExceptionEventArgs e) - { - Assert.Fail(e.Exception.ToString()); - }; - port1.Start(); - var boundport = port1.BoundPort; - - System.Threading.Tasks.Parallel.For(0, 5, - - //new ParallelOptions - //{ - // MaxDegreeOfParallelism = 1, - //}, - (counter) => - { - var cmd = client.CreateCommand(string.Format("wget -O- http://localhost:{0}", boundport)); - var result = cmd.Execute(); - var end = DateTime.Now; - System.Diagnostics.Debug.WriteLine(string.Format("Length: {0}", result.Length)); - } - ); - Thread.Sleep(1000 * 100); - port1.Stop(); - } - } -#endif // FEATURE_TPL } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.Tests/Classes/ForwardedPortRemoteTest_Start_PortStarted.cs b/src/Renci.SshNet.Tests/Classes/ForwardedPortRemoteTest_Start_PortStarted.cs index a7502680..0cd8a6ad 100644 --- a/src/Renci.SshNet.Tests/Classes/ForwardedPortRemoteTest_Start_PortStarted.cs +++ b/src/Renci.SshNet.Tests/Classes/ForwardedPortRemoteTest_Start_PortStarted.cs @@ -139,6 +139,10 @@ namespace Renci.SshNet.Tests.Classes new ForwardedTcpipChannelInfo(_forwardedPort.BoundHost, _forwardedPort.BoundPort, originatorAddress, originatorPort)))); + // CreateChannelForwardedTcpip gets called on a separate thread. + // Sleep on this thread briefly to avoid a race. + Thread.Sleep(500); + _sessionMock.Verify(p => p.CreateChannelForwardedTcpip(channelNumber, initialWindowSize, maximumPacketSize), Times.Once); channelMock.Verify(p => p.Bind(It.Is(ep => ep.Address.Equals(_remoteEndpoint.Address) && ep.Port == _remoteEndpoint.Port), _forwardedPort), Times.Once); channelMock.Verify(p => p.Dispose(), Times.Once); diff --git a/src/Renci.SshNet.Tests/Classes/ForwardedPortRemoteTest_Start_SessionNotConnected.cs b/src/Renci.SshNet.Tests/Classes/ForwardedPortRemoteTest_Start_SessionNotConnected.cs index fc56157b..af38f057 100644 --- a/src/Renci.SshNet.Tests/Classes/ForwardedPortRemoteTest_Start_SessionNotConnected.cs +++ b/src/Renci.SshNet.Tests/Classes/ForwardedPortRemoteTest_Start_SessionNotConnected.cs @@ -2,9 +2,11 @@ using System.Collections.Generic; using System.Globalization; using System.Net; -using System.Net.Sockets; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; + using Renci.SshNet.Channels; using Renci.SshNet.Common; using Renci.SshNet.Messages.Connection; @@ -35,8 +37,11 @@ namespace Renci.SshNet.Tests.Classes { if (_forwardedPort != null) { - _sessionMock.Setup(p => p.ConnectionInfo).Returns(_connectionInfoMock.Object); - _connectionInfoMock.Setup(p => p.Timeout).Returns(TimeSpan.FromSeconds(1)); + _ = _sessionMock.Setup(p => p.ConnectionInfo) + .Returns(_connectionInfoMock.Object); + _ = _connectionInfoMock.Setup(p => p.Timeout) + .Returns(TimeSpan.FromSeconds(1)); + _forwardedPort.Dispose(); _forwardedPort = null; } @@ -53,7 +58,8 @@ namespace Renci.SshNet.Tests.Classes _sessionMock = new Mock(MockBehavior.Strict); _connectionInfoMock = new Mock(MockBehavior.Strict); - _sessionMock.Setup(p => p.IsConnected).Returns(false); + _ = _sessionMock.Setup(p => p.IsConnected) + .Returns(false); _forwardedPort = new ForwardedPortRemote(_bindEndpoint.Address, (uint)_bindEndpoint.Port, _remoteEndpoint.Address, (uint)_remoteEndpoint.Port); _forwardedPort.Closing += (sender, args) => _closingRegister.Add(args); @@ -90,19 +96,17 @@ namespace Renci.SshNet.Tests.Classes [TestMethod] public void ForwardedPortShouldIgnoreReceivedSignalForNewConnection() { - var channelNumber = (uint)new Random().Next(1001, int.MaxValue); - var initialWindowSize = (uint)new Random().Next(0, int.MaxValue); - var maximumPacketSize = (uint)new Random().Next(0, int.MaxValue); + var channelNumber = (uint) new Random().Next(1001, int.MaxValue); + var initialWindowSize = (uint) new Random().Next(0, int.MaxValue); + var maximumPacketSize = (uint) new Random().Next(0, int.MaxValue); var originatorAddress = new Random().Next().ToString(CultureInfo.InvariantCulture); - var originatorPort = (uint)new Random().Next(0, int.MaxValue); + var originatorPort = (uint) new Random().Next(0, int.MaxValue); var channelMock = new Mock(MockBehavior.Strict); - _sessionMock.Setup( - p => - p.CreateChannelForwardedTcpip(channelNumber, initialWindowSize, maximumPacketSize)).Returns(channelMock.Object); - + _ = _sessionMock.Setup(p => p.CreateChannelForwardedTcpip(channelNumber, initialWindowSize, maximumPacketSize)).Returns(channelMock.Object); + _sessionMock.Raise(p => p.ChannelOpenReceived += null, - new MessageEventArgs(new ChannelOpenMessage(channelNumber, initialWindowSize, + new MessageEventArgs(new ChannelOpenMessage(channelNumber, initialWindowSize, maximumPacketSize, new ForwardedTcpipChannelInfo(_forwardedPort.BoundHost, _forwardedPort.BoundPort, originatorAddress, originatorPort)))); diff --git a/src/Renci.SshNet.Tests/Classes/ForwardedPortRemoteTest_Start_SessionNull.cs b/src/Renci.SshNet.Tests/Classes/ForwardedPortRemoteTest_Start_SessionNull.cs index 68931a0a..9cd3a304 100644 --- a/src/Renci.SshNet.Tests/Classes/ForwardedPortRemoteTest_Start_SessionNull.cs +++ b/src/Renci.SshNet.Tests/Classes/ForwardedPortRemoteTest_Start_SessionNull.cs @@ -1,13 +1,10 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.Net; -using System.Net.Sockets; + using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; -using Renci.SshNet.Channels; + using Renci.SshNet.Common; -using Renci.SshNet.Messages.Connection; namespace Renci.SshNet.Tests.Classes { diff --git a/src/Renci.SshNet.Tests/Classes/KeyboardInteractiveConnectionInfoTest.cs b/src/Renci.SshNet.Tests/Classes/KeyboardInteractiveConnectionInfoTest.cs index 0fc12c46..f50aaf9b 100644 --- a/src/Renci.SshNet.Tests/Classes/KeyboardInteractiveConnectionInfoTest.cs +++ b/src/Renci.SshNet.Tests/Classes/KeyboardInteractiveConnectionInfoTest.cs @@ -1,8 +1,6 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; -using Renci.SshNet.Common; + using Renci.SshNet.Tests.Common; -using Renci.SshNet.Tests.Properties; -using System; namespace Renci.SshNet.Tests.Classes { @@ -12,40 +10,6 @@ namespace Renci.SshNet.Tests.Classes [TestClass] public class KeyboardInteractiveConnectionInfoTest : TestBase { - [TestMethod] - [TestCategory("KeyboardInteractiveConnectionInfo")] - [TestCategory("integration")] - public void Test_KeyboardInteractiveConnectionInfo() - { - var host = Resources.HOST; - var username = Resources.USERNAME; - var password = Resources.PASSWORD; - - #region Example KeyboardInteractiveConnectionInfo AuthenticationPrompt - var connectionInfo = new KeyboardInteractiveConnectionInfo(host, username); - connectionInfo.AuthenticationPrompt += delegate(object sender, AuthenticationPromptEventArgs e) - { - System.Console.WriteLine(e.Instruction); - - foreach (var prompt in e.Prompts) - { - Console.WriteLine(prompt.Request); - prompt.Response = Console.ReadLine(); - } - }; - - using (var client = new SftpClient(connectionInfo)) - { - client.Connect(); - // Do something here - client.Disconnect(); - } - #endregion - - Assert.AreEqual(connectionInfo.Host, Resources.HOST); - Assert.AreEqual(connectionInfo.Username, Resources.USERNAME); - } - /// ///A test for Dispose /// @@ -192,4 +156,4 @@ namespace Renci.SshNet.Tests.Classes Assert.Inconclusive("TODO: Implement code to verify target"); } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.Tests/Classes/MessageEventArgsTest.cs b/src/Renci.SshNet.Tests/Classes/MessageEventArgsTest.cs index 387b5b29..da6ab851 100644 --- a/src/Renci.SshNet.Tests/Classes/MessageEventArgsTest.cs +++ b/src/Renci.SshNet.Tests/Classes/MessageEventArgsTest.cs @@ -6,7 +6,6 @@ namespace Renci.SshNet.Tests.Classes /// /// Provides data for message events. /// - /// Message type [TestClass] public class MessageEventArgsTest : TestBase { @@ -15,8 +14,8 @@ namespace Renci.SshNet.Tests.Classes /// public void MessageEventArgsConstructorTestHelper() { - T message = default(T); // TODO: Initialize to an appropriate value - MessageEventArgs target = new MessageEventArgs(message); + T message = default; // TODO: Initialize to an appropriate value + var target = new MessageEventArgs(message); Assert.Inconclusive("TODO: Implement code to verify target"); } @@ -27,4 +26,4 @@ namespace Renci.SshNet.Tests.Classes MessageEventArgsConstructorTestHelper(); } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.Tests/Classes/Messages/Authentication/FailureMessageTest.cs b/src/Renci.SshNet.Tests/Classes/Messages/Authentication/FailureMessageTest.cs index 9f0169f0..979b1d75 100644 --- a/src/Renci.SshNet.Tests/Classes/Messages/Authentication/FailureMessageTest.cs +++ b/src/Renci.SshNet.Tests/Classes/Messages/Authentication/FailureMessageTest.cs @@ -1,6 +1,6 @@ -using Renci.SshNet.Messages.Authentication; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Renci.SshNet.Messages.Authentication; using Renci.SshNet.Tests.Common; namespace Renci.SshNet.Tests.Messages.Authentication @@ -19,7 +19,7 @@ namespace Renci.SshNet.Tests.Messages.Authentication [Ignore] // placeholder public void FailureMessageConstructorTest() { - FailureMessage target = new FailureMessage(); + var target = new FailureMessage(); Assert.Inconclusive("TODO: Implement code to verify target"); } @@ -30,7 +30,7 @@ namespace Renci.SshNet.Tests.Messages.Authentication [Ignore] // placeholder public void AllowedAuthenticationsTest() { - FailureMessage target = new FailureMessage(); // TODO: Initialize to an appropriate value + var target = new FailureMessage(); // TODO: Initialize to an appropriate value string[] expected = null; // TODO: Initialize to an appropriate value string[] actual; target.AllowedAuthentications = expected; diff --git a/src/Renci.SshNet.Tests/Classes/Messages/Authentication/RequestMessagePublicKeyTest.cs b/src/Renci.SshNet.Tests/Classes/Messages/Authentication/RequestMessagePublicKeyTest.cs index f09aa4a4..fb4e36af 100644 --- a/src/Renci.SshNet.Tests/Classes/Messages/Authentication/RequestMessagePublicKeyTest.cs +++ b/src/Renci.SshNet.Tests/Classes/Messages/Authentication/RequestMessagePublicKeyTest.cs @@ -1,7 +1,7 @@ -using Renci.SshNet.Messages.Authentication; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; + using Renci.SshNet.Messages; +using Renci.SshNet.Messages.Authentication; using Renci.SshNet.Tests.Common; namespace Renci.SshNet.Tests.Messages.Authentication @@ -20,11 +20,11 @@ namespace Renci.SshNet.Tests.Messages.Authentication [Ignore] // placeholder public void RequestMessagePublicKeyConstructorTest() { - ServiceName serviceName = new ServiceName(); // TODO: Initialize to an appropriate value - string username = string.Empty; // TODO: Initialize to an appropriate value - string keyAlgorithmName = string.Empty; // TODO: Initialize to an appropriate value + var serviceName = new ServiceName(); // TODO: Initialize to an appropriate value + var username = string.Empty; // TODO: Initialize to an appropriate value + var keyAlgorithmName = string.Empty; // TODO: Initialize to an appropriate value byte[] keyData = null; // TODO: Initialize to an appropriate value - RequestMessagePublicKey target = new RequestMessagePublicKey(serviceName, username, keyAlgorithmName, keyData); + var target = new RequestMessagePublicKey(serviceName, username, keyAlgorithmName, keyData); Assert.Inconclusive("TODO: Implement code to verify target"); } @@ -35,12 +35,12 @@ namespace Renci.SshNet.Tests.Messages.Authentication [Ignore] // placeholder public void RequestMessagePublicKeyConstructorTest1() { - ServiceName serviceName = new ServiceName(); // TODO: Initialize to an appropriate value - string username = string.Empty; // TODO: Initialize to an appropriate value - string keyAlgorithmName = string.Empty; // TODO: Initialize to an appropriate value + var serviceName = new ServiceName(); // TODO: Initialize to an appropriate value + var username = string.Empty; // TODO: Initialize to an appropriate value + var keyAlgorithmName = string.Empty; // TODO: Initialize to an appropriate value byte[] keyData = null; // TODO: Initialize to an appropriate value byte[] signature = null; // TODO: Initialize to an appropriate value - RequestMessagePublicKey target = new RequestMessagePublicKey(serviceName, username, keyAlgorithmName, keyData, signature); + var target = new RequestMessagePublicKey(serviceName, username, keyAlgorithmName, keyData, signature); Assert.Inconclusive("TODO: Implement code to verify target"); } @@ -51,13 +51,12 @@ namespace Renci.SshNet.Tests.Messages.Authentication [Ignore] // placeholder public void MethodNameTest() { - ServiceName serviceName = new ServiceName(); // TODO: Initialize to an appropriate value - string username = string.Empty; // TODO: Initialize to an appropriate value - string keyAlgorithmName = string.Empty; // TODO: Initialize to an appropriate value + var serviceName = new ServiceName(); // TODO: Initialize to an appropriate value + var username = string.Empty; // TODO: Initialize to an appropriate value + var keyAlgorithmName = string.Empty; // TODO: Initialize to an appropriate value byte[] keyData = null; // TODO: Initialize to an appropriate value - RequestMessagePublicKey target = new RequestMessagePublicKey(serviceName, username, keyAlgorithmName, keyData); // TODO: Initialize to an appropriate value - string actual; - actual = target.MethodName; + var target = new RequestMessagePublicKey(serviceName, username, keyAlgorithmName, keyData); // TODO: Initialize to an appropriate value + var actual = target.MethodName; Assert.Inconclusive("Verify the correctness of this test method."); } @@ -68,11 +67,11 @@ namespace Renci.SshNet.Tests.Messages.Authentication [Ignore] // placeholder public void SignatureTest() { - ServiceName serviceName = new ServiceName(); // TODO: Initialize to an appropriate value - string username = string.Empty; // TODO: Initialize to an appropriate value - string keyAlgorithmName = string.Empty; // TODO: Initialize to an appropriate value + var serviceName = new ServiceName(); // TODO: Initialize to an appropriate value + var username = string.Empty; // TODO: Initialize to an appropriate value + var keyAlgorithmName = string.Empty; // TODO: Initialize to an appropriate value byte[] keyData = null; // TODO: Initialize to an appropriate value - RequestMessagePublicKey target = new RequestMessagePublicKey(serviceName, username, keyAlgorithmName, keyData); // TODO: Initialize to an appropriate value + var target = new RequestMessagePublicKey(serviceName, username, keyAlgorithmName, keyData); // TODO: Initialize to an appropriate value byte[] expected = null; // TODO: Initialize to an appropriate value target.Signature = expected; var actual = target.Signature; diff --git a/src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelCloseMessageTest.cs b/src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelCloseMessageTest.cs index 9d3ebc3c..995981a6 100644 --- a/src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelCloseMessageTest.cs +++ b/src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelCloseMessageTest.cs @@ -1,6 +1,5 @@ using Renci.SshNet.Messages.Connection; using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; using Renci.SshNet.Tests.Common; namespace Renci.SshNet.Tests.Classes.Messages.Connection @@ -19,7 +18,7 @@ namespace Renci.SshNet.Tests.Classes.Messages.Connection [Ignore] // placeholder public void ChannelCloseMessageConstructorTest() { - ChannelCloseMessage target = new ChannelCloseMessage(); + var target = new ChannelCloseMessage(); Assert.Inconclusive("TODO: Implement code to verify target"); } @@ -31,7 +30,7 @@ namespace Renci.SshNet.Tests.Classes.Messages.Connection public void ChannelCloseMessageConstructorTest1() { uint localChannelNumber = 0; // TODO: Initialize to an appropriate value - ChannelCloseMessage target = new ChannelCloseMessage(localChannelNumber); + var target = new ChannelCloseMessage(localChannelNumber); Assert.Inconclusive("TODO: Implement code to verify target"); } } diff --git a/src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelEofMessageTest.cs b/src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelEofMessageTest.cs index a302fb6e..ad2becd8 100644 --- a/src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelEofMessageTest.cs +++ b/src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelEofMessageTest.cs @@ -1,6 +1,5 @@ using Renci.SshNet.Messages.Connection; using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; using Renci.SshNet.Tests.Common; namespace Renci.SshNet.Tests.Classes.Messages.Connection @@ -19,7 +18,7 @@ namespace Renci.SshNet.Tests.Classes.Messages.Connection [Ignore] // placeholder public void ChannelEofMessageConstructorTest() { - ChannelEofMessage target = new ChannelEofMessage(); + var target = new ChannelEofMessage(); Assert.Inconclusive("TODO: Implement code to verify target"); } @@ -31,7 +30,7 @@ namespace Renci.SshNet.Tests.Classes.Messages.Connection public void ChannelEofMessageConstructorTest1() { uint localChannelNumber = 0; // TODO: Initialize to an appropriate value - ChannelEofMessage target = new ChannelEofMessage(localChannelNumber); + var target = new ChannelEofMessage(localChannelNumber); Assert.Inconclusive("TODO: Implement code to verify target"); } } diff --git a/src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelExtendedDataMessageTest.cs b/src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelExtendedDataMessageTest.cs index 469908ea..65e3dbcc 100644 --- a/src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelExtendedDataMessageTest.cs +++ b/src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelExtendedDataMessageTest.cs @@ -1,6 +1,6 @@ -using Renci.SshNet.Messages.Connection; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Renci.SshNet.Messages.Connection; using Renci.SshNet.Tests.Common; namespace Renci.SshNet.Tests.Classes.Messages.Connection @@ -19,7 +19,7 @@ namespace Renci.SshNet.Tests.Classes.Messages.Connection [Ignore] // placeholder public void ChannelExtendedDataMessageConstructorTest() { - ChannelExtendedDataMessage target = new ChannelExtendedDataMessage(); + var target = new ChannelExtendedDataMessage(); Assert.Inconclusive("TODO: Implement code to verify target"); } diff --git a/src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelFailureMessageTest.cs b/src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelFailureMessageTest.cs index ef1fe76c..070df2a6 100644 --- a/src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelFailureMessageTest.cs +++ b/src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelFailureMessageTest.cs @@ -1,6 +1,6 @@ -using Renci.SshNet.Messages.Connection; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Renci.SshNet.Messages.Connection; using Renci.SshNet.Tests.Common; namespace Renci.SshNet.Tests.Classes.Messages.Connection @@ -19,7 +19,7 @@ namespace Renci.SshNet.Tests.Classes.Messages.Connection [Ignore] // placeholder public void ChannelFailureMessageConstructorTest() { - ChannelFailureMessage target = new ChannelFailureMessage(); + var target = new ChannelFailureMessage(); Assert.Inconclusive("TODO: Implement code to verify target"); } @@ -31,7 +31,7 @@ namespace Renci.SshNet.Tests.Classes.Messages.Connection public void ChannelFailureMessageConstructorTest1() { uint localChannelNumber = 0; // TODO: Initialize to an appropriate value - ChannelFailureMessage target = new ChannelFailureMessage(localChannelNumber); + var target = new ChannelFailureMessage(localChannelNumber); Assert.Inconclusive("TODO: Implement code to verify target"); } } diff --git a/src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelMessageTest.cs b/src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelMessageTest.cs index baa4a7fc..53c56cf7 100644 --- a/src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelMessageTest.cs +++ b/src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelMessageTest.cs @@ -1,6 +1,6 @@ -using Renci.SshNet.Messages.Connection; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Renci.SshNet.Messages.Connection; using Renci.SshNet.Tests.Common; namespace Renci.SshNet.Tests.Classes.Messages.Connection @@ -26,10 +26,9 @@ namespace Renci.SshNet.Tests.Classes.Messages.Connection [TestMethod()] public void ToStringTest() { - ChannelMessage target = CreateChannelMessage(); // TODO: Initialize to an appropriate value - string expected = string.Empty; // TODO: Initialize to an appropriate value - string actual; - actual = target.ToString(); + var target = CreateChannelMessage(); // TODO: Initialize to an appropriate value + var expected = string.Empty; // TODO: Initialize to an appropriate value + var actual = target.ToString(); Assert.AreEqual(expected, actual); Assert.Inconclusive("Verify the correctness of this test method."); } diff --git a/src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelOpen/ChannelOpenMessageTest.cs b/src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelOpen/ChannelOpenMessageTest.cs index 368f5531..1a40d860 100644 --- a/src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelOpen/ChannelOpenMessageTest.cs +++ b/src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelOpen/ChannelOpenMessageTest.cs @@ -28,9 +28,9 @@ namespace Renci.SshNet.Tests.Classes.Messages.Connection Assert.IsNull(target.ChannelType); Assert.IsNull(target.Info); - Assert.AreEqual(default(uint), target.InitialWindowSize); - Assert.AreEqual(default(uint), target.LocalChannelNumber); - Assert.AreEqual(default(uint), target.MaximumPacketSize); + Assert.AreEqual(default, target.InitialWindowSize); + Assert.AreEqual(default, target.LocalChannelNumber); + Assert.AreEqual(default, target.MaximumPacketSize); } [TestMethod] diff --git a/src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelOpenConfirmationMessageTest.cs b/src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelOpenConfirmationMessageTest.cs index ce5abb61..3ad44b48 100644 --- a/src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelOpenConfirmationMessageTest.cs +++ b/src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelOpenConfirmationMessageTest.cs @@ -1,6 +1,6 @@ -using Renci.SshNet.Messages.Connection; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Renci.SshNet.Messages.Connection; using Renci.SshNet.Tests.Common; namespace Renci.SshNet.Tests.Classes.Messages.Connection @@ -19,7 +19,7 @@ namespace Renci.SshNet.Tests.Classes.Messages.Connection [Ignore] // placeholder public void ChannelOpenConfirmationMessageConstructorTest() { - ChannelOpenConfirmationMessage target = new ChannelOpenConfirmationMessage(); + var target = new ChannelOpenConfirmationMessage(); Assert.Inconclusive("TODO: Implement code to verify target"); } @@ -34,7 +34,7 @@ namespace Renci.SshNet.Tests.Classes.Messages.Connection uint initialWindowSize = 0; // TODO: Initialize to an appropriate value uint maximumPacketSize = 0; // TODO: Initialize to an appropriate value uint remoteChannelNumber = 0; // TODO: Initialize to an appropriate value - ChannelOpenConfirmationMessage target = new ChannelOpenConfirmationMessage(localChannelNumber, initialWindowSize, maximumPacketSize, remoteChannelNumber); + var target = new ChannelOpenConfirmationMessage(localChannelNumber, initialWindowSize, maximumPacketSize, remoteChannelNumber); Assert.Inconclusive("TODO: Implement code to verify target"); } } diff --git a/src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelOpenFailureMessageTest.cs b/src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelOpenFailureMessageTest.cs index 3992ca49..848f81ed 100644 --- a/src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelOpenFailureMessageTest.cs +++ b/src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelOpenFailureMessageTest.cs @@ -1,6 +1,6 @@ -using Renci.SshNet.Messages.Connection; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Renci.SshNet.Messages.Connection; using Renci.SshNet.Tests.Common; namespace Renci.SshNet.Tests.Classes.Messages.Connection @@ -19,7 +19,7 @@ namespace Renci.SshNet.Tests.Classes.Messages.Connection [Ignore] // placeholder public void ChannelOpenFailureMessageConstructorTest() { - ChannelOpenFailureMessage target = new ChannelOpenFailureMessage(); + var target = new ChannelOpenFailureMessage(); Assert.Inconclusive("TODO: Implement code to verify target"); } @@ -31,9 +31,9 @@ namespace Renci.SshNet.Tests.Classes.Messages.Connection public void ChannelOpenFailureMessageConstructorTest1() { uint localChannelNumber = 0; // TODO: Initialize to an appropriate value - string description = string.Empty; // TODO: Initialize to an appropriate value + var description = string.Empty; // TODO: Initialize to an appropriate value uint reasonCode = 0; // TODO: Initialize to an appropriate value - ChannelOpenFailureMessage target = new ChannelOpenFailureMessage(localChannelNumber, description, reasonCode); + var target = new ChannelOpenFailureMessage(localChannelNumber, description, reasonCode); Assert.Inconclusive("TODO: Implement code to verify target"); } } diff --git a/src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelOpenInfoTest.cs b/src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelOpenInfoTest.cs index 845cc8c0..a28e17ed 100644 --- a/src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelOpenInfoTest.cs +++ b/src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelOpenInfoTest.cs @@ -1,6 +1,6 @@ -using Renci.SshNet.Messages.Connection; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Renci.SshNet.Messages.Connection; using Renci.SshNet.Tests.Common; namespace Renci.SshNet.Tests.Classes.Messages.Connection @@ -26,9 +26,8 @@ namespace Renci.SshNet.Tests.Classes.Messages.Connection [TestMethod()] public void ChannelTypeTest() { - ChannelOpenInfo target = CreateChannelOpenInfo(); // TODO: Initialize to an appropriate value - string actual; - actual = target.ChannelType; + var target = CreateChannelOpenInfo(); // TODO: Initialize to an appropriate value + var actual = target.ChannelType; Assert.Inconclusive("Verify the correctness of this test method."); } } diff --git a/src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelRequest/EndOfWriteRequestInfoTest.cs b/src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelRequest/EndOfWriteRequestInfoTest.cs index 68ffa7af..0573bade 100644 --- a/src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelRequest/EndOfWriteRequestInfoTest.cs +++ b/src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelRequest/EndOfWriteRequestInfoTest.cs @@ -1,6 +1,6 @@ -using Renci.SshNet.Messages.Connection; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Renci.SshNet.Messages.Connection; using Renci.SshNet.Tests.Common; namespace Renci.SshNet.Tests.Classes.Messages.Connection @@ -19,7 +19,7 @@ namespace Renci.SshNet.Tests.Classes.Messages.Connection [Ignore] // placeholder public void EndOfWriteRequestInfoConstructorTest() { - EndOfWriteRequestInfo target = new EndOfWriteRequestInfo(); + var target = new EndOfWriteRequestInfo(); Assert.Inconclusive("TODO: Implement code to verify target"); } @@ -30,9 +30,9 @@ namespace Renci.SshNet.Tests.Classes.Messages.Connection [Ignore] // placeholder public void RequestNameTest() { - EndOfWriteRequestInfo target = new EndOfWriteRequestInfo(); // TODO: Initialize to an appropriate value - string actual; - actual = target.RequestName; + var target = new EndOfWriteRequestInfo(); // TODO: Initialize to an appropriate value + + var actual = target.RequestName; Assert.Inconclusive("Verify the correctness of this test method."); } } diff --git a/src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelRequest/KeepAliveRequestInfoTest.cs b/src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelRequest/KeepAliveRequestInfoTest.cs index 515a723a..77b8cc92 100644 --- a/src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelRequest/KeepAliveRequestInfoTest.cs +++ b/src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelRequest/KeepAliveRequestInfoTest.cs @@ -1,6 +1,6 @@ -using Renci.SshNet.Messages.Connection; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Renci.SshNet.Messages.Connection; using Renci.SshNet.Tests.Common; namespace Renci.SshNet.Tests.Classes.Messages.Connection @@ -19,7 +19,7 @@ namespace Renci.SshNet.Tests.Classes.Messages.Connection [Ignore] // placeholder public void KeepAliveRequestInfoConstructorTest() { - KeepAliveRequestInfo target = new KeepAliveRequestInfo(); + var target = new KeepAliveRequestInfo(); Assert.Inconclusive("TODO: Implement code to verify target"); } @@ -30,9 +30,8 @@ namespace Renci.SshNet.Tests.Classes.Messages.Connection [Ignore] // placeholder public void RequestNameTest() { - KeepAliveRequestInfo target = new KeepAliveRequestInfo(); // TODO: Initialize to an appropriate value - string actual; - actual = target.RequestName; + var target = new KeepAliveRequestInfo(); // TODO: Initialize to an appropriate value + var actual = target.RequestName; Assert.Inconclusive("Verify the correctness of this test method."); } } diff --git a/src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelRequest/PseudoTerminalInfoTest.cs b/src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelRequest/PseudoTerminalInfoTest.cs index b7e3e323..fedbdb7f 100644 --- a/src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelRequest/PseudoTerminalInfoTest.cs +++ b/src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelRequest/PseudoTerminalInfoTest.cs @@ -55,7 +55,7 @@ namespace Renci.SshNet.Tests.Classes.Messages.Connection expectedBytesLength += 4; // PixelWidth expectedBytesLength += 4; // PixelHeight expectedBytesLength += 4; // Length of "encoded terminal modes" - expectedBytesLength += _terminalModeValues.Count*(1 + 4) + 1; // encoded terminal modes + expectedBytesLength += (_terminalModeValues.Count * (1 + 4)) + 1; // encoded terminal modes Assert.AreEqual(expectedBytesLength, bytes.Length); @@ -67,7 +67,7 @@ namespace Renci.SshNet.Tests.Classes.Messages.Connection Assert.AreEqual(_rows, sshDataStream.ReadUInt32()); Assert.AreEqual(_width, sshDataStream.ReadUInt32()); Assert.AreEqual(_height, sshDataStream.ReadUInt32()); - Assert.AreEqual((uint) (_terminalModeValues.Count * (1 + 4) + 1), sshDataStream.ReadUInt32()); + Assert.AreEqual((uint) ((_terminalModeValues.Count * (1 + 4)) + 1), sshDataStream.ReadUInt32()); Assert.AreEqual((int) TerminalModes.CS8, sshDataStream.ReadByte()); Assert.AreEqual(_terminalModeValues[TerminalModes.CS8], sshDataStream.ReadUInt32()); Assert.AreEqual((int) TerminalModes.ECHO, sshDataStream.ReadByte()); @@ -180,4 +180,4 @@ namespace Renci.SshNet.Tests.Classes.Messages.Connection Assert.AreEqual("pty-req", PseudoTerminalRequestInfo.Name); } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelSuccessMessageTest.cs b/src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelSuccessMessageTest.cs index 6bfb5b0c..68efa44c 100644 --- a/src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelSuccessMessageTest.cs +++ b/src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelSuccessMessageTest.cs @@ -1,6 +1,6 @@ -using Renci.SshNet.Messages.Connection; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Renci.SshNet.Messages.Connection; using Renci.SshNet.Tests.Common; namespace Renci.SshNet.Tests.Classes.Messages.Connection @@ -19,7 +19,7 @@ namespace Renci.SshNet.Tests.Classes.Messages.Connection [Ignore] // placeholder public void ChannelSuccessMessageConstructorTest() { - ChannelSuccessMessage target = new ChannelSuccessMessage(); + var target = new ChannelSuccessMessage(); Assert.Inconclusive("TODO: Implement code to verify target"); } @@ -31,7 +31,7 @@ namespace Renci.SshNet.Tests.Classes.Messages.Connection public void ChannelSuccessMessageConstructorTest1() { uint localChannelNumber = 0; // TODO: Initialize to an appropriate value - ChannelSuccessMessage target = new ChannelSuccessMessage(localChannelNumber); + var target = new ChannelSuccessMessage(localChannelNumber); Assert.Inconclusive("TODO: Implement code to verify target"); } } diff --git a/src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelWindowAdjustMessageTest.cs b/src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelWindowAdjustMessageTest.cs index 131cdfda..12d19a61 100644 --- a/src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelWindowAdjustMessageTest.cs +++ b/src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelWindowAdjustMessageTest.cs @@ -1,6 +1,6 @@ -using Renci.SshNet.Messages.Connection; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Renci.SshNet.Messages.Connection; using Renci.SshNet.Tests.Common; namespace Renci.SshNet.Tests.Classes.Messages.Connection diff --git a/src/Renci.SshNet.Tests/Classes/Messages/Connection/RequestInfoTest.cs b/src/Renci.SshNet.Tests/Classes/Messages/Connection/RequestInfoTest.cs index fdf6d534..f37ecc18 100644 --- a/src/Renci.SshNet.Tests/Classes/Messages/Connection/RequestInfoTest.cs +++ b/src/Renci.SshNet.Tests/Classes/Messages/Connection/RequestInfoTest.cs @@ -1,6 +1,6 @@ -using Renci.SshNet.Messages.Connection; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Renci.SshNet.Messages.Connection; using Renci.SshNet.Tests.Common; namespace Renci.SshNet.Tests.Classes.Messages.Connection @@ -26,9 +26,8 @@ namespace Renci.SshNet.Tests.Classes.Messages.Connection [TestMethod()] public void RequestNameTest() { - RequestInfo target = CreateRequestInfo(); // TODO: Initialize to an appropriate value - string actual; - actual = target.RequestName; + var target = CreateRequestInfo(); // TODO: Initialize to an appropriate value + var actual = target.RequestName; Assert.Inconclusive("Verify the correctness of this test method."); } } diff --git a/src/Renci.SshNet.Tests/Classes/Messages/Connection/RequestSuccessMessageTest.cs b/src/Renci.SshNet.Tests/Classes/Messages/Connection/RequestSuccessMessageTest.cs index 55d7ec6d..fba92245 100644 --- a/src/Renci.SshNet.Tests/Classes/Messages/Connection/RequestSuccessMessageTest.cs +++ b/src/Renci.SshNet.Tests/Classes/Messages/Connection/RequestSuccessMessageTest.cs @@ -1,6 +1,6 @@ -using Renci.SshNet.Messages.Connection; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Renci.SshNet.Messages.Connection; using Renci.SshNet.Tests.Common; namespace Renci.SshNet.Tests.Classes.Messages.Connection @@ -19,7 +19,7 @@ namespace Renci.SshNet.Tests.Classes.Messages.Connection [Ignore] // placeholder public void RequestSuccessMessageConstructorTest() { - RequestSuccessMessage target = new RequestSuccessMessage(); + var target = new RequestSuccessMessage(); Assert.Inconclusive("TODO: Implement code to verify target"); } @@ -31,7 +31,7 @@ namespace Renci.SshNet.Tests.Classes.Messages.Connection public void RequestSuccessMessageConstructorTest1() { uint boundPort = 0; // TODO: Initialize to an appropriate value - RequestSuccessMessage target = new RequestSuccessMessage(boundPort); + var target = new RequestSuccessMessage(boundPort); Assert.Inconclusive("TODO: Implement code to verify target"); } } diff --git a/src/Renci.SshNet.Tests/Classes/Messages/MessageAttributeTest.cs b/src/Renci.SshNet.Tests/Classes/Messages/MessageAttributeTest.cs index 9c4f93cd..eb6118d6 100644 --- a/src/Renci.SshNet.Tests/Classes/Messages/MessageAttributeTest.cs +++ b/src/Renci.SshNet.Tests/Classes/Messages/MessageAttributeTest.cs @@ -1,6 +1,6 @@ -using Renci.SshNet.Messages; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Renci.SshNet.Messages; using Renci.SshNet.Tests.Common; namespace Renci.SshNet.Tests.Classes.Messages @@ -19,9 +19,9 @@ namespace Renci.SshNet.Tests.Classes.Messages [Ignore] // placeholder public void MessageAttributeConstructorTest() { - string name = string.Empty; // TODO: Initialize to an appropriate value + var name = string.Empty; // TODO: Initialize to an appropriate value byte number = 0; // TODO: Initialize to an appropriate value - MessageAttribute target = new MessageAttribute(name, number); + var target = new MessageAttribute(name, number); Assert.Inconclusive("TODO: Implement code to verify target"); } @@ -32,10 +32,10 @@ namespace Renci.SshNet.Tests.Classes.Messages [Ignore] // placeholder public void NameTest() { - string name = string.Empty; // TODO: Initialize to an appropriate value + var name = string.Empty; // TODO: Initialize to an appropriate value byte number = 0; // TODO: Initialize to an appropriate value - MessageAttribute target = new MessageAttribute(name, number); // TODO: Initialize to an appropriate value - string expected = string.Empty; // TODO: Initialize to an appropriate value + var target = new MessageAttribute(name, number); // TODO: Initialize to an appropriate value + var expected = string.Empty; // TODO: Initialize to an appropriate value target.Name = expected; var actual = target.Name; Assert.AreEqual(expected, actual); @@ -49,9 +49,9 @@ namespace Renci.SshNet.Tests.Classes.Messages [Ignore] // placeholder public void NumberTest() { - string name = string.Empty; // TODO: Initialize to an appropriate value + var name = string.Empty; // TODO: Initialize to an appropriate value byte number = 0; // TODO: Initialize to an appropriate value - MessageAttribute target = new MessageAttribute(name, number); // TODO: Initialize to an appropriate value + var target = new MessageAttribute(name, number); // TODO: Initialize to an appropriate value byte expected = 0; // TODO: Initialize to an appropriate value target.Number = expected; var actual = target.Number; diff --git a/src/Renci.SshNet.Tests/Classes/Messages/MessageTest.cs b/src/Renci.SshNet.Tests/Classes/Messages/MessageTest.cs index 5ad0d6bf..d3290f1f 100644 --- a/src/Renci.SshNet.Tests/Classes/Messages/MessageTest.cs +++ b/src/Renci.SshNet.Tests/Classes/Messages/MessageTest.cs @@ -1,6 +1,6 @@ -using Renci.SshNet.Messages; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Renci.SshNet.Messages; using Renci.SshNet.Tests.Common; namespace Renci.SshNet.Tests.Classes.Messages @@ -26,7 +26,7 @@ namespace Renci.SshNet.Tests.Classes.Messages [TestMethod] public void GetBytesTest() { - Message target = CreateMessage(); // TODO: Initialize to an appropriate value + var target = CreateMessage(); // TODO: Initialize to an appropriate value byte[] expected = null; // TODO: Initialize to an appropriate value var actual = target.GetBytes(); Assert.AreEqual(expected, actual); @@ -39,8 +39,8 @@ namespace Renci.SshNet.Tests.Classes.Messages [TestMethod] public void ToStringTest() { - Message target = CreateMessage(); // TODO: Initialize to an appropriate value - string expected = string.Empty; // TODO: Initialize to an appropriate value + var target = CreateMessage(); // TODO: Initialize to an appropriate value + var expected = string.Empty; // TODO: Initialize to an appropriate value var actual = target.ToString(); Assert.AreEqual(expected, actual); Assert.Inconclusive("Verify the correctness of this test method."); diff --git a/src/Renci.SshNet.Tests/Classes/Messages/Transport/DisconnectMessageTest.cs b/src/Renci.SshNet.Tests/Classes/Messages/Transport/DisconnectMessageTest.cs index 28037036..85365382 100644 --- a/src/Renci.SshNet.Tests/Classes/Messages/Transport/DisconnectMessageTest.cs +++ b/src/Renci.SshNet.Tests/Classes/Messages/Transport/DisconnectMessageTest.cs @@ -1,6 +1,6 @@ -using Renci.SshNet.Messages.Transport; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Renci.SshNet.Messages.Transport; using Renci.SshNet.Tests.Common; namespace Renci.SshNet.Tests.Classes.Messages.Transport @@ -19,7 +19,7 @@ namespace Renci.SshNet.Tests.Classes.Messages.Transport [Ignore] // placeholder public void DisconnectMessageConstructorTest() { - DisconnectMessage target = new DisconnectMessage(); + var target = new DisconnectMessage(); Assert.Inconclusive("TODO: Implement code to verify target"); } @@ -30,9 +30,9 @@ namespace Renci.SshNet.Tests.Classes.Messages.Transport [Ignore] // placeholder public void DisconnectMessageConstructorTest1() { - DisconnectReason reasonCode = new DisconnectReason(); // TODO: Initialize to an appropriate value - string message = string.Empty; // TODO: Initialize to an appropriate value - DisconnectMessage target = new DisconnectMessage(reasonCode, message); + var reasonCode = new DisconnectReason(); // TODO: Initialize to an appropriate value + var message = string.Empty; // TODO: Initialize to an appropriate value + var target = new DisconnectMessage(reasonCode, message); Assert.Inconclusive("TODO: Implement code to verify target"); } } diff --git a/src/Renci.SshNet.Tests/Classes/Messages/Transport/KeyExchangeDhGroupExchangeGroupTest.cs b/src/Renci.SshNet.Tests/Classes/Messages/Transport/KeyExchangeDhGroupExchangeGroupTest.cs index fc49a61d..b5a56f48 100644 --- a/src/Renci.SshNet.Tests/Classes/Messages/Transport/KeyExchangeDhGroupExchangeGroupTest.cs +++ b/src/Renci.SshNet.Tests/Classes/Messages/Transport/KeyExchangeDhGroupExchangeGroupTest.cs @@ -1,6 +1,6 @@ -using Renci.SshNet.Messages.Transport; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Renci.SshNet.Messages.Transport; using Renci.SshNet.Tests.Common; namespace Renci.SshNet.Tests.Classes.Messages.Transport @@ -19,7 +19,7 @@ namespace Renci.SshNet.Tests.Classes.Messages.Transport [Ignore] // placeholder public void KeyExchangeDhGroupExchangeGroupConstructorTest() { - KeyExchangeDhGroupExchangeGroup target = new KeyExchangeDhGroupExchangeGroup(); + var target = new KeyExchangeDhGroupExchangeGroup(); Assert.Inconclusive("TODO: Implement code to verify target"); } } diff --git a/src/Renci.SshNet.Tests/Classes/Messages/Transport/KeyExchangeDhGroupExchangeReplyBuilder.cs b/src/Renci.SshNet.Tests/Classes/Messages/Transport/KeyExchangeDhGroupExchangeReplyBuilder.cs index dbb5d0ed..81714f36 100644 --- a/src/Renci.SshNet.Tests/Classes/Messages/Transport/KeyExchangeDhGroupExchangeReplyBuilder.cs +++ b/src/Renci.SshNet.Tests/Classes/Messages/Transport/KeyExchangeDhGroupExchangeReplyBuilder.cs @@ -17,7 +17,10 @@ namespace Renci.SshNet.Tests.Classes.Messages.Transport var sshDataStream = new SshDataStream(0); foreach (var hostKey in hostKeys) + { sshDataStream.Write(hostKey); + } + _hostKeys = sshDataStream.ToArray(); return this; diff --git a/src/Renci.SshNet.Tests/Classes/Messages/Transport/KeyExchangeDhGroupExchangeRequestTest.cs b/src/Renci.SshNet.Tests/Classes/Messages/Transport/KeyExchangeDhGroupExchangeRequestTest.cs index ea2b7159..0120c555 100644 --- a/src/Renci.SshNet.Tests/Classes/Messages/Transport/KeyExchangeDhGroupExchangeRequestTest.cs +++ b/src/Renci.SshNet.Tests/Classes/Messages/Transport/KeyExchangeDhGroupExchangeRequestTest.cs @@ -1,8 +1,9 @@ using System; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Renci.SshNet.Common; using Renci.SshNet.Messages.Transport; -using Renci.SshNet.Tests.Common; namespace Renci.SshNet.Tests.Classes.Messages.Transport { @@ -67,4 +68,4 @@ namespace Renci.SshNet.Tests.Classes.Messages.Transport Assert.AreEqual(_maximum, target.Maximum); } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.Tests/Classes/Messages/Transport/KeyExchangeDhReplyMessageTest.cs b/src/Renci.SshNet.Tests/Classes/Messages/Transport/KeyExchangeDhReplyMessageTest.cs index 961fe437..a0d0d2f1 100644 --- a/src/Renci.SshNet.Tests/Classes/Messages/Transport/KeyExchangeDhReplyMessageTest.cs +++ b/src/Renci.SshNet.Tests/Classes/Messages/Transport/KeyExchangeDhReplyMessageTest.cs @@ -1,6 +1,6 @@ -using Renci.SshNet.Messages.Transport; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Renci.SshNet.Messages.Transport; using Renci.SshNet.Tests.Common; namespace Renci.SshNet.Tests.Classes.Messages.Transport @@ -19,7 +19,7 @@ namespace Renci.SshNet.Tests.Classes.Messages.Transport [Ignore] // placeholder public void KeyExchangeDhReplyMessageConstructorTest() { - KeyExchangeDhReplyMessage target = new KeyExchangeDhReplyMessage(); + var target = new KeyExchangeDhReplyMessage(); Assert.Inconclusive("TODO: Implement code to verify target"); } } diff --git a/src/Renci.SshNet.Tests/Classes/Messages/Transport/NewKeysMessageTest.cs b/src/Renci.SshNet.Tests/Classes/Messages/Transport/NewKeysMessageTest.cs index f1ea0398..430c2151 100644 --- a/src/Renci.SshNet.Tests/Classes/Messages/Transport/NewKeysMessageTest.cs +++ b/src/Renci.SshNet.Tests/Classes/Messages/Transport/NewKeysMessageTest.cs @@ -1,6 +1,6 @@ -using Renci.SshNet.Messages.Transport; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Renci.SshNet.Messages.Transport; using Renci.SshNet.Tests.Common; namespace Renci.SshNet.Tests.Classes.Messages.Transport @@ -19,7 +19,7 @@ namespace Renci.SshNet.Tests.Classes.Messages.Transport [Ignore] // placeholder public void NewKeysMessageConstructorTest() { - NewKeysMessage target = new NewKeysMessage(); + var target = new NewKeysMessage(); Assert.Inconclusive("TODO: Implement code to verify target"); } } diff --git a/src/Renci.SshNet.Tests/Classes/Messages/Transport/ServiceAcceptMessageTest.cs b/src/Renci.SshNet.Tests/Classes/Messages/Transport/ServiceAcceptMessageTest.cs index 5c8f4091..143cd700 100644 --- a/src/Renci.SshNet.Tests/Classes/Messages/Transport/ServiceAcceptMessageTest.cs +++ b/src/Renci.SshNet.Tests/Classes/Messages/Transport/ServiceAcceptMessageTest.cs @@ -1,6 +1,6 @@ -using Renci.SshNet.Messages.Transport; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Renci.SshNet.Messages.Transport; namespace Renci.SshNet.Tests.Classes.Messages.Transport { @@ -18,7 +18,7 @@ namespace Renci.SshNet.Tests.Classes.Messages.Transport [Ignore] // placeholder public void ServiceAcceptMessageConstructorTest() { - ServiceAcceptMessage target = new ServiceAcceptMessage(); + var target = new ServiceAcceptMessage(); Assert.Inconclusive("TODO: Implement code to verify target"); } } diff --git a/src/Renci.SshNet.Tests/Classes/Messages/Transport/ServiceRequestMessageTest.cs b/src/Renci.SshNet.Tests/Classes/Messages/Transport/ServiceRequestMessageTest.cs index dc1db6a4..9806fb86 100644 --- a/src/Renci.SshNet.Tests/Classes/Messages/Transport/ServiceRequestMessageTest.cs +++ b/src/Renci.SshNet.Tests/Classes/Messages/Transport/ServiceRequestMessageTest.cs @@ -1,6 +1,6 @@ -using Renci.SshNet.Messages.Transport; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Renci.SshNet.Messages.Transport; using Renci.SshNet.Messages; namespace Renci.SshNet.Tests.Classes.Messages.Transport @@ -19,8 +19,8 @@ namespace Renci.SshNet.Tests.Classes.Messages.Transport [Ignore] // placeholder public void ServiceRequestMessageConstructorTest() { - ServiceName serviceName = new ServiceName(); // TODO: Initialize to an appropriate value - ServiceRequestMessage target = new ServiceRequestMessage(serviceName); + var serviceName = new ServiceName(); // TODO: Initialize to an appropriate value + var target = new ServiceRequestMessage(serviceName); Assert.Inconclusive("TODO: Implement code to verify target"); } } diff --git a/src/Renci.SshNet.Tests/Classes/Messages/Transport/UnimplementedMessageTest.cs b/src/Renci.SshNet.Tests/Classes/Messages/Transport/UnimplementedMessageTest.cs index 8abdd4ad..5e8d53ad 100644 --- a/src/Renci.SshNet.Tests/Classes/Messages/Transport/UnimplementedMessageTest.cs +++ b/src/Renci.SshNet.Tests/Classes/Messages/Transport/UnimplementedMessageTest.cs @@ -1,6 +1,5 @@ using Renci.SshNet.Messages.Transport; using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; using Renci.SshNet.Tests.Common; namespace Renci.SshNet.Tests.Classes.Messages.Transport @@ -19,7 +18,7 @@ namespace Renci.SshNet.Tests.Classes.Messages.Transport [Ignore] // placeholder public void UnimplementedMessageConstructorTest() { - UnimplementedMessage target = new UnimplementedMessage(); + var target = new UnimplementedMessage(); Assert.Inconclusive("TODO: Implement code to verify target"); } } diff --git a/src/Renci.SshNet.Tests/Classes/NetConfClientTest.cs b/src/Renci.SshNet.Tests/Classes/NetConfClientTest.cs index d70fea36..0a76674b 100644 --- a/src/Renci.SshNet.Tests/Classes/NetConfClientTest.cs +++ b/src/Renci.SshNet.Tests/Classes/NetConfClientTest.cs @@ -86,7 +86,7 @@ namespace Renci.SshNet.Tests.Classes catch (ArgumentOutOfRangeException ex) { Assert.IsNull(ex.InnerException); - Assert.AreEqual("The timeout must represent a value between -1 and Int32.MaxValue, inclusive." + Environment.NewLine + "Parameter name: " + ex.ParamName, ex.Message); + ArgumentExceptionAssert.MessageEquals("The timeout must represent a value between -1 and Int32.MaxValue, inclusive.", ex); Assert.AreEqual("value", ex.ParamName); } } @@ -105,7 +105,7 @@ namespace Renci.SshNet.Tests.Classes catch (ArgumentOutOfRangeException ex) { Assert.IsNull(ex.InnerException); - Assert.AreEqual("The timeout must represent a value between -1 and Int32.MaxValue, inclusive." + Environment.NewLine + "Parameter name: " + ex.ParamName, ex.Message); + ArgumentExceptionAssert.MessageEquals("The timeout must represent a value between -1 and Int32.MaxValue, inclusive.", ex); Assert.AreEqual("value", ex.ParamName); } } diff --git a/src/Renci.SshNet.Tests/Classes/NetConfClientTestBase.cs b/src/Renci.SshNet.Tests/Classes/NetConfClientTestBase.cs index ee8cb018..4c6cbe7f 100644 --- a/src/Renci.SshNet.Tests/Classes/NetConfClientTestBase.cs +++ b/src/Renci.SshNet.Tests/Classes/NetConfClientTestBase.cs @@ -5,13 +5,13 @@ namespace Renci.SshNet.Tests.Classes { public abstract class NetConfClientTestBase : BaseClientTestBase { - internal Mock _netConfSessionMock { get; private set; } + internal Mock NetConfSessionMock { get; private set; } protected override void CreateMocks() { base.CreateMocks(); - _netConfSessionMock = new Mock(MockBehavior.Strict); + NetConfSessionMock = new Mock(MockBehavior.Strict); } } } diff --git a/src/Renci.SshNet.Tests/Classes/NetConfClientTest_Connect_NetConfSessionConnectFailure.cs b/src/Renci.SshNet.Tests/Classes/NetConfClientTest_Connect_NetConfSessionConnectFailure.cs index 980c574a..41c285a8 100644 --- a/src/Renci.SshNet.Tests/Classes/NetConfClientTest_Connect_NetConfSessionConnectFailure.cs +++ b/src/Renci.SshNet.Tests/Classes/NetConfClientTest_Connect_NetConfSessionConnectFailure.cs @@ -1,10 +1,13 @@ using System; +using System.Linq; using System.Reflection; using System.Threading; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; + using Renci.SshNet.Common; -using Renci.SshNet.NetConf; using Renci.SshNet.Security; namespace Renci.SshNet.Tests.Classes @@ -21,31 +24,31 @@ namespace Renci.SshNet.Tests.Classes { _connectionInfo = new ConnectionInfo("host", "user", new NoneAuthenticationMethod("userauth")); _netConfSessionConnectionException = new ApplicationException(); - _netConfClient = new NetConfClient(_connectionInfo, false, _serviceFactoryMock.Object); + _netConfClient = new NetConfClient(_connectionInfo, false, ServiceFactoryMock.Object); } protected override void SetupMocks() { var sequence = new MockSequence(); - _serviceFactoryMock.InSequence(sequence) - .Setup(p => p.CreateSocketFactory()) - .Returns(_socketFactoryMock.Object); - _serviceFactoryMock.InSequence(sequence) - .Setup(p => p.CreateSession(_connectionInfo, _socketFactoryMock.Object)) - .Returns(_sessionMock.Object); - _sessionMock.InSequence(sequence) - .Setup(p => p.Connect()); - _serviceFactoryMock.InSequence(sequence) - .Setup(p => p.CreateNetConfSession(_sessionMock.Object, -1)) - .Returns(_netConfSessionMock.Object); - _netConfSessionMock.InSequence(sequence) - .Setup(p => p.Connect()) - .Throws(_netConfSessionConnectionException); - _netConfSessionMock.InSequence(sequence) + _ = ServiceFactoryMock.InSequence(sequence) + .Setup(p => p.CreateSocketFactory()) + .Returns(SocketFactoryMock.Object); + _ = ServiceFactoryMock.InSequence(sequence) + .Setup(p => p.CreateSession(_connectionInfo, SocketFactoryMock.Object)) + .Returns(SessionMock.Object); + _ = SessionMock.InSequence(sequence) + .Setup(p => p.Connect()); + _ = ServiceFactoryMock.InSequence(sequence) + .Setup(p => p.CreateNetConfSession(SessionMock.Object, -1)) + .Returns(NetConfSessionMock.Object); + _ = NetConfSessionMock.InSequence(sequence) + .Setup(p => p.Connect()) + .Throws(_netConfSessionConnectionException); + _ = NetConfSessionMock.InSequence(sequence) + .Setup(p => p.Dispose()); + _ = SessionMock.InSequence(sequence) .Setup(p => p.Dispose()); - _sessionMock.InSequence(sequence) - .Setup(p => p.Dispose()); } protected override void Act() @@ -87,7 +90,7 @@ namespace Renci.SshNet.Tests.Classes _netConfClient.ErrorOccurred += (sender, args) => Interlocked.Increment(ref errorOccurredSignalCount); - _sessionMock.Raise(p => p.ErrorOccured += null, new ExceptionEventArgs(new Exception())); + SessionMock.Raise(p => p.ErrorOccured += null, new ExceptionEventArgs(new Exception())); Assert.AreEqual(0, errorOccurredSignalCount); } @@ -99,7 +102,7 @@ namespace Renci.SshNet.Tests.Classes _netConfClient.HostKeyReceived += (sender, args) => Interlocked.Increment(ref hostKeyReceivedSignalCount); - _sessionMock.Raise(p => p.HostKeyReceived += null, new HostKeyEventArgs(GetKeyHostAlgorithm())); + SessionMock.Raise(p => p.HostKeyReceived += null, new HostKeyEventArgs(GetKeyHostAlgorithm())); Assert.AreEqual(0, hostKeyReceivedSignalCount); } @@ -111,7 +114,7 @@ namespace Renci.SshNet.Tests.Classes using (var s = executingAssembly.GetManifestResourceStream(string.Format("Renci.SshNet.Tests.Data.{0}", "Key.RSA.txt"))) { var privateKey = new PrivateKeyFile(s); - return (KeyHostAlgorithm)privateKey.HostKey; + return (KeyHostAlgorithm)privateKey.HostKeyAlgorithms.First(); } } } diff --git a/src/Renci.SshNet.Tests/Classes/NetConfClientTest_Dispose_Connected.cs b/src/Renci.SshNet.Tests/Classes/NetConfClientTest_Dispose_Connected.cs index ba86a7c9..ed010383 100644 --- a/src/Renci.SshNet.Tests/Classes/NetConfClientTest_Dispose_Connected.cs +++ b/src/Renci.SshNet.Tests/Classes/NetConfClientTest_Dispose_Connected.cs @@ -1,7 +1,8 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; -using Renci.SshNet.NetConf; -using System; namespace Renci.SshNet.Tests.Classes { @@ -16,35 +17,37 @@ namespace Renci.SshNet.Tests.Classes { _connectionInfo = new ConnectionInfo("host", "user", new NoneAuthenticationMethod("userauth")); _operationTimeout = new Random().Next(1000, 10000); - _netConfClient = new NetConfClient(_connectionInfo, false, _serviceFactoryMock.Object); - _netConfClient.OperationTimeout = TimeSpan.FromMilliseconds(_operationTimeout); + _netConfClient = new NetConfClient(_connectionInfo, false, ServiceFactoryMock.Object) + { + OperationTimeout = TimeSpan.FromMilliseconds(_operationTimeout) + }; } protected override void SetupMocks() { var sequence = new MockSequence(); - _serviceFactoryMock.InSequence(sequence) - .Setup(p => p.CreateSocketFactory()) - .Returns(_socketFactoryMock.Object); - _serviceFactoryMock.InSequence(sequence) - .Setup(p => p.CreateSession(_connectionInfo, _socketFactoryMock.Object)) - .Returns(_sessionMock.Object); - _sessionMock.InSequence(sequence) - .Setup(p => p.Connect()); - _serviceFactoryMock.InSequence(sequence) - .Setup(p => p.CreateNetConfSession(_sessionMock.Object, _operationTimeout)) - .Returns(_netConfSessionMock.Object); - _netConfSessionMock.InSequence(sequence) - .Setup(p => p.Connect()); - _sessionMock.InSequence(sequence) - .Setup(p => p.OnDisconnecting()); - _netConfSessionMock.InSequence(sequence) - .Setup(p => p.Disconnect()); - _sessionMock.InSequence(sequence) - .Setup(p => p.Dispose()); - _netConfSessionMock.InSequence(sequence) - .Setup(p => p.Dispose()); + _ = ServiceFactoryMock.InSequence(sequence) + .Setup(p => p.CreateSocketFactory()) + .Returns(SocketFactoryMock.Object); + _ = ServiceFactoryMock.InSequence(sequence) + .Setup(p => p.CreateSession(_connectionInfo, SocketFactoryMock.Object)) + .Returns(SessionMock.Object); + _ = SessionMock.InSequence(sequence) + .Setup(p => p.Connect()); + _ = ServiceFactoryMock.InSequence(sequence) + .Setup(p => p.CreateNetConfSession(SessionMock.Object, _operationTimeout)) + .Returns(NetConfSessionMock.Object); + _ = NetConfSessionMock.InSequence(sequence) + .Setup(p => p.Connect()); + _ = SessionMock.InSequence(sequence) + .Setup(p => p.OnDisconnecting()); + _ = NetConfSessionMock.InSequence(sequence) + .Setup(p => p.Disconnect()); + _ = SessionMock.InSequence(sequence) + .Setup(p => p.Dispose()); + _ = NetConfSessionMock.InSequence(sequence) + .Setup(p => p.Dispose()); } protected override void Arrange() @@ -62,50 +65,50 @@ namespace Renci.SshNet.Tests.Classes [TestMethod] public void CreateNetConfSessionOnServiceFactoryShouldBeInvokedOnce() { - _serviceFactoryMock.Verify(p => p.CreateNetConfSession(_sessionMock.Object, _operationTimeout), Times.Once); + ServiceFactoryMock.Verify(p => p.CreateNetConfSession(SessionMock.Object, _operationTimeout), Times.Once); } [TestMethod] public void CreateSocketFactoryOnServiceFactoryShouldBeInvokedOnce() { - _serviceFactoryMock.Verify(p => p.CreateSocketFactory(), Times.Once); + ServiceFactoryMock.Verify(p => p.CreateSocketFactory(), Times.Once); } [TestMethod] public void CreateSessionOnServiceFactoryShouldBeInvokedOnce() { - _serviceFactoryMock.Verify(p => p.CreateSession(_connectionInfo, _socketFactoryMock.Object), + ServiceFactoryMock.Verify(p => p.CreateSession(_connectionInfo, SocketFactoryMock.Object), Times.Once); } [TestMethod] public void DisconnectOnNetConfSessionShouldBeInvokedOnce() { - _netConfSessionMock.Verify(p => p.Disconnect(), Times.Once); + NetConfSessionMock.Verify(p => p.Disconnect(), Times.Once); } [TestMethod] public void DisconnectOnSessionShouldNeverBeInvoked() { - _sessionMock.Verify(p => p.Disconnect(), Times.Never); + SessionMock.Verify(p => p.Disconnect(), Times.Never); } [TestMethod] public void DisposeOnNetConfSessionShouldBeInvokedOnce() { - _netConfSessionMock.Verify(p => p.Dispose(), Times.Once); + NetConfSessionMock.Verify(p => p.Dispose(), Times.Once); } [TestMethod] public void DisposeOnSessionShouldBeInvokedOnce() { - _sessionMock.Verify(p => p.Dispose(), Times.Once); + SessionMock.Verify(p => p.Dispose(), Times.Once); } [TestMethod] public void OnDisconnectingOnSessionShouldBeInvokedOnce() { - _sessionMock.Verify(p => p.OnDisconnecting(), Times.Once); + SessionMock.Verify(p => p.OnDisconnecting(), Times.Once); } } } diff --git a/src/Renci.SshNet.Tests/Classes/NetConfClientTest_Dispose_Disconnected.cs b/src/Renci.SshNet.Tests/Classes/NetConfClientTest_Dispose_Disconnected.cs index 8a4fbcd2..efbfe7ff 100644 --- a/src/Renci.SshNet.Tests/Classes/NetConfClientTest_Dispose_Disconnected.cs +++ b/src/Renci.SshNet.Tests/Classes/NetConfClientTest_Dispose_Disconnected.cs @@ -1,6 +1,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; -using Renci.SshNet.NetConf; + using System; namespace Renci.SshNet.Tests.Classes @@ -19,39 +20,43 @@ namespace Renci.SshNet.Tests.Classes Act(); } - [TestCleanup] - public void Cleanup() - { - } - protected override void SetupData() { _connectionInfo = new ConnectionInfo("host", "user", new NoneAuthenticationMethod("userauth")); _operationTimeout = new Random().Next(1000, 10000); - _netConfClient = new NetConfClient(_connectionInfo, false, _serviceFactoryMock.Object); - _netConfClient.OperationTimeout = TimeSpan.FromMilliseconds(_operationTimeout); + _netConfClient = new NetConfClient(_connectionInfo, false, ServiceFactoryMock.Object) + { + OperationTimeout = TimeSpan.FromMilliseconds(_operationTimeout) + }; } protected override void SetupMocks() { var sequence = new MockSequence(); - _serviceFactoryMock.InSequence(sequence) - .Setup(p => p.CreateSocketFactory()) - .Returns(_socketFactoryMock.Object); - _serviceFactoryMock.InSequence(sequence) - .Setup(p => p.CreateSession(_connectionInfo, _socketFactoryMock.Object)) - .Returns(_sessionMock.Object); - _sessionMock.InSequence(sequence).Setup(p => p.Connect()); - _serviceFactoryMock.InSequence(sequence) - .Setup(p => p.CreateNetConfSession(_sessionMock.Object, _operationTimeout)) - .Returns(_netConfSessionMock.Object); - _netConfSessionMock.InSequence(sequence).Setup(p => p.Connect()); - _sessionMock.InSequence(sequence).Setup(p => p.OnDisconnecting()); - _netConfSessionMock.InSequence(sequence).Setup(p => p.Disconnect()); - _sessionMock.InSequence(sequence).Setup(p => p.Dispose()); - _netConfSessionMock.InSequence(sequence).Setup(p => p.Disconnect()); - _netConfSessionMock.InSequence(sequence).Setup(p => p.Dispose()); + _ = ServiceFactoryMock.InSequence(sequence) + .Setup(p => p.CreateSocketFactory()) + .Returns(SocketFactoryMock.Object); + _ = ServiceFactoryMock.InSequence(sequence) + .Setup(p => p.CreateSession(_connectionInfo, SocketFactoryMock.Object)) + .Returns(SessionMock.Object); + _ = SessionMock.InSequence(sequence) + .Setup(p => p.Connect()); + _ = ServiceFactoryMock.InSequence(sequence) + .Setup(p => p.CreateNetConfSession(SessionMock.Object, _operationTimeout)) + .Returns(NetConfSessionMock.Object); + _ = NetConfSessionMock.InSequence(sequence) + .Setup(p => p.Connect()); + _ = SessionMock.InSequence(sequence) + .Setup(p => p.OnDisconnecting()); + _ = NetConfSessionMock.InSequence(sequence) + .Setup(p => p.Disconnect()); + _ = SessionMock.InSequence(sequence) + .Setup(p => p.Dispose()); + _ = NetConfSessionMock.InSequence(sequence) + .Setup(p => p.Disconnect()); + _ = NetConfSessionMock.InSequence(sequence) + .Setup(p => p.Dispose()); } protected override void Arrange() @@ -70,50 +75,50 @@ namespace Renci.SshNet.Tests.Classes [TestMethod] public void CreateNetConfSessionOnServiceFactoryShouldBeInvokedOnce() { - _serviceFactoryMock.Verify(p => p.CreateNetConfSession(_sessionMock.Object, _operationTimeout), Times.Once); + ServiceFactoryMock.Verify(p => p.CreateNetConfSession(SessionMock.Object, _operationTimeout), Times.Once); } [TestMethod] public void CreateSocketFactoryOnServiceFactoryShouldBeInvokedOnce() { - _serviceFactoryMock.Verify(p => p.CreateSocketFactory(), Times.Once); + ServiceFactoryMock.Verify(p => p.CreateSocketFactory(), Times.Once); } [TestMethod] public void CreateSessionOnServiceFactoryShouldBeInvokedOnce() { - _serviceFactoryMock.Verify(p => p.CreateSession(_connectionInfo, _socketFactoryMock.Object), + ServiceFactoryMock.Verify(p => p.CreateSession(_connectionInfo, SocketFactoryMock.Object), Times.Once); } [TestMethod] public void DisconnectOnNetConfSessionShouldBeInvokedTwice() { - _netConfSessionMock.Verify(p => p.Disconnect(), Times.Exactly(2)); + NetConfSessionMock.Verify(p => p.Disconnect(), Times.Exactly(2)); } [TestMethod] public void DisconnectOnSessionShouldNeverBeInvoked() { - _sessionMock.Verify(p => p.Disconnect(), Times.Never); + SessionMock.Verify(p => p.Disconnect(), Times.Never); } [TestMethod] public void DisposeOnNetConfSessionShouldBeInvokedOnce() { - _netConfSessionMock.Verify(p => p.Dispose(), Times.Once); + NetConfSessionMock.Verify(p => p.Dispose(), Times.Once); } [TestMethod] public void DisposeOnSessionShouldBeInvokedOnce() { - _sessionMock.Verify(p => p.Dispose(), Times.Once); + SessionMock.Verify(p => p.Dispose(), Times.Once); } [TestMethod] public void OnDisconnectingOnSessionShouldBeInvokedOnce() { - _sessionMock.Verify(p => p.OnDisconnecting(), Times.Once); + SessionMock.Verify(p => p.OnDisconnecting(), Times.Once); } } } diff --git a/src/Renci.SshNet.Tests/Classes/NetConfClientTest_Dispose_Disposed.cs b/src/Renci.SshNet.Tests/Classes/NetConfClientTest_Dispose_Disposed.cs index 9ed692a3..cc5f704f 100644 --- a/src/Renci.SshNet.Tests/Classes/NetConfClientTest_Dispose_Disposed.cs +++ b/src/Renci.SshNet.Tests/Classes/NetConfClientTest_Dispose_Disposed.cs @@ -1,6 +1,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; -using Renci.SshNet.NetConf; + using System; namespace Renci.SshNet.Tests.Classes @@ -16,29 +17,37 @@ namespace Renci.SshNet.Tests.Classes { _connectionInfo = new ConnectionInfo("host", "user", new NoneAuthenticationMethod("userauth")); _operationTimeout = new Random().Next(1000, 10000); - _netConfClient = new NetConfClient(_connectionInfo, false, _serviceFactoryMock.Object); - _netConfClient.OperationTimeout = TimeSpan.FromMilliseconds(_operationTimeout); + _netConfClient = new NetConfClient(_connectionInfo, false, ServiceFactoryMock.Object) + { + OperationTimeout = TimeSpan.FromMilliseconds(_operationTimeout) + }; } protected override void SetupMocks() { var sequence = new MockSequence(); - _serviceFactoryMock.InSequence(sequence) - .Setup(p => p.CreateSocketFactory()) - .Returns(_socketFactoryMock.Object); - _serviceFactoryMock.InSequence(sequence) - .Setup(p => p.CreateSession(_connectionInfo, _socketFactoryMock.Object)) - .Returns(_sessionMock.Object); - _sessionMock.InSequence(sequence).Setup(p => p.Connect()); - _serviceFactoryMock.InSequence(sequence) - .Setup(p => p.CreateNetConfSession(_sessionMock.Object, _operationTimeout)) - .Returns(_netConfSessionMock.Object); - _netConfSessionMock.InSequence(sequence).Setup(p => p.Connect()); - _sessionMock.InSequence(sequence).Setup(p => p.OnDisconnecting()); - _netConfSessionMock.InSequence(sequence).Setup(p => p.Disconnect()); - _sessionMock.InSequence(sequence).Setup(p => p.Dispose()); - _netConfSessionMock.InSequence(sequence).Setup(p => p.Dispose()); + _ = ServiceFactoryMock.InSequence(sequence) + .Setup(p => p.CreateSocketFactory()) + .Returns(SocketFactoryMock.Object); + _ = ServiceFactoryMock.InSequence(sequence) + .Setup(p => p.CreateSession(_connectionInfo, SocketFactoryMock.Object)) + .Returns(SessionMock.Object); + _ = SessionMock.InSequence(sequence) + .Setup(p => p.Connect()); + _ = ServiceFactoryMock.InSequence(sequence) + .Setup(p => p.CreateNetConfSession(SessionMock.Object, _operationTimeout)) + .Returns(NetConfSessionMock.Object); + _ = NetConfSessionMock.InSequence(sequence) + .Setup(p => p.Connect()); + _ = SessionMock.InSequence(sequence) + .Setup(p => p.OnDisconnecting()); + _ = NetConfSessionMock.InSequence(sequence) + .Setup(p => p.Disconnect()); + _ = SessionMock.InSequence(sequence) + .Setup(p => p.Dispose()); + _ = NetConfSessionMock.InSequence(sequence) + .Setup(p => p.Dispose()); } protected override void Arrange() @@ -57,50 +66,50 @@ namespace Renci.SshNet.Tests.Classes [TestMethod] public void CreateNetConfSessionOnServiceFactoryShouldBeInvokedOnce() { - _serviceFactoryMock.Verify(p => p.CreateNetConfSession(_sessionMock.Object, _operationTimeout), Times.Once); + ServiceFactoryMock.Verify(p => p.CreateNetConfSession(SessionMock.Object, _operationTimeout), Times.Once); } [TestMethod] public void CreateSocketFactoryOnServiceFactoryShouldBeInvokedOnce() { - _serviceFactoryMock.Verify(p => p.CreateSocketFactory(), Times.Once); + ServiceFactoryMock.Verify(p => p.CreateSocketFactory(), Times.Once); } [TestMethod] public void CreateSessionOnServiceFactoryShouldBeInvokedOnce() { - _serviceFactoryMock.Verify(p => p.CreateSession(_connectionInfo, _socketFactoryMock.Object), + ServiceFactoryMock.Verify(p => p.CreateSession(_connectionInfo, SocketFactoryMock.Object), Times.Once); } [TestMethod] public void DisconnectOnNetConfSessionShouldBeInvokedOnce() { - _netConfSessionMock.Verify(p => p.Disconnect(), Times.Once); + NetConfSessionMock.Verify(p => p.Disconnect(), Times.Once); } [TestMethod] public void DisconnectOnSessionShouldNeverBeInvoked() { - _sessionMock.Verify(p => p.Disconnect(), Times.Never); + SessionMock.Verify(p => p.Disconnect(), Times.Never); } [TestMethod] public void DisposeOnNetConfSessionShouldBeInvokedOnce() { - _netConfSessionMock.Verify(p => p.Dispose(), Times.Once); + NetConfSessionMock.Verify(p => p.Dispose(), Times.Once); } [TestMethod] public void DisposeOnSessionShouldBeInvokedOnce() { - _sessionMock.Verify(p => p.Dispose(), Times.Once); + SessionMock.Verify(p => p.Dispose(), Times.Once); } [TestMethod] public void OnDisconnectingOnSessionShouldBeInvokedOnce() { - _sessionMock.Verify(p => p.OnDisconnecting(), Times.Once); + SessionMock.Verify(p => p.OnDisconnecting(), Times.Once); } } } diff --git a/src/Renci.SshNet.Tests/Classes/NetConfClientTest_Finalize_Connected.cs b/src/Renci.SshNet.Tests/Classes/NetConfClientTest_Finalize_Connected.cs index 44761e96..ec827b1e 100644 --- a/src/Renci.SshNet.Tests/Classes/NetConfClientTest_Finalize_Connected.cs +++ b/src/Renci.SshNet.Tests/Classes/NetConfClientTest_Finalize_Connected.cs @@ -16,8 +16,10 @@ namespace Renci.SshNet.Tests.Classes { _connectionInfo = new ConnectionInfo("host", "user", new NoneAuthenticationMethod("userauth")); _operationTimeout = new Random().Next(1000, 10000); - _netConfClient = new NetConfClient(_connectionInfo, false, _serviceFactoryMock.Object); - _netConfClient.OperationTimeout = TimeSpan.FromMilliseconds(_operationTimeout); + _netConfClient = new NetConfClient(_connectionInfo, false, ServiceFactoryMock.Object) + { + OperationTimeout = TimeSpan.FromMilliseconds(_operationTimeout) + }; _netConfClientWeakRefence = new WeakReference(_netConfClient); } @@ -25,19 +27,19 @@ namespace Renci.SshNet.Tests.Classes { var sequence = new MockSequence(); - _serviceFactoryMock.InSequence(sequence) - .Setup(p => p.CreateSocketFactory()) - .Returns(_socketFactoryMock.Object); - _serviceFactoryMock.InSequence(sequence) - .Setup(p => p.CreateSession(_connectionInfo, _socketFactoryMock.Object)) - .Returns(_sessionMock.Object); - _sessionMock.InSequence(sequence) - .Setup(p => p.Connect()); - _serviceFactoryMock.InSequence(sequence) - .Setup(p => p.CreateNetConfSession(_sessionMock.Object, _operationTimeout)) - .Returns(_netConfSessionMock.Object); - _netConfSessionMock.InSequence(sequence) - .Setup(p => p.Connect()); + _ = ServiceFactoryMock.InSequence(sequence) + .Setup(p => p.CreateSocketFactory()) + .Returns(SocketFactoryMock.Object); + _ = ServiceFactoryMock.InSequence(sequence) + .Setup(p => p.CreateSession(_connectionInfo, SocketFactoryMock.Object)) + .Returns(SessionMock.Object); + _ = SessionMock.InSequence(sequence) + .Setup(p => p.Connect()); + _ = ServiceFactoryMock.InSequence(sequence) + .Setup(p => p.CreateNetConfSession(SessionMock.Object, _operationTimeout)) + .Returns(NetConfSessionMock.Object); + _ = NetConfSessionMock.InSequence(sequence) + .Setup(p => p.Connect()); } protected override void Arrange() @@ -64,7 +66,7 @@ namespace Renci.SshNet.Tests.Classes // Since we recreated the mocks, this test has no value // We'll leaving ths test just in case we have a solution that does not require us // to recreate the mocks - _netConfSessionMock.Verify(p => p.Disconnect(), Times.Never); + NetConfSessionMock.Verify(p => p.Disconnect(), Times.Never); } [TestMethod] @@ -73,7 +75,7 @@ namespace Renci.SshNet.Tests.Classes // Since we recreated the mocks, this test has no value // We'll leaving ths test just in case we have a solution that does not require us // to recreate the mocks - _netConfSessionMock.Verify(p => p.Dispose(), Times.Never); + NetConfSessionMock.Verify(p => p.Dispose(), Times.Never); } [TestMethod] diff --git a/src/Renci.SshNet.Tests/Classes/PasswordAuthenticationMethodTest.cs b/src/Renci.SshNet.Tests/Classes/PasswordAuthenticationMethodTest.cs index ec0d3fef..951314d1 100644 --- a/src/Renci.SshNet.Tests/Classes/PasswordAuthenticationMethodTest.cs +++ b/src/Renci.SshNet.Tests/Classes/PasswordAuthenticationMethodTest.cs @@ -1,6 +1,5 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Renci.SshNet.Tests.Common; -using Renci.SshNet.Tests.Properties; using System; namespace Renci.SshNet.Tests.Classes @@ -59,32 +58,6 @@ namespace Renci.SshNet.Tests.Classes new PasswordAuthenticationMethod("valid", string.Empty); } - [TestMethod] - [WorkItem(1140)] - [TestCategory("BaseClient")] - [TestCategory("integration")] - [Description("Test whether IsConnected is false after disconnect.")] - [Owner("Kenneth_aa")] - public void Test_BaseClient_IsConnected_True_After_Disconnect() - { - // 2012-04-29 - Kenneth_aa - // The problem with this test, is that after SSH Net calls .Disconnect(), the library doesn't wait - // for the server to confirm disconnect before IsConnected is checked. And now I'm not mentioning - // anything about Socket's either. - - var connectionInfo = new PasswordAuthenticationMethod(Resources.USERNAME, Resources.PASSWORD); - - using (SftpClient client = new SftpClient(Resources.HOST, int.Parse(Resources.PORT), Resources.USERNAME, Resources.PASSWORD)) - { - client.Connect(); - Assert.AreEqual(true, client.IsConnected, "IsConnected is not true after Connect() was called."); - - client.Disconnect(); - - Assert.AreEqual(false, client.IsConnected, "IsConnected is true after Disconnect() was called."); - } - } - /// ///A test for Name /// @@ -158,4 +131,4 @@ namespace Renci.SshNet.Tests.Classes Assert.Inconclusive("TODO: Implement code to verify target"); } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.Tests/Classes/PasswordConnectionInfoTest.cs b/src/Renci.SshNet.Tests/Classes/PasswordConnectionInfoTest.cs index 52088160..56a51014 100644 --- a/src/Renci.SshNet.Tests/Classes/PasswordConnectionInfoTest.cs +++ b/src/Renci.SshNet.Tests/Classes/PasswordConnectionInfoTest.cs @@ -1,5 +1,4 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; -using Renci.SshNet.Common; using Renci.SshNet.Tests.Common; using Renci.SshNet.Tests.Properties; using System; @@ -13,91 +12,13 @@ namespace Renci.SshNet.Tests.Classes [TestClass] public class PasswordConnectionInfoTest : TestBase { - [TestMethod] - [TestCategory("PasswordConnectionInfo")] - [TestCategory("integration")] - public void Test_PasswordConnectionInfo() - { - var host = Resources.HOST; - var username = Resources.USERNAME; - var password = Resources.PASSWORD; - - #region Example PasswordConnectionInfo - var connectionInfo = new PasswordConnectionInfo(host, username, password); - using (var client = new SftpClient(connectionInfo)) - { - client.Connect(); - // Do something here - client.Disconnect(); - } - #endregion - - Assert.AreEqual(connectionInfo.Host, Resources.HOST); - Assert.AreEqual(connectionInfo.Username, Resources.USERNAME); - } - - [TestMethod] - [TestCategory("PasswordConnectionInfo")] - [TestCategory("integration")] - public void Test_PasswordConnectionInfo_PasswordExpired() - { - var host = Resources.HOST; - var username = Resources.USERNAME; - var password = Resources.PASSWORD; - - #region Example PasswordConnectionInfo PasswordExpired - var connectionInfo = new PasswordConnectionInfo("host", "username", "password"); - var encoding = SshData.Ascii; - connectionInfo.PasswordExpired += delegate(object sender, AuthenticationPasswordChangeEventArgs e) - { - e.NewPassword = encoding.GetBytes("123456"); - }; - - using (var client = new SshClient(connectionInfo)) - { - client.Connect(); - - client.Disconnect(); - } - #endregion - - Assert.Inconclusive(); - } - [TestMethod] - [TestCategory("PasswordConnectionInfo")] - [TestCategory("integration")] - public void Test_PasswordConnectionInfo_AuthenticationBanner() - { - var host = Resources.HOST; - var username = Resources.USERNAME; - var password = Resources.PASSWORD; - - #region Example PasswordConnectionInfo AuthenticationBanner - var connectionInfo = new PasswordConnectionInfo(host, username, password); - connectionInfo.AuthenticationBanner += delegate(object sender, AuthenticationBannerEventArgs e) - { - Console.WriteLine(e.BannerMessage); - }; - using (var client = new SftpClient(connectionInfo)) - { - client.Connect(); - // Do something here - client.Disconnect(); - } - #endregion - - Assert.AreEqual(connectionInfo.Host, Resources.HOST); - Assert.AreEqual(connectionInfo.Username, Resources.USERNAME); - } - - [WorkItem(703), TestMethod] [TestCategory("PasswordConnectionInfo")] public void Test_ConnectionInfo_Host_Is_Null() { try { - new PasswordConnectionInfo(null, Resources.USERNAME, Resources.PASSWORD); + _ = new PasswordConnectionInfo(null, Resources.USERNAME, Resources.PASSWORD); Assert.Fail(); } catch (ArgumentNullException ex) @@ -113,7 +34,7 @@ namespace Renci.SshNet.Tests.Classes [ExpectedException(typeof(ArgumentException))] public void Test_ConnectionInfo_Username_Is_Null() { - var connectionInfo = new PasswordConnectionInfo(Resources.HOST, null, Resources.PASSWORD); + _ = new PasswordConnectionInfo(Resources.HOST, null, Resources.PASSWORD); } [WorkItem(703), TestMethod] @@ -121,7 +42,7 @@ namespace Renci.SshNet.Tests.Classes [ExpectedException(typeof(ArgumentNullException))] public void Test_ConnectionInfo_Password_Is_Null() { - var connectionInfo = new PasswordConnectionInfo(Resources.HOST, Resources.USERNAME, (string)null); + _ = new PasswordConnectionInfo(Resources.HOST, Resources.USERNAME, (string)null); } [TestMethod] @@ -130,7 +51,7 @@ namespace Renci.SshNet.Tests.Classes [ExpectedException(typeof(ArgumentException))] public void Test_ConnectionInfo_Username_Is_Whitespace() { - var connectionInfo = new PasswordConnectionInfo(Resources.HOST, " ", Resources.PASSWORD); + _ = new PasswordConnectionInfo(Resources.HOST, " ", Resources.PASSWORD); } [WorkItem(703), TestMethod] @@ -138,7 +59,7 @@ namespace Renci.SshNet.Tests.Classes [ExpectedException(typeof(ArgumentOutOfRangeException))] public void Test_ConnectionInfo_SmallPortNumber() { - var connectionInfo = new PasswordConnectionInfo(Resources.HOST, IPEndPoint.MinPort - 1, Resources.USERNAME, Resources.PASSWORD); + _ = new PasswordConnectionInfo(Resources.HOST, IPEndPoint.MinPort - 1, Resources.USERNAME, Resources.PASSWORD); } [WorkItem(703), TestMethod] @@ -146,62 +67,9 @@ namespace Renci.SshNet.Tests.Classes [ExpectedException(typeof(ArgumentOutOfRangeException))] public void Test_ConnectionInfo_BigPortNumber() { - var connectionInfo = new PasswordConnectionInfo(Resources.HOST, IPEndPoint.MaxPort + 1, Resources.USERNAME, Resources.PASSWORD); + _ = new PasswordConnectionInfo(Resources.HOST, IPEndPoint.MaxPort + 1, Resources.USERNAME, Resources.PASSWORD); } - - [TestMethod] - [Owner("Kenneth_aa")] - [Description("Test connect to remote server via a SOCKS4 proxy server.")] - [TestCategory("Proxy")] - [TestCategory("integration")] - public void Test_Ssh_Connect_Via_Socks4() - { - var connInfo = new PasswordConnectionInfo(Resources.HOST, Resources.USERNAME, Resources.PASSWORD, ProxyTypes.Socks4, Resources.PROXY_HOST, int.Parse(Resources.PROXY_PORT)); - using (var client = new SshClient(connInfo)) - { - client.Connect(); - - var ret = client.RunCommand("ls -la"); - - client.Disconnect(); - } - } - - [TestMethod] - [Owner("Kenneth_aa")] - [Description("Test connect to remote server via a TCP SOCKS5 proxy server.")] - [TestCategory("Proxy")] - [TestCategory("integration")] - public void Test_Ssh_Connect_Via_TcpSocks5() - { - var connInfo = new PasswordConnectionInfo(Resources.HOST, Resources.USERNAME, Resources.PASSWORD, ProxyTypes.Socks5, Resources.PROXY_HOST, int.Parse(Resources.PROXY_PORT)); - using (var client = new SshClient(connInfo)) - { - client.Connect(); - - var ret = client.RunCommand("ls -la"); - client.Disconnect(); - } - } - - [TestMethod] - [Owner("Kenneth_aa")] - [Description("Test connect to remote server via a HTTP proxy server.")] - [TestCategory("Proxy")] - [TestCategory("integration")] - public void Test_Ssh_Connect_Via_HttpProxy() - { - var connInfo = new PasswordConnectionInfo(Resources.HOST, Resources.USERNAME, Resources.PASSWORD, ProxyTypes.Http, Resources.PROXY_HOST, int.Parse(Resources.PROXY_PORT)); - using (var client = new SshClient(connInfo)) - { - client.Connect(); - - var ret = client.RunCommand("ls -la"); - - client.Disconnect(); - } - } - + /// ///A test for Dispose /// @@ -209,10 +77,10 @@ namespace Renci.SshNet.Tests.Classes [Ignore] // placeholder for actual test public void DisposeTest() { - string host = string.Empty; // TODO: Initialize to an appropriate value - string username = string.Empty; // TODO: Initialize to an appropriate value + var host = string.Empty; // TODO: Initialize to an appropriate value + var username = string.Empty; // TODO: Initialize to an appropriate value byte[] password = null; // TODO: Initialize to an appropriate value - PasswordConnectionInfo target = new PasswordConnectionInfo(host, username, password); // TODO: Initialize to an appropriate value + var target = new PasswordConnectionInfo(host, username, password); // TODO: Initialize to an appropriate value target.Dispose(); Assert.Inconclusive("A method that does not return a value cannot be verified."); } @@ -478,4 +346,4 @@ namespace Renci.SshNet.Tests.Classes } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.Tests/Classes/PrivateKeyConnectionInfoTest.cs b/src/Renci.SshNet.Tests/Classes/PrivateKeyConnectionInfoTest.cs index f8c863ba..40a78117 100644 --- a/src/Renci.SshNet.Tests/Classes/PrivateKeyConnectionInfoTest.cs +++ b/src/Renci.SshNet.Tests/Classes/PrivateKeyConnectionInfoTest.cs @@ -1,8 +1,5 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Renci.SshNet.Tests.Common; -using Renci.SshNet.Tests.Properties; -using System.IO; -using System.Text; namespace Renci.SshNet.Tests.Classes { @@ -12,53 +9,6 @@ namespace Renci.SshNet.Tests.Classes [TestClass] public class PrivateKeyConnectionInfoTest : TestBase { - [TestMethod] - [TestCategory("PrivateKeyConnectionInfo")] - [TestCategory("integration")] - public void Test_PrivateKeyConnectionInfo() - { - var host = Resources.HOST; - var username = Resources.USERNAME; - MemoryStream keyFileStream = new MemoryStream(Encoding.ASCII.GetBytes(Resources.RSA_KEY_WITHOUT_PASS)); - - #region Example PrivateKeyConnectionInfo PrivateKeyFile - var connectionInfo = new PrivateKeyConnectionInfo(host, username, new PrivateKeyFile(keyFileStream)); - using (var client = new SshClient(connectionInfo)) - { - client.Connect(); - client.Disconnect(); - } - #endregion - - Assert.AreEqual(connectionInfo.Host, Resources.HOST); - Assert.AreEqual(connectionInfo.Username, Resources.USERNAME); - } - - [TestMethod] - [TestCategory("PrivateKeyConnectionInfo")] - [TestCategory("integration")] - public void Test_PrivateKeyConnectionInfo_MultiplePrivateKey() - { - var host = Resources.HOST; - var username = Resources.USERNAME; - MemoryStream keyFileStream1 = new MemoryStream(Encoding.ASCII.GetBytes(Resources.RSA_KEY_WITHOUT_PASS)); - MemoryStream keyFileStream2 = new MemoryStream(Encoding.ASCII.GetBytes(Resources.RSA_KEY_WITHOUT_PASS)); - - #region Example PrivateKeyConnectionInfo PrivateKeyFile Multiple - var connectionInfo = new PrivateKeyConnectionInfo(host, username, - new PrivateKeyFile(keyFileStream1), - new PrivateKeyFile(keyFileStream2)); - using (var client = new SshClient(connectionInfo)) - { - client.Connect(); - client.Disconnect(); - } - #endregion - - Assert.AreEqual(connectionInfo.Host, Resources.HOST); - Assert.AreEqual(connectionInfo.Username, Resources.USERNAME); - } - /// ///A test for Dispose /// @@ -214,4 +164,4 @@ namespace Renci.SshNet.Tests.Classes Assert.Inconclusive("TODO: Implement code to verify target"); } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.Tests/Classes/PrivateKeyFileTest.cs b/src/Renci.SshNet.Tests/Classes/PrivateKeyFileTest.cs index 99434d71..879dfa84 100644 --- a/src/Renci.SshNet.Tests/Classes/PrivateKeyFileTest.cs +++ b/src/Renci.SshNet.Tests/Classes/PrivateKeyFileTest.cs @@ -3,6 +3,7 @@ using Renci.SshNet.Common; using Renci.SshNet.Tests.Common; using System; using System.IO; +using System.Linq; namespace Renci.SshNet.Tests.Classes { @@ -24,7 +25,9 @@ namespace Renci.SshNet.Tests.Classes public void TearDown() { if (_temporaryFile != null) + { File.Delete(_temporaryFile); + } } /// @@ -36,7 +39,7 @@ namespace Renci.SshNet.Tests.Classes var fileName = string.Empty; try { - new PrivateKeyFile(fileName); + _ = new PrivateKeyFile(fileName); Assert.Fail(); } catch (ArgumentNullException ex) @@ -55,7 +58,7 @@ namespace Renci.SshNet.Tests.Classes var fileName = string.Empty; try { - new PrivateKeyFile(fileName); + _ = new PrivateKeyFile(fileName); Assert.Fail(); } catch (ArgumentNullException ex) @@ -74,7 +77,7 @@ namespace Renci.SshNet.Tests.Classes var fileName = string.Empty; try { - new PrivateKeyFile(fileName, "12345"); + _ = new PrivateKeyFile(fileName, "12345"); Assert.Fail(); } catch (ArgumentNullException ex) @@ -93,7 +96,7 @@ namespace Renci.SshNet.Tests.Classes var fileName = string.Empty; try { - new PrivateKeyFile(fileName, "12345"); + _ = new PrivateKeyFile(fileName, "12345"); Assert.Fail(); } catch (ArgumentNullException ex) @@ -109,7 +112,7 @@ namespace Renci.SshNet.Tests.Classes Stream privateKey = null; try { - new PrivateKeyFile(privateKey); + _ = new PrivateKeyFile(privateKey); Assert.Fail(); } catch (ArgumentNullException ex) @@ -125,7 +128,7 @@ namespace Renci.SshNet.Tests.Classes Stream privateKey = null; try { - new PrivateKeyFile(privateKey, "12345"); + _ = new PrivateKeyFile(privateKey, "12345"); Assert.Fail(); } catch (ArgumentNullException ex) @@ -142,7 +145,7 @@ namespace Renci.SshNet.Tests.Classes { using (var stream = GetData("Key.RSA.txt")) { - new PrivateKeyFile(stream); + TestRsaKeyFile(new PrivateKeyFile(stream)); } } @@ -153,7 +156,7 @@ namespace Renci.SshNet.Tests.Classes { using (var stream = GetData("Key.SSH2.DSA.txt")) { - new PrivateKeyFile(stream); + _ = new PrivateKeyFile(stream); } } @@ -164,7 +167,7 @@ namespace Renci.SshNet.Tests.Classes { using (var stream = GetData("Key.SSH2.RSA.txt")) { - new PrivateKeyFile(stream); + TestRsaKeyFile(new PrivateKeyFile(stream)); } } @@ -175,7 +178,7 @@ namespace Renci.SshNet.Tests.Classes { using (var stream = GetData("Key.SSH2.DSA.Encrypted.Des.CBC.12345.txt")) { - new PrivateKeyFile(stream, "12345"); + _ = new PrivateKeyFile(stream, "12345"); } } @@ -186,7 +189,7 @@ namespace Renci.SshNet.Tests.Classes { using (var stream = GetData("Key.SSH2.RSA.Encrypted.Des.CBC.12345.txt")) { - new PrivateKeyFile(stream, "12345"); + TestRsaKeyFile(new PrivateKeyFile(stream, "12345")); } } @@ -199,7 +202,7 @@ namespace Renci.SshNet.Tests.Classes { try { - new PrivateKeyFile(stream, "34567"); + _ = new PrivateKeyFile(stream, "34567"); Assert.Fail(); } catch (SshException ex) @@ -220,7 +223,7 @@ namespace Renci.SshNet.Tests.Classes { try { - new PrivateKeyFile(stream, null); + _ = new PrivateKeyFile(stream, null); Assert.Fail(); } catch (SshPassPhraseNullOrEmptyException ex) @@ -241,7 +244,7 @@ namespace Renci.SshNet.Tests.Classes { try { - new PrivateKeyFile(stream, string.Empty); + _ = new PrivateKeyFile(stream, string.Empty); Assert.Fail(); } catch (SshPassPhraseNullOrEmptyException ex) @@ -260,7 +263,7 @@ namespace Renci.SshNet.Tests.Classes { using (var stream = GetData("Key.RSA.Encrypted.Des.CBC.12345.txt")) { - new PrivateKeyFile(stream, "12345"); + TestRsaKeyFile(new PrivateKeyFile(stream, "12345")); } } @@ -271,7 +274,7 @@ namespace Renci.SshNet.Tests.Classes { using (var stream = GetData("Key.RSA.Encrypted.Des.Ede3.CBC.12345.txt")) { - new PrivateKeyFile(stream, "12345"); + _ = new PrivateKeyFile(stream, "12345"); } } @@ -282,7 +285,7 @@ namespace Renci.SshNet.Tests.Classes { using (var stream = GetData("Key.RSA.Encrypted.Aes.128.CBC.12345.txt")) { - new PrivateKeyFile(stream, "12345"); + TestRsaKeyFile(new PrivateKeyFile(stream, "12345")); } } @@ -293,7 +296,7 @@ namespace Renci.SshNet.Tests.Classes { using (var stream = GetData("Key.RSA.Encrypted.Aes.192.CBC.12345.txt")) { - new PrivateKeyFile(stream, "12345"); + TestRsaKeyFile(new PrivateKeyFile(stream, "12345")); } } @@ -304,7 +307,7 @@ namespace Renci.SshNet.Tests.Classes { using (var stream = GetData("Key.RSA.Encrypted.Aes.256.CBC.12345.txt")) { - new PrivateKeyFile(stream, "12345"); + TestRsaKeyFile(new PrivateKeyFile(stream, "12345")); } } @@ -315,7 +318,7 @@ namespace Renci.SshNet.Tests.Classes { using (var stream = GetData("Key.RSA.Encrypted.Des.Ede3.CFB.1234567890.txt")) { - new PrivateKeyFile(stream, "1234567890"); + TestRsaKeyFile(new PrivateKeyFile(stream, "1234567890")); } } @@ -326,7 +329,7 @@ namespace Renci.SshNet.Tests.Classes { using (var stream = GetData("Key.ECDSA.txt")) { - new PrivateKeyFile(stream); + _ = new PrivateKeyFile(stream); } } @@ -337,7 +340,7 @@ namespace Renci.SshNet.Tests.Classes { using (var stream = GetData("Key.ECDSA384.txt")) { - new PrivateKeyFile(stream); + _ = new PrivateKeyFile(stream); } } @@ -348,7 +351,7 @@ namespace Renci.SshNet.Tests.Classes { using (var stream = GetData("Key.ECDSA521.txt")) { - new PrivateKeyFile(stream); + _ = new PrivateKeyFile(stream); } } @@ -359,7 +362,7 @@ namespace Renci.SshNet.Tests.Classes { using (var stream = GetData("Key.ECDSA.Encrypted.txt")) { - new PrivateKeyFile(stream, "12345"); + _ = new PrivateKeyFile(stream, "12345"); } } @@ -370,7 +373,7 @@ namespace Renci.SshNet.Tests.Classes { using (var stream = GetData("Key.ECDSA384.Encrypted.txt")) { - new PrivateKeyFile(stream, "12345"); + _ = new PrivateKeyFile(stream, "12345"); } } @@ -381,7 +384,7 @@ namespace Renci.SshNet.Tests.Classes { using (var stream = GetData("Key.ECDSA521.Encrypted.txt")) { - new PrivateKeyFile(stream, "12345"); + _ = new PrivateKeyFile(stream, "12345"); } } @@ -407,7 +410,7 @@ namespace Renci.SshNet.Tests.Classes using (var stream = GetData("Key.RSA.Encrypted.Aes.128.CBC.12345.txt")) { var privateKeyFile = new PrivateKeyFile(stream, "12345"); - Assert.IsNotNull(privateKeyFile.HostKey); + TestRsaKeyFile(privateKeyFile); } } @@ -425,7 +428,7 @@ namespace Renci.SshNet.Tests.Classes using (var fs = File.Open(_temporaryFile, FileMode.Open, FileAccess.Read, FileShare.Read)) { var privateKeyFile = new PrivateKeyFile(_temporaryFile, "12345"); - Assert.IsNotNull(privateKeyFile.HostKey); + TestRsaKeyFile(privateKeyFile); fs.Close(); } @@ -435,7 +438,7 @@ namespace Renci.SshNet.Tests.Classes /// A test for ctor. /// [TestMethod()] - public void ConstructorWithFileNameAndPassphraseShouldThrowSshPassPhraseNullOrEmptyExceptionWhenPrivateKeyIsEncryptedAndPassphraseIsEmpty() + public void ConstructorWithFileNameAndPassphraseShouldThrowSshPassPhraseNullOrEmptyExceptionWhenNeededPassphraseIsEmpty() { var passphrase = string.Empty; @@ -446,7 +449,7 @@ namespace Renci.SshNet.Tests.Classes try { - new PrivateKeyFile(_temporaryFile, passphrase); + _ = new PrivateKeyFile(_temporaryFile, passphrase); Assert.Fail(); } catch (SshPassPhraseNullOrEmptyException ex) @@ -460,7 +463,7 @@ namespace Renci.SshNet.Tests.Classes /// A test for ctor. /// [TestMethod()] - public void ConstructorWithFileNameAndPassphraseShouldThrowSshPassPhraseNullOrEmptyExceptionWhenPrivateKeyIsEncryptedAndPassphraseIsNull() + public void ConstructorWithFileNameAndPassphraseShouldThrowSshPassPhraseNullOrEmptyExceptionWhenNeededPassphraseIsNull() { string passphrase = null; @@ -471,7 +474,7 @@ namespace Renci.SshNet.Tests.Classes try { - new PrivateKeyFile(_temporaryFile, passphrase); + _ = new PrivateKeyFile(_temporaryFile, passphrase); Assert.Fail(); } catch (SshPassPhraseNullOrEmptyException ex) @@ -493,7 +496,7 @@ namespace Renci.SshNet.Tests.Classes } var privateKeyFile = new PrivateKeyFile(_temporaryFile, "12345"); - Assert.IsNotNull(privateKeyFile.HostKey); + TestRsaKeyFile(privateKeyFile); } /// @@ -505,7 +508,7 @@ namespace Renci.SshNet.Tests.Classes using (var stream = GetData("Key.RSA.txt")) { var privateKeyFile = new PrivateKeyFile(stream); - Assert.IsNotNull(privateKeyFile.HostKey); + TestRsaKeyFile(privateKeyFile); } } @@ -521,7 +524,7 @@ namespace Renci.SshNet.Tests.Classes using (var fs = File.Open(_temporaryFile, FileMode.Open, FileAccess.Read, FileShare.Read)) { var privateKeyFile = new PrivateKeyFile(_temporaryFile); - Assert.IsNotNull(privateKeyFile.HostKey); + TestRsaKeyFile(privateKeyFile); fs.Close(); } @@ -539,37 +542,119 @@ namespace Renci.SshNet.Tests.Classes using (var fs = File.Open(_temporaryFile, FileMode.Open, FileAccess.Read, FileShare.Read)) { var privateKeyFile = new PrivateKeyFile(_temporaryFile, "12345"); - Assert.IsNotNull(privateKeyFile.HostKey); + TestRsaKeyFile(privateKeyFile); fs.Close(); } } - /// - /// A test for opening an openssh v1 keyfile where there is no passphrase. - /// [TestMethod()] [Owner("bhalbright")] [TestCategory("PrivateKey")] - public void TestOpenSshV1KeyFileNoPassphrase() + public void Test_PrivateKey_OPENSSH_ED25519() { using (var stream = GetData("Key.OPENSSH.ED25519.txt")) { - new PrivateKeyFile(stream); + _ = new PrivateKeyFile(stream); } } - /// - /// A test for opening an openssh v1 keyfile where there is a passphrase. - /// [TestMethod()] [Owner("bhalbright")] [TestCategory("PrivateKey")] - public void TestOpenSshV1KeyFileWithPassphrase() + public void Test_PrivateKey_OPENSSH_ED25519_ENCRYPTED() { using (var stream = GetData("Key.OPENSSH.ED25519.Encrypted.txt")) { - new PrivateKeyFile(stream, "password"); + _ = new PrivateKeyFile(stream, "password"); + } + } + + [TestMethod()] + [Owner("darinkes")] + [TestCategory("PrivateKey")] + public void Test_PrivateKey_OPENSSH_RSA() + { + using (var stream = GetData("Key.OPENSSH.RSA.txt")) + { + TestRsaKeyFile(new PrivateKeyFile(stream)); + } + } + + [TestMethod()] + [Owner("darinkes")] + [TestCategory("PrivateKey")] + public void Test_PrivateKey_OPENSSH_RSA_ENCRYPTED() + { + using (var stream = GetData("Key.OPENSSH.RSA.Encrypted.txt")) + { + TestRsaKeyFile(new PrivateKeyFile(stream, "12345")); + } + } + + [TestMethod()] + [Owner("darinkes")] + [TestCategory("PrivateKey")] + public void Test_PrivateKey_OPENSSH_ECDSA() + { + using (var stream = GetData("Key.OPENSSH.ECDSA.txt")) + { + _ = new PrivateKeyFile(stream); + } + } + + [TestMethod()] + [Owner("darinkes")] + [TestCategory("PrivateKey")] + public void Test_PrivateKey_OPENSSH_ECDSA_ENCRYPTED() + { + using (var stream = GetData("Key.OPENSSH.ECDSA.Encrypted.txt")) + { + _ = new PrivateKeyFile(stream, "12345"); + } + } + + [TestMethod()] + [Owner("darinkes")] + [TestCategory("PrivateKey")] + public void Test_PrivateKey_OPENSSH_ECDSA384() + { + using (var stream = GetData("Key.OPENSSH.ECDSA384.txt")) + { + _ = new PrivateKeyFile(stream); + } + } + + [TestMethod()] + [Owner("darinkes")] + [TestCategory("PrivateKey")] + public void Test_PrivateKey_OPENSSH_ECDSA384_ENCRYPTED() + { + using (var stream = GetData("Key.OPENSSH.ECDSA384.Encrypted.txt")) + { + _ = new PrivateKeyFile(stream, "12345"); + } + } + + [TestMethod()] + [Owner("darinkes")] + [TestCategory("PrivateKey")] + public void Test_PrivateKey_OPENSSH_ECDSA521() + { + using (var stream = GetData("Key.OPENSSH.ECDSA521.txt")) + { + _ = new PrivateKeyFile(stream); + } + } + + [TestMethod()] + [Owner("darinkes")] + [TestCategory("PrivateKey")] + public void Test_PrivateKey_OPENSSH_ECDSA521_ENCRYPTED() + { + using (var stream = GetData("Key.OPENSSH.ECDSA521.Encrypted.txt")) + { + _ = new PrivateKeyFile(stream, "12345"); } } @@ -594,5 +679,17 @@ namespace Renci.SshNet.Tests.Classes File.Delete(tempFile); return tempFile; } + + private static void TestRsaKeyFile(PrivateKeyFile rsaPrivateKeyFile) + { + Assert.IsNotNull(rsaPrivateKeyFile.HostKeyAlgorithms); + Assert.AreEqual(3, rsaPrivateKeyFile.HostKeyAlgorithms.Count); + + var algorithms = rsaPrivateKeyFile.HostKeyAlgorithms.ToList(); + + Assert.AreEqual("rsa-sha2-512", algorithms[0].Name); + Assert.AreEqual("rsa-sha2-256", algorithms[1].Name); + Assert.AreEqual("ssh-rsa", algorithms[2].Name); + } } } diff --git a/src/Renci.SshNet.Tests/Classes/ScpClientTest.cs b/src/Renci.SshNet.Tests/Classes/ScpClientTest.cs index 038d952e..bcdf9396 100644 --- a/src/Renci.SshNet.Tests/Classes/ScpClientTest.cs +++ b/src/Renci.SshNet.Tests/Classes/ScpClientTest.cs @@ -1,15 +1,11 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.IO; +using System.Text; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + using Renci.SshNet.Common; using Renci.SshNet.Tests.Common; -using Renci.SshNet.Tests.Properties; -using System; -using System.IO; -using System.Linq; -using System.Security.Cryptography; -using System.Text; -#if FEATURE_TPL -using System.Threading.Tasks; -#endif // FEATURE_TPL namespace Renci.SshNet.Tests.Classes { @@ -34,7 +30,7 @@ namespace Renci.SshNet.Tests.Classes try { - new ScpClient(connectionInfo); + _ = new ScpClient(connectionInfo); Assert.Fail(); } catch (ArgumentNullException ex) @@ -219,202 +215,6 @@ namespace Renci.SshNet.Tests.Classes Assert.AreSame(RemotePathTransformation.ShellQuote, client.RemotePathTransformation); } - [TestMethod] - [TestCategory("Scp")] - [TestCategory("integration")] - public void Test_Scp_File_Upload_Download() - { - RemoveAllFiles(); - - using (var scp = new ScpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) - { - scp.Connect(); - - string uploadedFileName = Path.GetTempFileName(); - string downloadedFileName = Path.GetTempFileName(); - - this.CreateTestFile(uploadedFileName, 1); - - scp.Upload(new FileInfo(uploadedFileName), Path.GetFileName(uploadedFileName)); - - scp.Download(Path.GetFileName(uploadedFileName), new FileInfo(downloadedFileName)); - - // Calculate MD5 value - var uploadedHash = CalculateMD5(uploadedFileName); - var downloadedHash = CalculateMD5(downloadedFileName); - - File.Delete(uploadedFileName); - File.Delete(downloadedFileName); - - scp.Disconnect(); - - Assert.AreEqual(uploadedHash, downloadedHash); - } - } - - [TestMethod] - [TestCategory("Scp")] - [TestCategory("integration")] - public void Test_Scp_Stream_Upload_Download() - { - RemoveAllFiles(); - - using (var scp = new ScpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) - { - scp.Connect(); - - string uploadedFileName = Path.GetTempFileName(); - string downloadedFileName = Path.GetTempFileName(); - - this.CreateTestFile(uploadedFileName, 1); - - // Calculate has value - using (var stream = File.OpenRead(uploadedFileName)) - { - scp.Upload(stream, Path.GetFileName(uploadedFileName)); - } - - using (var stream = File.OpenWrite(downloadedFileName)) - { - scp.Download(Path.GetFileName(uploadedFileName), stream); - } - - // Calculate MD5 value - var uploadedHash = CalculateMD5(uploadedFileName); - var downloadedHash = CalculateMD5(downloadedFileName); - - File.Delete(uploadedFileName); - File.Delete(downloadedFileName); - - scp.Disconnect(); - - Assert.AreEqual(uploadedHash, downloadedHash); - } - } - - [TestMethod] - [TestCategory("Scp")] - [TestCategory("integration")] - public void Test_Scp_10MB_File_Upload_Download() - { - RemoveAllFiles(); - - using (var scp = new ScpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) - { - scp.Connect(); - - string uploadedFileName = Path.GetTempFileName(); - string downloadedFileName = Path.GetTempFileName(); - - this.CreateTestFile(uploadedFileName, 10); - - scp.Upload(new FileInfo(uploadedFileName), Path.GetFileName(uploadedFileName)); - - scp.Download(Path.GetFileName(uploadedFileName), new FileInfo(downloadedFileName)); - - // Calculate MD5 value - var uploadedHash = CalculateMD5(uploadedFileName); - var downloadedHash = CalculateMD5(downloadedFileName); - - File.Delete(uploadedFileName); - File.Delete(downloadedFileName); - - scp.Disconnect(); - - Assert.AreEqual(uploadedHash, downloadedHash); - } - } - - [TestMethod] - [TestCategory("Scp")] - [TestCategory("integration")] - public void Test_Scp_10MB_Stream_Upload_Download() - { - RemoveAllFiles(); - - using (var scp = new ScpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) - { - scp.Connect(); - - string uploadedFileName = Path.GetTempFileName(); - string downloadedFileName = Path.GetTempFileName(); - - this.CreateTestFile(uploadedFileName, 10); - - // Calculate has value - using (var stream = File.OpenRead(uploadedFileName)) - { - scp.Upload(stream, Path.GetFileName(uploadedFileName)); - } - - using (var stream = File.OpenWrite(downloadedFileName)) - { - scp.Download(Path.GetFileName(uploadedFileName), stream); - } - - // Calculate MD5 value - var uploadedHash = CalculateMD5(uploadedFileName); - var downloadedHash = CalculateMD5(downloadedFileName); - - File.Delete(uploadedFileName); - File.Delete(downloadedFileName); - - scp.Disconnect(); - - Assert.AreEqual(uploadedHash, downloadedHash); - } - } - - [TestMethod] - [TestCategory("Scp")] - [TestCategory("integration")] - public void Test_Scp_Directory_Upload_Download() - { - RemoveAllFiles(); - - using (var scp = new ScpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) - { - scp.Connect(); - - var uploadDirectory = - Directory.CreateDirectory(string.Format("{0}\\{1}", Path.GetTempPath(), Path.GetRandomFileName())); - for (int i = 0; i < 3; i++) - { - var subfolder = - Directory.CreateDirectory(string.Format(@"{0}\folder_{1}", uploadDirectory.FullName, i)); - for (int j = 0; j < 5; j++) - { - this.CreateTestFile(string.Format(@"{0}\file_{1}", subfolder.FullName, j), 1); - } - this.CreateTestFile(string.Format(@"{0}\file_{1}", uploadDirectory.FullName, i), 1); - } - - scp.Upload(uploadDirectory, "uploaded_dir"); - - var downloadDirectory = - Directory.CreateDirectory(string.Format("{0}\\{1}", Path.GetTempPath(), Path.GetRandomFileName())); - - scp.Download("uploaded_dir", downloadDirectory); - - var uploadedFiles = uploadDirectory.GetFiles("*.*", System.IO.SearchOption.AllDirectories); - var downloadFiles = downloadDirectory.GetFiles("*.*", System.IO.SearchOption.AllDirectories); - - var result = from f1 in uploadedFiles - from f2 in downloadFiles - where - f1.FullName.Substring(uploadDirectory.FullName.Length) == - f2.FullName.Substring(downloadDirectory.FullName.Length) - && CalculateMD5(f1.FullName) == CalculateMD5(f2.FullName) - select f1; - - var counter = result.Count(); - - scp.Disconnect(); - - Assert.IsTrue(counter == uploadedFiles.Length && uploadedFiles.Length == downloadFiles.Length); - } - } - /// ///A test for OperationTimeout /// @@ -423,11 +223,10 @@ namespace Renci.SshNet.Tests.Classes public void OperationTimeoutTest() { ConnectionInfo connectionInfo = null; // TODO: Initialize to an appropriate value - ScpClient target = new ScpClient(connectionInfo); // TODO: Initialize to an appropriate value - TimeSpan expected = new TimeSpan(); // TODO: Initialize to an appropriate value - TimeSpan actual; + var target = new ScpClient(connectionInfo); // TODO: Initialize to an appropriate value + var expected = new TimeSpan(); // TODO: Initialize to an appropriate value target.OperationTimeout = expected; - actual = target.OperationTimeout; + var actual = target.OperationTimeout; Assert.AreEqual(expected, actual); Assert.Inconclusive("Verify the correctness of this test method."); } @@ -440,7 +239,7 @@ namespace Renci.SshNet.Tests.Classes public void BufferSizeTest() { ConnectionInfo connectionInfo = null; // TODO: Initialize to an appropriate value - ScpClient target = new ScpClient(connectionInfo); // TODO: Initialize to an appropriate value + var target = new ScpClient(connectionInfo); // TODO: Initialize to an appropriate value uint expected = 0; // TODO: Initialize to an appropriate value uint actual; target.BufferSize = expected; @@ -457,9 +256,9 @@ namespace Renci.SshNet.Tests.Classes public void UploadTest() { ConnectionInfo connectionInfo = null; // TODO: Initialize to an appropriate value - ScpClient target = new ScpClient(connectionInfo); // TODO: Initialize to an appropriate value + var target = new ScpClient(connectionInfo); // TODO: Initialize to an appropriate value DirectoryInfo directoryInfo = null; // TODO: Initialize to an appropriate value - string filename = string.Empty; // TODO: Initialize to an appropriate value + var filename = string.Empty; // TODO: Initialize to an appropriate value target.Upload(directoryInfo, filename); Assert.Inconclusive("A method that does not return a value cannot be verified."); } @@ -472,9 +271,9 @@ namespace Renci.SshNet.Tests.Classes public void UploadTest1() { ConnectionInfo connectionInfo = null; // TODO: Initialize to an appropriate value - ScpClient target = new ScpClient(connectionInfo); // TODO: Initialize to an appropriate value + var target = new ScpClient(connectionInfo); // TODO: Initialize to an appropriate value FileInfo fileInfo = null; // TODO: Initialize to an appropriate value - string filename = string.Empty; // TODO: Initialize to an appropriate value + var filename = string.Empty; // TODO: Initialize to an appropriate value target.Upload(fileInfo, filename); Assert.Inconclusive("A method that does not return a value cannot be verified."); } @@ -487,9 +286,9 @@ namespace Renci.SshNet.Tests.Classes public void UploadTest2() { ConnectionInfo connectionInfo = null; // TODO: Initialize to an appropriate value - ScpClient target = new ScpClient(connectionInfo); // TODO: Initialize to an appropriate value + var target = new ScpClient(connectionInfo); // TODO: Initialize to an appropriate value Stream source = null; // TODO: Initialize to an appropriate value - string filename = string.Empty; // TODO: Initialize to an appropriate value + var filename = string.Empty; // TODO: Initialize to an appropriate value target.Upload(source, filename); Assert.Inconclusive("A method that does not return a value cannot be verified."); } @@ -502,8 +301,8 @@ namespace Renci.SshNet.Tests.Classes public void DownloadTest() { ConnectionInfo connectionInfo = null; // TODO: Initialize to an appropriate value - ScpClient target = new ScpClient(connectionInfo); // TODO: Initialize to an appropriate value - string directoryName = string.Empty; // TODO: Initialize to an appropriate value + var target = new ScpClient(connectionInfo); // TODO: Initialize to an appropriate value + var directoryName = string.Empty; // TODO: Initialize to an appropriate value DirectoryInfo directoryInfo = null; // TODO: Initialize to an appropriate value target.Download(directoryName, directoryInfo); Assert.Inconclusive("A method that does not return a value cannot be verified."); @@ -517,8 +316,8 @@ namespace Renci.SshNet.Tests.Classes public void DownloadTest1() { ConnectionInfo connectionInfo = null; // TODO: Initialize to an appropriate value - ScpClient target = new ScpClient(connectionInfo); // TODO: Initialize to an appropriate value - string filename = string.Empty; // TODO: Initialize to an appropriate value + var target = new ScpClient(connectionInfo); // TODO: Initialize to an appropriate value + var filename = string.Empty; // TODO: Initialize to an appropriate value FileInfo fileInfo = null; // TODO: Initialize to an appropriate value target.Download(filename, fileInfo); Assert.Inconclusive("A method that does not return a value cannot be verified."); @@ -532,136 +331,13 @@ namespace Renci.SshNet.Tests.Classes public void DownloadTest2() { ConnectionInfo connectionInfo = null; // TODO: Initialize to an appropriate value - ScpClient target = new ScpClient(connectionInfo); // TODO: Initialize to an appropriate value - string filename = string.Empty; // TODO: Initialize to an appropriate value + var target = new ScpClient(connectionInfo); // TODO: Initialize to an appropriate value + var filename = string.Empty; // TODO: Initialize to an appropriate value Stream destination = null; // TODO: Initialize to an appropriate value target.Download(filename, destination); Assert.Inconclusive("A method that does not return a value cannot be verified."); } -#if FEATURE_TPL - [TestMethod] - [TestCategory("Scp")] - [TestCategory("integration")] - public void Test_Scp_File_20_Parallel_Upload_Download() - { - using (var scp = new ScpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) - { - scp.Connect(); - - var uploadFilenames = new string[20]; - for (int i = 0; i < uploadFilenames.Length; i++) - { - uploadFilenames[i] = Path.GetTempFileName(); - this.CreateTestFile(uploadFilenames[i], 1); - } - - Parallel.ForEach(uploadFilenames, - (filename) => - { - scp.Upload(new FileInfo(filename), Path.GetFileName(filename)); - }); - - Parallel.ForEach(uploadFilenames, - (filename) => - { - scp.Download(Path.GetFileName(filename), new FileInfo(string.Format("{0}.down", filename))); - }); - - var result = from file in uploadFilenames - where - CalculateMD5(file) == CalculateMD5(string.Format("{0}.down", file)) - select file; - - scp.Disconnect(); - - Assert.IsTrue(result.Count() == uploadFilenames.Length); - } - } - - [TestMethod] - [TestCategory("Scp")] - [TestCategory("integration")] - public void Test_Scp_File_Upload_Download_Events() - { - using (var scp = new ScpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) - { - scp.Connect(); - - var uploadFilenames = new string[10]; - - for (int i = 0; i < uploadFilenames.Length; i++) - { - uploadFilenames[i] = Path.GetTempFileName(); - this.CreateTestFile(uploadFilenames[i], 1); - } - - var uploadedFiles = uploadFilenames.ToDictionary((filename) => Path.GetFileName(filename), (filename) => 0L); - var downloadedFiles = uploadFilenames.ToDictionary((filename) => string.Format("{0}.down", Path.GetFileName(filename)), (filename) => 0L); - - scp.Uploading += delegate (object sender, ScpUploadEventArgs e) - { - uploadedFiles[e.Filename] = e.Uploaded; - }; - - scp.Downloading += delegate (object sender, ScpDownloadEventArgs e) - { - downloadedFiles[string.Format("{0}.down", e.Filename)] = e.Downloaded; - }; - - Parallel.ForEach(uploadFilenames, - (filename) => - { - scp.Upload(new FileInfo(filename), Path.GetFileName(filename)); - }); - - Parallel.ForEach(uploadFilenames, - (filename) => - { - scp.Download(Path.GetFileName(filename), new FileInfo(string.Format("{0}.down", filename))); - }); - - var result = from uf in uploadedFiles - from df in downloadedFiles - where - string.Format("{0}.down", uf.Key) == df.Key - && uf.Value == df.Value - select uf; - - scp.Disconnect(); - - Assert.IsTrue(result.Count() == uploadFilenames.Length && uploadFilenames.Length == uploadedFiles.Count && uploadedFiles.Count == downloadedFiles.Count); - } - } -#endif // FEATURE_TPL - - protected static string CalculateMD5(string fileName) - { - using (var file = new FileStream(fileName, FileMode.Open)) - { - var md5 = new MD5CryptoServiceProvider(); - byte[] retVal = md5.ComputeHash(file); - file.Close(); - - var sb = new StringBuilder(); - for (var i = 0; i < retVal.Length; i++) - { - sb.Append(i.ToString("x2")); - } - return sb.ToString(); - } - } - - private static void RemoveAllFiles() - { - using (var client = new SshClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) - { - client.Connect(); - client.RunCommand("rm -rf *"); - client.Disconnect(); - } - } - private PrivateKeyFile GetRsaKey() { using (var stream = GetData("Key.RSA.txt")) @@ -678,4 +354,4 @@ namespace Renci.SshNet.Tests.Classes } } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.Tests/Classes/ScpClientTest_Download_PathAndDirectoryInfo_SendExecRequestReturnsFalse.cs b/src/Renci.SshNet.Tests/Classes/ScpClientTest_Download_PathAndDirectoryInfo_SendExecRequestReturnsFalse.cs index 9c5ed485..f64e039e 100644 --- a/src/Renci.SshNet.Tests/Classes/ScpClientTest_Download_PathAndDirectoryInfo_SendExecRequestReturnsFalse.cs +++ b/src/Renci.SshNet.Tests/Classes/ScpClientTest_Download_PathAndDirectoryInfo_SendExecRequestReturnsFalse.cs @@ -34,18 +34,18 @@ namespace Renci.SshNet.Tests.Classes { var sequence = new MockSequence(); - _serviceFactoryMock.InSequence(sequence) + ServiceFactoryMock.InSequence(sequence) .Setup(p => p.CreateRemotePathDoubleQuoteTransformation()) .Returns(_remotePathTransformationMock.Object); - _serviceFactoryMock.InSequence(sequence) + ServiceFactoryMock.InSequence(sequence) .Setup(p => p.CreateSocketFactory()) - .Returns(_socketFactoryMock.Object); - _serviceFactoryMock.InSequence(sequence) - .Setup(p => p.CreateSession(_connectionInfo, _socketFactoryMock.Object)) - .Returns(_sessionMock.Object); - _sessionMock.InSequence(sequence).Setup(p => p.Connect()); - _serviceFactoryMock.InSequence(sequence).Setup(p => p.CreatePipeStream()).Returns(_pipeStreamMock.Object); - _sessionMock.InSequence(sequence).Setup(p => p.CreateChannelSession()).Returns(_channelSessionMock.Object); + .Returns(SocketFactoryMock.Object); + ServiceFactoryMock.InSequence(sequence) + .Setup(p => p.CreateSession(_connectionInfo, SocketFactoryMock.Object)) + .Returns(SessionMock.Object); + SessionMock.InSequence(sequence).Setup(p => p.Connect()); + ServiceFactoryMock.InSequence(sequence).Setup(p => p.CreatePipeStream()).Returns(_pipeStreamMock.Object); + SessionMock.InSequence(sequence).Setup(p => p.CreateChannelSession()).Returns(_channelSessionMock.Object); _channelSessionMock.InSequence(sequence).Setup(p => p.Open()); _remotePathTransformationMock.InSequence(sequence) .Setup(p => p.Transform(_path)) @@ -54,18 +54,14 @@ namespace Renci.SshNet.Tests.Classes .Setup(p => p.SendExecRequest(string.Format("scp -prf {0}", _transformedPath))) .Returns(false); _channelSessionMock.InSequence(sequence).Setup(p => p.Dispose()); - _pipeStreamMock.As().InSequence(sequence).Setup(p => p.Dispose()); - - // On .NET Core, Dispose() in turn invokes Close() and since we're not mocking - // an interface, we need to expect this call as well - _pipeStreamMock.Setup(p => p.Close()); + _pipeStreamMock.InSequence(sequence).Setup(p => p.Close()); } protected override void Arrange() { base.Arrange(); - _scpClient = new ScpClient(_connectionInfo, false, _serviceFactoryMock.Object); + _scpClient = new ScpClient(_connectionInfo, false, ServiceFactoryMock.Object); _scpClient.Uploading += (sender, args) => _uploadingRegister.Add(args); _scpClient.Connect(); } @@ -106,7 +102,7 @@ namespace Renci.SshNet.Tests.Classes [TestMethod] public void DisposeOnPipeStreamShouldBeInvokedOnce() { - _pipeStreamMock.As().Verify(p => p.Dispose(), Times.Once); + _pipeStreamMock.Verify(p => p.Close(), Times.Once); } [TestMethod] diff --git a/src/Renci.SshNet.Tests/Classes/ScpClientTest_Download_PathAndFileInfo_SendExecRequestReturnsFalse.cs b/src/Renci.SshNet.Tests/Classes/ScpClientTest_Download_PathAndFileInfo_SendExecRequestReturnsFalse.cs index 8ba4525b..08eba4ff 100644 --- a/src/Renci.SshNet.Tests/Classes/ScpClientTest_Download_PathAndFileInfo_SendExecRequestReturnsFalse.cs +++ b/src/Renci.SshNet.Tests/Classes/ScpClientTest_Download_PathAndFileInfo_SendExecRequestReturnsFalse.cs @@ -34,18 +34,18 @@ namespace Renci.SshNet.Tests.Classes { var sequence = new MockSequence(); - _serviceFactoryMock.InSequence(sequence) + ServiceFactoryMock.InSequence(sequence) .Setup(p => p.CreateRemotePathDoubleQuoteTransformation()) .Returns(_remotePathTransformationMock.Object); - _serviceFactoryMock.InSequence(sequence) + ServiceFactoryMock.InSequence(sequence) .Setup(p => p.CreateSocketFactory()) - .Returns(_socketFactoryMock.Object); - _serviceFactoryMock.InSequence(sequence) - .Setup(p => p.CreateSession(_connectionInfo, _socketFactoryMock.Object)) - .Returns(_sessionMock.Object); - _sessionMock.InSequence(sequence).Setup(p => p.Connect()); - _serviceFactoryMock.InSequence(sequence).Setup(p => p.CreatePipeStream()).Returns(_pipeStreamMock.Object); - _sessionMock.InSequence(sequence).Setup(p => p.CreateChannelSession()).Returns(_channelSessionMock.Object); + .Returns(SocketFactoryMock.Object); + ServiceFactoryMock.InSequence(sequence) + .Setup(p => p.CreateSession(_connectionInfo, SocketFactoryMock.Object)) + .Returns(SessionMock.Object); + SessionMock.InSequence(sequence).Setup(p => p.Connect()); + ServiceFactoryMock.InSequence(sequence).Setup(p => p.CreatePipeStream()).Returns(_pipeStreamMock.Object); + SessionMock.InSequence(sequence).Setup(p => p.CreateChannelSession()).Returns(_channelSessionMock.Object); _channelSessionMock.InSequence(sequence).Setup(p => p.Open()); _remotePathTransformationMock.InSequence(sequence) .Setup(p => p.Transform(_path)) @@ -53,18 +53,14 @@ namespace Renci.SshNet.Tests.Classes _channelSessionMock.InSequence(sequence) .Setup(p => p.SendExecRequest(string.Format("scp -pf {0}", _transformedPath))).Returns(false); _channelSessionMock.InSequence(sequence).Setup(p => p.Dispose()); - _pipeStreamMock.As().InSequence(sequence).Setup(p => p.Dispose()); - - // On .NET Core, Dispose() in turn invokes Close() and since we're not mocking - // an interface, we need to expect this call as well - _pipeStreamMock.Setup(p => p.Close()); + _pipeStreamMock.InSequence(sequence).Setup(p => p.Close()); } protected override void Arrange() { base.Arrange(); - _scpClient = new ScpClient(_connectionInfo, false, _serviceFactoryMock.Object); + _scpClient = new ScpClient(_connectionInfo, false, ServiceFactoryMock.Object); _scpClient.Uploading += (sender, args) => _uploadingRegister.Add(args); _scpClient.Connect(); } @@ -105,7 +101,7 @@ namespace Renci.SshNet.Tests.Classes [TestMethod] public void DisposeOnPipeStreamShouldBeInvokedOnce() { - _pipeStreamMock.As().Verify(p => p.Dispose(), Times.Once); + _pipeStreamMock.Verify(p => p.Close(), Times.Once); } [TestMethod] diff --git a/src/Renci.SshNet.Tests/Classes/ScpClientTest_Download_PathAndStream_SendExecRequestReturnsFalse.cs b/src/Renci.SshNet.Tests/Classes/ScpClientTest_Download_PathAndStream_SendExecRequestReturnsFalse.cs index c1504aec..143938c3 100644 --- a/src/Renci.SshNet.Tests/Classes/ScpClientTest_Download_PathAndStream_SendExecRequestReturnsFalse.cs +++ b/src/Renci.SshNet.Tests/Classes/ScpClientTest_Download_PathAndStream_SendExecRequestReturnsFalse.cs @@ -34,38 +34,42 @@ namespace Renci.SshNet.Tests.Classes { var sequence = new MockSequence(); - _serviceFactoryMock.InSequence(sequence) - .Setup(p => p.CreateRemotePathDoubleQuoteTransformation()) - .Returns(_remotePathTransformationMock.Object); - _serviceFactoryMock.InSequence(sequence) - .Setup(p => p.CreateSocketFactory()) - .Returns(_socketFactoryMock.Object); - _serviceFactoryMock.InSequence(sequence) - .Setup(p => p.CreateSession(_connectionInfo, _socketFactoryMock.Object)) - .Returns(_sessionMock.Object); - _sessionMock.InSequence(sequence).Setup(p => p.Connect()); - _serviceFactoryMock.InSequence(sequence).Setup(p => p.CreatePipeStream()).Returns(_pipeStreamMock.Object); - _sessionMock.InSequence(sequence).Setup(p => p.CreateChannelSession()).Returns(_channelSessionMock.Object); - _channelSessionMock.InSequence(sequence).Setup(p => p.Open()); - _remotePathTransformationMock.InSequence(sequence) - .Setup(p => p.Transform(_path)) - .Returns(_transformedPath); - _channelSessionMock.InSequence(sequence) - .Setup(p => p.SendExecRequest(string.Format("scp -f {0}", _transformedPath))) - .Returns(false); - _channelSessionMock.InSequence(sequence).Setup(p => p.Dispose()); - _pipeStreamMock.As().InSequence(sequence).Setup(p => p.Dispose()); - - // On .NET Core, Dispose() in turn invokes Close() and since we're not mocking - // an interface, we need to expect this call as well - _pipeStreamMock.Setup(p => p.Close()); + _ = ServiceFactoryMock.InSequence(sequence) + .Setup(p => p.CreateRemotePathDoubleQuoteTransformation()) + .Returns(_remotePathTransformationMock.Object); + _ = ServiceFactoryMock.InSequence(sequence) + .Setup(p => p.CreateSocketFactory()) + .Returns(SocketFactoryMock.Object); + _ = ServiceFactoryMock.InSequence(sequence) + .Setup(p => p.CreateSession(_connectionInfo, SocketFactoryMock.Object)) + .Returns(SessionMock.Object); + _ = SessionMock.InSequence(sequence) + .Setup(p => p.Connect()); + _ = ServiceFactoryMock.InSequence(sequence) + .Setup(p => p.CreatePipeStream()) + .Returns(_pipeStreamMock.Object); + _ = SessionMock.InSequence(sequence) + .Setup(p => p.CreateChannelSession()) + .Returns(_channelSessionMock.Object); + _ = _channelSessionMock.InSequence(sequence) + .Setup(p => p.Open()); + _ = _remotePathTransformationMock.InSequence(sequence) + .Setup(p => p.Transform(_path)) + .Returns(_transformedPath); + _ = _channelSessionMock.InSequence(sequence) + .Setup(p => p.SendExecRequest(string.Format("scp -f {0}", _transformedPath))) + .Returns(false); + _ = _channelSessionMock.InSequence(sequence) + .Setup(p => p.Dispose()); + _ = _pipeStreamMock.InSequence(sequence) + .Setup(p => p.Close()); } protected override void Arrange() { base.Arrange(); - _scpClient = new ScpClient(_connectionInfo, false, _serviceFactoryMock.Object); + _scpClient = new ScpClient(_connectionInfo, false, ServiceFactoryMock.Object); _scpClient.Uploading += (sender, args) => _uploadingRegister.Add(args); _scpClient.Connect(); } @@ -74,10 +78,7 @@ namespace Renci.SshNet.Tests.Classes { base.TearDown(); - if (_destination != null) - { - _destination.Dispose(); - } + _destination?.Dispose(); } protected override void Act() @@ -116,7 +117,7 @@ namespace Renci.SshNet.Tests.Classes [TestMethod] public void DisposeOnPipeStreamShouldBeInvokedOnce() { - _pipeStreamMock.As().Verify(p => p.Dispose(), Times.Once); + _pipeStreamMock.Verify(p => p.Close(), Times.Once); } [TestMethod] diff --git a/src/Renci.SshNet.Tests/Classes/ScpClientTest_Upload_DirectoryInfoAndPath_SendExecRequestReturnsFalse.cs b/src/Renci.SshNet.Tests/Classes/ScpClientTest_Upload_DirectoryInfoAndPath_SendExecRequestReturnsFalse.cs index 6791e296..71c1c0cd 100644 --- a/src/Renci.SshNet.Tests/Classes/ScpClientTest_Upload_DirectoryInfoAndPath_SendExecRequestReturnsFalse.cs +++ b/src/Renci.SshNet.Tests/Classes/ScpClientTest_Upload_DirectoryInfoAndPath_SendExecRequestReturnsFalse.cs @@ -33,18 +33,18 @@ namespace Renci.SshNet.Tests.Classes { var sequence = new MockSequence(); - _serviceFactoryMock.InSequence(sequence) + ServiceFactoryMock.InSequence(sequence) .Setup(p => p.CreateRemotePathDoubleQuoteTransformation()) .Returns(_remotePathTransformationMock.Object); - _serviceFactoryMock.InSequence(sequence) + ServiceFactoryMock.InSequence(sequence) .Setup(p => p.CreateSocketFactory()) - .Returns(_socketFactoryMock.Object); - _serviceFactoryMock.InSequence(sequence) - .Setup(p => p.CreateSession(_connectionInfo, _socketFactoryMock.Object)) - .Returns(_sessionMock.Object); - _sessionMock.InSequence(sequence).Setup(p => p.Connect()); - _serviceFactoryMock.InSequence(sequence).Setup(p => p.CreatePipeStream()).Returns(_pipeStreamMock.Object); - _sessionMock.InSequence(sequence).Setup(p => p.CreateChannelSession()).Returns(_channelSessionMock.Object); + .Returns(SocketFactoryMock.Object); + ServiceFactoryMock.InSequence(sequence) + .Setup(p => p.CreateSession(_connectionInfo, SocketFactoryMock.Object)) + .Returns(SessionMock.Object); + SessionMock.InSequence(sequence).Setup(p => p.Connect()); + ServiceFactoryMock.InSequence(sequence).Setup(p => p.CreatePipeStream()).Returns(_pipeStreamMock.Object); + SessionMock.InSequence(sequence).Setup(p => p.CreateChannelSession()).Returns(_channelSessionMock.Object); _channelSessionMock.InSequence(sequence).Setup(p => p.Open()); _remotePathTransformationMock.InSequence(sequence) .Setup(p => p.Transform(_path)) @@ -53,18 +53,14 @@ namespace Renci.SshNet.Tests.Classes .Setup(p => p.SendExecRequest(string.Format("scp -r -p -d -t {0}", _transformedPath))) .Returns(false); _channelSessionMock.InSequence(sequence).Setup(p => p.Dispose()); - _pipeStreamMock.As().InSequence(sequence).Setup(p => p.Dispose()); - - // On .NET Core, Dispose() in turn invokes Close() and since we're not mocking - // an interface, we need to expect this call as well - _pipeStreamMock.Setup(p => p.Close()); + _pipeStreamMock.InSequence(sequence).Setup(p => p.Close()); } protected override void Arrange() { base.Arrange(); - _scpClient = new ScpClient(_connectionInfo, false, _serviceFactoryMock.Object); + _scpClient = new ScpClient(_connectionInfo, false, ServiceFactoryMock.Object); _scpClient.Uploading += (sender, args) => _uploadingRegister.Add(args); _scpClient.Connect(); } @@ -105,7 +101,7 @@ namespace Renci.SshNet.Tests.Classes [TestMethod] public void DisposeOnPipeStreamShouldBeInvokedOnce() { - _pipeStreamMock.As().Verify(p => p.Dispose(), Times.Once); + _pipeStreamMock.Verify(p => p.Close(), Times.Once); } [TestMethod] diff --git a/src/Renci.SshNet.Tests/Classes/ScpClientTest_Upload_FileInfoAndPath_SendExecRequestReturnsFalse.cs b/src/Renci.SshNet.Tests/Classes/ScpClientTest_Upload_FileInfoAndPath_SendExecRequestReturnsFalse.cs index 9a065ee2..288790d2 100644 --- a/src/Renci.SshNet.Tests/Classes/ScpClientTest_Upload_FileInfoAndPath_SendExecRequestReturnsFalse.cs +++ b/src/Renci.SshNet.Tests/Classes/ScpClientTest_Upload_FileInfoAndPath_SendExecRequestReturnsFalse.cs @@ -39,18 +39,18 @@ namespace Renci.SshNet.Tests.Classes { var sequence = new MockSequence(); - _serviceFactoryMock.InSequence(sequence) + ServiceFactoryMock.InSequence(sequence) .Setup(p => p.CreateRemotePathDoubleQuoteTransformation()) .Returns(_remotePathTransformationMock.Object); - _serviceFactoryMock.InSequence(sequence) + ServiceFactoryMock.InSequence(sequence) .Setup(p => p.CreateSocketFactory()) - .Returns(_socketFactoryMock.Object); - _serviceFactoryMock.InSequence(sequence) - .Setup(p => p.CreateSession(_connectionInfo, _socketFactoryMock.Object)) - .Returns(_sessionMock.Object); - _sessionMock.InSequence(sequence).Setup(p => p.Connect()); - _serviceFactoryMock.InSequence(sequence).Setup(p => p.CreatePipeStream()).Returns(_pipeStreamMock.Object); - _sessionMock.InSequence(sequence).Setup(p => p.CreateChannelSession()).Returns(_channelSessionMock.Object); + .Returns(SocketFactoryMock.Object); + ServiceFactoryMock.InSequence(sequence) + .Setup(p => p.CreateSession(_connectionInfo, SocketFactoryMock.Object)) + .Returns(SessionMock.Object); + SessionMock.InSequence(sequence).Setup(p => p.Connect()); + ServiceFactoryMock.InSequence(sequence).Setup(p => p.CreatePipeStream()).Returns(_pipeStreamMock.Object); + SessionMock.InSequence(sequence).Setup(p => p.CreateChannelSession()).Returns(_channelSessionMock.Object); _channelSessionMock.InSequence(sequence).Setup(p => p.Open()); _remotePathTransformationMock.InSequence(sequence) .Setup(p => p.Transform(_remoteDirectory)) @@ -59,18 +59,14 @@ namespace Renci.SshNet.Tests.Classes .Setup(p => p.SendExecRequest(string.Format("scp -t -d {0}", _transformedPath))) .Returns(false); _channelSessionMock.InSequence(sequence).Setup(p => p.Dispose()); - _pipeStreamMock.As().InSequence(sequence).Setup(p => p.Dispose()); - - // On .NET Core, Dispose() in turn invokes Close() and since we're not mocking - // an interface, we need to expect this call as well - _pipeStreamMock.Setup(p => p.Close()); -} + _pipeStreamMock.InSequence(sequence).Setup(p => p.Close()); + } protected override void Arrange() { base.Arrange(); - _scpClient = new ScpClient(_connectionInfo, false, _serviceFactoryMock.Object); + _scpClient = new ScpClient(_connectionInfo, false, ServiceFactoryMock.Object); _scpClient.Uploading += (sender, args) => _uploadingRegister.Add(args); _scpClient.Connect(); } @@ -122,7 +118,7 @@ namespace Renci.SshNet.Tests.Classes [TestMethod] public void DisposeOnPipeStreamShouldBeInvokedOnce() { - _pipeStreamMock.As().Verify(p => p.Dispose(), Times.Once); + _pipeStreamMock.Verify(p => p.Close(), Times.Once); } [TestMethod] diff --git a/src/Renci.SshNet.Tests/Classes/ScpClientTest_Upload_FileInfoAndPath_Success.cs b/src/Renci.SshNet.Tests/Classes/ScpClientTest_Upload_FileInfoAndPath_Success.cs index d86191e5..1df8d6ac 100644 --- a/src/Renci.SshNet.Tests/Classes/ScpClientTest_Upload_FileInfoAndPath_Success.cs +++ b/src/Renci.SshNet.Tests/Classes/ScpClientTest_Upload_FileInfoAndPath_Success.cs @@ -46,56 +46,64 @@ namespace Renci.SshNet.Tests.Classes { var sequence = new MockSequence(); - _serviceFactoryMock.InSequence(sequence) - .Setup(p => p.CreateRemotePathDoubleQuoteTransformation()) - .Returns(_remotePathTransformationMock.Object); - _serviceFactoryMock.InSequence(sequence) - .Setup(p => p.CreateSocketFactory()) - .Returns(_socketFactoryMock.Object); - _serviceFactoryMock.InSequence(sequence) - .Setup(p => p.CreateSession(_connectionInfo, _socketFactoryMock.Object)) - .Returns(_sessionMock.Object); - _sessionMock.InSequence(sequence).Setup(p => p.Connect()); - _serviceFactoryMock.InSequence(sequence).Setup(p => p.CreatePipeStream()).Returns(_pipeStreamMock.Object); - _sessionMock.InSequence(sequence).Setup(p => p.CreateChannelSession()).Returns(_channelSessionMock.Object); - _channelSessionMock.InSequence(sequence).Setup(p => p.Open()); - _remotePathTransformationMock.InSequence(sequence) - .Setup(p => p.Transform(_remoteDirectory)) - .Returns(_transformedPath); - _channelSessionMock.InSequence(sequence) - .Setup(p => p.SendExecRequest(string.Format("scp -t -d {0}", _transformedPath))) - .Returns(true); - _pipeStreamMock.InSequence(sequence).Setup(p => p.ReadByte()).Returns(0); - _channelSessionMock.InSequence(sequence).Setup(p => p.SendData(It.IsAny())); - _pipeStreamMock.InSequence(sequence).Setup(p => p.ReadByte()).Returns(0); - _channelSessionMock.InSequence(sequence) - .Setup(p => p.SendData(It.Is(b => b.SequenceEqual( - CreateData(string.Format("C0644 {0} {1}\n", _fileInfo.Length, _remoteFile), - _connectionInfo.Encoding))))); - _pipeStreamMock.InSequence(sequence).Setup(p => p.ReadByte()).Returns(0); - _channelSessionMock.InSequence(sequence) - .Setup( - p => p.SendData(It.Is(b => b.SequenceEqual(_fileContent.Take(_bufferSize))), 0, _bufferSize)); - _channelSessionMock.InSequence(sequence) - .Setup( - p => p.SendData(It.Is(b => b.Take(0, _fileContent.Length - _bufferSize).SequenceEqual(_fileContent.Take(_bufferSize, _fileContent.Length - _bufferSize))), 0, _fileContent.Length - _bufferSize)); - _channelSessionMock.InSequence(sequence) - .Setup( - p => p.SendData(It.Is(b => b.SequenceEqual(new byte[] {0})))); - _pipeStreamMock.InSequence(sequence).Setup(p => p.ReadByte()).Returns(0); - _channelSessionMock.InSequence(sequence).Setup(p => p.Dispose()); - _pipeStreamMock.As().InSequence(sequence).Setup(p => p.Dispose()); - - // On .NET Core, Dispose() in turn invokes Close() and since we're not mocking - // an interface, we need to expect this call as well - _pipeStreamMock.Setup(p => p.Close()); + _ = ServiceFactoryMock.InSequence(sequence) + .Setup(p => p.CreateRemotePathDoubleQuoteTransformation()) + .Returns(_remotePathTransformationMock.Object); + _ = ServiceFactoryMock.InSequence(sequence) + .Setup(p => p.CreateSocketFactory()) + .Returns(SocketFactoryMock.Object); + _ = ServiceFactoryMock.InSequence(sequence) + .Setup(p => p.CreateSession(_connectionInfo, SocketFactoryMock.Object)) + .Returns(SessionMock.Object); + _ = SessionMock.InSequence(sequence) + .Setup(p => p.Connect()); + _ = ServiceFactoryMock.InSequence(sequence) + .Setup(p => p.CreatePipeStream()) + .Returns(_pipeStreamMock.Object); + _ = SessionMock.InSequence(sequence) + .Setup(p => p.CreateChannelSession()) + .Returns(_channelSessionMock.Object); + _ = _channelSessionMock.InSequence(sequence) + .Setup(p => p.Open()); + _ = _remotePathTransformationMock.InSequence(sequence) + .Setup(p => p.Transform(_remoteDirectory)) + .Returns(_transformedPath); + _ = _channelSessionMock.InSequence(sequence) + .Setup(p => p.SendExecRequest(string.Format("scp -t -d {0}", _transformedPath))) + .Returns(true); + _ = _pipeStreamMock.InSequence(sequence) + .Setup(p => p.ReadByte()) + .Returns(0); + _ = _channelSessionMock.InSequence(sequence) + .Setup(p => p.SendData(It.IsAny())); + _ = _pipeStreamMock.InSequence(sequence) + .Setup(p => p.ReadByte()) + .Returns(0); + _ = _channelSessionMock.InSequence(sequence) + .Setup(p => p.SendData(It.Is(b => b.SequenceEqual(CreateData(string.Format("C0644 {0} {1}\n", _fileInfo.Length, _remoteFile), _connectionInfo.Encoding))))); + _ = _pipeStreamMock.InSequence(sequence) + .Setup(p => p.ReadByte()) + .Returns(0); + _ = _channelSessionMock.InSequence(sequence) + .Setup(p => p.SendData(It.Is(b => b.SequenceEqual(_fileContent.Take(_bufferSize))), 0, _bufferSize)); + _ = _channelSessionMock.InSequence(sequence) + .Setup(p => p.SendData(It.Is(b => b.Take(0, _fileContent.Length - _bufferSize).SequenceEqual(_fileContent.Take(_bufferSize, _fileContent.Length - _bufferSize))), 0, _fileContent.Length - _bufferSize)); + _ = _channelSessionMock.InSequence(sequence) + .Setup(p => p.SendData(It.Is(b => b.SequenceEqual(new byte[] {0})))); + _ = _pipeStreamMock.InSequence(sequence) + .Setup(p => p.ReadByte()) + .Returns(0); + _ = _channelSessionMock.InSequence(sequence) + .Setup(p => p.Dispose()); + _ = _pipeStreamMock.InSequence(sequence) + .Setup(p => p.Close()); } protected override void Arrange() { base.Arrange(); - _scpClient = new ScpClient(_connectionInfo, false, _serviceFactoryMock.Object) + _scpClient = new ScpClient(_connectionInfo, false, ServiceFactoryMock.Object) { BufferSize = (uint) _bufferSize }; @@ -134,7 +142,7 @@ namespace Renci.SshNet.Tests.Classes [TestMethod] public void DisposeOnPipeStreamShouldBeInvokedOnce() { - _pipeStreamMock.As().Verify(p => p.Dispose(), Times.Once); + _pipeStreamMock.Verify(p => p.Close(), Times.Once); } [TestMethod] @@ -166,7 +174,10 @@ namespace Renci.SshNet.Tests.Classes var content = new byte[length]; for (var i = 0; i < length; i++) + { content[i] = (byte) random.Next(byte.MinValue, byte.MaxValue); + } + return content; } diff --git a/src/Renci.SshNet.Tests/Classes/ScpClientTest_Upload_StreamAndPath_SendExecRequestReturnsFalse.cs b/src/Renci.SshNet.Tests/Classes/ScpClientTest_Upload_StreamAndPath_SendExecRequestReturnsFalse.cs index 5facca29..64fd8bc2 100644 --- a/src/Renci.SshNet.Tests/Classes/ScpClientTest_Upload_StreamAndPath_SendExecRequestReturnsFalse.cs +++ b/src/Renci.SshNet.Tests/Classes/ScpClientTest_Upload_StreamAndPath_SendExecRequestReturnsFalse.cs @@ -1,8 +1,11 @@ using System; using System.Collections.Generic; using System.IO; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; + using Renci.SshNet.Common; namespace Renci.SshNet.Tests.Classes @@ -37,38 +40,42 @@ namespace Renci.SshNet.Tests.Classes { var sequence = new MockSequence(); - _serviceFactoryMock.InSequence(sequence) - .Setup(p => p.CreateRemotePathDoubleQuoteTransformation()) - .Returns(_remotePathTransformationMock.Object); - _serviceFactoryMock.InSequence(sequence) - .Setup(p => p.CreateSocketFactory()) - .Returns(_socketFactoryMock.Object); - _serviceFactoryMock.InSequence(sequence) - .Setup(p => p.CreateSession(_connectionInfo, _socketFactoryMock.Object)) - .Returns(_sessionMock.Object); - _sessionMock.InSequence(sequence).Setup(p => p.Connect()); - _serviceFactoryMock.InSequence(sequence).Setup(p => p.CreatePipeStream()).Returns(_pipeStreamMock.Object); - _sessionMock.InSequence(sequence).Setup(p => p.CreateChannelSession()).Returns(_channelSessionMock.Object); - _channelSessionMock.InSequence(sequence).Setup(p => p.Open()); - _remotePathTransformationMock.InSequence(sequence) - .Setup(p => p.Transform(_remoteDirectory)) - .Returns(_transformedPath); - _channelSessionMock.InSequence(sequence) - .Setup(p => p.SendExecRequest(string.Format("scp -t -d {0}", _transformedPath))) - .Returns(false); - _channelSessionMock.InSequence(sequence).Setup(p => p.Dispose()); - _pipeStreamMock.As().InSequence(sequence).Setup(p => p.Dispose()); - - // On .NET Core, Dispose() in turn invokes Close() and since we're not mocking - // an interface, we need to expect this call as well - _pipeStreamMock.Setup(p => p.Close()); + _ = ServiceFactoryMock.InSequence(sequence) + .Setup(p => p.CreateRemotePathDoubleQuoteTransformation()) + .Returns(_remotePathTransformationMock.Object); + _ = ServiceFactoryMock.InSequence(sequence) + .Setup(p => p.CreateSocketFactory()) + .Returns(SocketFactoryMock.Object); + _ = ServiceFactoryMock.InSequence(sequence) + .Setup(p => p.CreateSession(_connectionInfo, SocketFactoryMock.Object)) + .Returns(SessionMock.Object); + _ = SessionMock.InSequence(sequence) + .Setup(p => p.Connect()); + _ = ServiceFactoryMock.InSequence(sequence) + .Setup(p => p.CreatePipeStream()) + .Returns(_pipeStreamMock.Object); + _ = SessionMock.InSequence(sequence) + .Setup(p => p.CreateChannelSession()) + .Returns(_channelSessionMock.Object); + _ = _channelSessionMock.InSequence(sequence) + .Setup(p => p.Open()); + _ = _remotePathTransformationMock.InSequence(sequence) + .Setup(p => p.Transform(_remoteDirectory)) + .Returns(_transformedPath); + _ = _channelSessionMock.InSequence(sequence) + .Setup(p => p.SendExecRequest(string.Format("scp -t -d {0}", _transformedPath))) + .Returns(false); + _ = _channelSessionMock.InSequence(sequence) + .Setup(p => p.Dispose()); + _ = _pipeStreamMock.InSequence(sequence) + .Setup(p => p.Close()); } protected override void Arrange() { base.Arrange(); - _scpClient = new ScpClient(_connectionInfo, false, _serviceFactoryMock.Object); + _scpClient = new ScpClient(_connectionInfo, false, ServiceFactoryMock.Object); _scpClient.Uploading += (sender, args) => _uploadingRegister.Add(args); _scpClient.Connect(); } @@ -77,10 +84,7 @@ namespace Renci.SshNet.Tests.Classes { base.TearDown(); - if (_source != null) - { - _source.Dispose(); - } + _source?.Dispose(); } protected override void Act() @@ -119,7 +123,7 @@ namespace Renci.SshNet.Tests.Classes [TestMethod] public void DisposeOnPipeStreamShouldBeInvokedOnce() { - _pipeStreamMock.As().Verify(p => p.Dispose(), Times.Once); + _pipeStreamMock.Verify(p => p.Close(), Times.Once); } [TestMethod] diff --git a/src/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/AesCipherTest.cs b/src/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/AesCipherTest.cs index ef6b6919..03a2e34e 100644 --- a/src/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/AesCipherTest.cs +++ b/src/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/AesCipherTest.cs @@ -3,7 +3,6 @@ using Renci.SshNet.Common; using Renci.SshNet.Security.Cryptography.Ciphers; using Renci.SshNet.Security.Cryptography.Ciphers.Modes; using Renci.SshNet.Tests.Common; -using Renci.SshNet.Tests.Properties; namespace Renci.SshNet.Tests.Classes.Security.Cryptography.Ciphers { @@ -84,125 +83,6 @@ namespace Renci.SshNet.Tests.Classes.Security.Cryptography.Ciphers Assert.IsTrue(expected.IsEqualTo(actual)); } - [TestMethod] - [Owner("olegkap")] - [TestCategory("Cipher")] - [TestCategory("integration")] - public void Test_Cipher_AEes128CBC_Connection() - { - var connectionInfo = new PasswordConnectionInfo(Resources.HOST, int.Parse(Resources.PORT), Resources.USERNAME, Resources.PASSWORD); - connectionInfo.Encryptions.Clear(); - connectionInfo.Encryptions.Add("aes128-cbc", new CipherInfo(128, (key, iv) => { return new AesCipher(key, new CbcCipherMode(iv), null); })); - - using (var client = new SshClient(connectionInfo)) - { - client.Connect(); - client.Disconnect(); - } - } - - [TestMethod] - [Owner("olegkap")] - [TestCategory("Cipher")] - [TestCategory("integration")] - public void Test_Cipher_Aes192CBC_Connection() - { - var connectionInfo = new PasswordConnectionInfo(Resources.HOST, int.Parse(Resources.PORT), Resources.USERNAME, Resources.PASSWORD); - connectionInfo.Encryptions.Clear(); - connectionInfo.Encryptions.Add("aes192-cbc", new CipherInfo(192, (key, iv) => { return new AesCipher(key, new CbcCipherMode(iv), null); })); - - using (var client = new SshClient(connectionInfo)) - { - client.Connect(); - client.Disconnect(); - } - } - - [TestMethod] - [Owner("olegkap")] - [TestCategory("Cipher")] - [TestCategory("integration")] - public void Test_Cipher_Aes256CBC_Connection() - { - var connectionInfo = new PasswordConnectionInfo(Resources.HOST, int.Parse(Resources.PORT), Resources.USERNAME, Resources.PASSWORD); - connectionInfo.Encryptions.Clear(); - connectionInfo.Encryptions.Add("aes256-cbc", new CipherInfo(256, (key, iv) => { return new AesCipher(key, new CbcCipherMode(iv), null); })); - - using (var client = new SshClient(connectionInfo)) - { - client.Connect(); - client.Disconnect(); - } - } - - [TestMethod] - [Owner("olegkap")] - [TestCategory("Cipher")] - [TestCategory("integration")] - public void Test_Cipher_Aes128CTR_Connection() - { - var connectionInfo = new PasswordConnectionInfo(Resources.HOST, int.Parse(Resources.PORT), Resources.USERNAME, Resources.PASSWORD); - connectionInfo.Encryptions.Clear(); - connectionInfo.Encryptions.Add("aes128-ctr", new CipherInfo(128, (key, iv) => { return new AesCipher(key, new CtrCipherMode(iv), null); })); - - using (var client = new SshClient(connectionInfo)) - { - client.Connect(); - client.Disconnect(); - } - } - - [TestMethod] - [Owner("olegkap")] - [TestCategory("Cipher")] - [TestCategory("integration")] - public void Test_Cipher_Aes192CTR_Connection() - { - var connectionInfo = new PasswordConnectionInfo(Resources.HOST, int.Parse(Resources.PORT), Resources.USERNAME, Resources.PASSWORD); - connectionInfo.Encryptions.Clear(); - connectionInfo.Encryptions.Add("aes192-ctr", new CipherInfo(192, (key, iv) => { return new AesCipher(key, new CtrCipherMode(iv), null); })); - - using (var client = new SshClient(connectionInfo)) - { - client.Connect(); - client.Disconnect(); - } - } - - [TestMethod] - [Owner("olegkap")] - [TestCategory("Cipher")] - [TestCategory("integration")] - public void Test_Cipher_Aes256CTR_Connection() - { - var connectionInfo = new PasswordConnectionInfo(Resources.HOST, int.Parse(Resources.PORT), Resources.USERNAME, Resources.PASSWORD); - connectionInfo.Encryptions.Clear(); - connectionInfo.Encryptions.Add("aes256-ctr", new CipherInfo(256, (key, iv) => { return new AesCipher(key, new CtrCipherMode(iv), null); })); - - using (var client = new SshClient(connectionInfo)) - { - client.Connect(); - client.Disconnect(); - } - } - - [TestMethod] - [Owner("olegkap")] - [TestCategory("Cipher")] - [TestCategory("integration")] - public void Test_Cipher_Arcfour_Connection() - { - var connectionInfo = new PasswordConnectionInfo(Resources.HOST, int.Parse(Resources.PORT), Resources.USERNAME, Resources.PASSWORD); - connectionInfo.Encryptions.Clear(); - connectionInfo.Encryptions.Add("arcfour", new CipherInfo(128, (key, iv) => { return new Arc4Cipher(key, false); })); - - using (var client = new SshClient(connectionInfo)) - { - client.Connect(); - client.Disconnect(); - } - } - /// ///A test for DecryptBlock /// @@ -263,4 +143,4 @@ namespace Renci.SshNet.Tests.Classes.Security.Cryptography.Ciphers Assert.Inconclusive("Verify the correctness of this test method."); } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/Arc4CipherTest.cs b/src/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/Arc4CipherTest.cs index f83aa7aa..5b972021 100644 --- a/src/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/Arc4CipherTest.cs +++ b/src/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/Arc4CipherTest.cs @@ -3,7 +3,6 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Renci.SshNet.Common; using Renci.SshNet.Security.Cryptography.Ciphers; using Renci.SshNet.Tests.Common; -using Renci.SshNet.Tests.Properties; namespace Renci.SshNet.Tests.Classes.Security.Cryptography.Ciphers { @@ -156,40 +155,5 @@ namespace Renci.SshNet.Tests.Classes.Security.Cryptography.Ciphers Assert.AreEqual(expected, actual); Assert.Inconclusive("Verify the correctness of this test method."); } - - [TestMethod] - [Owner("olegkap")] - [TestCategory("Cipher")] - [TestCategory("integration")] - public void Test_Cipher_Arcfour128_Connection() - { - var connectionInfo = new PasswordConnectionInfo(Resources.HOST, int.Parse(Resources.PORT), Resources.USERNAME, Resources.PASSWORD); - connectionInfo.Encryptions.Clear(); - connectionInfo.Encryptions.Add("arcfour128", new CipherInfo(128, (key, iv) => { return new Arc4Cipher(key, true); })); - - using (var client = new SshClient(connectionInfo)) - { - client.Connect(); - client.Disconnect(); - } - } - - [TestMethod] - [Owner("olegkap")] - [TestCategory("Cipher")] - [TestCategory("integration")] - public void Test_Cipher_Arcfour256_Connection() - { - var connectionInfo = new PasswordConnectionInfo(Resources.HOST, int.Parse(Resources.PORT), Resources.USERNAME, Resources.PASSWORD); - connectionInfo.Encryptions.Clear(); - connectionInfo.Encryptions.Add("arcfour256", new CipherInfo(256, (key, iv) => { return new Arc4Cipher(key, true); })); - - using (var client = new SshClient(connectionInfo)) - { - client.Connect(); - client.Disconnect(); - } - } - } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/BlowfishCipherTest.cs b/src/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/BlowfishCipherTest.cs index 1fd7b7d1..168b1eaf 100644 --- a/src/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/BlowfishCipherTest.cs +++ b/src/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/BlowfishCipherTest.cs @@ -2,7 +2,7 @@ using Renci.SshNet.Security.Cryptography.Ciphers; using Renci.SshNet.Security.Cryptography.Ciphers.Modes; using Renci.SshNet.Tests.Common; -using Renci.SshNet.Tests.Properties; + using System.Linq; namespace Renci.SshNet.Tests.Classes.Security.Cryptography.Ciphers @@ -20,27 +20,12 @@ namespace Renci.SshNet.Tests.Classes.Security.Cryptography.Ciphers var key = new byte[] { 0xe4, 0x94, 0xf9, 0xb1, 0x00, 0x4f, 0x16, 0x2a, 0x80, 0x11, 0xea, 0x73, 0x0d, 0xb9, 0xbf, 0x64 }; var iv = new byte[] { 0x74, 0x8b, 0x4f, 0xe6, 0xc1, 0x29, 0xb3, 0x54, 0xec, 0x77, 0x92, 0xf3, 0x15, 0xa0, 0x41, 0xa8 }; var output = new byte[] { 0x50, 0x49, 0xe0, 0xce, 0x98, 0x93, 0x8b, 0xec, 0x82, 0x7d, 0x14, 0x1b, 0x3e, 0xdc, 0xca, 0x63, 0xef, 0x36, 0x20, 0x67, 0x58, 0x63, 0x1f, 0x9c, 0xd2, 0x12, 0x6b, 0xca, 0xea, 0xd0, 0x78, 0x8b, 0x61, 0x50, 0x4f, 0xc4, 0x5b, 0x32, 0x91, 0xd6, 0x65, 0xcb, 0x74, 0xe5, 0x6e, 0xf5, 0xde, 0x14 }; - var testCipher = new Renci.SshNet.Security.Cryptography.Ciphers.BlowfishCipher(key, new Renci.SshNet.Security.Cryptography.Ciphers.Modes.CbcCipherMode(iv), null); + var testCipher = new BlowfishCipher(key, new CbcCipherMode(iv), null); var r = testCipher.Encrypt(input); if (!r.SequenceEqual(output)) - Assert.Fail("Invalid encryption"); - } - - [TestMethod] - [Owner("olegkap")] - [TestCategory("Cipher")] - [TestCategory("integration")] - public void Test_Cipher_BlowfishCBC_Connection() - { - var connectionInfo = new PasswordConnectionInfo(Resources.HOST, int.Parse(Resources.PORT), Resources.USERNAME, Resources.PASSWORD); - connectionInfo.Encryptions.Clear(); - connectionInfo.Encryptions.Add("blowfish-cbc", new CipherInfo(128, (key, iv) => { return new BlowfishCipher(key, new CbcCipherMode(iv), null); })); - - using (var client = new SshClient(connectionInfo)) { - client.Connect(); - client.Disconnect(); + Assert.Fail("Invalid encryption"); } } @@ -54,7 +39,7 @@ namespace Renci.SshNet.Tests.Classes.Security.Cryptography.Ciphers byte[] key = null; // TODO: Initialize to an appropriate value CipherMode mode = null; // TODO: Initialize to an appropriate value CipherPadding padding = null; // TODO: Initialize to an appropriate value - BlowfishCipher target = new BlowfishCipher(key, mode, padding); + var target = new BlowfishCipher(key, mode, padding); Assert.Inconclusive("TODO: Implement code to verify target"); } @@ -68,15 +53,14 @@ namespace Renci.SshNet.Tests.Classes.Security.Cryptography.Ciphers byte[] key = null; // TODO: Initialize to an appropriate value CipherMode mode = null; // TODO: Initialize to an appropriate value CipherPadding padding = null; // TODO: Initialize to an appropriate value - BlowfishCipher target = new BlowfishCipher(key, mode, padding); // TODO: Initialize to an appropriate value + var target = new BlowfishCipher(key, mode, padding); // TODO: Initialize to an appropriate value byte[] inputBuffer = null; // TODO: Initialize to an appropriate value - int inputOffset = 0; // TODO: Initialize to an appropriate value - int inputCount = 0; // TODO: Initialize to an appropriate value + var inputOffset = 0; // TODO: Initialize to an appropriate value + var inputCount = 0; // TODO: Initialize to an appropriate value byte[] outputBuffer = null; // TODO: Initialize to an appropriate value - int outputOffset = 0; // TODO: Initialize to an appropriate value - int expected = 0; // TODO: Initialize to an appropriate value - int actual; - actual = target.DecryptBlock(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset); + var outputOffset = 0; // TODO: Initialize to an appropriate value + var expected = 0; // TODO: Initialize to an appropriate value + var actual = target.DecryptBlock(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset); Assert.AreEqual(expected, actual); Assert.Inconclusive("Verify the correctness of this test method."); } @@ -91,18 +75,17 @@ namespace Renci.SshNet.Tests.Classes.Security.Cryptography.Ciphers byte[] key = null; // TODO: Initialize to an appropriate value CipherMode mode = null; // TODO: Initialize to an appropriate value CipherPadding padding = null; // TODO: Initialize to an appropriate value - BlowfishCipher target = new BlowfishCipher(key, mode, padding); // TODO: Initialize to an appropriate value + var target = new BlowfishCipher(key, mode, padding); // TODO: Initialize to an appropriate value byte[] inputBuffer = null; // TODO: Initialize to an appropriate value - int inputOffset = 0; // TODO: Initialize to an appropriate value - int inputCount = 0; // TODO: Initialize to an appropriate value + var inputOffset = 0; // TODO: Initialize to an appropriate value + var inputCount = 0; // TODO: Initialize to an appropriate value byte[] outputBuffer = null; // TODO: Initialize to an appropriate value - int outputOffset = 0; // TODO: Initialize to an appropriate value - int expected = 0; // TODO: Initialize to an appropriate value - int actual; - actual = target.EncryptBlock(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset); + var outputOffset = 0; // TODO: Initialize to an appropriate value + var expected = 0; // TODO: Initialize to an appropriate value + var actual = target.EncryptBlock(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset); Assert.AreEqual(expected, actual); Assert.Inconclusive("Verify the correctness of this test method."); } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/CastCipherTest.cs b/src/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/CastCipherTest.cs index c077a309..a04c5f2d 100644 --- a/src/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/CastCipherTest.cs +++ b/src/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/CastCipherTest.cs @@ -2,7 +2,7 @@ using Renci.SshNet.Security.Cryptography.Ciphers; using Renci.SshNet.Security.Cryptography.Ciphers.Modes; using Renci.SshNet.Tests.Common; -using Renci.SshNet.Tests.Properties; + using System.Linq; namespace Renci.SshNet.Tests.Classes.Security.Cryptography.Ciphers @@ -39,22 +39,6 @@ namespace Renci.SshNet.Tests.Classes.Security.Cryptography.Ciphers Assert.IsTrue(r.SequenceEqual(input)); } - [TestMethod] - [Owner("olegkap")] - [TestCategory("Cipher")] - [TestCategory("integration")] - public void Test_Cipher_Cast128CBC_Connection() - { - var connectionInfo = new PasswordConnectionInfo(Resources.HOST, int.Parse(Resources.PORT), Resources.USERNAME, Resources.PASSWORD); - connectionInfo.Encryptions.Clear(); - connectionInfo.Encryptions.Add("cast128-cbc", new CipherInfo(128, (key, iv) => { return new CastCipher(key, new CbcCipherMode(iv), null); })); - - using (var client = new SshClient(connectionInfo)) - { - client.Connect(); - client.Disconnect(); - } - } /// ///A test for CastCipher Constructor /// @@ -115,4 +99,4 @@ namespace Renci.SshNet.Tests.Classes.Security.Cryptography.Ciphers Assert.Inconclusive("Verify the correctness of this test method."); } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/TripleDesCipherTest.cs b/src/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/TripleDesCipherTest.cs index f791c9df..9dc6a2b6 100644 --- a/src/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/TripleDesCipherTest.cs +++ b/src/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/TripleDesCipherTest.cs @@ -1,9 +1,10 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Linq; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + using Renci.SshNet.Security.Cryptography.Ciphers; using Renci.SshNet.Security.Cryptography.Ciphers.Modes; using Renci.SshNet.Tests.Common; -using Renci.SshNet.Tests.Properties; -using System.Linq; namespace Renci.SshNet.Tests.Classes.Security.Cryptography.Ciphers { @@ -24,25 +25,11 @@ namespace Renci.SshNet.Tests.Classes.Security.Cryptography.Ciphers var r = testCipher.Encrypt(input); if (!r.SequenceEqual(output)) - Assert.Fail("Invalid encryption"); - } - - [TestMethod] - [Owner("olegkap")] - [TestCategory("Cipher")] - [TestCategory("integration")] - public void Test_Cipher_TripleDESCBC_Connection() - { - var connectionInfo = new PasswordConnectionInfo(Resources.HOST, int.Parse(Resources.PORT), Resources.USERNAME, Resources.PASSWORD); - connectionInfo.Encryptions.Clear(); - connectionInfo.Encryptions.Add("3des-cbc", new CipherInfo(192, (key, iv) => { return new TripleDesCipher(key, new CbcCipherMode(iv), null); })); - - using (var client = new SshClient(connectionInfo)) { - client.Connect(); - client.Disconnect(); + Assert.Fail("Invalid encryption"); } } + /// ///A test for TripleDesCipher Constructor /// @@ -103,4 +90,4 @@ namespace Renci.SshNet.Tests.Classes.Security.Cryptography.Ciphers Assert.Inconclusive("Verify the correctness of this test method."); } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.Tests/Classes/Security/Cryptography/DsaDigitalSignatureTest.cs b/src/Renci.SshNet.Tests/Classes/Security/Cryptography/DsaDigitalSignatureTest.cs index 0d3423c9..947ec9c7 100644 --- a/src/Renci.SshNet.Tests/Classes/Security/Cryptography/DsaDigitalSignatureTest.cs +++ b/src/Renci.SshNet.Tests/Classes/Security/Cryptography/DsaDigitalSignatureTest.cs @@ -19,7 +19,7 @@ namespace Renci.SshNet.Tests.Classes.Security.Cryptography public void DsaDigitalSignatureConstructorTest() { DsaKey key = null; // TODO: Initialize to an appropriate value - DsaDigitalSignature target = new DsaDigitalSignature(key); + var target = new DsaDigitalSignature(key); Assert.Inconclusive("TODO: Implement code to verify target"); } @@ -31,7 +31,7 @@ namespace Renci.SshNet.Tests.Classes.Security.Cryptography public void DisposeTest() { DsaKey key = null; // TODO: Initialize to an appropriate value - DsaDigitalSignature target = new DsaDigitalSignature(key); // TODO: Initialize to an appropriate value + var target = new DsaDigitalSignature(key); // TODO: Initialize to an appropriate value target.Dispose(); Assert.Inconclusive("A method that does not return a value cannot be verified."); } @@ -44,11 +44,10 @@ namespace Renci.SshNet.Tests.Classes.Security.Cryptography public void SignTest() { DsaKey key = null; // TODO: Initialize to an appropriate value - DsaDigitalSignature target = new DsaDigitalSignature(key); // TODO: Initialize to an appropriate value + var target = new DsaDigitalSignature(key); // TODO: Initialize to an appropriate value byte[] input = null; // TODO: Initialize to an appropriate value byte[] expected = null; // TODO: Initialize to an appropriate value - byte[] actual; - actual = target.Sign(input); + var actual = target.Sign(input); Assert.AreEqual(expected, actual); Assert.Inconclusive("Verify the correctness of this test method."); } @@ -61,14 +60,13 @@ namespace Renci.SshNet.Tests.Classes.Security.Cryptography public void VerifyTest() { DsaKey key = null; // TODO: Initialize to an appropriate value - DsaDigitalSignature target = new DsaDigitalSignature(key); // TODO: Initialize to an appropriate value + var target = new DsaDigitalSignature(key); // TODO: Initialize to an appropriate value byte[] input = null; // TODO: Initialize to an appropriate value byte[] signature = null; // TODO: Initialize to an appropriate value - bool expected = false; // TODO: Initialize to an appropriate value - bool actual; - actual = target.Verify(input, signature); + var expected = false; // TODO: Initialize to an appropriate value + var actual = target.Verify(input, signature); Assert.AreEqual(expected, actual); Assert.Inconclusive("Verify the correctness of this test method."); } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.Tests/Classes/Security/Cryptography/HMacTest.cs b/src/Renci.SshNet.Tests/Classes/Security/Cryptography/HMacTest.cs deleted file mode 100644 index 13d8f1dd..00000000 --- a/src/Renci.SshNet.Tests/Classes/Security/Cryptography/HMacTest.cs +++ /dev/null @@ -1,135 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Renci.SshNet.Tests.Common; -using Renci.SshNet.Tests.Properties; -using Renci.SshNet.Abstractions; - -namespace Renci.SshNet.Tests.Classes.Security.Cryptography -{ - /// - /// Provides HMAC algorithm implementation. - /// - /// - [TestClass] - public class HMacTest : TestBase - { - [TestMethod] - [TestCategory("integration")] - public void Test_HMac_MD5_Connection() - { - var connectionInfo = new PasswordConnectionInfo(Resources.HOST, int.Parse(Resources.PORT), Resources.USERNAME, Resources.PASSWORD); - connectionInfo.HmacAlgorithms.Clear(); - connectionInfo.HmacAlgorithms.Add("hmac-md5", new HashInfo(16 * 8, CryptoAbstraction.CreateHMACMD5)); - - using (var client = new SshClient(connectionInfo)) - { - client.Connect(); - client.Disconnect(); - } - } - - [TestMethod] - [TestCategory("integration")] - public void Test_HMac_Sha1_Connection() - { - var connectionInfo = new PasswordConnectionInfo(Resources.HOST, int.Parse(Resources.PORT), Resources.USERNAME, Resources.PASSWORD); - connectionInfo.HmacAlgorithms.Clear(); - connectionInfo.HmacAlgorithms.Add("hmac-sha1", new HashInfo(20 * 8, CryptoAbstraction.CreateHMACSHA1)); - - using (var client = new SshClient(connectionInfo)) - { - client.Connect(); - client.Disconnect(); - } - } - - [TestMethod] - [TestCategory("integration")] - public void Test_HMac_MD5_96_Connection() - { - var connectionInfo = new PasswordConnectionInfo(Resources.HOST, int.Parse(Resources.PORT), Resources.USERNAME, Resources.PASSWORD); - connectionInfo.HmacAlgorithms.Clear(); - connectionInfo.HmacAlgorithms.Add("hmac-md5", new HashInfo(16 * 8, key => CryptoAbstraction.CreateHMACMD5(key, 96))); - - using (var client = new SshClient(connectionInfo)) - { - client.Connect(); - client.Disconnect(); - } - } - - [TestMethod] - [TestCategory("integration")] - public void Test_HMac_Sha1_96_Connection() - { - var connectionInfo = new PasswordConnectionInfo(Resources.HOST, int.Parse(Resources.PORT), Resources.USERNAME, Resources.PASSWORD); - connectionInfo.HmacAlgorithms.Clear(); - connectionInfo.HmacAlgorithms.Add("hmac-sha1", new HashInfo(20 * 8, key => CryptoAbstraction.CreateHMACSHA1(key, 96))); - - using (var client = new SshClient(connectionInfo)) - { - client.Connect(); - client.Disconnect(); - } - } - - [TestMethod] - [TestCategory("integration")] - public void Test_HMac_Sha256_Connection() - { - var connectionInfo = new PasswordConnectionInfo(Resources.HOST, int.Parse(Resources.PORT), Resources.USERNAME, Resources.PASSWORD); - connectionInfo.HmacAlgorithms.Clear(); - connectionInfo.HmacAlgorithms.Add("hmac-sha2-256", new HashInfo(32 * 8, CryptoAbstraction.CreateHMACSHA256)); - - using (var client = new SshClient(connectionInfo)) - { - client.Connect(); - client.Disconnect(); - } - } - - [TestMethod] - [TestCategory("integration")] - public void Test_HMac_Sha256_96_Connection() - { - var connectionInfo = new PasswordConnectionInfo(Resources.HOST, int.Parse(Resources.PORT), Resources.USERNAME, Resources.PASSWORD); - connectionInfo.HmacAlgorithms.Clear(); - connectionInfo.HmacAlgorithms.Add("hmac-sha2-256-96", new HashInfo(32 * 8, (key) => CryptoAbstraction.CreateHMACSHA256(key, 96))); - - using (var client = new SshClient(connectionInfo)) - { - client.Connect(); - client.Disconnect(); - } - } - - [TestMethod] - [TestCategory("integration")] - public void Test_HMac_RIPEMD160_Connection() - { - var connectionInfo = new PasswordConnectionInfo(Resources.HOST, int.Parse(Resources.PORT), Resources.USERNAME, Resources.PASSWORD); - connectionInfo.HmacAlgorithms.Clear(); - connectionInfo.HmacAlgorithms.Add("hmac-ripemd160", new HashInfo(160, CryptoAbstraction.CreateHMACRIPEMD160)); - - using (var client = new SshClient(connectionInfo)) - { - client.Connect(); - client.Disconnect(); - } - } - - [TestMethod] - [TestCategory("integration")] - public void Test_HMac_RIPEMD160_OPENSSH_Connection() - { - var connectionInfo = new PasswordConnectionInfo(Resources.HOST, int.Parse(Resources.PORT), Resources.USERNAME, Resources.PASSWORD); - connectionInfo.HmacAlgorithms.Clear(); - connectionInfo.HmacAlgorithms.Add("hmac-ripemd160@openssh.com", new HashInfo(160, CryptoAbstraction.CreateHMACRIPEMD160)); - - using (var client = new SshClient(connectionInfo)) - { - client.Connect(); - client.Disconnect(); - } - } - } -} \ No newline at end of file diff --git a/src/Renci.SshNet.Tests/Classes/Security/Cryptography/RsaDigitalSignatureTest.cs b/src/Renci.SshNet.Tests/Classes/Security/Cryptography/RsaDigitalSignatureTest.cs index 93a76bbe..4691fef2 100644 --- a/src/Renci.SshNet.Tests/Classes/Security/Cryptography/RsaDigitalSignatureTest.cs +++ b/src/Renci.SshNet.Tests/Classes/Security/Cryptography/RsaDigitalSignatureTest.cs @@ -1,4 +1,9 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Security.Cryptography; +using System.Text; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + using Renci.SshNet.Security; using Renci.SshNet.Security.Cryptography; using Renci.SshNet.Tests.Common; @@ -11,17 +16,156 @@ namespace Renci.SshNet.Tests.Classes.Security.Cryptography [TestClass] public class RsaDigitalSignatureTest : TestBase { - - /// - ///A test for RsaDigitalSignature Constructor - /// [TestMethod] - [Ignore] // placeholder for actual test - public void RsaDigitalSignatureConstructorTest() + public void Sha1_SignAndVerify() { - RsaKey rsaKey = null; // TODO: Initialize to an appropriate value - RsaDigitalSignature target = new RsaDigitalSignature(rsaKey); - Assert.Inconclusive("TODO: Implement code to verify target"); + byte[] data = Encoding.UTF8.GetBytes("hello world"); + + RsaKey rsaKey = GetRsaKey(); + + var digitalSignature = new RsaDigitalSignature(rsaKey); // Verify SHA-1 is the default + + byte[] signedBytes = digitalSignature.Sign(data); + + byte[] expectedSignedBytes = new byte[] + { + // echo -n 'hello world' | openssl dgst -sha1 -sign Key.RSA.txt -out test.signed + 0x41, 0x50, 0x12, 0x14, 0xd3, 0x7c, 0xe0, 0x40, 0x50, 0x65, 0xfb, 0x33, 0xd9, 0x17, 0x89, 0xbf, + 0xb2, 0x4b, 0x85, 0x15, 0xbf, 0x9e, 0x57, 0x3b, 0x01, 0x15, 0x2b, 0x99, 0xfa, 0x62, 0x9b, 0x2a, + 0x05, 0xa0, 0x73, 0xc7, 0xb7, 0x5b, 0xd9, 0x01, 0xaa, 0x56, 0x73, 0x95, 0x13, 0x41, 0x33, 0x0d, + 0x7f, 0x83, 0x8a, 0x60, 0x4d, 0x19, 0xdc, 0x9b, 0xba, 0x8e, 0x61, 0xed, 0xd0, 0x8a, 0x3e, 0x38, + 0x71, 0xee, 0x34, 0xc3, 0x55, 0x0f, 0x55, 0x65, 0x89, 0xbb, 0x3e, 0x41, 0xee, 0xdf, 0xf5, 0x2f, + 0xab, 0x9e, 0x89, 0x37, 0x68, 0x1f, 0x9f, 0x38, 0x00, 0x81, 0x29, 0x93, 0xeb, 0x61, 0x37, 0xad, + 0x8d, 0x35, 0xf1, 0x3d, 0x4b, 0x9b, 0x99, 0x74, 0x7b, 0xeb, 0xf4, 0xfb, 0x76, 0xb4, 0xb6, 0xb4, + 0x09, 0x33, 0x5c, 0xfa, 0x6a, 0xad, 0x1e, 0xed, 0x1c, 0xe1, 0xb4, 0x4d, 0xf2, 0xa5, 0xc3, 0x64, + 0x9a, 0x45, 0x81, 0xee, 0x1b, 0xa6, 0x1d, 0x01, 0x3c, 0x4d, 0xb5, 0x62, 0x9e, 0xff, 0x8e, 0xff, + 0x6c, 0x18, 0xed, 0xe9, 0x8e, 0x03, 0x2c, 0xc5, 0x94, 0x81, 0xca, 0x8b, 0x18, 0x3f, 0x25, 0xcd, + 0xe5, 0x42, 0x49, 0x43, 0x23, 0x1f, 0xdc, 0x3f, 0xa2, 0x43, 0xbc, 0xbd, 0x42, 0xf5, 0x60, 0xfb, + 0x01, 0xd3, 0x67, 0x0d, 0x8d, 0x85, 0x7b, 0x51, 0x14, 0xec, 0x26, 0x53, 0x00, 0x61, 0x25, 0x16, + 0x19, 0x10, 0x3c, 0x86, 0x16, 0x59, 0x84, 0x08, 0xd1, 0xf9, 0x1e, 0x05, 0x88, 0xbd, 0x4a, 0x01, + 0x43, 0x4e, 0xec, 0x76, 0x0b, 0xd7, 0x2c, 0xe9, 0x98, 0xb1, 0x4c, 0x0a, 0x13, 0xc6, 0x95, 0xf9, + 0x8f, 0x95, 0x5c, 0x98, 0x4c, 0x8f, 0x97, 0x4a, 0xad, 0x0d, 0xfe, 0x84, 0xf0, 0x56, 0xc3, 0x29, + 0x73, 0x75, 0x55, 0x3c, 0xd9, 0x5e, 0x5b, 0x6f, 0xf9, 0x81, 0xbc, 0xbc, 0x50, 0x75, 0x7d, 0xa8 + }; + + CollectionAssert.AreEqual(expectedSignedBytes, signedBytes); + + // Also verify RsaKey uses SHA-1 by default + CollectionAssert.AreEqual(expectedSignedBytes, rsaKey.Sign(data)); + + // The following fails due to the _isPrivate decision in RsaCipher.Transform. Is that really correct? + //Assert.IsTrue(digitalSignature.Verify(data, signedBytes)); + + // 'Workaround': use a key with no private key information + var digitalSignaturePublic = new RsaDigitalSignature(new RsaKey() + { + Public = rsaKey.Public + }); + Assert.IsTrue(digitalSignaturePublic.Verify(data, signedBytes)); + } + + [TestMethod] + public void Sha256_SignAndVerify() + { + byte[] data = Encoding.UTF8.GetBytes("hello world"); + + RsaKey rsaKey = GetRsaKey(); + + var digitalSignature = new RsaDigitalSignature(rsaKey, HashAlgorithmName.SHA256); + + byte[] signedBytes = digitalSignature.Sign(data); + + CollectionAssert.AreEqual(new byte[] + { + // echo -n 'hello world' | openssl dgst -sha256 -sign Key.RSA.txt -out test.signed + 0x2e, 0xef, 0x01, 0x49, 0x5c, 0x66, 0x37, 0x56, 0xc2, 0xfb, 0x7b, 0xfa, 0x80, 0x2f, 0xdb, 0xaa, + 0x0d, 0x15, 0xd9, 0x8d, 0xa9, 0xad, 0x81, 0x4f, 0x09, 0x2e, 0x53, 0x9e, 0xce, 0x5d, 0x68, 0x07, + 0xae, 0xb9, 0xc0, 0x45, 0xfa, 0x30, 0xd0, 0xf7, 0xd6, 0xa6, 0x8d, 0x19, 0x24, 0x3a, 0xea, 0x91, + 0x3e, 0xa2, 0x4a, 0x42, 0x2e, 0x21, 0xf1, 0x48, 0x57, 0xca, 0x2b, 0x6c, 0x9f, 0x79, 0x54, 0x91, + 0x3e, 0x3a, 0x4d, 0xd1, 0x70, 0x87, 0x3d, 0xbe, 0x22, 0x97, 0xc9, 0xb0, 0x02, 0xf0, 0xa2, 0xae, + 0x7a, 0xbb, 0x8b, 0xaf, 0xc0, 0x3b, 0xab, 0x71, 0xe8, 0x29, 0x1c, 0x18, 0x88, 0xca, 0x74, 0x1b, + 0x34, 0x4f, 0xd1, 0x83, 0x39, 0x6e, 0x8f, 0x69, 0x3d, 0x7e, 0xef, 0xef, 0x57, 0x7c, 0xff, 0x21, + 0x9c, 0x10, 0x2b, 0xd1, 0x4f, 0x26, 0xbe, 0xaa, 0xd2, 0xd9, 0x03, 0x14, 0x75, 0x97, 0x11, 0xaf, + 0xf0, 0x28, 0xf2, 0xd3, 0x07, 0x79, 0x5b, 0x27, 0xdc, 0x97, 0xd8, 0xce, 0x4e, 0x78, 0x89, 0x16, + 0x91, 0x2a, 0xb2, 0x47, 0x53, 0x94, 0xe9, 0xa1, 0x15, 0x98, 0x29, 0x0c, 0xa1, 0xf5, 0xe2, 0x8e, + 0x11, 0xdc, 0x0c, 0x1c, 0x10, 0xa4, 0xf2, 0x46, 0x5c, 0x78, 0x0c, 0xc1, 0x4a, 0x65, 0x21, 0x8a, + 0x2e, 0x32, 0x6c, 0x72, 0x06, 0xf9, 0x7f, 0xa1, 0x6c, 0x2e, 0x13, 0x06, 0x41, 0xaa, 0x23, 0xdd, + 0xc8, 0x1c, 0x61, 0xb6, 0x96, 0x87, 0xc4, 0x84, 0xc8, 0x61, 0xec, 0x4e, 0xdd, 0x49, 0x9e, 0x4f, + 0x0d, 0x8c, 0xf1, 0x7f, 0xf2, 0x6c, 0x73, 0x5a, 0xa6, 0x3b, 0xbf, 0x4e, 0xba, 0x57, 0x6b, 0xb3, + 0x1e, 0x6c, 0x57, 0x76, 0x87, 0x9f, 0xb4, 0x3b, 0xcb, 0xcd, 0xe5, 0x10, 0x7a, 0x4c, 0xeb, 0xc0, + 0xc4, 0xc3, 0x75, 0x51, 0x5f, 0xb7, 0x7c, 0xbc, 0x55, 0x8d, 0x05, 0xc7, 0xed, 0xc7, 0x52, 0x4a + }, signedBytes); + + + // The following fails due to the _isPrivate decision in RsaCipher.Transform. Is that really correct? + //Assert.IsTrue(digitalSignature.Verify(data, signedBytes)); + + // 'Workaround': use a key with no private key information + var digitalSignaturePublic = new RsaDigitalSignature(new RsaKey() + { + Public = rsaKey.Public + }, HashAlgorithmName.SHA256); + Assert.IsTrue(digitalSignaturePublic.Verify(data, signedBytes)); + } + + [TestMethod] + public void Sha512_SignAndVerify() + { + byte[] data = Encoding.UTF8.GetBytes("hello world"); + + RsaKey rsaKey = GetRsaKey(); + + var digitalSignature = new RsaDigitalSignature(rsaKey, HashAlgorithmName.SHA512); + + byte[] signedBytes = digitalSignature.Sign(data); + + CollectionAssert.AreEqual(new byte[] + { + // echo -n 'hello world' | openssl dgst -sha512 -sign Key.RSA.txt -out test.signed + 0x69, 0x70, 0xb5, 0x9f, 0x32, 0x86, 0x3b, 0xae, 0xc0, 0x79, 0x6e, 0xdb, 0x35, 0xd5, 0xa6, 0x22, + 0xcd, 0x2b, 0x4b, 0xd2, 0x68, 0x1a, 0x65, 0x41, 0xa6, 0xd9, 0x20, 0x54, 0x31, 0x9a, 0xb1, 0x44, + 0x6e, 0x8f, 0x56, 0x4b, 0xfc, 0x27, 0x7f, 0x3f, 0xe7, 0x47, 0xcb, 0x78, 0x03, 0x05, 0x79, 0x8a, + 0x16, 0x7b, 0x12, 0x01, 0x3a, 0xa2, 0xd5, 0x0d, 0x2b, 0x16, 0x38, 0xef, 0x84, 0x6b, 0xd7, 0x19, + 0xeb, 0xac, 0x54, 0x01, 0x9d, 0xa6, 0x80, 0x74, 0x43, 0xa8, 0x6e, 0x5e, 0x33, 0x05, 0x06, 0x1d, + 0x6d, 0xfe, 0x32, 0x4f, 0xe3, 0xcb, 0x3e, 0x2d, 0x4e, 0xe1, 0x47, 0x03, 0x69, 0xb4, 0x59, 0x80, + 0x59, 0x05, 0x15, 0xa0, 0x11, 0x34, 0x47, 0x58, 0xd7, 0x93, 0x2d, 0x40, 0xf2, 0x2c, 0x37, 0x48, + 0x6b, 0x3c, 0xd3, 0x03, 0x09, 0x32, 0x74, 0xa0, 0x2d, 0x33, 0x11, 0x99, 0x10, 0xb4, 0x09, 0x31, + 0xec, 0xa3, 0x2c, 0x63, 0xba, 0x50, 0xd1, 0x02, 0x45, 0xae, 0xb5, 0x75, 0x7e, 0xfa, 0xfc, 0x06, + 0xb6, 0x6a, 0xb2, 0xa1, 0x73, 0x14, 0xa5, 0xaa, 0x17, 0x88, 0x03, 0x19, 0x14, 0x9b, 0xe1, 0x10, + 0xf8, 0x2f, 0x73, 0x01, 0xc7, 0x8d, 0x37, 0xef, 0x98, 0x69, 0xc2, 0xe2, 0x7a, 0x11, 0xd5, 0xb8, + 0xc9, 0x35, 0x45, 0xcb, 0x56, 0x4b, 0x92, 0x4a, 0xe0, 0x4c, 0xd6, 0x82, 0xae, 0xad, 0x5b, 0xe9, + 0x40, 0x7e, 0x2a, 0x48, 0x7d, 0x57, 0xc5, 0xfd, 0xe9, 0x98, 0xe0, 0xbb, 0x09, 0xa1, 0xf5, 0x48, + 0x45, 0xcb, 0xee, 0xb9, 0x99, 0x81, 0x44, 0x15, 0x2e, 0x50, 0x39, 0x64, 0x58, 0x4c, 0x34, 0x86, + 0xf8, 0x81, 0x9e, 0x1d, 0xb6, 0x97, 0xe0, 0xce, 0x16, 0xca, 0x20, 0x46, 0xe9, 0x49, 0x8f, 0xe6, + 0xa0, 0x23, 0x08, 0x80, 0xa6, 0x37, 0x70, 0x06, 0xcc, 0x8f, 0xf4, 0xa0, 0x74, 0x53, 0x26, 0x38 + }, signedBytes); + + // The following fails due to the _isPrivate decision in RsaCipher.Transform. Is that really correct? + //Assert.IsTrue(digitalSignature.Verify(data, signedBytes)); + + // 'Workaround': use a key with no private key information + var digitalSignaturePublic = new RsaDigitalSignature(new RsaKey() + { + Public = rsaKey.Public + }, HashAlgorithmName.SHA512); + Assert.IsTrue(digitalSignaturePublic.Verify(data, signedBytes)); + } + + [TestMethod] + public void Constructor_InvalidHashAlgorithm_ThrowsArgumentException() + { + ArgumentException exception = Assert.ThrowsException( + () => new RsaDigitalSignature(new RsaKey(), new HashAlgorithmName("invalid"))); + + Assert.AreEqual("hashAlgorithmName", exception.ParamName); + } + + private static RsaKey GetRsaKey() + { + using (var stream = GetData("Key.RSA.txt")) + { + return (RsaKey) new PrivateKeyFile(stream).Key; + } } /// @@ -37,4 +181,4 @@ namespace Renci.SshNet.Tests.Classes.Security.Cryptography Assert.Inconclusive("A method that does not return a value cannot be verified."); } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.Tests/Classes/Security/Cryptography/SymmetricCipherTest.cs b/src/Renci.SshNet.Tests/Classes/Security/Cryptography/SymmetricCipherTest.cs index 8dce8881..92f9da37 100644 --- a/src/Renci.SshNet.Tests/Classes/Security/Cryptography/SymmetricCipherTest.cs +++ b/src/Renci.SshNet.Tests/Classes/Security/Cryptography/SymmetricCipherTest.cs @@ -1,6 +1,6 @@ -using Renci.SshNet.Security.Cryptography; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Renci.SshNet.Security.Cryptography; using Renci.SshNet.Tests.Common; namespace Renci.SshNet.Tests @@ -26,15 +26,14 @@ namespace Renci.SshNet.Tests [Ignore] // placeholder for actual test public void DecryptBlockTest() { - SymmetricCipher target = CreateSymmetricCipher(); // TODO: Initialize to an appropriate value + var target = CreateSymmetricCipher(); // TODO: Initialize to an appropriate value byte[] inputBuffer = null; // TODO: Initialize to an appropriate value - int inputOffset = 0; // TODO: Initialize to an appropriate value - int inputCount = 0; // TODO: Initialize to an appropriate value + var inputOffset = 0; // TODO: Initialize to an appropriate value + var inputCount = 0; // TODO: Initialize to an appropriate value byte[] outputBuffer = null; // TODO: Initialize to an appropriate value - int outputOffset = 0; // TODO: Initialize to an appropriate value - int expected = 0; // TODO: Initialize to an appropriate value - int actual; - actual = target.DecryptBlock(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset); + var outputOffset = 0; // TODO: Initialize to an appropriate value + var expected = 0; // TODO: Initialize to an appropriate value + var actual = target.DecryptBlock(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset); Assert.AreEqual(expected, actual); Assert.Inconclusive("Verify the correctness of this test method."); } @@ -46,15 +45,14 @@ namespace Renci.SshNet.Tests [Ignore] // placeholder for actual test public void EncryptBlockTest() { - SymmetricCipher target = CreateSymmetricCipher(); // TODO: Initialize to an appropriate value + var target = CreateSymmetricCipher(); // TODO: Initialize to an appropriate value byte[] inputBuffer = null; // TODO: Initialize to an appropriate value - int inputOffset = 0; // TODO: Initialize to an appropriate value - int inputCount = 0; // TODO: Initialize to an appropriate value + var inputOffset = 0; // TODO: Initialize to an appropriate value + var inputCount = 0; // TODO: Initialize to an appropriate value byte[] outputBuffer = null; // TODO: Initialize to an appropriate value - int outputOffset = 0; // TODO: Initialize to an appropriate value - int expected = 0; // TODO: Initialize to an appropriate value - int actual; - actual = target.EncryptBlock(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset); + var outputOffset = 0; // TODO: Initialize to an appropriate value + var expected = 0; // TODO: Initialize to an appropriate value + var actual = target.EncryptBlock(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset); Assert.AreEqual(expected, actual); Assert.Inconclusive("Verify the correctness of this test method."); } diff --git a/src/Renci.SshNet.Tests/Classes/Security/KeyAlgorithmTest.cs b/src/Renci.SshNet.Tests/Classes/Security/KeyAlgorithmTest.cs new file mode 100644 index 00000000..d024c05a --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Security/KeyAlgorithmTest.cs @@ -0,0 +1,215 @@ +using System.Security.Cryptography; +using System.Text; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Renci.SshNet.Security; +using Renci.SshNet.Security.Cryptography; +using Renci.SshNet.Tests.Common; + +namespace Renci.SshNet.Tests.Classes.Security +{ + [TestClass] + public class KeyHostAlgorithmTest : TestBase + { + [TestMethod] + public void NoSuppliedDigitalSignature_PropertyIsKeyDigitalSignature() + { + RsaKey rsaKey = GetRsaKey(); + + KeyHostAlgorithm keyHostAlgorithm = new KeyHostAlgorithm("ssh-rsa", rsaKey); + + Assert.AreEqual("ssh-rsa", keyHostAlgorithm.Name); + Assert.AreSame(rsaKey, keyHostAlgorithm.Key); + Assert.AreSame(rsaKey.DigitalSignature, keyHostAlgorithm.DigitalSignature); + } + + [TestMethod] + public void SuppliedDigitalSignature_PropertyIsSuppliedDigitalSignature() + { + RsaKey rsaKey = GetRsaKey(); + RsaDigitalSignature rsaDigitalSignature = new RsaDigitalSignature(rsaKey, HashAlgorithmName.SHA256); + KeyHostAlgorithm keyHostAlgorithm = new KeyHostAlgorithm("rsa-sha2-256", rsaKey, rsaDigitalSignature); + + Assert.AreEqual("rsa-sha2-256", keyHostAlgorithm.Name); + Assert.AreSame(rsaKey, keyHostAlgorithm.Key); + Assert.AreSame(rsaDigitalSignature, keyHostAlgorithm.DigitalSignature); + } + + [TestMethod] + public void RsaPublicKeyDataDoesNotDependOnSignatureAlgorithm() + { + TestRsaPublicKeyData("ssh-rsa", HashAlgorithmName.SHA1); + TestRsaPublicKeyData("rsa-sha2-256", HashAlgorithmName.SHA256); + } + + private void TestRsaPublicKeyData(string signatureIdentifier, HashAlgorithmName hashAlgorithmName) + { + RsaKey key = GetRsaKey(); + KeyHostAlgorithm keyAlgorithm = new KeyHostAlgorithm(signatureIdentifier, key, new RsaDigitalSignature(key, hashAlgorithmName)); + + CollectionAssert.AreEqual(GetRsaPublicKeyBytes(), keyAlgorithm.Data); + } + + [TestMethod] + public void SshRsa_SignAndVerify() + { + byte[] data = Encoding.UTF8.GetBytes("hello world"); + + RsaKey key = GetRsaKey(); + KeyHostAlgorithm keyAlgorithm = new KeyHostAlgorithm("ssh-rsa", key); + + byte[] expectedEncodedSignatureBytes = new byte[] + { + 0, 0, 0, 7, // byte count of "ssh-rsa" + (byte)'s', (byte)'s', (byte)'h', (byte)'-', (byte)'r', (byte)'s', (byte)'a', // ssh-rsa + 0, 0, 1, 0, // byte count of signature (=256) + + // echo -n 'hello world' | openssl dgst -sha1 -sign Key.RSA.txt -out test.signed + 0x41, 0x50, 0x12, 0x14, 0xd3, 0x7c, 0xe0, 0x40, 0x50, 0x65, 0xfb, 0x33, 0xd9, 0x17, 0x89, 0xbf, + 0xb2, 0x4b, 0x85, 0x15, 0xbf, 0x9e, 0x57, 0x3b, 0x01, 0x15, 0x2b, 0x99, 0xfa, 0x62, 0x9b, 0x2a, + 0x05, 0xa0, 0x73, 0xc7, 0xb7, 0x5b, 0xd9, 0x01, 0xaa, 0x56, 0x73, 0x95, 0x13, 0x41, 0x33, 0x0d, + 0x7f, 0x83, 0x8a, 0x60, 0x4d, 0x19, 0xdc, 0x9b, 0xba, 0x8e, 0x61, 0xed, 0xd0, 0x8a, 0x3e, 0x38, + 0x71, 0xee, 0x34, 0xc3, 0x55, 0x0f, 0x55, 0x65, 0x89, 0xbb, 0x3e, 0x41, 0xee, 0xdf, 0xf5, 0x2f, + 0xab, 0x9e, 0x89, 0x37, 0x68, 0x1f, 0x9f, 0x38, 0x00, 0x81, 0x29, 0x93, 0xeb, 0x61, 0x37, 0xad, + 0x8d, 0x35, 0xf1, 0x3d, 0x4b, 0x9b, 0x99, 0x74, 0x7b, 0xeb, 0xf4, 0xfb, 0x76, 0xb4, 0xb6, 0xb4, + 0x09, 0x33, 0x5c, 0xfa, 0x6a, 0xad, 0x1e, 0xed, 0x1c, 0xe1, 0xb4, 0x4d, 0xf2, 0xa5, 0xc3, 0x64, + 0x9a, 0x45, 0x81, 0xee, 0x1b, 0xa6, 0x1d, 0x01, 0x3c, 0x4d, 0xb5, 0x62, 0x9e, 0xff, 0x8e, 0xff, + 0x6c, 0x18, 0xed, 0xe9, 0x8e, 0x03, 0x2c, 0xc5, 0x94, 0x81, 0xca, 0x8b, 0x18, 0x3f, 0x25, 0xcd, + 0xe5, 0x42, 0x49, 0x43, 0x23, 0x1f, 0xdc, 0x3f, 0xa2, 0x43, 0xbc, 0xbd, 0x42, 0xf5, 0x60, 0xfb, + 0x01, 0xd3, 0x67, 0x0d, 0x8d, 0x85, 0x7b, 0x51, 0x14, 0xec, 0x26, 0x53, 0x00, 0x61, 0x25, 0x16, + 0x19, 0x10, 0x3c, 0x86, 0x16, 0x59, 0x84, 0x08, 0xd1, 0xf9, 0x1e, 0x05, 0x88, 0xbd, 0x4a, 0x01, + 0x43, 0x4e, 0xec, 0x76, 0x0b, 0xd7, 0x2c, 0xe9, 0x98, 0xb1, 0x4c, 0x0a, 0x13, 0xc6, 0x95, 0xf9, + 0x8f, 0x95, 0x5c, 0x98, 0x4c, 0x8f, 0x97, 0x4a, 0xad, 0x0d, 0xfe, 0x84, 0xf0, 0x56, 0xc3, 0x29, + 0x73, 0x75, 0x55, 0x3c, 0xd9, 0x5e, 0x5b, 0x6f, 0xf9, 0x81, 0xbc, 0xbc, 0x50, 0x75, 0x7d, 0xa8 + }; + + CollectionAssert.AreEqual(expectedEncodedSignatureBytes, keyAlgorithm.Sign(data)); + + keyAlgorithm = new KeyHostAlgorithm("ssh-rsa", new RsaKey(), GetRsaPublicKeyBytes()); + Assert.IsTrue(keyAlgorithm.VerifySignature(data, expectedEncodedSignatureBytes)); + } + + [TestMethod] + public void RsaSha256_SignAndVerify() + { + byte[] data = Encoding.UTF8.GetBytes("hello world"); + + RsaKey key = GetRsaKey(); + KeyHostAlgorithm keyAlgorithm = new KeyHostAlgorithm("rsa-sha2-256", key, new RsaDigitalSignature(key, HashAlgorithmName.SHA256)); + + byte[] expectedEncodedSignatureBytes = new byte[] + { + 0, 0, 0, 12, // byte count of "rsa-sha2-256" + (byte)'r', (byte)'s', (byte)'a', (byte)'-', (byte)'s', (byte)'h', (byte)'a', (byte)'2', + (byte)'-', (byte)'2', (byte)'5', (byte)'6', + 0, 0, 1, 0, // byte count of signature (=256) + + // echo -n 'hello world' | openssl dgst -sha256 -sign Key.RSA.txt -out test.signed + 0x2e, 0xef, 0x01, 0x49, 0x5c, 0x66, 0x37, 0x56, 0xc2, 0xfb, 0x7b, 0xfa, 0x80, 0x2f, 0xdb, 0xaa, + 0x0d, 0x15, 0xd9, 0x8d, 0xa9, 0xad, 0x81, 0x4f, 0x09, 0x2e, 0x53, 0x9e, 0xce, 0x5d, 0x68, 0x07, + 0xae, 0xb9, 0xc0, 0x45, 0xfa, 0x30, 0xd0, 0xf7, 0xd6, 0xa6, 0x8d, 0x19, 0x24, 0x3a, 0xea, 0x91, + 0x3e, 0xa2, 0x4a, 0x42, 0x2e, 0x21, 0xf1, 0x48, 0x57, 0xca, 0x2b, 0x6c, 0x9f, 0x79, 0x54, 0x91, + 0x3e, 0x3a, 0x4d, 0xd1, 0x70, 0x87, 0x3d, 0xbe, 0x22, 0x97, 0xc9, 0xb0, 0x02, 0xf0, 0xa2, 0xae, + 0x7a, 0xbb, 0x8b, 0xaf, 0xc0, 0x3b, 0xab, 0x71, 0xe8, 0x29, 0x1c, 0x18, 0x88, 0xca, 0x74, 0x1b, + 0x34, 0x4f, 0xd1, 0x83, 0x39, 0x6e, 0x8f, 0x69, 0x3d, 0x7e, 0xef, 0xef, 0x57, 0x7c, 0xff, 0x21, + 0x9c, 0x10, 0x2b, 0xd1, 0x4f, 0x26, 0xbe, 0xaa, 0xd2, 0xd9, 0x03, 0x14, 0x75, 0x97, 0x11, 0xaf, + 0xf0, 0x28, 0xf2, 0xd3, 0x07, 0x79, 0x5b, 0x27, 0xdc, 0x97, 0xd8, 0xce, 0x4e, 0x78, 0x89, 0x16, + 0x91, 0x2a, 0xb2, 0x47, 0x53, 0x94, 0xe9, 0xa1, 0x15, 0x98, 0x29, 0x0c, 0xa1, 0xf5, 0xe2, 0x8e, + 0x11, 0xdc, 0x0c, 0x1c, 0x10, 0xa4, 0xf2, 0x46, 0x5c, 0x78, 0x0c, 0xc1, 0x4a, 0x65, 0x21, 0x8a, + 0x2e, 0x32, 0x6c, 0x72, 0x06, 0xf9, 0x7f, 0xa1, 0x6c, 0x2e, 0x13, 0x06, 0x41, 0xaa, 0x23, 0xdd, + 0xc8, 0x1c, 0x61, 0xb6, 0x96, 0x87, 0xc4, 0x84, 0xc8, 0x61, 0xec, 0x4e, 0xdd, 0x49, 0x9e, 0x4f, + 0x0d, 0x8c, 0xf1, 0x7f, 0xf2, 0x6c, 0x73, 0x5a, 0xa6, 0x3b, 0xbf, 0x4e, 0xba, 0x57, 0x6b, 0xb3, + 0x1e, 0x6c, 0x57, 0x76, 0x87, 0x9f, 0xb4, 0x3b, 0xcb, 0xcd, 0xe5, 0x10, 0x7a, 0x4c, 0xeb, 0xc0, + 0xc4, 0xc3, 0x75, 0x51, 0x5f, 0xb7, 0x7c, 0xbc, 0x55, 0x8d, 0x05, 0xc7, 0xed, 0xc7, 0x52, 0x4a + }; + + CollectionAssert.AreEqual(expectedEncodedSignatureBytes, keyAlgorithm.Sign(data)); + + key = new RsaKey(); + keyAlgorithm = new KeyHostAlgorithm("rsa-sha2-256", key, GetRsaPublicKeyBytes(), new RsaDigitalSignature(key, HashAlgorithmName.SHA256)); + Assert.IsTrue(keyAlgorithm.VerifySignature(data, expectedEncodedSignatureBytes)); + } + + [TestMethod] + public void RsaSha512_SignAndVerify() + { + byte[] data = Encoding.UTF8.GetBytes("hello world"); + + RsaKey key = GetRsaKey(); + KeyHostAlgorithm keyAlgorithm = new KeyHostAlgorithm("rsa-sha2-512", key, new RsaDigitalSignature(key, HashAlgorithmName.SHA512)); + + byte[] expectedEncodedSignatureBytes = new byte[] + { + 0, 0, 0, 12, // byte count of "rsa-sha2-512" + (byte)'r', (byte)'s', (byte)'a', (byte)'-', (byte)'s', (byte)'h', (byte)'a', (byte)'2', + (byte)'-', (byte)'5', (byte)'1', (byte)'2', + 0, 0, 1, 0, // byte count of signature (=256) + + // echo -n 'hello world' | openssl dgst -sha512 -sign Key.RSA.txt -out test.signed + 0x69, 0x70, 0xb5, 0x9f, 0x32, 0x86, 0x3b, 0xae, 0xc0, 0x79, 0x6e, 0xdb, 0x35, 0xd5, 0xa6, 0x22, + 0xcd, 0x2b, 0x4b, 0xd2, 0x68, 0x1a, 0x65, 0x41, 0xa6, 0xd9, 0x20, 0x54, 0x31, 0x9a, 0xb1, 0x44, + 0x6e, 0x8f, 0x56, 0x4b, 0xfc, 0x27, 0x7f, 0x3f, 0xe7, 0x47, 0xcb, 0x78, 0x03, 0x05, 0x79, 0x8a, + 0x16, 0x7b, 0x12, 0x01, 0x3a, 0xa2, 0xd5, 0x0d, 0x2b, 0x16, 0x38, 0xef, 0x84, 0x6b, 0xd7, 0x19, + 0xeb, 0xac, 0x54, 0x01, 0x9d, 0xa6, 0x80, 0x74, 0x43, 0xa8, 0x6e, 0x5e, 0x33, 0x05, 0x06, 0x1d, + 0x6d, 0xfe, 0x32, 0x4f, 0xe3, 0xcb, 0x3e, 0x2d, 0x4e, 0xe1, 0x47, 0x03, 0x69, 0xb4, 0x59, 0x80, + 0x59, 0x05, 0x15, 0xa0, 0x11, 0x34, 0x47, 0x58, 0xd7, 0x93, 0x2d, 0x40, 0xf2, 0x2c, 0x37, 0x48, + 0x6b, 0x3c, 0xd3, 0x03, 0x09, 0x32, 0x74, 0xa0, 0x2d, 0x33, 0x11, 0x99, 0x10, 0xb4, 0x09, 0x31, + 0xec, 0xa3, 0x2c, 0x63, 0xba, 0x50, 0xd1, 0x02, 0x45, 0xae, 0xb5, 0x75, 0x7e, 0xfa, 0xfc, 0x06, + 0xb6, 0x6a, 0xb2, 0xa1, 0x73, 0x14, 0xa5, 0xaa, 0x17, 0x88, 0x03, 0x19, 0x14, 0x9b, 0xe1, 0x10, + 0xf8, 0x2f, 0x73, 0x01, 0xc7, 0x8d, 0x37, 0xef, 0x98, 0x69, 0xc2, 0xe2, 0x7a, 0x11, 0xd5, 0xb8, + 0xc9, 0x35, 0x45, 0xcb, 0x56, 0x4b, 0x92, 0x4a, 0xe0, 0x4c, 0xd6, 0x82, 0xae, 0xad, 0x5b, 0xe9, + 0x40, 0x7e, 0x2a, 0x48, 0x7d, 0x57, 0xc5, 0xfd, 0xe9, 0x98, 0xe0, 0xbb, 0x09, 0xa1, 0xf5, 0x48, + 0x45, 0xcb, 0xee, 0xb9, 0x99, 0x81, 0x44, 0x15, 0x2e, 0x50, 0x39, 0x64, 0x58, 0x4c, 0x34, 0x86, + 0xf8, 0x81, 0x9e, 0x1d, 0xb6, 0x97, 0xe0, 0xce, 0x16, 0xca, 0x20, 0x46, 0xe9, 0x49, 0x8f, 0xe6, + 0xa0, 0x23, 0x08, 0x80, 0xa6, 0x37, 0x70, 0x06, 0xcc, 0x8f, 0xf4, 0xa0, 0x74, 0x53, 0x26, 0x38 + }; + + CollectionAssert.AreEqual(expectedEncodedSignatureBytes, keyAlgorithm.Sign(data)); + + key = new RsaKey(); + keyAlgorithm = new KeyHostAlgorithm("rsa-sha2-512", key, GetRsaPublicKeyBytes(), new RsaDigitalSignature(key, HashAlgorithmName.SHA512)); + Assert.IsTrue(keyAlgorithm.VerifySignature(data, expectedEncodedSignatureBytes)); + } + + private static RsaKey GetRsaKey() + { + using (var stream = GetData("Key.RSA.txt")) + { + return (RsaKey) new PrivateKeyFile(stream).Key; + } + } + + private static byte[] GetRsaPublicKeyBytes() + { + return new byte[] + { + 0, 0, 0, 7, // byte count of "ssh-rsa" + (byte)'s', (byte)'s', (byte)'h', (byte)'-', (byte)'r', (byte)'s', (byte)'a', // ssh-rsa + 0, 0, 0, 1, // byte count of exponent + 35, // exponent + 0, 0, 1, 1, // byte count of modulus (=257) + + // openssl rsa -in Key.RSA.txt -text + 0x00, 0xb9, 0x3b, 0x57, 0x9f, 0xe0, 0x5a, 0xb5, 0x7d, 0x68, 0x26, 0xeb, 0xe1, 0xa9, 0xf2, + 0x59, 0xc3, 0x98, 0xdc, 0xfe, 0x97, 0x08, 0xc4, 0x95, 0x0f, 0x9a, 0xea, 0x05, 0x08, 0x7d, + 0xfe, 0x6d, 0x77, 0xca, 0x04, 0x9f, 0xfd, 0xe2, 0x2c, 0x4d, 0x11, 0x3c, 0xd9, 0x05, 0xab, + 0x32, 0xbd, 0x3f, 0xe8, 0xcd, 0xba, 0x00, 0x6c, 0x21, 0xb7, 0xa9, 0xc2, 0x4e, 0x63, 0x17, + 0xf6, 0x04, 0x47, 0x93, 0x00, 0x85, 0xde, 0xd6, 0x32, 0xc0, 0xa1, 0x37, 0x75, 0x18, 0xa0, + 0xb0, 0x32, 0xf6, 0x4e, 0xca, 0x39, 0xec, 0x3c, 0xdf, 0x79, 0xfe, 0x50, 0xa1, 0xc1, 0xf7, + 0x67, 0x05, 0xb3, 0x33, 0xa5, 0x96, 0x13, 0x19, 0xfa, 0x14, 0xca, 0x55, 0xe6, 0x7b, 0xf9, + 0xb3, 0x8e, 0x32, 0xee, 0xfc, 0x9d, 0x2a, 0x5e, 0x04, 0x79, 0x97, 0x29, 0x3d, 0x1c, 0x54, + 0xfe, 0xc7, 0x96, 0x04, 0xb5, 0x19, 0x7c, 0x55, 0x21, 0xe2, 0x0e, 0x42, 0xca, 0x4d, 0x9d, + 0xfb, 0x77, 0x08, 0x6c, 0xaa, 0x07, 0x2c, 0xf8, 0xf9, 0x1f, 0xbd, 0x83, 0x14, 0x2b, 0xe0, + 0xbc, 0x7a, 0xf9, 0xdf, 0x13, 0x4b, 0x60, 0x5a, 0x02, 0x99, 0x93, 0x41, 0x1a, 0xb6, 0x5f, + 0x3b, 0x9c, 0xb5, 0xb2, 0x55, 0x70, 0x78, 0x2f, 0x38, 0x52, 0x0e, 0xd1, 0x8a, 0x2c, 0x23, + 0xc0, 0x3a, 0x0a, 0xd7, 0xed, 0xf6, 0x1f, 0xa6, 0x50, 0xf0, 0x27, 0x65, 0x8a, 0xd4, 0xde, + 0xa7, 0x1b, 0x41, 0x67, 0xc5, 0x6d, 0x47, 0x84, 0x37, 0x92, 0x2b, 0xb7, 0xb6, 0x4d, 0xb0, + 0x1a, 0xda, 0xf6, 0x50, 0x82, 0xf1, 0x57, 0x31, 0x69, 0xce, 0xe0, 0xef, 0xcd, 0x64, 0xaa, + 0x78, 0x08, 0xea, 0x4e, 0x45, 0xec, 0xa5, 0x89, 0x68, 0x5d, 0xb4, 0xa0, 0x23, 0xaf, 0xff, + 0x9c, 0x0f, 0x8c, 0x83, 0x7c, 0xf8, 0xe1, 0x8e, 0x32, 0x8e, 0x61, 0xfc, 0x5b, 0xbd, 0xd4, + 0x46, 0xe1 + }; + } + } +} diff --git a/src/Renci.SshNet.Tests/Classes/Security/KeyExchangeDiffieHellmanGroup14Sha256Test.cs b/src/Renci.SshNet.Tests/Classes/Security/KeyExchangeDiffieHellmanGroup14Sha256Test.cs index 744a434f..915e4b8f 100644 --- a/src/Renci.SshNet.Tests/Classes/Security/KeyExchangeDiffieHellmanGroup14Sha256Test.cs +++ b/src/Renci.SshNet.Tests/Classes/Security/KeyExchangeDiffieHellmanGroup14Sha256Test.cs @@ -1,8 +1,8 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; + using Renci.SshNet.Common; using Renci.SshNet.Security; using Renci.SshNet.Tests.Common; -using System.Text; namespace Renci.SshNet.Tests.Classes.Security { @@ -58,4 +58,4 @@ namespace Renci.SshNet.Tests.Classes.Security Assert.AreEqual("diffie-hellman-group14-sha256", _group14.Name); } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.Tests/Classes/Security/KeyExchangeTest.cs b/src/Renci.SshNet.Tests/Classes/Security/KeyExchangeTest.cs index 839394e6..a288292e 100644 --- a/src/Renci.SshNet.Tests/Classes/Security/KeyExchangeTest.cs +++ b/src/Renci.SshNet.Tests/Classes/Security/KeyExchangeTest.cs @@ -1,6 +1,6 @@ -using Renci.SshNet.Security; +using Microsoft.VisualStudio.TestTools.UnitTesting; + using Renci.SshNet.Tests.Common; -using Microsoft.VisualStudio.TestTools.UnitTesting; namespace Renci.SshNet.Tests { diff --git a/src/Renci.SshNet.Tests/Classes/Security/KeyHostAlgorithmTest.cs b/src/Renci.SshNet.Tests/Classes/Security/KeyHostAlgorithmTest.cs deleted file mode 100644 index 9bd5d10c..00000000 --- a/src/Renci.SshNet.Tests/Classes/Security/KeyHostAlgorithmTest.cs +++ /dev/null @@ -1,123 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Renci.SshNet.Security; -using Renci.SshNet.Tests.Common; -using Renci.SshNet.Tests.Properties; - -namespace Renci.SshNet.Tests.Classes.Security -{ - /// - /// Implements key support for host algorithm. - /// - [TestClass] - public class KeyHostAlgorithmTest : TestBase - { - [TestMethod] - [TestCategory("integration")] - public void Test_HostKey_SshRsa_Connection() - { - var connectionInfo = new PasswordConnectionInfo(Resources.HOST, int.Parse(Resources.PORT), Resources.USERNAME, Resources.PASSWORD); - connectionInfo.HostKeyAlgorithms.Clear(); - connectionInfo.HostKeyAlgorithms.Add("ssh-rsa", (data) => { return new KeyHostAlgorithm("ssh-rsa", new RsaKey(), data); }); - - using (var client = new SshClient(connectionInfo)) - { - client.Connect(); - client.Disconnect(); - } - } - - [TestMethod] - [TestCategory("integration")] - public void Test_HostKey_SshDss_Connection() - { - var connectionInfo = new PasswordConnectionInfo(Resources.HOST, int.Parse(Resources.PORT), Resources.USERNAME, Resources.PASSWORD); - connectionInfo.HostKeyAlgorithms.Clear(); - connectionInfo.HostKeyAlgorithms.Add("ssh-dss", (data) => { return new KeyHostAlgorithm("ssh-dss", new DsaKey(), data); }); - - using (var client = new SshClient(connectionInfo)) - { - client.Connect(); - client.Disconnect(); - } - } - - /// - ///A test for KeyHostAlgorithm Constructor - /// - [TestMethod] - [Ignore] // placeholder for actual test - public void KeyHostAlgorithmConstructorTest() - { - string name = string.Empty; // TODO: Initialize to an appropriate value - Key key = null; // TODO: Initialize to an appropriate value - KeyHostAlgorithm target = new KeyHostAlgorithm(name, key); - Assert.Inconclusive("TODO: Implement code to verify target"); - } - - /// - ///A test for KeyHostAlgorithm Constructor - /// - [TestMethod] - [Ignore] // placeholder for actual test - public void KeyHostAlgorithmConstructorTest1() - { - string name = string.Empty; // TODO: Initialize to an appropriate value - Key key = null; // TODO: Initialize to an appropriate value - byte[] data = null; // TODO: Initialize to an appropriate value - KeyHostAlgorithm target = new KeyHostAlgorithm(name, key, data); - Assert.Inconclusive("TODO: Implement code to verify target"); - } - - /// - ///A test for Sign - /// - [TestMethod] - [Ignore] // placeholder for actual test - public void SignTest() - { - string name = string.Empty; // TODO: Initialize to an appropriate value - Key key = null; // TODO: Initialize to an appropriate value - KeyHostAlgorithm target = new KeyHostAlgorithm(name, key); // TODO: Initialize to an appropriate value - byte[] data = null; // TODO: Initialize to an appropriate value - byte[] expected = null; // TODO: Initialize to an appropriate value - byte[] actual; - actual = target.Sign(data); - Assert.AreEqual(expected, actual); - Assert.Inconclusive("Verify the correctness of this test method."); - } - - /// - ///A test for VerifySignature - /// - [TestMethod] - [Ignore] // placeholder for actual test - public void VerifySignatureTest() - { - string name = string.Empty; // TODO: Initialize to an appropriate value - Key key = null; // TODO: Initialize to an appropriate value - KeyHostAlgorithm target = new KeyHostAlgorithm(name, key); // TODO: Initialize to an appropriate value - byte[] data = null; // TODO: Initialize to an appropriate value - byte[] signature = null; // TODO: Initialize to an appropriate value - bool expected = false; // TODO: Initialize to an appropriate value - bool actual; - actual = target.VerifySignature(data, signature); - Assert.AreEqual(expected, actual); - Assert.Inconclusive("Verify the correctness of this test method."); - } - - /// - ///A test for Data - /// - [TestMethod] - [Ignore] // placeholder for actual test - public void DataTest() - { - string name = string.Empty; // TODO: Initialize to an appropriate value - Key key = null; // TODO: Initialize to an appropriate value - KeyHostAlgorithm target = new KeyHostAlgorithm(name, key); // TODO: Initialize to an appropriate value - byte[] actual; - actual = target.Data; - Assert.Inconclusive("Verify the correctness of this test method."); - } - } -} \ No newline at end of file diff --git a/src/Renci.SshNet.Tests/Classes/SessionTest.HttpProxy.cs b/src/Renci.SshNet.Tests/Classes/SessionTest.HttpProxy.cs deleted file mode 100644 index 03ce2491..00000000 --- a/src/Renci.SshNet.Tests/Classes/SessionTest.HttpProxy.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using System.Net; -using System.Linq; -using System.Text; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Renci.SshNet.Common; -using Renci.SshNet.Tests.Common; -using Renci.SshNet.Connection; - -namespace Renci.SshNet.Tests.Classes -{ - public partial class SessionTest - { - private static ConnectionInfo CreateConnectionInfoWithHttpProxy(IPEndPoint proxyEndPoint, IPEndPoint serverEndPoint, string proxyUserName) - { - return new ConnectionInfo( - serverEndPoint.Address.ToString(), - serverEndPoint.Port, - "eric", - ProxyTypes.Http, - proxyEndPoint.Address.ToString(), - proxyEndPoint.Port, - proxyUserName, - "proxypwd", - new NoneAuthenticationMethod("eric")); - } - - private static string CreateProxyAuthorizationHeader(ConnectionInfo connectionInfo) - { - return string.Format("Proxy-Authorization: Basic {0}", - Convert.ToBase64String( - Encoding.ASCII.GetBytes(string.Format("{0}:{1}", connectionInfo.ProxyUsername, - connectionInfo.ProxyPassword)))); - } - } -} diff --git a/src/Renci.SshNet.Tests/Classes/SessionTest.cs b/src/Renci.SshNet.Tests/Classes/SessionTest.cs index 972757e4..bfb9a428 100644 --- a/src/Renci.SshNet.Tests/Classes/SessionTest.cs +++ b/src/Renci.SshNet.Tests/Classes/SessionTest.cs @@ -1,7 +1,10 @@ using System; using System.Net; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; + using Renci.SshNet.Connection; using Renci.SshNet.Tests.Common; @@ -15,8 +18,6 @@ namespace Renci.SshNet.Tests.Classes { private Mock _serviceFactoryMock; private Mock _socketFactoryMock; - private Mock _connectorMock; - private Mock _protocolVersionExchangeMock; protected override void OnInit() { @@ -24,8 +25,6 @@ namespace Renci.SshNet.Tests.Classes _serviceFactoryMock = new Mock(MockBehavior.Strict); _socketFactoryMock = new Mock(MockBehavior.Strict); - _connectorMock = new Mock(MockBehavior.Strict); - _protocolVersionExchangeMock = new Mock(MockBehavior.Strict); } [TestMethod] @@ -35,7 +34,7 @@ namespace Renci.SshNet.Tests.Classes try { - new Session(connectionInfo, _serviceFactoryMock.Object, _socketFactoryMock.Object); + _ = new Session(connectionInfo, _serviceFactoryMock.Object, _socketFactoryMock.Object); Assert.Fail(); } catch (ArgumentNullException ex) @@ -54,7 +53,7 @@ namespace Renci.SshNet.Tests.Classes try { - new Session(connectionInfo, serviceFactory, _socketFactoryMock.Object); + _ = new Session(connectionInfo, serviceFactory, _socketFactoryMock.Object); Assert.Fail(); } catch (ArgumentNullException ex) @@ -73,7 +72,7 @@ namespace Renci.SshNet.Tests.Classes try { - new Session(connectionInfo, _serviceFactoryMock.Object, socketFactory); + _ = new Session(connectionInfo, _serviceFactoryMock.Object, socketFactory); Assert.Fail(); } catch (ArgumentNullException ex) @@ -85,13 +84,10 @@ namespace Renci.SshNet.Tests.Classes private static ConnectionInfo CreateConnectionInfo(IPEndPoint serverEndPoint, TimeSpan timeout) { - var connectionInfo = new ConnectionInfo( - serverEndPoint.Address.ToString(), - serverEndPoint.Port, - "eric", - new NoneAuthenticationMethod("eric")); - connectionInfo.Timeout = timeout; - return connectionInfo; + return new ConnectionInfo(serverEndPoint.Address.ToString(), serverEndPoint.Port, "eric", new NoneAuthenticationMethod("eric")) + { + Timeout = timeout + }; } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.Tests/Classes/SessionTestBase.cs b/src/Renci.SshNet.Tests/Classes/SessionTestBase.cs index cbe69e2a..8b3cce37 100644 --- a/src/Renci.SshNet.Tests/Classes/SessionTestBase.cs +++ b/src/Renci.SshNet.Tests/Classes/SessionTestBase.cs @@ -6,15 +6,15 @@ namespace Renci.SshNet.Tests.Classes { public abstract class SessionTestBase : TripleATestBase { - internal Mock _serviceFactoryMock { get; private set; } - internal Mock _socketFactoryMock { get; private set; } - internal Mock _connectorMock { get; private set; } + internal Mock ServiceFactoryMock { get; private set; } + internal Mock SocketFactoryMock { get; private set; } + internal Mock ConnectorMock { get; private set; } protected virtual void CreateMocks() { - _serviceFactoryMock = new Mock(MockBehavior.Strict); - _socketFactoryMock = new Mock(MockBehavior.Strict); - _connectorMock = new Mock(MockBehavior.Strict); + ServiceFactoryMock = new Mock(MockBehavior.Strict); + SocketFactoryMock = new Mock(MockBehavior.Strict); + ConnectorMock = new Mock(MockBehavior.Strict); } protected virtual void SetupData() diff --git a/src/Renci.SshNet.Tests/Classes/SessionTest_ConnectToServerFails.cs b/src/Renci.SshNet.Tests/Classes/SessionTest_ConnectToServerFails.cs index df19c8ec..16cfbb1e 100644 --- a/src/Renci.SshNet.Tests/Classes/SessionTest_ConnectToServerFails.cs +++ b/src/Renci.SshNet.Tests/Classes/SessionTest_ConnectToServerFails.cs @@ -1,7 +1,9 @@ using System; using System.Net; using System.Threading; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Renci.SshNet.Common; using Renci.SshNet.Messages.Transport; @@ -21,7 +23,7 @@ namespace Renci.SshNet.Tests.Classes var serverEndPoint = new IPEndPoint(IPAddress.Loopback, 8122); _connectionInfo = CreateConnectionInfo(serverEndPoint, TimeSpan.FromSeconds(5)); - _session = new Session(_connectionInfo, _serviceFactoryMock.Object, _socketFactoryMock.Object); + _session = new Session(_connectionInfo, ServiceFactoryMock.Object, SocketFactoryMock.Object); _connectException = new SshConnectionException(); } @@ -29,10 +31,10 @@ namespace Renci.SshNet.Tests.Classes { base.SetupMocks(); - _serviceFactoryMock.Setup(p => p.CreateConnector(_connectionInfo, _socketFactoryMock.Object)) - .Returns(_connectorMock.Object); - _connectorMock.Setup(p => p.Connect(_connectionInfo)) - .Throws(_connectException); + _ = ServiceFactoryMock.Setup(p => p.CreateConnector(_connectionInfo, SocketFactoryMock.Object)) + .Returns(ConnectorMock.Object); + _ = ConnectorMock.Setup(p => p.Connect(_connectionInfo)) + .Throws(_connectException); } protected override void Act() @@ -163,9 +165,8 @@ namespace Renci.SshNet.Tests.Classes { var session = (ISession)_session; var waitHandle = new ManualResetEvent(false); - Exception exception; - var result = session.TryWait(waitHandle, Session.InfiniteTimeSpan, out exception); + var result = session.TryWait(waitHandle, Session.InfiniteTimeSpan, out var exception); Assert.AreEqual(WaitResult.Disconnected, result); Assert.IsNull(exception); @@ -253,13 +254,10 @@ namespace Renci.SshNet.Tests.Classes private static ConnectionInfo CreateConnectionInfo(IPEndPoint serverEndPoint, TimeSpan timeout) { - var connectionInfo = new ConnectionInfo( - serverEndPoint.Address.ToString(), - serverEndPoint.Port, - "eric", - new NoneAuthenticationMethod("eric")); - connectionInfo.Timeout = timeout; - return connectionInfo; + return new ConnectionInfo(serverEndPoint.Address.ToString(), serverEndPoint.Port, "eric", new NoneAuthenticationMethod("eric")) + { + Timeout = timeout + }; } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.Tests/Classes/SessionTest_Connected.cs b/src/Renci.SshNet.Tests/Classes/SessionTest_Connected.cs index fa36aed5..f841926b 100644 --- a/src/Renci.SshNet.Tests/Classes/SessionTest_Connected.cs +++ b/src/Renci.SshNet.Tests/Classes/SessionTest_Connected.cs @@ -197,7 +197,7 @@ namespace Renci.SshNet.Tests.Classes try { - session.TryWait(waitHandle, Session.InfiniteTimeSpan); + _ = session.TryWait(waitHandle, Session.InfiniteTimeSpan); Assert.Fail(); } catch (ArgumentNullException ex) @@ -212,9 +212,8 @@ namespace Renci.SshNet.Tests.Classes { var session = (ISession) Session; var waitHandle = new ManualResetEvent(true); - Exception exception; - var result = session.TryWait(waitHandle, TimeSpan.FromMilliseconds(0), out exception); + var result = session.TryWait(waitHandle, TimeSpan.FromMilliseconds(0), out var exception); Assert.AreEqual(WaitResult.Success, result); Assert.IsNull(exception); @@ -225,9 +224,8 @@ namespace Renci.SshNet.Tests.Classes { var session = (ISession) Session; var waitHandle = new ManualResetEvent(false); - Exception exception; - var result = session.TryWait(waitHandle, TimeSpan.FromMilliseconds(0), out exception); + var result = session.TryWait(waitHandle, TimeSpan.FromMilliseconds(0), out var exception); Assert.AreEqual(WaitResult.TimedOut, result); Assert.IsNull(exception); @@ -272,4 +270,4 @@ namespace Renci.SshNet.Tests.Classes ConnectorMock.Verify(p => p.Connect(ConnectionInfo), Times.Once()); } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.Tests/Classes/SessionTest_ConnectedBase.cs b/src/Renci.SshNet.Tests/Classes/SessionTest_ConnectedBase.cs index a920b190..8f4bed7c 100644 --- a/src/Renci.SshNet.Tests/Classes/SessionTest_ConnectedBase.cs +++ b/src/Renci.SshNet.Tests/Classes/SessionTest_ConnectedBase.cs @@ -4,9 +4,11 @@ using System.Globalization; using System.Net; using System.Net.Sockets; using System.Security.Cryptography; -using System.Text; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; + using Renci.SshNet.Common; using Renci.SshNet.Compression; using Renci.SshNet.Connection; @@ -111,11 +113,13 @@ namespace Renci.SshNet.Tests.Classes { var newKeysMessage = new NewKeysMessage(); var newKeys = newKeysMessage.GetPacket(8, null); - ServerSocket.Send(newKeys, 4, newKeys.Length - 4, SocketFlags.None); + _ = ServerSocket.Send(newKeys, 4, newKeys.Length - 4, SocketFlags.None); }; - ServerListener = new AsyncSocketListener(_serverEndPoint); - ServerListener.ShutdownRemoteCommunicationSocket = false; + ServerListener = new AsyncSocketListener(_serverEndPoint) + { + ShutdownRemoteCommunicationSocket = false + }; ServerListener.Connected += socket => { ServerSocket = socket; @@ -137,7 +141,7 @@ namespace Renci.SshNet.Tests.Classes ServerHostKeyAlgorithms = new string[0] }; var keyExchangeInit = keyExchangeInitMessage.GetPacket(8, null); - ServerSocket.Send(keyExchangeInit, 4, keyExchangeInit.Length - 4, SocketFlags.None); + _ = ServerSocket.Send(keyExchangeInit, 4, keyExchangeInit.Length - 4, SocketFlags.None); }; ServerListener.BytesReceived += (received, socket) => { @@ -147,7 +151,7 @@ namespace Renci.SshNet.Tests.Classes { var serviceAcceptMessage = ServiceAcceptMessageBuilder.Create(ServiceName.UserAuthentication) .Build(); - ServerSocket.Send(serviceAcceptMessage, 0, serviceAcceptMessage.Length, SocketFlags.None); + _ = ServerSocket.Send(serviceAcceptMessage, 0, serviceAcceptMessage.Length, SocketFlags.None); _authenticationStarted = true; } @@ -169,32 +173,37 @@ namespace Renci.SshNet.Tests.Classes private void SetupMocks() { - ServiceFactoryMock.Setup(p => p.CreateConnector(ConnectionInfo, SocketFactoryMock.Object)) - .Returns(ConnectorMock.Object); - ConnectorMock.Setup(p => p.Connect(ConnectionInfo)) - .Returns(ClientSocket); - ServiceFactoryMock.Setup(p => p.CreateProtocolVersionExchange()) - .Returns(_protocolVersionExchangeMock.Object); - _protocolVersionExchangeMock.Setup(p => p.Start(Session.ClientVersion, ClientSocket, ConnectionInfo.Timeout)) - .Returns(ServerIdentification); - - ServiceFactoryMock.Setup( - p => - p.CreateKeyExchange(ConnectionInfo.KeyExchangeAlgorithms, new[] { _keyExchangeAlgorithm })).Returns(_keyExchangeMock.Object); - _keyExchangeMock.Setup(p => p.Name).Returns(_keyExchangeAlgorithm); - _keyExchangeMock.Setup(p => p.Start(Session, It.IsAny())); - _keyExchangeMock.Setup(p => p.ExchangeHash).Returns(SessionId); - _keyExchangeMock.Setup(p => p.CreateServerCipher()).Returns((Cipher) null); - _keyExchangeMock.Setup(p => p.CreateClientCipher()).Returns((Cipher) null); - _keyExchangeMock.Setup(p => p.CreateServerHash()).Returns((HashAlgorithm) null); - _keyExchangeMock.Setup(p => p.CreateClientHash()).Returns((HashAlgorithm) null); - _keyExchangeMock.Setup(p => p.CreateCompressor()).Returns((Compressor) null); - _keyExchangeMock.Setup(p => p.CreateDecompressor()).Returns((Compressor) null); - _keyExchangeMock.Setup(p => p.Dispose()); - ServiceFactoryMock.Setup(p => p.CreateClientAuthentication()) - .Callback(ClientAuthentication_Callback) - .Returns(_clientAuthenticationMock.Object); - _clientAuthenticationMock.Setup(p => p.Authenticate(ConnectionInfo, Session)); + _ = ServiceFactoryMock.Setup(p => p.CreateConnector(ConnectionInfo, SocketFactoryMock.Object)) + .Returns(ConnectorMock.Object); + _ = ConnectorMock.Setup(p => p.Connect(ConnectionInfo)) + .Returns(ClientSocket); + _ = ServiceFactoryMock.Setup(p => p.CreateProtocolVersionExchange()) + .Returns(_protocolVersionExchangeMock.Object); + _ = _protocolVersionExchangeMock.Setup(p => p.Start(Session.ClientVersion, ClientSocket, ConnectionInfo.Timeout)) + .Returns(ServerIdentification); + _ = ServiceFactoryMock.Setup(p => p.CreateKeyExchange(ConnectionInfo.KeyExchangeAlgorithms, new[] { _keyExchangeAlgorithm })).Returns(_keyExchangeMock.Object); + _ = _keyExchangeMock.Setup(p => p.Name) + .Returns(_keyExchangeAlgorithm); + _ = _keyExchangeMock.Setup(p => p.Start(Session, It.IsAny())); + _ = _keyExchangeMock.Setup(p => p.ExchangeHash) + .Returns(SessionId); + _ = _keyExchangeMock.Setup(p => p.CreateServerCipher()) + .Returns((Cipher) null); + _ = _keyExchangeMock.Setup(p => p.CreateClientCipher()) + .Returns((Cipher) null); + _ = _keyExchangeMock.Setup(p => p.CreateServerHash()) + .Returns((HashAlgorithm) null); + _ = _keyExchangeMock.Setup(p => p.CreateClientHash()) + .Returns((HashAlgorithm) null); + _ = _keyExchangeMock.Setup(p => p.CreateCompressor()) + .Returns((Compressor) null); + _ = _keyExchangeMock.Setup(p => p.CreateDecompressor()) + .Returns((Compressor) null); + _ = _keyExchangeMock.Setup(p => p.Dispose()); + _ = ServiceFactoryMock.Setup(p => p.CreateClientAuthentication()) + .Callback(ClientAuthentication_Callback) + .Returns(_clientAuthenticationMock.Object); + _ = _clientAuthenticationMock.Setup(p => p.Authenticate(ConnectionInfo, Session)); } protected void Arrange() diff --git a/src/Renci.SshNet.Tests/Classes/SessionTest_Connected_ConnectionReset.cs b/src/Renci.SshNet.Tests/Classes/SessionTest_Connected_ConnectionReset.cs index a177317f..fb49e66e 100644 --- a/src/Renci.SshNet.Tests/Classes/SessionTest_Connected_ConnectionReset.cs +++ b/src/Renci.SshNet.Tests/Classes/SessionTest_Connected_ConnectionReset.cs @@ -1,8 +1,8 @@ -using System; -using System.Diagnostics; -using System.Net.Sockets; +using System.Diagnostics; using System.Threading; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Renci.SshNet.Common; using Renci.SshNet.Messages.Transport; using Renci.SshNet.Tests.Common; @@ -17,7 +17,7 @@ namespace Renci.SshNet.Tests.Classes ServerSocket.Close(); // give session some time to react to connection reset - Thread.Sleep(200); + Thread.Sleep(300); } [TestMethod] @@ -187,12 +187,11 @@ namespace Renci.SshNet.Tests.Classes { var session = (ISession) Session; var waitHandle = new ManualResetEvent(false); - Exception exception; - var result = session.TryWait(waitHandle, Session.InfiniteTimeSpan, out exception); + var result = session.TryWait(waitHandle, Session.InfiniteTimeSpan, out var exception); Assert.AreEqual(WaitResult.Disconnected, result); Assert.IsNull(exception); } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.Tests/Classes/SessionTest_Connected_Disconnect.cs b/src/Renci.SshNet.Tests/Classes/SessionTest_Connected_Disconnect.cs index eacb27c3..3b0c9b55 100644 --- a/src/Renci.SshNet.Tests/Classes/SessionTest_Connected_Disconnect.cs +++ b/src/Renci.SshNet.Tests/Classes/SessionTest_Connected_Disconnect.cs @@ -1,8 +1,9 @@ -using System; -using System.Diagnostics; +using System.Diagnostics; using System.Net.Sockets; using System.Threading; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Renci.SshNet.Common; using Renci.SshNet.Messages.Transport; using Renci.SshNet.Tests.Common; @@ -100,7 +101,7 @@ namespace Renci.SshNet.Tests.Classes Assert.IsTrue(session.MessageListenerCompleted.WaitOne()); } - [TestMethodAttribute] + [TestMethod] public void ISession_SendMessageShouldThrowSshConnectionException() { var session = (ISession) Session; @@ -182,9 +183,8 @@ namespace Renci.SshNet.Tests.Classes { var session = (ISession) Session; var waitHandle = new ManualResetEvent(false); - Exception exception; - var result = session.TryWait(waitHandle, Session.InfiniteTimeSpan, out exception); + var result = session.TryWait(waitHandle, Session.InfiniteTimeSpan, out var exception); Assert.AreEqual(WaitResult.Disconnected, result); Assert.IsNull(exception); @@ -197,4 +197,4 @@ namespace Renci.SshNet.Tests.Classes Assert.IsFalse(ClientSocket.Connected); } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.Tests/Classes/SessionTest_Connected_ServerAndClientDisconnectRace.cs b/src/Renci.SshNet.Tests/Classes/SessionTest_Connected_ServerAndClientDisconnectRace.cs index 68bcbdaa..b565fe1f 100644 --- a/src/Renci.SshNet.Tests/Classes/SessionTest_Connected_ServerAndClientDisconnectRace.cs +++ b/src/Renci.SshNet.Tests/Classes/SessionTest_Connected_ServerAndClientDisconnectRace.cs @@ -47,15 +47,8 @@ namespace Renci.SshNet.Tests.Classes private void TearDown() { - if (ServerListener != null) - { - ServerListener.Dispose(); - } - - if (Session != null) - { - Session.Dispose(); - } + ServerListener?.Dispose(); + Session?.Dispose(); if (ClientSocket != null && ClientSocket.Connected) { @@ -95,7 +88,7 @@ namespace Renci.SshNet.Tests.Classes { var newKeysMessage = new NewKeysMessage(); var newKeys = newKeysMessage.GetPacket(8, null); - ServerSocket.Send(newKeys, 4, newKeys.Length - 4, SocketFlags.None); + _ = ServerSocket.Send(newKeys, 4, newKeys.Length - 4, SocketFlags.None); }; ServerListener = new AsyncSocketListener(_serverEndPoint); @@ -120,7 +113,7 @@ namespace Renci.SshNet.Tests.Classes ServerHostKeyAlgorithms = new string[0] }; var keyExchangeInit = keyExchangeInitMessage.GetPacket(8, null); - ServerSocket.Send(keyExchangeInit, 4, keyExchangeInit.Length - 4, SocketFlags.None); + _ = ServerSocket.Send(keyExchangeInit, 4, keyExchangeInit.Length - 4, SocketFlags.None); }; ServerListener.BytesReceived += (received, socket) => { @@ -129,7 +122,7 @@ namespace Renci.SshNet.Tests.Classes if (!_authenticationStarted) { var serviceAcceptMessage =ServiceAcceptMessageBuilder.Create(ServiceName.UserAuthentication).Build(); - ServerSocket.Send(serviceAcceptMessage, 0, serviceAcceptMessage.Length, SocketFlags.None); + _ = ServerSocket.Send(serviceAcceptMessage, 0, serviceAcceptMessage.Length, SocketFlags.None); _authenticationStarted = true; } }; @@ -151,29 +144,37 @@ namespace Renci.SshNet.Tests.Classes private void SetupMocks() { - _serviceFactoryMock.Setup(p => p.CreateConnector(ConnectionInfo, _socketFactoryMock.Object)) - .Returns(_connectorMock.Object); - _connectorMock.Setup(p => p.Connect(ConnectionInfo)) - .Returns(ClientSocket); - _serviceFactoryMock.Setup(p => p.CreateProtocolVersionExchange()) - .Returns(_protocolVersionExchangeMock.Object); - _protocolVersionExchangeMock.Setup(p => p.Start(Session.ClientVersion, ClientSocket, ConnectionInfo.Timeout)) - .Returns(ServerIdentification); - _serviceFactoryMock.Setup( - p => - p.CreateKeyExchange(ConnectionInfo.KeyExchangeAlgorithms, new[] { _keyExchangeAlgorithm })).Returns(_keyExchangeMock.Object); - _keyExchangeMock.Setup(p => p.Name).Returns(_keyExchangeAlgorithm); - _keyExchangeMock.Setup(p => p.Start(Session, It.IsAny())); - _keyExchangeMock.Setup(p => p.ExchangeHash).Returns(SessionId); - _keyExchangeMock.Setup(p => p.CreateServerCipher()).Returns((Cipher)null); - _keyExchangeMock.Setup(p => p.CreateClientCipher()).Returns((Cipher)null); - _keyExchangeMock.Setup(p => p.CreateServerHash()).Returns((HashAlgorithm)null); - _keyExchangeMock.Setup(p => p.CreateClientHash()).Returns((HashAlgorithm)null); - _keyExchangeMock.Setup(p => p.CreateCompressor()).Returns((Compressor)null); - _keyExchangeMock.Setup(p => p.CreateDecompressor()).Returns((Compressor)null); - _keyExchangeMock.Setup(p => p.Dispose()); - _serviceFactoryMock.Setup(p => p.CreateClientAuthentication()).Returns(_clientAuthenticationMock.Object); - _clientAuthenticationMock.Setup(p => p.Authenticate(ConnectionInfo, Session)); + _ = _serviceFactoryMock.Setup(p => p.CreateConnector(ConnectionInfo, _socketFactoryMock.Object)) + .Returns(_connectorMock.Object); + _ = _connectorMock.Setup(p => p.Connect(ConnectionInfo)) + .Returns(ClientSocket); + _ = _serviceFactoryMock.Setup(p => p.CreateProtocolVersionExchange()) + .Returns(_protocolVersionExchangeMock.Object); + _ = _protocolVersionExchangeMock.Setup(p => p.Start(Session.ClientVersion, ClientSocket, ConnectionInfo.Timeout)) + .Returns(ServerIdentification); + _ = _serviceFactoryMock.Setup(p => p.CreateKeyExchange(ConnectionInfo.KeyExchangeAlgorithms, new[] { _keyExchangeAlgorithm })) + .Returns(_keyExchangeMock.Object); + _ = _keyExchangeMock.Setup(p => p.Name) + .Returns(_keyExchangeAlgorithm); + _ = _keyExchangeMock.Setup(p => p.Start(Session, It.IsAny())); + _ = _keyExchangeMock.Setup(p => p.ExchangeHash) + .Returns(SessionId); + _ = _keyExchangeMock.Setup(p => p.CreateServerCipher()) + .Returns((Cipher) null); + _ = _keyExchangeMock.Setup(p => p.CreateClientCipher()) + .Returns((Cipher) null); + _ = _keyExchangeMock.Setup(p => p.CreateServerHash()) + .Returns((HashAlgorithm) null); + _ = _keyExchangeMock.Setup(p => p.CreateClientHash()) + .Returns((HashAlgorithm) null); + _ = _keyExchangeMock.Setup(p => p.CreateCompressor()) + .Returns((Compressor) null); + _ = _keyExchangeMock.Setup(p => p.CreateDecompressor()) + .Returns((Compressor) null); + _ = _keyExchangeMock.Setup(p => p.Dispose()); + _ = _serviceFactoryMock.Setup(p => p.CreateClientAuthentication()) + .Returns(_clientAuthenticationMock.Object); + _ = _clientAuthenticationMock.Setup(p => p.Authenticate(ConnectionInfo, Session)); } protected virtual void Arrange() @@ -194,7 +195,7 @@ namespace Renci.SshNet.Tests.Classes try { var disconnect = _disconnectMessage.GetPacket(8, null); - ServerSocket.Send(disconnect, 4, disconnect.Length - 4, SocketFlags.None); + _ = ServerSocket.Send(disconnect, 4, disconnect.Length - 4, SocketFlags.None); Session.Disconnect(); } finally diff --git a/src/Renci.SshNet.Tests/Classes/SessionTest_Connected_ServerSendsBadPacket.cs b/src/Renci.SshNet.Tests/Classes/SessionTest_Connected_ServerSendsBadPacket.cs index df9790f9..c1f0fb5b 100644 --- a/src/Renci.SshNet.Tests/Classes/SessionTest_Connected_ServerSendsBadPacket.cs +++ b/src/Renci.SshNet.Tests/Classes/SessionTest_Connected_ServerSendsBadPacket.cs @@ -1,8 +1,9 @@ -using System; -using System.Diagnostics; +using System.Diagnostics; using System.Net.Sockets; using System.Threading; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Renci.SshNet.Common; using Renci.SshNet.Messages.Transport; using Renci.SshNet.Tests.Common; @@ -23,7 +24,7 @@ namespace Renci.SshNet.Tests.Classes protected override void Act() { - ServerSocket.Send(_packet, 0, _packet.Length, SocketFlags.None); + _ = ServerSocket.Send(_packet, 0, _packet.Length, SocketFlags.None); // give session some time to process packet Thread.Sleep(200); @@ -124,7 +125,7 @@ namespace Renci.SshNet.Tests.Classes Assert.IsTrue(session.MessageListenerCompleted.WaitOne()); } - [TestMethodAttribute] + [TestMethod] public void ISession_SendMessageShouldThrowSshConnectionException() { var session = (ISession) Session; @@ -206,12 +207,11 @@ namespace Renci.SshNet.Tests.Classes { var session = (ISession) Session; var waitHandle = new ManualResetEvent(false); - Exception exception; - var result = session.TryWait(waitHandle, Session.InfiniteTimeSpan, out exception); + var result = session.TryWait(waitHandle, Session.InfiniteTimeSpan, out var exception); Assert.AreEqual(WaitResult.Disconnected, result); Assert.IsNull(exception); } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.Tests/Classes/SessionTest_Connected_ServerSendsDisconnectMessage.cs b/src/Renci.SshNet.Tests/Classes/SessionTest_Connected_ServerSendsDisconnectMessage.cs index 36a73d82..7000a223 100644 --- a/src/Renci.SshNet.Tests/Classes/SessionTest_Connected_ServerSendsDisconnectMessage.cs +++ b/src/Renci.SshNet.Tests/Classes/SessionTest_Connected_ServerSendsDisconnectMessage.cs @@ -1,9 +1,10 @@ -using System; -using System.Diagnostics; +using System.Diagnostics; using System.Globalization; using System.Net.Sockets; using System.Threading; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Renci.SshNet.Common; using Renci.SshNet.Messages.Transport; using Renci.SshNet.Tests.Common; @@ -26,7 +27,7 @@ namespace Renci.SshNet.Tests.Classes protected override void Act() { - ServerSocket.Send(_packet, 4, _packet.Length - 4, SocketFlags.None); + _ = ServerSocket.Send(_packet, 4, _packet.Length - 4, SocketFlags.None); // give session some time to process packet Thread.Sleep(200); @@ -121,7 +122,7 @@ namespace Renci.SshNet.Tests.Classes Assert.IsTrue(session.MessageListenerCompleted.WaitOne()); } - [TestMethodAttribute] + [TestMethod] public void ISession_SendMessageShouldThrowSshConnectionException() { var session = (ISession) Session; @@ -211,12 +212,11 @@ namespace Renci.SshNet.Tests.Classes { var session = (ISession) Session; var waitHandle = new ManualResetEvent(false); - Exception exception; - var result = session.TryWait(waitHandle, Session.InfiniteTimeSpan, out exception); + var result = session.TryWait(waitHandle, Session.InfiniteTimeSpan, out var exception); Assert.AreEqual(WaitResult.Disconnected, result); Assert.IsNull(exception); } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.Tests/Classes/SessionTest_Connected_ServerSendsDisconnectMessageAndShutsDownSocket.cs b/src/Renci.SshNet.Tests/Classes/SessionTest_Connected_ServerSendsDisconnectMessageAndShutsDownSocket.cs index f3bb9877..b91832a4 100644 --- a/src/Renci.SshNet.Tests/Classes/SessionTest_Connected_ServerSendsDisconnectMessageAndShutsDownSocket.cs +++ b/src/Renci.SshNet.Tests/Classes/SessionTest_Connected_ServerSendsDisconnectMessageAndShutsDownSocket.cs @@ -1,9 +1,10 @@ -using System; -using System.Diagnostics; +using System.Diagnostics; using System.Globalization; using System.Net.Sockets; using System.Threading; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Renci.SshNet.Common; using Renci.SshNet.Messages.Transport; using Renci.SshNet.Tests.Common; @@ -26,7 +27,8 @@ namespace Renci.SshNet.Tests.Classes { // server sends SSH_MSG_DISCONNECT var disconnect = _disconnectMessage.GetPacket(8, null); - ServerSocket.Send(disconnect, 4, disconnect.Length - 4, SocketFlags.None); + + _ = ServerSocket.Send(disconnect, 4, disconnect.Length - 4, SocketFlags.None); // server shuts down the socket ServerSocket.Shutdown(SocketShutdown.Send); @@ -124,7 +126,7 @@ namespace Renci.SshNet.Tests.Classes Assert.IsTrue(session.MessageListenerCompleted.WaitOne()); } - [TestMethodAttribute] + [TestMethod] public void ISession_SendMessageShouldThrowSshConnectionException() { var session = (ISession) Session; @@ -191,12 +193,11 @@ namespace Renci.SshNet.Tests.Classes { var session = (ISession) Session; var waitHandle = new ManualResetEvent(false); - Exception exception; - var result = session.TryWait(waitHandle, Session.InfiniteTimeSpan, out exception); + var result = session.TryWait(waitHandle, Session.InfiniteTimeSpan, out var exception); Assert.AreEqual(WaitResult.Disconnected, result); Assert.IsNull(exception); } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.Tests/Classes/SessionTest_Connected_ServerSendsUnsupportedMessageType.cs b/src/Renci.SshNet.Tests/Classes/SessionTest_Connected_ServerSendsUnsupportedMessageType.cs index a40f6e03..a748eae4 100644 --- a/src/Renci.SshNet.Tests/Classes/SessionTest_Connected_ServerSendsUnsupportedMessageType.cs +++ b/src/Renci.SshNet.Tests/Classes/SessionTest_Connected_ServerSendsUnsupportedMessageType.cs @@ -1,8 +1,9 @@ -using System; -using System.Diagnostics; +using System.Diagnostics; using System.Net.Sockets; using System.Threading; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Renci.SshNet.Common; using Renci.SshNet.Messages.Transport; using Renci.SshNet.Tests.Common; @@ -28,7 +29,7 @@ namespace Renci.SshNet.Tests.Classes protected override void Act() { - ServerSocket.Send(_packet, 0, _packet.Length, SocketFlags.None); + _ = ServerSocket.Send(_packet, 0, _packet.Length, SocketFlags.None); // give session some time to process packet Thread.Sleep(200); @@ -101,7 +102,7 @@ namespace Renci.SshNet.Tests.Classes ServerSocket.ReceiveTimeout = 500; try { - ServerSocket.Receive(buffer, 0, buffer.Length, SocketFlags.None); + _ = ServerSocket.Receive(buffer, 0, buffer.Length, SocketFlags.None); Assert.Fail(); } catch (SocketException ex) @@ -134,7 +135,7 @@ namespace Renci.SshNet.Tests.Classes Assert.IsTrue(session.MessageListenerCompleted.WaitOne()); } - [TestMethodAttribute] + [TestMethod] public void ISession_SendMessageShouldSendMessageToServer() { var session = (ISession) Session; @@ -204,9 +205,8 @@ namespace Renci.SshNet.Tests.Classes { var session = (ISession) Session; var waitHandle = new ManualResetEvent(false); - Exception exception; - var result = session.TryWait(waitHandle, Session.InfiniteTimeSpan, out exception); + var result = session.TryWait(waitHandle, Session.InfiniteTimeSpan, out var exception); Assert.AreEqual(WaitResult.Failed, result); Assert.IsNotNull(exception); @@ -234,4 +234,4 @@ namespace Renci.SshNet.Tests.Classes return sshDataStream.ToArray(); } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.Tests/Classes/SessionTest_Connected_ServerShutsDownSendAfterSendingIncompletePacket.cs b/src/Renci.SshNet.Tests/Classes/SessionTest_Connected_ServerShutsDownSendAfterSendingIncompletePacket.cs index 5fbfb8a2..23db9166 100644 --- a/src/Renci.SshNet.Tests/Classes/SessionTest_Connected_ServerShutsDownSendAfterSendingIncompletePacket.cs +++ b/src/Renci.SshNet.Tests/Classes/SessionTest_Connected_ServerShutsDownSendAfterSendingIncompletePacket.cs @@ -1,8 +1,9 @@ -using System; -using System.Diagnostics; +using System.Diagnostics; using System.Net.Sockets; using System.Threading; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Renci.SshNet.Common; using Renci.SshNet.Messages.Transport; using Renci.SshNet.Tests.Common; @@ -15,7 +16,8 @@ namespace Renci.SshNet.Tests.Classes protected override void Act() { var incompletePacket = new byte[] {0x0a, 0x05, 0x05}; - ServerSocket.Send(incompletePacket, 0, incompletePacket.Length, SocketFlags.None); + + _ = ServerSocket.Send(incompletePacket, 0, incompletePacket.Length, SocketFlags.None); // give session some time to start reading packet Thread.Sleep(100); @@ -199,12 +201,11 @@ namespace Renci.SshNet.Tests.Classes { var session = (ISession) Session; var waitHandle = new ManualResetEvent(false); - Exception exception; - var result = session.TryWait(waitHandle, Session.InfiniteTimeSpan, out exception); + var result = session.TryWait(waitHandle, Session.InfiniteTimeSpan, out var exception); Assert.AreEqual(WaitResult.Disconnected, result); Assert.IsNull(exception); } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.Tests/Classes/SessionTest_Connected_ServerShutsDownSocket.cs b/src/Renci.SshNet.Tests/Classes/SessionTest_Connected_ServerShutsDownSocket.cs index 02d1f4c7..00a8cd54 100644 --- a/src/Renci.SshNet.Tests/Classes/SessionTest_Connected_ServerShutsDownSocket.cs +++ b/src/Renci.SshNet.Tests/Classes/SessionTest_Connected_ServerShutsDownSocket.cs @@ -114,7 +114,7 @@ namespace Renci.SshNet.Tests.Classes Assert.IsTrue(session.MessageListenerCompleted.WaitOne()); } - [TestMethodAttribute] + [TestMethod] public void ISession_SendMessageShouldThrowSshConnectionException() { var session = (ISession) Session; @@ -197,7 +197,7 @@ namespace Renci.SshNet.Tests.Classes try { - session.TryWait(waitHandle, timeout); + _ = session.TryWait(waitHandle, timeout); Assert.Fail(); } catch (ArgumentNullException ex) @@ -212,9 +212,8 @@ namespace Renci.SshNet.Tests.Classes { var session = (ISession) Session; var waitHandle = new ManualResetEvent(false); - Exception exception; - var result = session.TryWait(waitHandle, Session.InfiniteTimeSpan, out exception); + var result = session.TryWait(waitHandle, Session.InfiniteTimeSpan, out var exception); Assert.AreEqual(WaitResult.Disconnected, result); Assert.IsNull(exception); @@ -230,7 +229,7 @@ namespace Renci.SshNet.Tests.Classes try { - session.TryWait(waitHandle, timeout, out exception); + _ = session.TryWait(waitHandle, timeout, out exception); Assert.Fail(); } catch (ArgumentNullException ex) @@ -242,4 +241,4 @@ namespace Renci.SshNet.Tests.Classes Assert.IsNull(exception); } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.Tests/Classes/SessionTest_NotConnected.cs b/src/Renci.SshNet.Tests/Classes/SessionTest_NotConnected.cs index 97a49a0e..04491fbc 100644 --- a/src/Renci.SshNet.Tests/Classes/SessionTest_NotConnected.cs +++ b/src/Renci.SshNet.Tests/Classes/SessionTest_NotConnected.cs @@ -1,12 +1,11 @@ using System; using System.Net; using System.Threading; + using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; + using Renci.SshNet.Common; -using Renci.SshNet.Connection; using Renci.SshNet.Messages.Transport; -using Renci.SshNet.Tests.Common; namespace Renci.SshNet.Tests.Classes { @@ -24,7 +23,7 @@ namespace Renci.SshNet.Tests.Classes protected override void Act() { - _session = new Session(_connectionInfo, _serviceFactoryMock.Object, _socketFactoryMock.Object); + _session = new Session(_connectionInfo, ServiceFactoryMock.Object, SocketFactoryMock.Object); } [TestMethod] @@ -136,9 +135,8 @@ namespace Renci.SshNet.Tests.Classes { var session = (ISession) _session; var waitHandle = new ManualResetEvent(false); - Exception exception; - var result = session.TryWait(waitHandle, Session.InfiniteTimeSpan, out exception); + var result = session.TryWait(waitHandle, Session.InfiniteTimeSpan, out var exception); Assert.AreEqual(WaitResult.Disconnected, result); Assert.IsNull(exception); @@ -226,13 +224,10 @@ namespace Renci.SshNet.Tests.Classes private static ConnectionInfo CreateConnectionInfo(IPEndPoint serverEndPoint, TimeSpan timeout) { - var connectionInfo = new ConnectionInfo( - serverEndPoint.Address.ToString(), - serverEndPoint.Port, - "eric", - new NoneAuthenticationMethod("eric")); - connectionInfo.Timeout = timeout; - return connectionInfo; + return new ConnectionInfo(serverEndPoint.Address.ToString(), serverEndPoint.Port, "eric", new NoneAuthenticationMethod("eric")) + { + Timeout = timeout + }; } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.Tests/Classes/SessionTest_SocketConnected_BadPacketAndDispose.cs b/src/Renci.SshNet.Tests/Classes/SessionTest_SocketConnected_BadPacketAndDispose.cs index f5fb4d79..a077c774 100644 --- a/src/Renci.SshNet.Tests/Classes/SessionTest_SocketConnected_BadPacketAndDispose.cs +++ b/src/Renci.SshNet.Tests/Classes/SessionTest_SocketConnected_BadPacketAndDispose.cs @@ -1,8 +1,11 @@ using System; using System.Net; using System.Net.Sockets; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; + using Renci.SshNet.Common; using Renci.SshNet.Connection; using Renci.SshNet.Messages.Transport; @@ -36,10 +39,7 @@ namespace Renci.SshNet.Tests.Classes [TestCleanup] public void TearDown() { - if (_serverListener != null) - { - _serverListener.Dispose(); - } + _serverListener?.Dispose(); } protected void CreateMocks() @@ -53,12 +53,10 @@ namespace Renci.SshNet.Tests.Classes protected void SetupData() { _serverEndPoint = new IPEndPoint(IPAddress.Loopback, 8122); - _connectionInfo = new ConnectionInfo( - _serverEndPoint.Address.ToString(), - _serverEndPoint.Port, - "user", - new PasswordAuthenticationMethod("user", "password")); - _connectionInfo.Timeout = TimeSpan.FromMilliseconds(200); + _connectionInfo = new ConnectionInfo(_serverEndPoint.Address.ToString(), _serverEndPoint.Port, "user", new PasswordAuthenticationMethod("user", "password")) + { + Timeout = TimeSpan.FromMilliseconds(200) + }; _actualException = null; _socketFactory = new SocketFactory(); @@ -71,7 +69,9 @@ namespace Renci.SshNet.Tests.Classes // packet upon establishing the connection var badPacket = new byte[] { 0x0a, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05 }; - _serverSocket.Send(badPacket, 0, badPacket.Length, SocketFlags.None); + + _ = _serverSocket.Send(badPacket, 0, badPacket.Length, SocketFlags.None); + _serverSocket.Shutdown(SocketShutdown.Send); }; _serverListener.Start(); @@ -83,14 +83,14 @@ namespace Renci.SshNet.Tests.Classes protected void SetupMocks() { - _serviceFactoryMock.Setup(p => p.CreateConnector(_connectionInfo, _socketFactoryMock.Object)) - .Returns(_connectorMock.Object); - _connectorMock.Setup(p => p.Connect(_connectionInfo)) - .Returns(_clientSocket); - _serviceFactoryMock.Setup(p => p.CreateProtocolVersionExchange()) - .Returns(_protocolVersionExchangeMock.Object); - _protocolVersionExchangeMock.Setup(p => p.Start(_session.ClientVersion, _clientSocket, _connectionInfo.Timeout)) - .Returns(new SshIdentification("2.0", "XXX")); + _ = _serviceFactoryMock.Setup(p => p.CreateConnector(_connectionInfo, _socketFactoryMock.Object)) + .Returns(_connectorMock.Object); + _ = _connectorMock.Setup(p => p.Connect(_connectionInfo)) + .Returns(_clientSocket); + _ = _serviceFactoryMock.Setup(p => p.CreateProtocolVersionExchange()) + .Returns(_protocolVersionExchangeMock.Object); + _ = _protocolVersionExchangeMock.Setup(p => p.Start(_session.ClientVersion, _clientSocket, _connectionInfo.Timeout)) + .Returns(new SshIdentification("2.0", "XXX")); } protected void Arrange() @@ -104,10 +104,8 @@ namespace Renci.SshNet.Tests.Classes { try { - - { - _session.Connect(); - } + _session.Connect(); + Assert.Fail(); } catch (SshConnectionException ex) { diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/Requests/SftpRmDirRequestTest.cs b/src/Renci.SshNet.Tests/Classes/Sftp/Requests/SftpRmDirRequestTest.cs index bd7f12c3..f171a127 100644 --- a/src/Renci.SshNet.Tests/Classes/Sftp/Requests/SftpRmDirRequestTest.cs +++ b/src/Renci.SshNet.Tests/Classes/Sftp/Requests/SftpRmDirRequestTest.cs @@ -3,12 +3,13 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Renci.SshNet.Common; using Renci.SshNet.Sftp; using Renci.SshNet.Sftp.Requests; using Renci.SshNet.Sftp.Responses; -using Renci.SshNet.Tests.Common; namespace Renci.SshNet.Tests.Classes.Sftp.Requests { @@ -87,10 +88,10 @@ namespace Renci.SshNet.Tests.Classes.Sftp.Requests Assert.AreEqual((uint) _pathBytes.Length, sshDataStream.ReadUInt32()); var actualPath = new byte[_pathBytes.Length]; - sshDataStream.Read(actualPath, 0, actualPath.Length); + _ = sshDataStream.Read(actualPath, 0, actualPath.Length); Assert.IsTrue(_pathBytes.SequenceEqual(actualPath)); Assert.IsTrue(sshDataStream.IsEndOfData); } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/Requests/SftpUnblockRequestTest.cs b/src/Renci.SshNet.Tests/Classes/Sftp/Requests/SftpUnblockRequestTest.cs index 40ea7c3d..aabd4d85 100644 --- a/src/Renci.SshNet.Tests/Classes/Sftp/Requests/SftpUnblockRequestTest.cs +++ b/src/Renci.SshNet.Tests/Classes/Sftp/Requests/SftpUnblockRequestTest.cs @@ -1,12 +1,13 @@ using System; using System.Collections.Generic; using System.Linq; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Renci.SshNet.Common; using Renci.SshNet.Sftp; using Renci.SshNet.Sftp.Requests; using Renci.SshNet.Sftp.Responses; -using Renci.SshNet.Tests.Common; namespace Renci.SshNet.Tests.Classes.Sftp.Requests { @@ -84,7 +85,7 @@ namespace Renci.SshNet.Tests.Classes.Sftp.Requests Assert.AreEqual((uint) _handle.Length, sshDataStream.ReadUInt32()); var actualHandle = new byte[_handle.Length]; - sshDataStream.Read(actualHandle, 0, actualHandle.Length); + _ = sshDataStream.Read(actualHandle, 0, actualHandle.Length); Assert.IsTrue(_handle.SequenceEqual(actualHandle)); Assert.AreEqual(_offset, sshDataStream.ReadUInt64()); diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/Responses/ExtendedReplies/StatVfsReplyInfoTest.cs b/src/Renci.SshNet.Tests/Classes/Sftp/Responses/ExtendedReplies/StatVfsReplyInfoTest.cs index d2b4f6c8..4ea37565 100644 --- a/src/Renci.SshNet.Tests/Classes/Sftp/Responses/ExtendedReplies/StatVfsReplyInfoTest.cs +++ b/src/Renci.SshNet.Tests/Classes/Sftp/Responses/ExtendedReplies/StatVfsReplyInfoTest.cs @@ -1,5 +1,7 @@ using System; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Renci.SshNet.Common; using Renci.SshNet.Sftp; using Renci.SshNet.Sftp.Responses; @@ -88,4 +90,4 @@ namespace Renci.SshNet.Tests.Classes.Sftp.Responses Assert.AreEqual(_files, information.TotalNodes); } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/Responses/SftpExtendedReplyResponseTest.cs b/src/Renci.SshNet.Tests/Classes/Sftp/Responses/SftpExtendedReplyResponseTest.cs index 6596459b..ff5f9ac3 100644 --- a/src/Renci.SshNet.Tests/Classes/Sftp/Responses/SftpExtendedReplyResponseTest.cs +++ b/src/Renci.SshNet.Tests/Classes/Sftp/Responses/SftpExtendedReplyResponseTest.cs @@ -1,5 +1,7 @@ using System; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Renci.SshNet.Common; using Renci.SshNet.Sftp; using Renci.SshNet.Sftp.Responses; @@ -60,8 +62,10 @@ namespace Renci.SshNet.Tests.Classes.Sftp.Responses var sid = (ulong) _random.Next(0, int.MaxValue); var namemax = (ulong) _random.Next(0, int.MaxValue); - var sshDataStream = new SshDataStream(4 + 1 + 4 + 88); - sshDataStream.Position = 4; // skip 4 bytes for SSH packet length + var sshDataStream = new SshDataStream(4 + 1 + 4 + 88) + { + Position = 4 // skip 4 bytes for SSH packet length + }; sshDataStream.WriteByte((byte)SftpMessageTypes.Attrs); sshDataStream.Write(_responseId); sshDataStream.Write(bsize); @@ -100,4 +104,4 @@ namespace Renci.SshNet.Tests.Classes.Sftp.Responses Assert.AreEqual(files, information.TotalNodes); } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileReaderTestBase.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileReaderTestBase.cs index 2f2f1bbf..0e952d33 100644 --- a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileReaderTestBase.cs +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileReaderTestBase.cs @@ -38,8 +38,8 @@ namespace Renci.SshNet.Tests.Classes.Sftp protected static SftpFileAttributes CreateSftpFileAttributes(long size) { - var utcDefault = DateTime.SpecifyKind(default(DateTime), DateTimeKind.Utc); - return new SftpFileAttributes(utcDefault, utcDefault, size, default(int), default(int), default(uint), null); + var utcDefault = DateTime.SpecifyKind(default, DateTimeKind.Utc); + return new SftpFileAttributes(utcDefault, utcDefault, size, default, default, default, null); } protected static byte[] CreateByteArray(Random random, int length) @@ -54,7 +54,10 @@ namespace Renci.SshNet.Tests.Classes.Sftp var result = WaitHandle.WaitAny(waitHandles, millisecondsTimeout); if (result == WaitHandle.WaitTimeout) + { throw new SshOperationTimeoutException(); + } + return result; } } diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileReaderTest_DisposeShouldUnblockReadAndReadAhead.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileReaderTest_DisposeShouldUnblockReadAndReadAhead.cs index 2e90f9ce..b49de6e3 100644 --- a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileReaderTest_DisposeShouldUnblockReadAndReadAhead.cs +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileReaderTest_DisposeShouldUnblockReadAndReadAhead.cs @@ -1,13 +1,14 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; -#if !FEATURE_EVENTWAITHANDLE_DISPOSE -using Renci.SshNet.Common; -#endif // !FEATURE_EVENTWAITHANDLE_DISPOSE -using Renci.SshNet.Abstractions; -using Renci.SshNet.Sftp; -using System; +using System; using System.Diagnostics; using System.Threading; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Moq; + +using Renci.SshNet.Abstractions; +using Renci.SshNet.Sftp; + using BufferedRead = Renci.SshNet.Sftp.SftpFileReader.BufferedRead; namespace Renci.SshNet.Tests.Classes.Sftp @@ -31,10 +32,7 @@ namespace Renci.SshNet.Tests.Classes.Sftp [TestCleanup] public void TearDown() { - if (_disposeCompleted != null) - { - _disposeCompleted.Dispose(); - } + _disposeCompleted?.Dispose(); } protected override void SetupData() @@ -54,39 +52,41 @@ namespace Renci.SshNet.Tests.Classes.Sftp { _seq = new MockSequence(); - SftpSessionMock.InSequence(_seq) - .Setup(p => p.CreateWaitHandleArray(It.IsNotNull(), It.IsNotNull())) - .Returns((disposingWaitHandle, semaphoreAvailableWaitHandle) => - { - _waitHandleArray[0] = disposingWaitHandle; - _waitHandleArray[1] = semaphoreAvailableWaitHandle; - return _waitHandleArray; - }); - SftpSessionMock.InSequence(_seq) - .Setup(p => p.OperationTimeout) - .Returns(_operationTimeout); - SftpSessionMock.InSequence(_seq) - .Setup(p => p.WaitAny(_waitHandleArray, _operationTimeout)) - .Returns(() => WaitAny(_waitHandleArray, _operationTimeout)); - SftpSessionMock.InSequence(_seq) - .Setup(p => p.BeginRead(_handle, 0, ChunkLength, It.IsNotNull(), It.IsAny())) - .Returns((handle, offset, length, callback, state) => - { - _readAsyncCallback = callback; - return null; - }); - SftpSessionMock.InSequence(_seq).Setup(p => p.OperationTimeout).Returns(_operationTimeout); - SftpSessionMock.InSequence(_seq) - .Setup(p => p.WaitAny(_waitHandleArray, _operationTimeout)) - .Returns(() => WaitAny(_waitHandleArray, _operationTimeout)); - SftpSessionMock.InSequence(_seq) - .Setup(p => p.IsOpen) - .Returns(true); - SftpSessionMock.InSequence(_seq) - .Setup(p => p.BeginClose(_handle, null, null)) - .Returns(_closeAsyncResult); - SftpSessionMock.InSequence(_seq) - .Setup(p => p.EndClose(_closeAsyncResult)); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.CreateWaitHandleArray(It.IsNotNull(), It.IsNotNull())) + .Returns((disposingWaitHandle, semaphoreAvailableWaitHandle) => + { + _waitHandleArray[0] = disposingWaitHandle; + _waitHandleArray[1] = semaphoreAvailableWaitHandle; + return _waitHandleArray; + }); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.OperationTimeout) + .Returns(_operationTimeout); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.WaitAny(_waitHandleArray, _operationTimeout)) + .Returns(() => WaitAny(_waitHandleArray, _operationTimeout)); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.BeginRead(_handle, 0, ChunkLength, It.IsNotNull(), It.IsAny())) + .Returns((handle, offset, length, callback, state) => + { + _readAsyncCallback = callback; + return null; + }); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.OperationTimeout) + .Returns(_operationTimeout); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.WaitAny(_waitHandleArray, _operationTimeout)) + .Returns(() => WaitAny(_waitHandleArray, _operationTimeout)); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.IsOpen) + .Returns(true); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.BeginClose(_handle, null, null)) + .Returns(_closeAsyncResult); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.EndClose(_closeAsyncResult)); } protected override void Arrange() @@ -99,15 +99,15 @@ namespace Renci.SshNet.Tests.Classes.Sftp protected override void Act() { ThreadAbstraction.ExecuteThread(() => - { - Thread.Sleep(500); - _reader.Dispose(); - _disposeCompleted.Set(); - }); + { + Thread.Sleep(500); + _reader.Dispose(); + _ = _disposeCompleted.Set(); + }); try { - _reader.Read(); + _ = _reader.Read(); Assert.Fail(); } catch (ObjectDisposedException ex) @@ -117,7 +117,7 @@ namespace Renci.SshNet.Tests.Classes.Sftp // Dispose may unblock Read() before the dispose has fully completed, so // let's wait until it has completed - _disposeCompleted.WaitOne(500); + _ = _disposeCompleted.WaitOne(500); } [TestMethod] @@ -132,7 +132,7 @@ namespace Renci.SshNet.Tests.Classes.Sftp { try { - _reader.Read(); + _ = _reader.Read(); Assert.Fail(); } catch (ObjectDisposedException ex) diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileReaderTest_Dispose_SftpSessionIsNotOpen.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileReaderTest_Dispose_SftpSessionIsNotOpen.cs index 7c2d87ce..6b8612ab 100644 --- a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileReaderTest_Dispose_SftpSessionIsNotOpen.cs +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileReaderTest_Dispose_SftpSessionIsNotOpen.cs @@ -1,11 +1,12 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; -#if !FEATURE_EVENTWAITHANDLE_DISPOSE -using Renci.SshNet.Common; -#endif // !FEATURE_EVENTWAITHANDLE_DISPOSE -using Renci.SshNet.Sftp; -using System; +using System; using System.Threading; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Moq; + +using Renci.SshNet.Sftp; + using BufferedRead = Renci.SshNet.Sftp.SftpFileReader.BufferedRead; namespace Renci.SshNet.Tests.Classes.Sftp @@ -28,15 +29,8 @@ namespace Renci.SshNet.Tests.Classes.Sftp [TestCleanup] public void TearDown() { - if (_beginReadInvoked != null) - { - _beginReadInvoked.Dispose(); - } - - if (_disposeCompleted != null) - { - _disposeCompleted.Dispose(); - } + _beginReadInvoked?.Dispose(); + _disposeCompleted?.Dispose(); } protected override void SetupData() @@ -56,41 +50,41 @@ namespace Renci.SshNet.Tests.Classes.Sftp { _seq = new MockSequence(); - SftpSessionMock.InSequence(_seq) - .Setup(p => p.CreateWaitHandleArray(It.IsNotNull(), It.IsNotNull())) - .Returns((disposingWaitHandle, semaphoreAvailableWaitHandle) => - { - _waitHandleArray[0] = disposingWaitHandle; - _waitHandleArray[1] = semaphoreAvailableWaitHandle; - return _waitHandleArray; - }); - SftpSessionMock.InSequence(_seq) - .Setup(p => p.OperationTimeout) - .Returns(_operationTimeout); - SftpSessionMock.InSequence(_seq) - .Setup(p => p.WaitAny(_waitHandleArray, _operationTimeout)) - .Returns(() => WaitAny(_waitHandleArray, _operationTimeout)); - SftpSessionMock.InSequence(_seq) - .Setup(p => p.BeginRead(_handle, 0, ChunkLength, It.IsNotNull(), It.IsAny())) - .Callback(() => - { - // harden test by making sure that we've invoked BeginRead before Dispose is invoked - _beginReadInvoked.Set(); - }) - .Returns((handle, offset, length, callback, state) => - { - _readAsyncCallback = callback; - return null; - }) - .Callback(() => - { - // wait until Dispose has been invoked on reader to allow us to harden test, and - // verify whether Dispose will prevent us from entering the read-ahead loop again - _waitHandleArray[0].WaitOne(); - }); - SftpSessionMock.InSequence(_seq) - .Setup(p => p.IsOpen) - .Returns(false); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.CreateWaitHandleArray(It.IsNotNull(), It.IsNotNull())) + .Returns((disposingWaitHandle, semaphoreAvailableWaitHandle) => + { + _waitHandleArray[0] = disposingWaitHandle; + _waitHandleArray[1] = semaphoreAvailableWaitHandle; + return _waitHandleArray; + }); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.OperationTimeout) + .Returns(_operationTimeout); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.WaitAny(_waitHandleArray, _operationTimeout)) + .Returns(() => WaitAny(_waitHandleArray, _operationTimeout)); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.BeginRead(_handle, 0, ChunkLength, It.IsNotNull(), It.IsAny())) + .Callback(() => + { + // harden test by making sure that we've invoked BeginRead before Dispose is invoked + _ = _beginReadInvoked.Set(); + }) + .Returns((handle, offset, length, callback, state) => + { + _readAsyncCallback = callback; + return null; + }) + .Callback(() => + { + // wait until Dispose has been invoked on reader to allow us to harden test, and + // verify whether Dispose will prevent us from entering the read-ahead loop again + _ = _waitHandleArray[0].WaitOne(); + }); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.IsOpen) + .Returns(false); } protected override void Arrange() @@ -111,7 +105,7 @@ namespace Renci.SshNet.Tests.Classes.Sftp { try { - _reader.Read(); + _ = _reader.Read(); Assert.Fail(); } catch (ObjectDisposedException ex) diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileReaderTest_Dispose_SftpSessionIsOpen_BeginCloseThrowsException.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileReaderTest_Dispose_SftpSessionIsOpen_BeginCloseThrowsException.cs index 015aba53..a678dfff 100644 --- a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileReaderTest_Dispose_SftpSessionIsOpen_BeginCloseThrowsException.cs +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileReaderTest_Dispose_SftpSessionIsOpen_BeginCloseThrowsException.cs @@ -1,11 +1,13 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; -#if !FEATURE_EVENTWAITHANDLE_DISPOSE -using Renci.SshNet.Common; -#endif // !FEATURE_EVENTWAITHANDLE_DISPOSE -using Renci.SshNet.Sftp; -using System; +using System; using System.Threading; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Moq; + +using Renci.SshNet.Common; +using Renci.SshNet.Sftp; + using BufferedRead = Renci.SshNet.Sftp.SftpFileReader.BufferedRead; namespace Renci.SshNet.Tests.Classes.Sftp @@ -28,15 +30,8 @@ namespace Renci.SshNet.Tests.Classes.Sftp [TestCleanup] public void TearDown() { - if (_beginReadInvoked != null) - { - _beginReadInvoked.Dispose(); - } - - if (_disposeCompleted != null) - { - _disposeCompleted.Dispose(); - } + _beginReadInvoked?.Dispose(); + _disposeCompleted?.Dispose(); } protected override void SetupData() @@ -56,44 +51,44 @@ namespace Renci.SshNet.Tests.Classes.Sftp { _seq = new MockSequence(); - SftpSessionMock.InSequence(_seq) - .Setup(p => p.CreateWaitHandleArray(It.IsNotNull(), It.IsNotNull())) - .Returns((disposingWaitHandle, semaphoreAvailableWaitHandle) => - { - _waitHandleArray[0] = disposingWaitHandle; - _waitHandleArray[1] = semaphoreAvailableWaitHandle; - return _waitHandleArray; - }); - SftpSessionMock.InSequence(_seq) - .Setup(p => p.OperationTimeout) - .Returns(_operationTimeout); - SftpSessionMock.InSequence(_seq) - .Setup(p => p.WaitAny(_waitHandleArray, _operationTimeout)) - .Returns(() => WaitAny(_waitHandleArray, _operationTimeout)); - SftpSessionMock.InSequence(_seq) - .Setup(p => p.BeginRead(_handle, 0, ChunkLength, It.IsNotNull(), It.IsAny())) - .Callback(() => - { - // harden test by making sure that we've invoked BeginRead before Dispose is invoked - _beginReadInvoked.Set(); - }) - .Returns((handle, offset, length, callback, state) => - { - _readAsyncCallback = callback; - return null; - }) - .Callback(() => - { - // wait until Dispose has been invoked on reader to allow us to harden test, and - // verify whether Dispose will prevent us from entering the read-ahead loop again - _waitHandleArray[0].WaitOne(); - }); - SftpSessionMock.InSequence(_seq) - .Setup(p => p.IsOpen) - .Returns(true); - SftpSessionMock.InSequence(_seq) - .Setup(p => p.BeginClose(_handle, null, null)) - .Throws(new SshException()); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.CreateWaitHandleArray(It.IsNotNull(), It.IsNotNull())) + .Returns((disposingWaitHandle, semaphoreAvailableWaitHandle) => + { + _waitHandleArray[0] = disposingWaitHandle; + _waitHandleArray[1] = semaphoreAvailableWaitHandle; + return _waitHandleArray; + }); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.OperationTimeout) + .Returns(_operationTimeout); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.WaitAny(_waitHandleArray, _operationTimeout)) + .Returns(() => WaitAny(_waitHandleArray, _operationTimeout)); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.BeginRead(_handle, 0, ChunkLength, It.IsNotNull(), It.IsAny())) + .Callback(() => + { + // harden test by making sure that we've invoked BeginRead before Dispose is invoked + _ = _beginReadInvoked.Set(); + }) + .Returns((handle, offset, length, callback, state) => + { + _readAsyncCallback = callback; + return null; + }) + .Callback(() => + { + // wait until Dispose has been invoked on reader to allow us to harden test, and + // verify whether Dispose will prevent us from entering the read-ahead loop again + _ = _waitHandleArray[0].WaitOne(); + }); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.IsOpen) + .Returns(true); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.BeginClose(_handle, null, null)) + .Throws(new SshException()); } protected override void Arrange() @@ -114,7 +109,7 @@ namespace Renci.SshNet.Tests.Classes.Sftp { try { - _reader.Read(); + _ = _reader.Read(); Assert.Fail(); } catch (ObjectDisposedException ex) diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileReaderTest_Dispose_SftpSessionIsOpen_EndCloseThrowsException.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileReaderTest_Dispose_SftpSessionIsOpen_EndCloseThrowsException.cs index 4a175648..6f6002e3 100644 --- a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileReaderTest_Dispose_SftpSessionIsOpen_EndCloseThrowsException.cs +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileReaderTest_Dispose_SftpSessionIsOpen_EndCloseThrowsException.cs @@ -1,11 +1,15 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Threading; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; + #if !FEATURE_EVENTWAITHANDLE_DISPOSE using Renci.SshNet.Common; #endif // !FEATURE_EVENTWAITHANDLE_DISPOSE using Renci.SshNet.Sftp; -using System; -using System.Threading; + using BufferedRead = Renci.SshNet.Sftp.SftpFileReader.BufferedRead; namespace Renci.SshNet.Tests.Classes.Sftp @@ -29,15 +33,8 @@ namespace Renci.SshNet.Tests.Classes.Sftp [TestCleanup] public void TearDown() { - if (_beginReadInvoked != null) - { - _beginReadInvoked.Dispose(); - } - - if (_disposeCompleted != null) - { - _disposeCompleted.Dispose(); - } + _beginReadInvoked?.Dispose(); + _disposeCompleted?.Dispose(); } protected override void SetupData() @@ -58,47 +55,47 @@ namespace Renci.SshNet.Tests.Classes.Sftp { _seq = new MockSequence(); - SftpSessionMock.InSequence(_seq) - .Setup(p => p.CreateWaitHandleArray(It.IsNotNull(), It.IsNotNull())) - .Returns((disposingWaitHandle, semaphoreAvailableWaitHandle) => - { - _waitHandleArray[0] = disposingWaitHandle; - _waitHandleArray[1] = semaphoreAvailableWaitHandle; - return _waitHandleArray; - }); - SftpSessionMock.InSequence(_seq) - .Setup(p => p.OperationTimeout) - .Returns(_operationTimeout); - SftpSessionMock.InSequence(_seq) - .Setup(p => p.WaitAny(_waitHandleArray, _operationTimeout)) - .Returns(() => WaitAny(_waitHandleArray, _operationTimeout)); - SftpSessionMock.InSequence(_seq) - .Setup(p => p.BeginRead(_handle, 0, ChunkLength, It.IsNotNull(), It.IsAny())) - .Callback(() => - { - // harden test by making sure that we've invoked BeginRead before Dispose is invoked - _beginReadInvoked.Set(); - }) - .Returns((handle, offset, length, callback, state) => - { - _readAsyncCallback = callback; - return null; - }) - .Callback(() => - { - // wait until Dispose has been invoked on reader to allow us to harden test, and - // verify whether Dispose will prevent us from entering the read-ahead loop again - _waitHandleArray[0].WaitOne(); - }); - SftpSessionMock.InSequence(_seq) - .Setup(p => p.IsOpen) - .Returns(true); - SftpSessionMock.InSequence(_seq) - .Setup(p => p.BeginClose(_handle, null, null)) - .Returns(_closeAsyncResult); - SftpSessionMock.InSequence(_seq) - .Setup(p => p.EndClose(_closeAsyncResult)) - .Throws(new SshException()); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.CreateWaitHandleArray(It.IsNotNull(), It.IsNotNull())) + .Returns((disposingWaitHandle, semaphoreAvailableWaitHandle) => + { + _waitHandleArray[0] = disposingWaitHandle; + _waitHandleArray[1] = semaphoreAvailableWaitHandle; + return _waitHandleArray; + }); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.OperationTimeout) + .Returns(_operationTimeout); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.WaitAny(_waitHandleArray, _operationTimeout)) + .Returns(() => WaitAny(_waitHandleArray, _operationTimeout)); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.BeginRead(_handle, 0, ChunkLength, It.IsNotNull(), It.IsAny())) + .Callback(() => + { + // harden test by making sure that we've invoked BeginRead before Dispose is invoked + _ = _beginReadInvoked.Set(); + }) + .Returns((handle, offset, length, callback, state) => + { + _readAsyncCallback = callback; + return null; + }) + .Callback(() => + { + // wait until Dispose has been invoked on reader to allow us to harden test, and + // verify whether Dispose will prevent us from entering the read-ahead loop again + _ = _waitHandleArray[0].WaitOne(); + }); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.IsOpen) + .Returns(true); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.BeginClose(_handle, null, null)) + .Returns(_closeAsyncResult); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.EndClose(_closeAsyncResult)) + .Throws(new SshException()); } protected override void Arrange() @@ -119,7 +116,7 @@ namespace Renci.SshNet.Tests.Classes.Sftp { try { - _reader.Read(); + _ = _reader.Read(); Assert.Fail(); } catch (ObjectDisposedException ex) diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileReaderTest_PreviousChunkIsIncompleteAndEofIsNotReached.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileReaderTest_PreviousChunkIsIncompleteAndEofIsNotReached.cs index 8c38a000..cedcff5c 100644 --- a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileReaderTest_PreviousChunkIsIncompleteAndEofIsNotReached.cs +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileReaderTest_PreviousChunkIsIncompleteAndEofIsNotReached.cs @@ -75,99 +75,111 @@ namespace Renci.SshNet.Tests.Classes.Sftp { _seq = new MockSequence(); - SftpSessionMock.InSequence(_seq) - .Setup(p => p.CreateWaitHandleArray(It.IsNotNull(), It.IsNotNull())) - .Returns((disposingWaitHandle, semaphoreAvailableWaitHandle) => - { - _waitHandleArray[0] = disposingWaitHandle; - _waitHandleArray[1] = semaphoreAvailableWaitHandle; - return _waitHandleArray; - }); - SftpSessionMock.InSequence(_seq).Setup(p => p.OperationTimeout).Returns(_operationTimeout); - SftpSessionMock.InSequence(_seq) - .Setup(p => p.WaitAny(_waitHandleArray, _operationTimeout)) - .Returns(() => WaitAny(_waitHandleArray, _operationTimeout)); - SftpSessionMock.InSequence(_seq) - .Setup(p => p.BeginRead(_handle, 0, ChunkLength, It.IsNotNull(), It.IsAny())) - .Callback((handle, offset, length, callback, state) => - { - _chunk1BeginRead.Set(); - var asyncResult = new SftpReadAsyncResult(callback, state); - asyncResult.SetAsCompleted(_chunk1, false); - }) - .Returns((SftpReadAsyncResult)null); - SftpSessionMock.InSequence(_seq).Setup(p => p.OperationTimeout).Returns(_operationTimeout); - SftpSessionMock.InSequence(_seq) - .Setup(p => p.WaitAny(_waitHandleArray, _operationTimeout)) - .Returns(() => WaitAny(_waitHandleArray, _operationTimeout)); - SftpSessionMock.InSequence(_seq) - .Setup(p => p.BeginRead(_handle, ChunkLength, ChunkLength, It.IsNotNull(), It.IsAny())) - .Callback((handle, offset, length, callback, state) => - { - _chunk2BeginRead.Set(); - var asyncResult = new SftpReadAsyncResult(callback, state); - asyncResult.SetAsCompleted(_chunk2, false); - }) - .Returns((SftpReadAsyncResult)null); - SftpSessionMock.InSequence(_seq).Setup(p => p.OperationTimeout).Returns(_operationTimeout); - SftpSessionMock.InSequence(_seq) - .Setup(p => p.WaitAny(_waitHandleArray, _operationTimeout)) - .Returns(() => WaitAny(_waitHandleArray, _operationTimeout)); - SftpSessionMock.InSequence(_seq) - .Setup(p => p.BeginRead(_handle, 2 * ChunkLength, ChunkLength, It.IsNotNull(), It.IsAny())) - .Callback((handle, offset, length, callback, state) => - { - _chunk3BeginRead.Set(); - var asyncResult = new SftpReadAsyncResult(callback, state); - asyncResult.SetAsCompleted(_chunk3, false); - }) - .Returns((SftpReadAsyncResult)null); - SftpSessionMock.InSequence(_seq).Setup(p => p.OperationTimeout).Returns(_operationTimeout); - SftpSessionMock.InSequence(_seq) - .Setup(p => p.WaitAny(_waitHandleArray, _operationTimeout)) - .Returns(() => WaitAny(_waitHandleArray, _operationTimeout)); - SftpSessionMock.InSequence(_seq) - .Setup(p => p.BeginRead(_handle, 3 * ChunkLength, ChunkLength, It.IsNotNull(), It.IsAny())) - .Callback((handle, offset, length, callback, state) => - { - _chunk4BeginRead.Set(); - var asyncResult = new SftpReadAsyncResult(callback, state); - asyncResult.SetAsCompleted(_chunk4, false); - }) - .Returns((SftpReadAsyncResult)null); - SftpSessionMock.InSequence(_seq).Setup(p => p.OperationTimeout).Returns(_operationTimeout); - SftpSessionMock.InSequence(_seq) - .Setup(p => p.WaitAny(_waitHandleArray, _operationTimeout)) - .Returns(() => WaitAny(_waitHandleArray, _operationTimeout)); - SftpSessionMock.InSequence(_seq) - .Setup(p => p.BeginRead(_handle, 4 * ChunkLength, ChunkLength, It.IsNotNull(), It.IsAny())) - .Callback((handle, offset, length, callback, state) => - { - _chunk5BeginRead.Set(); - var asyncResult = new SftpReadAsyncResult(callback, state); - asyncResult.SetAsCompleted(_chunk5, false); - }) - .Returns((SftpReadAsyncResult)null); - SftpSessionMock.InSequence(_seq).Setup(p => p.OperationTimeout).Returns(_operationTimeout); - SftpSessionMock.InSequence(_seq) - .Setup(p => p.WaitAny(_waitHandleArray, _operationTimeout)) - .Callback(() => _waitBeforeChunk6.Set()) - .Returns(() => WaitAny(_waitHandleArray, _operationTimeout)); - SftpSessionMock.InSequence(_seq) - .Setup(p => p.RequestRead(_handle, 2 * ChunkLength - 17, 17)) - .Returns(_chunk2CatchUp1); - SftpSessionMock.InSequence(_seq) - .Setup(p => p.RequestRead(_handle, 2 * ChunkLength - 7, 7)) - .Returns(_chunk2CatchUp2); - SftpSessionMock.InSequence(_seq) - .Setup(p => p.BeginRead(_handle, 5 * ChunkLength, ChunkLength, It.IsNotNull(), It.IsAny())) - .Callback((handle, offset, length, callback, state) => - { - _chunk6BeginRead.Set(); - var asyncResult = new SftpReadAsyncResult(callback, state); - asyncResult.SetAsCompleted(_chunk6, false); - }) - .Returns((SftpReadAsyncResult)null); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.CreateWaitHandleArray(It.IsNotNull(), It.IsNotNull())) + .Returns((disposingWaitHandle, semaphoreAvailableWaitHandle) => + { + _waitHandleArray[0] = disposingWaitHandle; + _waitHandleArray[1] = semaphoreAvailableWaitHandle; + return _waitHandleArray; + }); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.OperationTimeout) + .Returns(_operationTimeout); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.WaitAny(_waitHandleArray, _operationTimeout)) + .Returns(() => WaitAny(_waitHandleArray, _operationTimeout)); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.BeginRead(_handle, 0, ChunkLength, It.IsNotNull(), It.IsAny())) + .Callback((handle, offset, length, callback, state) => + { + _ = _chunk1BeginRead.Set(); + var asyncResult = new SftpReadAsyncResult(callback, state); + asyncResult.SetAsCompleted(_chunk1, false); + }) + .Returns((SftpReadAsyncResult)null); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.OperationTimeout) + .Returns(_operationTimeout); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.WaitAny(_waitHandleArray, _operationTimeout)) + .Returns(() => WaitAny(_waitHandleArray, _operationTimeout)); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.BeginRead(_handle, ChunkLength, ChunkLength, It.IsNotNull(), It.IsAny())) + .Callback((handle, offset, length, callback, state) => + { + _ = _chunk2BeginRead.Set(); + var asyncResult = new SftpReadAsyncResult(callback, state); + asyncResult.SetAsCompleted(_chunk2, false); + }) + .Returns((SftpReadAsyncResult) null); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.OperationTimeout) + .Returns(_operationTimeout); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.WaitAny(_waitHandleArray, _operationTimeout)) + .Returns(() => WaitAny(_waitHandleArray, _operationTimeout)); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.BeginRead(_handle, 2 * ChunkLength, ChunkLength, It.IsNotNull(), It.IsAny())) + .Callback((handle, offset, length, callback, state) => + { + _ = _chunk3BeginRead.Set(); + var asyncResult = new SftpReadAsyncResult(callback, state); + asyncResult.SetAsCompleted(_chunk3, false); + }) + .Returns((SftpReadAsyncResult) null); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.OperationTimeout) + .Returns(_operationTimeout); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.WaitAny(_waitHandleArray, _operationTimeout)) + .Returns(() => WaitAny(_waitHandleArray, _operationTimeout)); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.BeginRead(_handle, 3 * ChunkLength, ChunkLength, It.IsNotNull(), It.IsAny())) + .Callback((handle, offset, length, callback, state) => + { + _ = _chunk4BeginRead.Set(); + var asyncResult = new SftpReadAsyncResult(callback, state); + asyncResult.SetAsCompleted(_chunk4, false); + }) + .Returns((SftpReadAsyncResult) null); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.OperationTimeout) + .Returns(_operationTimeout); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.WaitAny(_waitHandleArray, _operationTimeout)) + .Returns(() => WaitAny(_waitHandleArray, _operationTimeout)); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.BeginRead(_handle, 4 * ChunkLength, ChunkLength, It.IsNotNull(), It.IsAny())) + .Callback((handle, offset, length, callback, state) => + { + _ = _chunk5BeginRead.Set(); + var asyncResult = new SftpReadAsyncResult(callback, state); + asyncResult.SetAsCompleted(_chunk5, false); + }) + .Returns((SftpReadAsyncResult) null); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.OperationTimeout) + .Returns(_operationTimeout); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.WaitAny(_waitHandleArray, _operationTimeout)) + .Callback(() => _waitBeforeChunk6.Set()) + .Returns(() => WaitAny(_waitHandleArray, _operationTimeout)); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.RequestRead(_handle, (2 * ChunkLength) - 17, 17)) + .Returns(_chunk2CatchUp1); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.RequestRead(_handle, (2 * ChunkLength) - 7, 7)) + .Returns(_chunk2CatchUp2); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.BeginRead(_handle, 5 * ChunkLength, ChunkLength, It.IsNotNull(), It.IsAny())) + .Callback((handle, offset, length, callback, state) => + { + _ = _chunk6BeginRead.Set(); + var asyncResult = new SftpReadAsyncResult(callback, state); + asyncResult.SetAsCompleted(_chunk6, false); + }) + .Returns((SftpReadAsyncResult) null); } protected override void Arrange() @@ -271,7 +283,7 @@ namespace Renci.SshNet.Tests.Classes.Sftp { try { - _reader.Read(); + _ = _reader.Read(); Assert.Fail(); } catch (SshException ex) @@ -284,9 +296,14 @@ namespace Renci.SshNet.Tests.Classes.Sftp [TestMethod] public void DisposeShouldCloseHandleAndCompleteImmediately() { - SftpSessionMock.InSequence(_seq).Setup(p => p.IsOpen).Returns(true); - SftpSessionMock.InSequence(_seq).Setup(p => p.BeginClose(_handle, null, null)).Returns(_closeAsyncResult); - SftpSessionMock.InSequence(_seq).Setup(p => p.EndClose(_closeAsyncResult)); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.IsOpen) + .Returns(true); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.BeginClose(_handle, null, null)) + .Returns(_closeAsyncResult); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.EndClose(_closeAsyncResult)); var stopwatch = Stopwatch.StartNew(); _reader.Dispose(); diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileReaderTest_PreviousChunkIsIncompleteAndEofIsReached.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileReaderTest_PreviousChunkIsIncompleteAndEofIsReached.cs index 2b5cc0f2..5d2ed04f 100644 --- a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileReaderTest_PreviousChunkIsIncompleteAndEofIsReached.cs +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileReaderTest_PreviousChunkIsIncompleteAndEofIsReached.cs @@ -55,56 +55,62 @@ namespace Renci.SshNet.Tests.Classes.Sftp { _seq = new MockSequence(); - SftpSessionMock.InSequence(_seq) - .Setup(p => p.CreateWaitHandleArray(It.IsNotNull(), It.IsNotNull())) - .Returns((disposingWaitHandle, semaphoreAvailableWaitHandle) => - { - _waitHandleArray[0] = disposingWaitHandle; - _waitHandleArray[1] = semaphoreAvailableWaitHandle; - return _waitHandleArray; - }); - SftpSessionMock.InSequence(_seq).Setup(p => p.OperationTimeout).Returns(_operationTimeout); - SftpSessionMock.InSequence(_seq) - .Setup(p => p.WaitAny(_waitHandleArray, _operationTimeout)) - .Returns(() => WaitAny(_waitHandleArray, _operationTimeout)); - SftpSessionMock.InSequence(_seq) - .Setup(p => p.BeginRead(_handle, 0, ChunkLength, It.IsNotNull(), It.IsAny())) - .Callback((handle, offset, length, callback, state) => - { - _chunk1BeginRead.Set(); - var asyncResult = new SftpReadAsyncResult(callback, state); - asyncResult.SetAsCompleted(_chunk1, false); - }) - .Returns((SftpReadAsyncResult)null); - SftpSessionMock.InSequence(_seq).Setup(p => p.OperationTimeout).Returns(_operationTimeout); - SftpSessionMock.InSequence(_seq) - .Setup(p => p.WaitAny(_waitHandleArray, _operationTimeout)) - .Returns(() => WaitAny(_waitHandleArray, _operationTimeout)); - SftpSessionMock.InSequence(_seq) - .Setup(p => p.BeginRead(_handle, ChunkLength, ChunkLength, It.IsNotNull(), It.IsAny())) - .Callback((handle, offset, length, callback, state) => - { - _chunk2BeginRead.Set(); - var asyncResult = new SftpReadAsyncResult(callback, state); - asyncResult.SetAsCompleted(_chunk2, false); - }) - .Returns((SftpReadAsyncResult)null); - SftpSessionMock.InSequence(_seq).Setup(p => p.OperationTimeout).Returns(_operationTimeout); - SftpSessionMock.InSequence(_seq) - .Setup(p => p.WaitAny(_waitHandleArray, _operationTimeout)) - .Returns(() => WaitAny(_waitHandleArray, _operationTimeout)); - SftpSessionMock.InSequence(_seq) - .Setup(p => p.BeginRead(_handle, 2 * ChunkLength, ChunkLength, It.IsNotNull(), It.IsAny())) - .Callback((handle, offset, length, callback, state) => - { - _chunk3BeginRead.Set(); - var asyncResult = new SftpReadAsyncResult(callback, state); - asyncResult.SetAsCompleted(_chunk3, false); - }) - .Returns((SftpReadAsyncResult)null); - SftpSessionMock.InSequence(_seq) - .Setup(p => p.RequestRead(_handle, 2 * ChunkLength - 10, 10)) - .Returns(_chunk2CatchUp); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.CreateWaitHandleArray(It.IsNotNull(), It.IsNotNull())) + .Returns((disposingWaitHandle, semaphoreAvailableWaitHandle) => + { + _waitHandleArray[0] = disposingWaitHandle; + _waitHandleArray[1] = semaphoreAvailableWaitHandle; + return _waitHandleArray; + }); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.OperationTimeout) + .Returns(_operationTimeout); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.WaitAny(_waitHandleArray, _operationTimeout)) + .Returns(() => WaitAny(_waitHandleArray, _operationTimeout)); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.BeginRead(_handle, 0, ChunkLength, It.IsNotNull(), It.IsAny())) + .Callback((handle, offset, length, callback, state) => + { + _ = _chunk1BeginRead.Set(); + var asyncResult = new SftpReadAsyncResult(callback, state); + asyncResult.SetAsCompleted(_chunk1, false); + }) + .Returns((SftpReadAsyncResult) null); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.OperationTimeout) + .Returns(_operationTimeout); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.WaitAny(_waitHandleArray, _operationTimeout)) + .Returns(() => WaitAny(_waitHandleArray, _operationTimeout)); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.BeginRead(_handle, ChunkLength, ChunkLength, It.IsNotNull(), It.IsAny())) + .Callback((handle, offset, length, callback, state) => + { + _ = _chunk2BeginRead.Set(); + var asyncResult = new SftpReadAsyncResult(callback, state); + asyncResult.SetAsCompleted(_chunk2, false); + }) + .Returns((SftpReadAsyncResult) null); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.OperationTimeout) + .Returns(_operationTimeout); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.WaitAny(_waitHandleArray, _operationTimeout)) + .Returns(() => WaitAny(_waitHandleArray, _operationTimeout)); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.BeginRead(_handle, 2 * ChunkLength, ChunkLength, It.IsNotNull(), It.IsAny())) + .Callback((handle, offset, length, callback, state) => + { + _ = _chunk3BeginRead.Set(); + var asyncResult = new SftpReadAsyncResult(callback, state); + asyncResult.SetAsCompleted(_chunk3, false); + }) + .Returns((SftpReadAsyncResult) null); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.RequestRead(_handle, (2 * ChunkLength) - 10, 10)) + .Returns(_chunk2CatchUp); } protected override void Arrange() @@ -161,7 +167,7 @@ namespace Renci.SshNet.Tests.Classes.Sftp { try { - _reader.Read(); + _ = _reader.Read(); Assert.Fail(); } catch (SshException ex) @@ -174,9 +180,14 @@ namespace Renci.SshNet.Tests.Classes.Sftp [TestMethod] public void DisposeShouldCloseHandleAndCompleteImmediately() { - SftpSessionMock.InSequence(_seq).Setup(p => p.IsOpen).Returns(true); - SftpSessionMock.InSequence(_seq).Setup(p => p.BeginClose(_handle, null, null)).Returns(_closeAsyncResult); - SftpSessionMock.InSequence(_seq).Setup(p => p.EndClose(_closeAsyncResult)); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.IsOpen) + .Returns(true); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.BeginClose(_handle, null, null)) + .Returns(_closeAsyncResult); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.EndClose(_closeAsyncResult)); var stopwatch = Stopwatch.StartNew(); _reader.Dispose(); diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileReaderTest_ReadAheadEndInvokeException_PreventsFurtherReadAheads.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileReaderTest_ReadAheadEndInvokeException_PreventsFurtherReadAheads.cs index 0e4e65ea..06f2c543 100644 --- a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileReaderTest_ReadAheadEndInvokeException_PreventsFurtherReadAheads.cs +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileReaderTest_ReadAheadEndInvokeException_PreventsFurtherReadAheads.cs @@ -1,11 +1,15 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Diagnostics; +using System.Threading; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; + using Renci.SshNet.Abstractions; using Renci.SshNet.Common; using Renci.SshNet.Sftp; -using System; -using System.Diagnostics; -using System.Threading; + using BufferedRead = Renci.SshNet.Sftp.SftpFileReader.BufferedRead; namespace Renci.SshNet.Tests.Classes.Sftp @@ -22,7 +26,6 @@ namespace Renci.SshNet.Tests.Classes.Sftp private int _operationTimeout; private SftpCloseAsyncResult _closeAsyncResult; private byte[] _chunk1; - private byte[] _chunk3; private SftpFileReader _reader; private ManualResetEvent _readAheadChunk2; private ManualResetEvent _readChunk2; @@ -35,7 +38,6 @@ namespace Renci.SshNet.Tests.Classes.Sftp _handle = CreateByteArray(random, 5); _chunk1 = CreateByteArray(random, ChunkLength); - _chunk3 = CreateByteArray(random, ChunkLength); _fileSize = 3 * _chunk1.Length; _waitHandleArray = new WaitHandle[2]; _operationTimeout = random.Next(10000, 20000); @@ -51,52 +53,58 @@ namespace Renci.SshNet.Tests.Classes.Sftp { _seq = new MockSequence(); - SftpSessionMock.InSequence(_seq) - .Setup(p => p.CreateWaitHandleArray(It.IsNotNull(), It.IsNotNull())) - .Returns((disposingWaitHandle, semaphoreAvailableWaitHandle) => - { - _waitHandleArray[0] = disposingWaitHandle; - _waitHandleArray[1] = semaphoreAvailableWaitHandle; - return _waitHandleArray; - }); - SftpSessionMock.InSequence(_seq).Setup(p => p.OperationTimeout).Returns(_operationTimeout); - SftpSessionMock.InSequence(_seq) - .Setup(p => p.WaitAny(_waitHandleArray, _operationTimeout)) - .Returns(() => WaitAny(_waitHandleArray, _operationTimeout)); - SftpSessionMock.InSequence(_seq) - .Setup(p => p.BeginRead(_handle, 0, ChunkLength, It.IsNotNull(), It.IsAny())) - .Callback((handle, offset, length, callback, state) => - { - var asyncResult = new SftpReadAsyncResult(callback, state); - asyncResult.SetAsCompleted(_chunk1, false); - }) - .Returns((SftpReadAsyncResult)null); - SftpSessionMock.InSequence(_seq).Setup(p => p.OperationTimeout).Returns(_operationTimeout); - SftpSessionMock.InSequence(_seq) - .Setup(p => p.WaitAny(_waitHandleArray, _operationTimeout)) - .Returns(() => WaitAny(_waitHandleArray, _operationTimeout)); - SftpSessionMock.InSequence(_seq) - .Setup(p => p.BeginRead(_handle, ChunkLength, ChunkLength, It.IsNotNull(), It.IsAny())) - .Callback((handle, offset, length, callback, state) => - { - ThreadAbstraction.ExecuteThread(() => - { - // signal that we're in the read-ahead for chunk2 - _readAheadChunk2.Set(); - // wait for client to start reading this chunk - _readChunk2.WaitOne(TimeSpan.FromSeconds(5)); - // sleep a short time to make sure the client is in the blocking wait - Thread.Sleep(500); - // complete async read of chunk2 with exception - var asyncResult = new SftpReadAsyncResult(callback, state); - asyncResult.SetAsCompleted(_exception, false); - }); - }) - .Returns((SftpReadAsyncResult)null); - SftpSessionMock.InSequence(_seq).Setup(p => p.OperationTimeout).Returns(_operationTimeout); - SftpSessionMock.InSequence(_seq) - .Setup(p => p.WaitAny(_waitHandleArray, _operationTimeout)) - .Returns(() => WaitAny(_waitHandleArray, _operationTimeout)); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.CreateWaitHandleArray(It.IsNotNull(), It.IsNotNull())) + .Returns((disposingWaitHandle, semaphoreAvailableWaitHandle) => + { + _waitHandleArray[0] = disposingWaitHandle; + _waitHandleArray[1] = semaphoreAvailableWaitHandle; + return _waitHandleArray; + }); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.OperationTimeout) + .Returns(_operationTimeout); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.WaitAny(_waitHandleArray, _operationTimeout)) + .Returns(() => WaitAny(_waitHandleArray, _operationTimeout)); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.BeginRead(_handle, 0, ChunkLength, It.IsNotNull(), It.IsAny())) + .Callback((handle, offset, length, callback, state) => + { + var asyncResult = new SftpReadAsyncResult(callback, state); + asyncResult.SetAsCompleted(_chunk1, false); + }) + .Returns((SftpReadAsyncResult) null); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.OperationTimeout) + .Returns(_operationTimeout); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.WaitAny(_waitHandleArray, _operationTimeout)) + .Returns(() => WaitAny(_waitHandleArray, _operationTimeout)); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.BeginRead(_handle, ChunkLength, ChunkLength, It.IsNotNull(), It.IsAny())) + .Callback((handle, offset, length, callback, state) => + { + ThreadAbstraction.ExecuteThread(() => + { + // signal that we're in the read-ahead for chunk2 + _ = _readAheadChunk2.Set(); + // wait for client to start reading this chunk + _ = _readChunk2.WaitOne(TimeSpan.FromSeconds(5)); + // sleep a short time to make sure the client is in the blocking wait + Thread.Sleep(500); + // complete async read of chunk2 with exception + var asyncResult = new SftpReadAsyncResult(callback, state); + asyncResult.SetAsCompleted(_exception, false); + }); + }) + .Returns((SftpReadAsyncResult)null); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.OperationTimeout) + .Returns(_operationTimeout); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.WaitAny(_waitHandleArray, _operationTimeout)) + .Returns(() => WaitAny(_waitHandleArray, _operationTimeout)); } protected override void Arrange() @@ -110,16 +118,16 @@ namespace Renci.SshNet.Tests.Classes.Sftp protected override void Act() { - _reader.Read(); + _ = _reader.Read(); // wait until SftpFileReader has starting reading ahead chunk 2 Assert.IsTrue(_readAheadChunk2.WaitOne(TimeSpan.FromSeconds(5))); // signal that we are about to read chunk 2 - _readChunk2.Set(); + _ = _readChunk2.Set(); try { - _reader.Read(); + _ = _reader.Read(); Assert.Fail(); } catch (SshException ex) @@ -140,7 +148,7 @@ namespace Renci.SshNet.Tests.Classes.Sftp { try { - _reader.Read(); + _ = _reader.Read(); Assert.Fail(); } catch (SshException ex) @@ -152,9 +160,14 @@ namespace Renci.SshNet.Tests.Classes.Sftp [TestMethod] public void DisposeShouldCloseHandleAndCompleteImmediately() { - SftpSessionMock.InSequence(_seq).Setup(p => p.IsOpen).Returns(true); - SftpSessionMock.InSequence(_seq).Setup(p => p.BeginClose(_handle, null, null)).Returns(_closeAsyncResult); - SftpSessionMock.InSequence(_seq).Setup(p => p.EndClose(_closeAsyncResult)); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.IsOpen) + .Returns(true); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.BeginClose(_handle, null, null)) + .Returns(_closeAsyncResult); + _ = SftpSessionMock.InSequence(_seq) + .Setup(p => p.EndClose(_closeAsyncResult)); var stopwatch = Stopwatch.StartNew(); _reader.Dispose(); diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileReaderTest_ReadBackBeginReadException.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileReaderTest_ReadBackBeginReadException.cs index f00a0273..a89d4c97 100644 --- a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileReaderTest_ReadBackBeginReadException.cs +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileReaderTest_ReadBackBeginReadException.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Renci.SshNet.Tests.Classes.Sftp +namespace Renci.SshNet.Tests.Classes.Sftp { class SftpFileReaderTest_ReadBackBeginReadException { diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileReaderTest_ReadBackEndInvokeException.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileReaderTest_ReadBackEndInvokeException.cs index 19c816fc..324dbd3d 100644 --- a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileReaderTest_ReadBackEndInvokeException.cs +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileReaderTest_ReadBackEndInvokeException.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Renci.SshNet.Tests.Classes.Sftp +namespace Renci.SshNet.Tests.Classes.Sftp { class SftpFileReaderTest_ReadBackEndInvokeException { diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamAsyncTestBase.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamAsyncTestBase.cs new file mode 100644 index 00000000..110a20be --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamAsyncTestBase.cs @@ -0,0 +1,67 @@ +using System; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using Renci.SshNet.Sftp; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + public abstract class SftpFileStreamAsyncTestBase + { + internal Mock SftpSessionMock; + protected MockSequence MockSequence; + + protected virtual Task ArrangeAsync() + { + SetupData(); + CreateMocks(); + SetupMocks(); + return Task.CompletedTask; + } + + protected virtual void SetupData() + { + MockSequence = new MockSequence(); + } + + protected abstract void SetupMocks(); + + private void CreateMocks() + { + SftpSessionMock = new Mock(MockBehavior.Strict); + } + + [TestInitialize] + public async Task SetUpAsync() + { + await ArrangeAsync(); + await ActAsync(); + } + + protected abstract Task ActAsync(); + + protected byte[] GenerateRandom(int length) + { + return GenerateRandom(length, new Random()); + } + + protected byte[] GenerateRandom(int length, Random random) + { + var buffer = new byte[length]; + random.NextBytes(buffer); + return buffer; + } + + protected byte[] GenerateRandom(uint length) + { + return GenerateRandom(length, new Random()); + } + + protected byte[] GenerateRandom(uint length, Random random) + { + var buffer = new byte[length]; + random.NextBytes(buffer); + return buffer; + } + } +} diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_CanWrite_Disposed_FileAccessWrite.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_CanWrite_Disposed_FileAccessWrite.cs index 1d8c3393..4474cafd 100644 --- a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_CanWrite_Disposed_FileAccessWrite.cs +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_CanWrite_Disposed_FileAccessWrite.cs @@ -31,17 +31,20 @@ namespace Renci.SshNet.Tests.Classes.Sftp protected override void SetupMocks() { - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.RequestOpen(_path, Flags.Write, false)) - .Returns(_handle); - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.CalculateOptimalReadLength(_bufferSize)) - .Returns(_readBufferSize); - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.CalculateOptimalWriteLength(_bufferSize, _handle)) - .Returns(_writeBufferSize); - SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); - SftpSessionMock.InSequence(MockSequence).Setup(p => p.RequestClose(_handle)); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestOpen(_path, Flags.Write, false)) + .Returns(_handle); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalReadLength(_bufferSize)) + .Returns(_readBufferSize); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalWriteLength(_bufferSize, _handle)) + .Returns(_writeBufferSize); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestClose(_handle)); } protected override void Arrange() diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Close_Closed.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Close_Closed.cs index 760b65ea..b52537fb 100644 --- a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Close_Closed.cs +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Close_Closed.cs @@ -1,8 +1,11 @@ using System; using System.Globalization; using System.IO; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; + using Renci.SshNet.Sftp; namespace Renci.SshNet.Tests.Classes.Sftp @@ -37,20 +40,21 @@ namespace Renci.SshNet.Tests.Classes.Sftp _sftpSessionMock = new Mock(MockBehavior.Strict); var sequence = new MockSequence(); - _sftpSessionMock.InSequence(sequence) - .Setup(p => p.RequestOpen(_path, Flags.Read, false)) - .Returns(_handle); - _sftpSessionMock.InSequence(sequence) - .Setup(p => p.CalculateOptimalReadLength(_bufferSize)) - .Returns(_readBufferSize); - _sftpSessionMock.InSequence(sequence) - .Setup(p => p.CalculateOptimalWriteLength(_bufferSize, _handle)) - .Returns(_writeBufferSize); - _sftpSessionMock.InSequence(sequence) - .Setup(p => p.IsOpen) - .Returns(true); - _sftpSessionMock.InSequence(sequence) - .Setup(p => p.RequestClose(_handle)); + + _ = _sftpSessionMock.InSequence(sequence) + .Setup(p => p.RequestOpen(_path, Flags.Read, false)) + .Returns(_handle); + _ = _sftpSessionMock.InSequence(sequence) + .Setup(p => p.CalculateOptimalReadLength(_bufferSize)) + .Returns(_readBufferSize); + _ = _sftpSessionMock.InSequence(sequence) + .Setup(p => p.CalculateOptimalWriteLength(_bufferSize, _handle)) + .Returns(_writeBufferSize); + _ = _sftpSessionMock.InSequence(sequence) + .Setup(p => p.IsOpen) + .Returns(true); + _ = _sftpSessionMock.InSequence(sequence) + .Setup(p => p.RequestClose(_handle)); _sftpFileStream = new SftpFileStream(_sftpSessionMock.Object, _path, FileMode.Open, FileAccess.Read, (int)_bufferSize); _sftpFileStream.Close(); diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Close_Disposed.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Close_Disposed.cs index 08a3399e..59a6357b 100644 --- a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Close_Disposed.cs +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Close_Disposed.cs @@ -30,17 +30,20 @@ namespace Renci.SshNet.Tests.Classes.Sftp protected override void SetupMocks() { - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.RequestOpen(_path, Flags.Read, false)) - .Returns(_handle); - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.CalculateOptimalReadLength(_bufferSize)) - .Returns(_readBufferSize); - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.CalculateOptimalWriteLength(_bufferSize, _handle)) - .Returns(_writeBufferSize); - SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); - SftpSessionMock.InSequence(MockSequence).Setup(p => p.RequestClose(_handle)); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestOpen(_path, Flags.Read, false)) + .Returns(_handle); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalReadLength(_bufferSize)) + .Returns(_readBufferSize); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalWriteLength(_bufferSize, _handle)) + .Returns(_writeBufferSize); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestClose(_handle)); } protected override void Arrange() diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Close_SessionOpen.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Close_SessionOpen.cs index c2ec5c4c..8ad19bce 100644 --- a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Close_SessionOpen.cs +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Close_SessionOpen.cs @@ -1,8 +1,10 @@ using System; -using System.Globalization; using System.IO; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; + using Renci.SshNet.Sftp; namespace Renci.SshNet.Tests.Classes.Sftp @@ -31,20 +33,20 @@ namespace Renci.SshNet.Tests.Classes.Sftp protected override void SetupMocks() { - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.RequestOpen(_path, Flags.Read, false)) - .Returns(_handle); - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.CalculateOptimalReadLength(_bufferSize)) - .Returns(_readBufferSize); - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.CalculateOptimalWriteLength(_bufferSize, _handle)) - .Returns(_writeBufferSize); - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.IsOpen) - .Returns(true); - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.RequestClose(_handle)); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestOpen(_path, Flags.Read, false)) + .Returns(_handle); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalReadLength(_bufferSize)) + .Returns(_readBufferSize); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalWriteLength(_bufferSize, _handle)) + .Returns(_writeBufferSize); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestClose(_handle)); } protected override void Arrange() diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Ctor_FileModeAppend_FileAccessWrite.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Ctor_FileModeAppend_FileAccessWrite.cs index 87183067..25171b9f 100644 --- a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Ctor_FileModeAppend_FileAccessWrite.cs +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Ctor_FileModeAppend_FileAccessWrite.cs @@ -45,18 +45,18 @@ namespace Renci.SshNet.Tests.Classes.Sftp protected override void SetupMocks() { - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.RequestOpen(_path, Flags.Write | Flags.Append | Flags.CreateNewOrOpen, false)) - .Returns(_handle); - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.CalculateOptimalReadLength((uint) _bufferSize)) - .Returns(_readBufferSize); - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.CalculateOptimalWriteLength((uint) _bufferSize, _handle)) - .Returns(_writeBufferSize); - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.RequestFStat(_handle, false)) - .Returns(_fileAttributes); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestOpen(_path, Flags.Write | Flags.Append | Flags.CreateNewOrOpen, false)) + .Returns(_handle); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalReadLength((uint) _bufferSize)) + .Returns(_readBufferSize); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalWriteLength((uint) _bufferSize, _handle)) + .Returns(_writeBufferSize); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestFStat(_handle, false)) + .Returns(_fileAttributes); } protected override void Act() @@ -95,7 +95,9 @@ namespace Renci.SshNet.Tests.Classes.Sftp [TestMethod] public void PositionShouldReturnSizeOfFile() { - SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); var actual = _target.Position; @@ -109,11 +111,13 @@ namespace Renci.SshNet.Tests.Classes.Sftp { var buffer = new byte[_readBufferSize]; - SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); try { - _target.Read(buffer, 0, buffer.Length); + _ = _target.Read(buffer, 0, buffer.Length); Assert.Fail(); } catch (NotSupportedException ex) @@ -128,11 +132,13 @@ namespace Renci.SshNet.Tests.Classes.Sftp [TestMethod] public void ReadByteShouldThrowNotSupportedException() { - SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); try { - _target.ReadByte(); + _ = _target.ReadByte(); Assert.Fail(); } catch (NotSupportedException ex) @@ -149,8 +155,11 @@ namespace Renci.SshNet.Tests.Classes.Sftp { var buffer = new byte[_writeBufferSize]; - SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); - SftpSessionMock.InSequence(MockSequence).Setup(p => p.RequestWrite(_handle, (ulong) _fileAttributes.Size, buffer, 0, buffer.Length, It.IsNotNull(), null)); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestWrite(_handle, (ulong) _fileAttributes.Size, buffer, 0, buffer.Length, It.IsNotNull(), null)); _target.Write(buffer, 0, buffer.Length); diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Ctor_FileModeCreateNew_FileAccessRead.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Ctor_FileModeCreateNew_FileAccessRead.cs index bce4afba..47cabd2a 100644 --- a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Ctor_FileModeCreateNew_FileAccessRead.cs +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Ctor_FileModeCreateNew_FileAccessRead.cs @@ -1,6 +1,8 @@ using System; using System.IO; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Renci.SshNet.Sftp; namespace Renci.SshNet.Tests.Classes.Sftp @@ -34,7 +36,7 @@ namespace Renci.SshNet.Tests.Classes.Sftp { try { - new SftpFileStream(SftpSessionMock.Object, _path, _fileMode, _fileAccess, _bufferSize); + _ = new SftpFileStream(SftpSessionMock.Object, _path, _fileMode, _fileAccess, _bufferSize); Assert.Fail(); } catch (ArgumentException ex) @@ -48,7 +50,7 @@ namespace Renci.SshNet.Tests.Classes.Sftp { Assert.IsNotNull(_actualException); Assert.IsNull(_actualException.InnerException); - Assert.AreEqual(string.Format("Combining {0}: {1} with {2}: {3} is invalid.", typeof(FileMode).Name, _fileMode, typeof(FileAccess).Name, _fileAccess), _actualException.Message); + Assert.AreEqual(string.Format("Combining {0}: {1} with {2}: {3} is invalid.", nameof(FileMode), _fileMode, nameof(FileAccess), _fileAccess), _actualException.Message); Assert.IsNull(_actualException.ParamName); } } diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Ctor_FileModeCreateNew_FileAccessReadWrite.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Ctor_FileModeCreateNew_FileAccessReadWrite.cs index c6a87ce1..c8e46a2b 100644 --- a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Ctor_FileModeCreateNew_FileAccessReadWrite.cs +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Ctor_FileModeCreateNew_FileAccessReadWrite.cs @@ -37,15 +37,15 @@ namespace Renci.SshNet.Tests.Classes.Sftp protected override void SetupMocks() { - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.RequestOpen(_path, Flags.Read | Flags.Write | Flags.CreateNew, false)) - .Returns(_handle); - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.CalculateOptimalReadLength((uint) _bufferSize)) - .Returns(_readBufferSize); - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.CalculateOptimalWriteLength((uint) _bufferSize, _handle)) - .Returns(_writeBufferSize); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestOpen(_path, Flags.Read | Flags.Write | Flags.CreateNew, false)) + .Returns(_handle); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalReadLength((uint) _bufferSize)) + .Returns(_readBufferSize); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalWriteLength((uint) _bufferSize, _handle)) + .Returns(_writeBufferSize); } protected override void Act() @@ -84,7 +84,9 @@ namespace Renci.SshNet.Tests.Classes.Sftp [TestMethod] public void PositionShouldReturnZero() { - SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); var actual = _target.Position; @@ -100,8 +102,12 @@ namespace Renci.SshNet.Tests.Classes.Sftp var data = new byte[] { 5, 4, 3, 2, 1 }; var expected = new byte[] { 0, 5, 4, 3, 2, 1, 0, 0 }; - SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); - SftpSessionMock.InSequence(MockSequence).Setup(p => p.RequestRead(_handle, 0UL, _readBufferSize)).Returns(data); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestRead(_handle, 0UL, _readBufferSize)) + .Returns(data); var actual = _target.Read(buffer, 1, data.Length); @@ -117,9 +123,12 @@ namespace Renci.SshNet.Tests.Classes.Sftp { var data = GenerateRandom(5, _random); - SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); - SftpSessionMock.InSequence(MockSequence).Setup(p => p.RequestRead(_handle, 0UL, _readBufferSize)) - .Returns(data); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestRead(_handle, 0UL, _readBufferSize)) + .Returns(data); var actual = _target.ReadByte(); @@ -134,8 +143,11 @@ namespace Renci.SshNet.Tests.Classes.Sftp { var buffer = new byte[_writeBufferSize]; - SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); - SftpSessionMock.InSequence(MockSequence).Setup(p => p.RequestWrite(_handle, 0UL, buffer, 0, buffer.Length, It.IsNotNull(), null)); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestWrite(_handle, 0UL, buffer, 0, buffer.Length, It.IsNotNull(), null)); _target.Write(buffer, 0, buffer.Length); diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Ctor_FileModeCreate_FileAccessRead.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Ctor_FileModeCreate_FileAccessRead.cs index 493e4819..29e4956a 100644 --- a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Ctor_FileModeCreate_FileAccessRead.cs +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Ctor_FileModeCreate_FileAccessRead.cs @@ -1,6 +1,8 @@ using System; using System.IO; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Renci.SshNet.Sftp; namespace Renci.SshNet.Tests.Classes.Sftp @@ -34,7 +36,7 @@ namespace Renci.SshNet.Tests.Classes.Sftp { try { - new SftpFileStream(SftpSessionMock.Object, _path, _fileMode, _fileAccess, _bufferSize); + _ =new SftpFileStream(SftpSessionMock.Object, _path, _fileMode, _fileAccess, _bufferSize); Assert.Fail(); } catch (ArgumentException ex) @@ -48,7 +50,7 @@ namespace Renci.SshNet.Tests.Classes.Sftp { Assert.IsNotNull(_actualException); Assert.IsNull(_actualException.InnerException); - Assert.AreEqual(string.Format("Combining {0}: {1} with {2}: {3} is invalid.", typeof(FileMode).Name, _fileMode, typeof(FileAccess).Name, _fileAccess), _actualException.Message); + Assert.AreEqual(string.Format("Combining {0}: {1} with {2}: {3} is invalid.", nameof(FileMode), _fileMode, nameof(FileAccess), _fileAccess), _actualException.Message); Assert.IsNull(_actualException.ParamName); } } diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Ctor_FileModeCreate_FileAccessReadWrite_FileDoesNotExist.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Ctor_FileModeCreate_FileAccessReadWrite_FileDoesNotExist.cs index 8b8f50fb..73052060 100644 --- a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Ctor_FileModeCreate_FileAccessReadWrite_FileDoesNotExist.cs +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Ctor_FileModeCreate_FileAccessReadWrite_FileDoesNotExist.cs @@ -37,18 +37,18 @@ namespace Renci.SshNet.Tests.Classes.Sftp protected override void SetupMocks() { - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.RequestOpen(_path, Flags.Read | Flags.Write | Flags.Truncate, true)) - .Returns((byte[]) null); - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.RequestOpen(_path, Flags.Read | Flags.Write | Flags.CreateNew, false)) - .Returns(_handle); - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.CalculateOptimalReadLength((uint) _bufferSize)) - .Returns(_readBufferSize); - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.CalculateOptimalWriteLength((uint) _bufferSize, _handle)) - .Returns(_writeBufferSize); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestOpen(_path, Flags.Read | Flags.Write | Flags.Truncate, true)) + .Returns((byte[]) null); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestOpen(_path, Flags.Read | Flags.Write | Flags.CreateNew, false)) + .Returns(_handle); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalReadLength((uint) _bufferSize)) + .Returns(_readBufferSize); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalWriteLength((uint) _bufferSize, _handle)) + .Returns(_writeBufferSize); } protected override void Act() @@ -87,7 +87,9 @@ namespace Renci.SshNet.Tests.Classes.Sftp [TestMethod] public void PositionShouldReturnZero() { - SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); var actual = _target.Position; @@ -103,8 +105,12 @@ namespace Renci.SshNet.Tests.Classes.Sftp var data = new byte[] { 5, 4, 3, 2, 1 }; var expected = new byte[] { 0, 5, 4, 3, 2, 1, 0, 0 }; - SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); - SftpSessionMock.InSequence(MockSequence).Setup(p => p.RequestRead(_handle, 0UL, _readBufferSize)).Returns(data); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestRead(_handle, 0UL, _readBufferSize)) + .Returns(data); var actual = _target.Read(buffer, 1, data.Length); @@ -120,9 +126,12 @@ namespace Renci.SshNet.Tests.Classes.Sftp { var data = GenerateRandom(5, _random); - SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); - SftpSessionMock.InSequence(MockSequence).Setup(p => p.RequestRead(_handle, 0UL, _readBufferSize)) - .Returns(data); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestRead(_handle, 0UL, _readBufferSize)) + .Returns(data); var actual = _target.ReadByte(); @@ -137,8 +146,11 @@ namespace Renci.SshNet.Tests.Classes.Sftp { var buffer = new byte[_writeBufferSize]; - SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); - SftpSessionMock.InSequence(MockSequence).Setup(p => p.RequestWrite(_handle, 0UL, buffer, 0, buffer.Length, It.IsNotNull(), null)); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestWrite(_handle, 0UL, buffer, 0, buffer.Length, It.IsNotNull(), null)); _target.Write(buffer, 0, buffer.Length); diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Ctor_FileModeTruncate_FileAccessRead.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Ctor_FileModeTruncate_FileAccessRead.cs index c0a21667..2ed97778 100644 --- a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Ctor_FileModeTruncate_FileAccessRead.cs +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Ctor_FileModeTruncate_FileAccessRead.cs @@ -34,7 +34,7 @@ namespace Renci.SshNet.Tests.Classes.Sftp { try { - new SftpFileStream(SftpSessionMock.Object, _path, _fileMode, _fileAccess, _bufferSize); + _ = new SftpFileStream(SftpSessionMock.Object, _path, _fileMode, _fileAccess, _bufferSize); Assert.Fail(); } catch (ArgumentException ex) @@ -48,7 +48,7 @@ namespace Renci.SshNet.Tests.Classes.Sftp { Assert.IsNotNull(_actualException); Assert.IsNull(_actualException.InnerException); - Assert.AreEqual(string.Format("Combining {0}: {1} with {2}: {3} is invalid.", typeof(FileMode).Name, _fileMode, typeof(FileAccess).Name, _fileAccess), _actualException.Message); + Assert.AreEqual(string.Format("Combining {0}: {1} with {2}: {3} is invalid.", nameof(FileMode), _fileMode, nameof(FileAccess), _fileAccess), _actualException.Message); Assert.IsNull(_actualException.ParamName); } } diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Ctor_FileModeTruncate_FileAccessReadWrite.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Ctor_FileModeTruncate_FileAccessReadWrite.cs index db3d9b99..59e669c5 100644 --- a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Ctor_FileModeTruncate_FileAccessReadWrite.cs +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Ctor_FileModeTruncate_FileAccessReadWrite.cs @@ -1,8 +1,11 @@ using System; using System.IO; using System.Threading; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; + using Renci.SshNet.Common; using Renci.SshNet.Sftp; @@ -37,15 +40,15 @@ namespace Renci.SshNet.Tests.Classes.Sftp protected override void SetupMocks() { - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.RequestOpen(_path, Flags.Read | Flags.Write | Flags.Truncate, false)) - .Returns(_handle); - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.CalculateOptimalReadLength((uint) _bufferSize)) - .Returns(_readBufferSize); - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.CalculateOptimalWriteLength((uint) _bufferSize, _handle)) - .Returns(_writeBufferSize); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestOpen(_path, Flags.Read | Flags.Write | Flags.Truncate, false)) + .Returns(_handle); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalReadLength((uint) _bufferSize)) + .Returns(_readBufferSize); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalWriteLength((uint) _bufferSize, _handle)) + .Returns(_writeBufferSize); } protected override void Act() @@ -84,7 +87,9 @@ namespace Renci.SshNet.Tests.Classes.Sftp [TestMethod] public void PositionShouldReturnZero() { - SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); var actual = _target.Position; @@ -100,8 +105,12 @@ namespace Renci.SshNet.Tests.Classes.Sftp var data = new byte[] { 5, 4, 3, 2, 1 }; var expected = new byte[] { 0, 5, 4, 3, 2, 1, 0, 0 }; - SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); - SftpSessionMock.InSequence(MockSequence).Setup(p => p.RequestRead(_handle, 0UL, _readBufferSize)).Returns(data); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestRead(_handle, 0UL, _readBufferSize)) + .Returns(data); var actual = _target.Read(buffer, 1, data.Length); @@ -117,9 +126,12 @@ namespace Renci.SshNet.Tests.Classes.Sftp { var data = GenerateRandom(5, _random); - SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); - SftpSessionMock.InSequence(MockSequence).Setup(p => p.RequestRead(_handle, 0UL, _readBufferSize)) - .Returns(data); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestRead(_handle, 0UL, _readBufferSize)) + .Returns(data); var actual = _target.ReadByte(); @@ -134,8 +146,11 @@ namespace Renci.SshNet.Tests.Classes.Sftp { var buffer = new byte[_writeBufferSize]; - SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); - SftpSessionMock.InSequence(MockSequence).Setup(p => p.RequestWrite(_handle, 0UL, buffer, 0, buffer.Length, It.IsNotNull(), null)); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestWrite(_handle, 0UL, buffer, 0, buffer.Length, It.IsNotNull(), null)); _target.Write(buffer, 0, buffer.Length); diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Dispose_Closed.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Dispose_Closed.cs index 2bfe5428..0c698624 100644 --- a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Dispose_Closed.cs +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Dispose_Closed.cs @@ -1,7 +1,10 @@ using System; using System.IO; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; + using Renci.SshNet.Sftp; namespace Renci.SshNet.Tests.Classes.Sftp @@ -30,17 +33,20 @@ namespace Renci.SshNet.Tests.Classes.Sftp protected override void SetupMocks() { - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.RequestOpen(_path, Flags.Read, false)) - .Returns(_handle); - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.CalculateOptimalReadLength(_bufferSize)) - .Returns(_readBufferSize); - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.CalculateOptimalWriteLength(_bufferSize, _handle)) - .Returns(_writeBufferSize); - SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); - SftpSessionMock.InSequence(MockSequence).Setup(p => p.RequestClose(_handle)); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestOpen(_path, Flags.Read, false)) + .Returns(_handle); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalReadLength(_bufferSize)) + .Returns(_readBufferSize); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalWriteLength(_bufferSize, _handle)) + .Returns(_writeBufferSize); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestClose(_handle)); } protected override void Arrange() diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Dispose_SessionNotOpen.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Dispose_SessionNotOpen.cs index d340b403..e1aae67b 100644 --- a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Dispose_SessionNotOpen.cs +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Dispose_SessionNotOpen.cs @@ -1,7 +1,10 @@ using System; using System.IO; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; + using Renci.SshNet.Sftp; namespace Renci.SshNet.Tests.Classes.Sftp @@ -30,17 +33,20 @@ namespace Renci.SshNet.Tests.Classes.Sftp protected override void SetupMocks() { - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.RequestOpen(_path, Flags.Write | Flags.CreateNew, false)) - .Returns(_handle); - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.CalculateOptimalReadLength(_bufferSize)) - .Returns(_readBufferSize); - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.CalculateOptimalWriteLength(_bufferSize, _handle)) - .Returns(_writeBufferSize); - SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(false); - SftpSessionMock.InSequence(MockSequence).Setup(p => p.RequestClose(_handle)); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestOpen(_path, Flags.Write | Flags.CreateNew, false)) + .Returns(_handle); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalReadLength(_bufferSize)) + .Returns(_readBufferSize); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalWriteLength(_bufferSize, _handle)) + .Returns(_writeBufferSize); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(false); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestClose(_handle)); } protected override void Arrange() diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Dispose_SessionOpen.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Dispose_SessionOpen.cs index 35563fcc..9429579f 100644 --- a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Dispose_SessionOpen.cs +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Dispose_SessionOpen.cs @@ -1,8 +1,11 @@ using System; using System.Globalization; using System.IO; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; + using Renci.SshNet.Sftp; namespace Renci.SshNet.Tests.Classes.Sftp @@ -31,17 +34,19 @@ namespace Renci.SshNet.Tests.Classes.Sftp protected override void SetupMocks() { - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.RequestOpen(_path, Flags.Write | Flags.Truncate, false)) - .Returns(_handle); - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.CalculateOptimalReadLength(_bufferSize)) - .Returns(_readBufferSize); - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.CalculateOptimalWriteLength(_bufferSize, _handle)) - .Returns(_writeBufferSize); - SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); - SftpSessionMock.InSequence(MockSequence).Setup(p => p.RequestClose(_handle)); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestOpen(_path, Flags.Write | Flags.Truncate, false)) + .Returns(_handle); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalReadLength(_bufferSize)) + .Returns(_readBufferSize); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalWriteLength(_bufferSize, _handle)) + .Returns(_writeBufferSize); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen).Returns(true); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestClose(_handle)); } protected override void Arrange() diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Finalize_SessionOpen.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Finalize_SessionOpen.cs index 41312f66..7ea74887 100644 --- a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Finalize_SessionOpen.cs +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Finalize_SessionOpen.cs @@ -10,7 +10,7 @@ namespace Renci.SshNet.Tests.Classes.Sftp [TestClass] public class SftpFileStreamTest_Finalize_SessionOpen : SftpFileStreamTestBase { - private SftpFileStream _target; + private WeakReference _target; private string _path; private byte[] _handle; private uint _bufferSize; @@ -31,29 +31,27 @@ namespace Renci.SshNet.Tests.Classes.Sftp protected override void SetupMocks() { - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.RequestOpen(_path, Flags.Read | Flags.Write | Flags.CreateNewOrOpen, false)) - .Returns(_handle); - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.CalculateOptimalReadLength(_bufferSize)) - .Returns(_readBufferSize); - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.CalculateOptimalWriteLength(_bufferSize, _handle)) - .Returns(_writeBufferSize); - SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); - SftpSessionMock.InSequence(MockSequence).Setup(p => p.RequestClose(_handle)); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestOpen(_path, Flags.Read | Flags.Write | Flags.CreateNewOrOpen, false)) + .Returns(_handle); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalReadLength(_bufferSize)) + .Returns(_readBufferSize); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalWriteLength(_bufferSize, _handle)) + .Returns(_writeBufferSize); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestClose(_handle)); } protected override void Arrange() { base.Arrange(); - _target = new SftpFileStream(SftpSessionMock.Object, - _path, - FileMode.OpenOrCreate, - FileAccess.ReadWrite, - (int) _bufferSize); - _target = null; + _target = CreateWeakSftpFileStream(); } protected override void Act() @@ -62,6 +60,12 @@ namespace Renci.SshNet.Tests.Classes.Sftp GC.WaitForPendingFinalizers(); } + [TestMethod] + public void SftpFileStreamShouldHaveBeenFinalized() + { + Assert.IsFalse(_target.TryGetTarget(out _)); + } + [TestMethod] public void IsOpenOnSftpSessionShouldNeverBeInvoked() { @@ -73,5 +77,15 @@ namespace Renci.SshNet.Tests.Classes.Sftp { SftpSessionMock.Verify(p => p.RequestClose(_handle), Times.Never); } + + private WeakReference CreateWeakSftpFileStream() + { + var sftpFileStream = new SftpFileStream(SftpSessionMock.Object, + _path, + FileMode.OpenOrCreate, + FileAccess.ReadWrite, + (int) _bufferSize); + return new WeakReference(sftpFileStream); + } } } diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Flush_ReadMode_DataInBuffer_NotReadFromBuffer.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Flush_ReadMode_DataInBuffer_NotReadFromBuffer.cs index ee44c62f..9795c514 100644 --- a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Flush_ReadMode_DataInBuffer_NotReadFromBuffer.cs +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Flush_ReadMode_DataInBuffer_NotReadFromBuffer.cs @@ -1,10 +1,13 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.IO; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; + using Renci.SshNet.Common; using Renci.SshNet.Sftp; using Renci.SshNet.Tests.Common; -using System; -using System.IO; namespace Renci.SshNet.Tests.Classes.Sftp { @@ -36,24 +39,24 @@ namespace Renci.SshNet.Tests.Classes.Sftp protected override void SetupMocks() { - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.RequestOpen(_path, Flags.Read, false)) - .Returns(_handle); - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.CalculateOptimalReadLength(_bufferSize)) - .Returns(_readBufferSize); - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.CalculateOptimalWriteLength(_bufferSize, _handle)) - .Returns(_writeBufferSize); - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.IsOpen) - .Returns(true); - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.RequestRead(_handle, 0UL, _readBufferSize)) - .Returns(_serverBytes); - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.IsOpen) - .Returns(true); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestOpen(_path, Flags.Read, false)) + .Returns(_handle); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalReadLength(_bufferSize)) + .Returns(_readBufferSize); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalWriteLength(_bufferSize, _handle)) + .Returns(_writeBufferSize); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestRead(_handle, 0UL, _readBufferSize)) + .Returns(_serverBytes); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); } protected override void Arrange() @@ -65,7 +68,7 @@ namespace Renci.SshNet.Tests.Classes.Sftp FileMode.Open, FileAccess.Read, (int)_bufferSize); - _target.Read(_readBytes, 0, _readBytes.Length); + _ = _target.Read(_readBytes, 0, _readBytes.Length); } protected override void Act() @@ -76,9 +79,9 @@ namespace Renci.SshNet.Tests.Classes.Sftp [TestMethod] public void PositionShouldReturnSameValueAsBeforeFlush() { - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.IsOpen) - .Returns(true); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); Assert.AreEqual(_readBytes.Length, _target.Position); @@ -94,12 +97,12 @@ namespace Renci.SshNet.Tests.Classes.Sftp .Add(serverBytes2.Take(0, 3)) .Build(); - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.IsOpen) - .Returns(true); - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.RequestRead(_handle, (ulong)_readBytes.Length, _readBufferSize)) - .Returns(serverBytes2); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestRead(_handle, (ulong)_readBytes.Length, _readBufferSize)) + .Returns(serverBytes2); var bytesRead = _target.Read(readBytes2, 2, 3); diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Flush_ReadMode_DataInBuffer_ReadFromBuffer.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Flush_ReadMode_DataInBuffer_ReadFromBuffer.cs index f5b25be4..d873f320 100644 --- a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Flush_ReadMode_DataInBuffer_ReadFromBuffer.cs +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Flush_ReadMode_DataInBuffer_ReadFromBuffer.cs @@ -26,6 +26,7 @@ namespace Renci.SshNet.Tests.Classes.Sftp base.SetupData(); var random = new Random(); + _path = random.Next().ToString(); _handle = GenerateRandom(5, random); _bufferSize = (uint)random.Next(1, 1000); @@ -38,27 +39,27 @@ namespace Renci.SshNet.Tests.Classes.Sftp protected override void SetupMocks() { - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.RequestOpen(_path, Flags.Read, false)) - .Returns(_handle); - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.CalculateOptimalReadLength(_bufferSize)) - .Returns(_readBufferSize); - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.CalculateOptimalWriteLength(_bufferSize, _handle)) - .Returns(_writeBufferSize); - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.IsOpen) - .Returns(true); - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.RequestRead(_handle, 0UL, _readBufferSize)) - .Returns(_serverBytes); - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.IsOpen) - .Returns(true); - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.IsOpen) - .Returns(true); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestOpen(_path, Flags.Read, false)) + .Returns(_handle); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalReadLength(_bufferSize)) + .Returns(_readBufferSize); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalWriteLength(_bufferSize, _handle)) + .Returns(_writeBufferSize); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestRead(_handle, 0UL, _readBufferSize)) + .Returns(_serverBytes); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); } protected override void Arrange() @@ -70,8 +71,8 @@ namespace Renci.SshNet.Tests.Classes.Sftp FileMode.Open, FileAccess.Read, (int)_bufferSize); - _target.Read(_readBytes1, 0, _readBytes1.Length); - _target.Read(_readBytes2, 0, _readBytes2.Length); + _ = _target.Read(_readBytes1, 0, _readBytes1.Length); + _ = _target.Read(_readBytes2, 0, _readBytes2.Length); } protected override void Act() @@ -82,9 +83,9 @@ namespace Renci.SshNet.Tests.Classes.Sftp [TestMethod] public void PositionShouldReturnSameValueAsBeforeFlush() { - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.IsOpen) - .Returns(true); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); Assert.AreEqual(_readBytes1.Length + _readBytes2.Length, _target.Position); @@ -100,12 +101,12 @@ namespace Renci.SshNet.Tests.Classes.Sftp .Add(serverBytes3.Take(0, 2)) .Build(); - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.IsOpen) - .Returns(true); - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.RequestRead(_handle, (ulong) (_readBytes1.Length + _readBytes2.Length), _readBufferSize)) - .Returns(serverBytes3); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestRead(_handle, (ulong) (_readBytes1.Length + _readBytes2.Length), _readBufferSize)) + .Returns(serverBytes3); var bytesRead = _target.Read(readBytes3, 1, 2); diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Flush_ReadMode_NoDataInBuffer.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Flush_ReadMode_NoDataInBuffer.cs index 41ba6f43..5388971e 100644 --- a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Flush_ReadMode_NoDataInBuffer.cs +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Flush_ReadMode_NoDataInBuffer.cs @@ -1,10 +1,13 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.IO; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; + using Renci.SshNet.Common; using Renci.SshNet.Sftp; using Renci.SshNet.Tests.Common; -using System; -using System.IO; namespace Renci.SshNet.Tests.Classes.Sftp { @@ -36,24 +39,24 @@ namespace Renci.SshNet.Tests.Classes.Sftp protected override void SetupMocks() { - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.RequestOpen(_path, Flags.Read, false)) - .Returns(_handle); - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.CalculateOptimalReadLength(_bufferSize)) - .Returns(_readBufferSize); - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.CalculateOptimalWriteLength(_bufferSize, _handle)) - .Returns(_writeBufferSize); - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.IsOpen) - .Returns(true); - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.RequestRead(_handle, 0UL, _readBufferSize)) - .Returns(_serverBytes); - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.IsOpen) - .Returns(true); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestOpen(_path, Flags.Read, false)) + .Returns(_handle); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalReadLength(_bufferSize)) + .Returns(_readBufferSize); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalWriteLength(_bufferSize, _handle)) + .Returns(_writeBufferSize); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestRead(_handle, 0UL, _readBufferSize)) + .Returns(_serverBytes); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); } protected override void Arrange() @@ -65,7 +68,7 @@ namespace Renci.SshNet.Tests.Classes.Sftp FileMode.Open, FileAccess.Read, (int) _bufferSize); - _target.Read(_readBytes, 0, _readBytes.Length); + _ = _target.Read(_readBytes, 0, _readBytes.Length); } protected override void Act() @@ -76,9 +79,9 @@ namespace Renci.SshNet.Tests.Classes.Sftp [TestMethod] public void PositionShouldReturnSameValueAsBeforeFlush() { - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.IsOpen) - .Returns(true); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); Assert.AreEqual(_readBytes.Length, _target.Position); @@ -94,12 +97,12 @@ namespace Renci.SshNet.Tests.Classes.Sftp .Add(serverBytes2.Take(0, 3)) .Build(); - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.IsOpen) - .Returns(true); - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.RequestRead(_handle, (ulong) _readBytes.Length, _readBufferSize)) - .Returns(serverBytes2); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestRead(_handle, (ulong) _readBytes.Length, _readBufferSize)) + .Returns(serverBytes2); var bytesRead = _target.Read(readBytes2, 2, 3); diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Flush_WriteMode_DataInBuffer.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Flush_WriteMode_DataInBuffer.cs index 8dbc7066..2bf5c2c6 100644 --- a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Flush_WriteMode_DataInBuffer.cs +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Flush_WriteMode_DataInBuffer.cs @@ -1,12 +1,15 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.IO; +using System.Threading; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; + using Renci.SshNet.Common; using Renci.SshNet.Sftp; using Renci.SshNet.Sftp.Responses; using Renci.SshNet.Tests.Common; -using System; -using System.IO; -using System.Threading; namespace Renci.SshNet.Tests.Classes.Sftp { @@ -29,6 +32,7 @@ namespace Renci.SshNet.Tests.Classes.Sftp base.SetupData(); var random = new Random(); + _path = random.Next().ToString(); _handle = GenerateRandom(5, random); _bufferSize = (uint)random.Next(1, 1000); @@ -42,42 +46,40 @@ namespace Renci.SshNet.Tests.Classes.Sftp protected override void SetupMocks() { - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.RequestOpen(_path, Flags.Read | Flags.Write, false)) - .Returns(_handle); - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.CalculateOptimalReadLength(_bufferSize)) - .Returns(_readBufferSize); - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.CalculateOptimalWriteLength(_bufferSize, _handle)) - .Returns(_writeBufferSize); - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.IsOpen) - .Returns(true); - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.RequestWrite(_handle, 0UL, It.IsAny(), 0, _writeBytes1.Length, It.IsAny(), null)) - .Callback>((handle, serverOffset, data, offset, length, wait, writeCompleted) - => - { - wait.Set(); - }); - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.IsOpen) - .Returns(true); - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.IsOpen) - .Returns(true); - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.IsOpen) - .Returns(true); - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.RequestWrite(_handle, (ulong) _writeBytes1.Length, It.IsAny(), 0, _writeBytes2.Length + _writeBytes3.Length, It.IsAny(), null)) - .Callback>((handle, serverOffset, data, offset, length, wait, writeCompleted) - => - { - _flushedBytes = data.Take(offset, length); - wait.Set(); - }); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestOpen(_path, Flags.Read | Flags.Write, false)) + .Returns(_handle); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalReadLength(_bufferSize)) + .Returns(_readBufferSize); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalWriteLength(_bufferSize, _handle)) + .Returns(_writeBufferSize); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestWrite(_handle, 0UL, It.IsAny(), 0, _writeBytes1.Length, It.IsAny(), null)) + .Callback>((handle, serverOffset, data, offset, length, wait, writeCompleted) => + { + _ = wait.Set(); + }); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestWrite(_handle, (ulong) _writeBytes1.Length, It.IsAny(), 0, _writeBytes2.Length + _writeBytes3.Length, It.IsAny(), null)) + .Callback>((handle, serverOffset, data, offset, length, wait, writeCompleted) => + { + _flushedBytes = data.Take(offset, length); + _ = wait.Set(); + }); } protected override void Arrange() @@ -102,9 +104,9 @@ namespace Renci.SshNet.Tests.Classes.Sftp [TestMethod] public void PositionShouldReturnSameValueAsBeforeFlush() { - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.IsOpen) - .Returns(true); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); Assert.AreEqual(_writeBytes1.Length + _writeBytes2.Length + _writeBytes3.Length, _target.Position); @@ -131,12 +133,12 @@ namespace Renci.SshNet.Tests.Classes.Sftp .Add(serverBytes.Take(0, 3)) .Build(); - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.IsOpen) - .Returns(true); - SftpSessionMock.InSequence(MockSequence) - .Setup(p => p.RequestRead(_handle, (ulong) (_writeBytes1.Length + _writeBytes2.Length + _writeBytes3.Length), _readBufferSize)) - .Returns(serverBytes); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestRead(_handle, (ulong) (_writeBytes1.Length + _writeBytes2.Length + _writeBytes3.Length), _readBufferSize)) + .Returns(serverBytes); var bytesRead = _target.Read(readBytes, 2, 3); diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileAccessInvalid.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileAccessInvalid.cs new file mode 100644 index 00000000..4febea07 --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileAccessInvalid.cs @@ -0,0 +1,55 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Renci.SshNet.Sftp; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + [TestClass] + public class SftpFileStreamTest_OpenAsync_FileAccessInvalid : SftpFileStreamAsyncTestBase + { + private Random _random; + private string _path; + private FileMode _fileMode; + private FileAccess _fileAccess; + private int _bufferSize; + private ArgumentOutOfRangeException _actualException; + + protected override void SetupData() + { + base.SetupData(); + + _random = new Random(); + _path = _random.Next().ToString(); + _fileMode = FileMode.Open; + _fileAccess = 0; + _bufferSize = _random.Next(5, 1000); + } + + protected override void SetupMocks() + { + } + + protected override async Task ActAsync() + { + try + { + await SftpFileStream.OpenAsync(SftpSessionMock.Object, _path, _fileMode, _fileAccess, _bufferSize, default); + Assert.Fail(); + } + catch (ArgumentOutOfRangeException ex) + { + _actualException = ex; + } + } + + [TestMethod] + public void CtorShouldHaveThrownArgumentException() + { + Assert.IsNotNull(_actualException); + Assert.IsNull(_actualException.InnerException); + Assert.AreEqual("access", _actualException.ParamName); + } + } +} diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeAppend_FileAccessRead.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeAppend_FileAccessRead.cs new file mode 100644 index 00000000..14a2338b --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeAppend_FileAccessRead.cs @@ -0,0 +1,56 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Renci.SshNet.Sftp; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + [TestClass] + public class SftpFileStreamTest_OpenAsync_FileModeAppend_FileAccessRead : SftpFileStreamAsyncTestBase + { + private Random _random; + private string _path; + private FileMode _fileMode; + private FileAccess _fileAccess; + private int _bufferSize; + private ArgumentException _actualException; + + protected override void SetupData() + { + base.SetupData(); + + _random = new Random(); + _path = _random.Next().ToString(); + _fileMode = FileMode.Append; + _fileAccess = FileAccess.Read; + _bufferSize = _random.Next(5, 1000); + } + + protected override void SetupMocks() + { + } + + protected override async Task ActAsync() + { + try + { + await SftpFileStream.OpenAsync(SftpSessionMock.Object, _path, _fileMode, _fileAccess, _bufferSize, default); + Assert.Fail(); + } + catch (ArgumentException ex) + { + _actualException = ex; + } + } + + [TestMethod] + public void CtorShouldHaveThrownArgumentException() + { + Assert.IsNotNull(_actualException); + Assert.IsNull(_actualException.InnerException); + Assert.AreEqual(string.Format("{0} mode can be requested only when combined with write-only access.", _fileMode), _actualException.Message); + Assert.IsNull(_actualException.ParamName); + } + } +} diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeAppend_FileAccessReadWrite.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeAppend_FileAccessReadWrite.cs new file mode 100644 index 00000000..c58f3387 --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeAppend_FileAccessReadWrite.cs @@ -0,0 +1,56 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Renci.SshNet.Sftp; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + [TestClass] + public class SftpFileStreamTest_OpenAsync_FileModeAppend_FileAccessReadWrite : SftpFileStreamAsyncTestBase + { + private Random _random; + private string _path; + private FileMode _fileMode; + private FileAccess _fileAccess; + private int _bufferSize; + private ArgumentException _actualException; + + protected override void SetupData() + { + base.SetupData(); + + _random = new Random(); + _path = _random.Next().ToString(); + _fileMode = FileMode.Append; + _fileAccess = FileAccess.ReadWrite; + _bufferSize = _random.Next(5, 1000); + } + + protected override void SetupMocks() + { + } + + protected override async Task ActAsync() + { + try + { + await SftpFileStream.OpenAsync(SftpSessionMock.Object, _path, _fileMode, _fileAccess, _bufferSize, default); + Assert.Fail(); + } + catch (ArgumentException ex) + { + _actualException = ex; + } + } + + [TestMethod] + public void CtorShouldHaveThrownArgumentException() + { + Assert.IsNotNull(_actualException); + Assert.IsNull(_actualException.InnerException); + Assert.AreEqual(string.Format("{0} mode can be requested only when combined with write-only access.", _fileMode), _actualException.Message); + Assert.IsNull(_actualException.ParamName); + } + } +} diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeAppend_FileAccessWrite.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeAppend_FileAccessWrite.cs new file mode 100644 index 00000000..14cce04e --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeAppend_FileAccessWrite.cs @@ -0,0 +1,164 @@ +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Moq; + +using Renci.SshNet.Sftp; +using Renci.SshNet.Tests.Common; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + [TestClass] + public class SftpFileStreamTest_OpenAsync_FileModeAppend_FileAccessWrite : SftpFileStreamAsyncTestBase + { + private Random _random; + private string _path; + private FileMode _fileMode; + private FileAccess _fileAccess; + private int _bufferSize; + private uint _readBufferSize; + private uint _writeBufferSize; + private byte[] _handle; + private SftpFileStream _target; + private SftpFileAttributes _fileAttributes; + private CancellationToken _cancellationToken; + + protected override void SetupData() + { + base.SetupData(); + + _random = new Random(); + _path = _random.Next().ToString(); + _fileMode = FileMode.Append; + _fileAccess = FileAccess.Write; + _bufferSize = _random.Next(5, 1000); + _readBufferSize = (uint) _random.Next(5, 1000); + _writeBufferSize = (uint) _random.Next(5, 1000); + _handle = GenerateRandom(_random.Next(1, 10), _random); + _fileAttributes = new SftpFileAttributesBuilder().WithLastAccessTime(DateTime.UtcNow.AddSeconds(_random.Next())) + .WithLastWriteTime(DateTime.UtcNow.AddSeconds(_random.Next())) + .WithSize(_random.Next()) + .WithUserId(_random.Next()) + .WithGroupId(_random.Next()) + .WithPermissions((uint) _random.Next()) + .Build(); + _cancellationToken = new CancellationToken(); + } + + protected override void SetupMocks() + { + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestOpenAsync(_path, Flags.Write | Flags.Append | Flags.CreateNewOrOpen, _cancellationToken)) + .ReturnsAsync(_handle); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestFStatAsync(_handle, _cancellationToken)) + .ReturnsAsync(_fileAttributes); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalReadLength((uint)_bufferSize)) + .Returns(_readBufferSize); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalWriteLength((uint)_bufferSize, _handle)) + .Returns(_writeBufferSize); + } + + protected override async Task ActAsync() + { + _target = await SftpFileStream.OpenAsync(SftpSessionMock.Object, _path, _fileMode, _fileAccess, _bufferSize, _cancellationToken); + } + + + [TestMethod] + public void CanReadShouldReturnFalse() + { + Assert.IsFalse(_target.CanRead); + } + + [TestMethod] + public void CanSeekShouldReturnTrue() + { + Assert.IsTrue(_target.CanSeek); + } + + [TestMethod] + public void CanWriteShouldReturnTrue() + { + Assert.IsTrue(_target.CanWrite); + } + + [TestMethod] + public void CanTimeoutShouldReturnTrue() + { + Assert.IsTrue(_target.CanTimeout); + } + + [TestMethod] + public void PositionShouldReturnSizeOfFile() + { + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); + + var actual = _target.Position; + + Assert.AreEqual(_fileAttributes.Size, actual); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + } + + [TestMethod] + public async Task ReadShouldThrowNotSupportedException() + { + var buffer = new byte[_readBufferSize]; + + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); + + try + { + _ = await _target.ReadAsync(buffer, 0, buffer.Length, _cancellationToken); + Assert.Fail(); + } + catch (NotSupportedException ex) + { + Assert.IsNull(ex.InnerException); + } + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + } + + [TestMethod] + public async Task WriteShouldStartWritingAtEndOfFile() + { + var buffer = new byte[_writeBufferSize]; + + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestWriteAsync(_handle, (ulong)_fileAttributes.Size, buffer, 0, buffer.Length, _cancellationToken)) + .Returns(Task.CompletedTask); + + await _target.WriteAsync(buffer, 0, buffer.Length, _cancellationToken); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + SftpSessionMock.Verify(p => p.RequestWriteAsync(_handle, (ulong)_fileAttributes.Size, buffer, 0, buffer.Length, _cancellationToken), Times.Once); + } + + [TestMethod] + public void RequestOpenOnSftpSessionShouldBeInvokedOnce() + { + SftpSessionMock.Verify(p => p.RequestOpenAsync(_path, Flags.Write | Flags.Append | Flags.CreateNewOrOpen, default), Times.Once); + } + + [TestMethod] + public void RequestFStatOnSftpSessionShouldBeInvokedOnce() + { + SftpSessionMock.Verify(p => p.RequestFStatAsync(_handle, default), Times.Once); + } + } +} diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreateNew_FileAccessRead.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreateNew_FileAccessRead.cs new file mode 100644 index 00000000..1d8b792d --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreateNew_FileAccessRead.cs @@ -0,0 +1,58 @@ +using System; +using System.IO; +using System.Threading.Tasks; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Renci.SshNet.Sftp; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + [TestClass] + public class SftpFileStreamTest_OpenAsync_FileModeCreateNew_FileAccessRead : SftpFileStreamAsyncTestBase + { + private Random _random; + private string _path; + private FileMode _fileMode; + private FileAccess _fileAccess; + private int _bufferSize; + private ArgumentException _actualException; + + protected override void SetupData() + { + base.SetupData(); + + _random = new Random(); + _path = _random.Next().ToString(); + _fileMode = FileMode.CreateNew; + _fileAccess = FileAccess.Read; + _bufferSize = _random.Next(5, 1000); + } + + protected override void SetupMocks() + { + } + + protected override async Task ActAsync() + { + try + { + _ = await SftpFileStream.OpenAsync(SftpSessionMock.Object, _path, _fileMode, _fileAccess, _bufferSize, default); + Assert.Fail(); + } + catch (ArgumentException ex) + { + _actualException = ex; + } + } + + [TestMethod] + public void CtorShouldHaveThrownArgumentException() + { + Assert.IsNotNull(_actualException); + Assert.IsNull(_actualException.InnerException); + Assert.AreEqual(string.Format("Combining {0}: {1} with {2}: {3} is invalid.", nameof(FileMode), _fileMode, nameof(FileAccess), _fileAccess), _actualException.Message); + Assert.IsNull(_actualException.ParamName); + } + } +} diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreateNew_FileAccessReadWrite.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreateNew_FileAccessReadWrite.cs new file mode 100644 index 00000000..c1b271dd --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreateNew_FileAccessReadWrite.cs @@ -0,0 +1,134 @@ +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using Renci.SshNet.Common; +using Renci.SshNet.Sftp; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + [TestClass] + public class SftpFileStreamTest_OpenAsync_FileModeCreateNew_FileAccessReadWrite : SftpFileStreamAsyncTestBase + { + private Random _random; + private string _path; + private FileMode _fileMode; + private FileAccess _fileAccess; + private int _bufferSize; + private uint _readBufferSize; + private uint _writeBufferSize; + private byte[] _handle; + private SftpFileStream _target; + private CancellationToken _cancellationToken; + + protected override void SetupData() + { + base.SetupData(); + + _random = new Random(); + _path = _random.Next().ToString(); + _fileMode = FileMode.CreateNew; + _fileAccess = FileAccess.ReadWrite; + _bufferSize = _random.Next(5, 1000); + _readBufferSize = (uint)_random.Next(5, 1000); + _writeBufferSize = (uint)_random.Next(5, 1000); + _handle = GenerateRandom(_random.Next(1, 10), _random); + _cancellationToken = new CancellationToken(); + } + + protected override void SetupMocks() + { + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestOpenAsync(_path, Flags.Read | Flags.Write | Flags.CreateNew, _cancellationToken)) + .ReturnsAsync(_handle); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalReadLength((uint) _bufferSize)) + .Returns(_readBufferSize); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalWriteLength((uint) _bufferSize, _handle)) + .Returns(_writeBufferSize); + } + + protected override async Task ActAsync() + { + _target = await SftpFileStream.OpenAsync(SftpSessionMock.Object, _path, _fileMode, _fileAccess, _bufferSize, _cancellationToken); + } + + [TestMethod] + public void CanReadShouldReturnTrue() + { + Assert.IsTrue(_target.CanRead); + } + + [TestMethod] + public void CanSeekShouldReturnTrue() + { + Assert.IsTrue(_target.CanSeek); + } + + [TestMethod] + public void CanWriteShouldReturnTrue() + { + Assert.IsTrue(_target.CanWrite); + } + + [TestMethod] + public void CanTimeoutShouldReturnTrue() + { + Assert.IsTrue(_target.CanTimeout); + } + + [TestMethod] + public void PositionShouldReturnZero() + { + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + + var actual = _target.Position; + + Assert.AreEqual(0L, actual); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + } + + [TestMethod] + public async Task ReadShouldStartReadingAtBeginningOfFile() + { + var buffer = new byte[8]; + var data = new byte[] { 5, 4, 3, 2, 1 }; + var expected = new byte[] { 0, 5, 4, 3, 2, 1, 0, 0 }; + + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + SftpSessionMock.InSequence(MockSequence).Setup(p => p.RequestReadAsync(_handle, 0UL, _readBufferSize, _cancellationToken)).ReturnsAsync(data); + + var actual = await _target.ReadAsync(buffer, 1, data.Length, _cancellationToken); + + Assert.AreEqual(data.Length, actual); + Assert.IsTrue(buffer.IsEqualTo(expected)); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + SftpSessionMock.Verify(p => p.RequestReadAsync(_handle, 0UL, _readBufferSize, _cancellationToken), Times.Once); + } + + [TestMethod] + public async Task WriteShouldStartWritingAtBeginningOfFile() + { + var buffer = new byte[_writeBufferSize]; + + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + SftpSessionMock.InSequence(MockSequence).Setup(p => p.RequestWriteAsync(_handle, 0UL, buffer, 0, buffer.Length, _cancellationToken)).Returns(Task.CompletedTask); + + await _target.WriteAsync(buffer, 0, buffer.Length, _cancellationToken); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + SftpSessionMock.Verify(p => p.RequestWriteAsync(_handle, 0UL, buffer, 0, buffer.Length, _cancellationToken), Times.Once); + } + + [TestMethod] + public void RequestOpenOnSftpSessionShouldBeInvokedOnce() + { + SftpSessionMock.Verify(p => p.RequestOpenAsync(_path, Flags.Read | Flags.Write | Flags.CreateNew, _cancellationToken), Times.Once); + } + } +} diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreateNew_FileAccessWrite.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreateNew_FileAccessWrite.cs new file mode 100644 index 00000000..98ba5c91 --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreateNew_FileAccessWrite.cs @@ -0,0 +1,145 @@ +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Moq; + +using Renci.SshNet.Sftp; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + [TestClass] + public class SftpFileStreamTest_OpenAsync_FileModeCreateNew_FileAccessWrite : SftpFileStreamAsyncTestBase + { + private Random _random; + private string _path; + private FileMode _fileMode; + private FileAccess _fileAccess; + private int _bufferSize; + private uint _readBufferSize; + private uint _writeBufferSize; + private byte[] _handle; + private SftpFileStream _target; + private CancellationToken _cancellationToken; + + protected override void SetupData() + { + base.SetupData(); + + _random = new Random(); + _path = _random.Next().ToString(); + _fileMode = FileMode.CreateNew; + _fileAccess = FileAccess.Write; + _bufferSize = _random.Next(5, 1000); + _readBufferSize = (uint) _random.Next(5, 1000); + _writeBufferSize = (uint) _random.Next(5, 1000); + _handle = GenerateRandom(_random.Next(1, 10), _random); + _cancellationToken = new CancellationToken(); + } + + protected override void SetupMocks() + { + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestOpenAsync(_path, Flags.Write | Flags.CreateNew, _cancellationToken)) + .ReturnsAsync(_handle); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalReadLength((uint) _bufferSize)) + .Returns(_readBufferSize); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalWriteLength((uint) _bufferSize, _handle)) + .Returns(_writeBufferSize); + } + + protected override async Task ActAsync() + { + _target = await SftpFileStream.OpenAsync(SftpSessionMock.Object, _path, _fileMode, _fileAccess, _bufferSize, _cancellationToken); + } + + [TestMethod] + public void CanReadShouldReturnFalse() + { + Assert.IsFalse(_target.CanRead); + } + + [TestMethod] + public void CanSeekShouldReturnTrue() + { + Assert.IsTrue(_target.CanSeek); + } + + [TestMethod] + public void CanWriteShouldReturnTrue() + { + Assert.IsTrue(_target.CanWrite); + } + + [TestMethod] + public void CanTimeoutShouldReturnTrue() + { + Assert.IsTrue(_target.CanTimeout); + } + + [TestMethod] + public void PositionShouldReturnZero() + { + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); + + var actual = _target.Position; + + Assert.AreEqual(0L, actual); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + } + + [TestMethod] + public async Task ReadShouldThrowNotSupportedException() + { + var buffer = new byte[_readBufferSize]; + + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); + + try + { + _ = await _target.ReadAsync(buffer, 0, buffer.Length); + Assert.Fail(); + } + catch (NotSupportedException ex) + { + Assert.IsNull(ex.InnerException); + } + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + } + + [TestMethod] + public async Task WriteShouldStartWritingAtBeginningOfFile() + { + var buffer = new byte[_writeBufferSize]; + + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestWriteAsync(_handle, 0UL, buffer, 0, buffer.Length, _cancellationToken)) + .Returns(Task.CompletedTask); + + await _target.WriteAsync(buffer, 0, buffer.Length); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + SftpSessionMock.Verify(p => p.RequestWriteAsync(_handle, 0UL, buffer, 0, buffer.Length, _cancellationToken), Times.Once); + } + + [TestMethod] + public void RequestOpenOnSftpSessionShouldBeInvokedOnce() + { + SftpSessionMock.Verify(p => p.RequestOpenAsync(_path, Flags.Write | Flags.CreateNew, _cancellationToken), Times.Once); + } + } +} diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreate_FileAccessRead.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreate_FileAccessRead.cs new file mode 100644 index 00000000..d253c30e --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreate_FileAccessRead.cs @@ -0,0 +1,58 @@ +using System; +using System.IO; +using System.Threading.Tasks; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Renci.SshNet.Sftp; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + [TestClass] + public class SftpFileStreamTest_OpenAsync_FileModeCreate_FileAccessRead : SftpFileStreamAsyncTestBase + { + private Random _random; + private string _path; + private FileMode _fileMode; + private FileAccess _fileAccess; + private int _bufferSize; + private ArgumentException _actualException; + + protected override void SetupData() + { + base.SetupData(); + + _random = new Random(); + _path = _random.Next().ToString(); + _fileMode = FileMode.Create; + _fileAccess = FileAccess.Read; + _bufferSize = _random.Next(5, 1000); + } + + protected override void SetupMocks() + { + } + + protected override async Task ActAsync() + { + try + { + _ = await SftpFileStream.OpenAsync(SftpSessionMock.Object, _path, _fileMode, _fileAccess, _bufferSize, default); + Assert.Fail(); + } + catch (ArgumentException ex) + { + _actualException = ex; + } + } + + [TestMethod] + public void CtorShouldHaveThrownArgumentException() + { + Assert.IsNotNull(_actualException); + Assert.IsNull(_actualException.InnerException); + Assert.AreEqual(string.Format("Combining {0}: {1} with {2}: {3} is invalid.", nameof(FileMode), _fileMode, nameof(FileAccess), _fileAccess), _actualException.Message); + Assert.IsNull(_actualException.ParamName); + } + } +} diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreate_FileAccessReadWrite_FileDoesNotExist.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreate_FileAccessReadWrite_FileDoesNotExist.cs new file mode 100644 index 00000000..100f08ac --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreate_FileAccessReadWrite_FileDoesNotExist.cs @@ -0,0 +1,147 @@ +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Moq; + +using Renci.SshNet.Common; +using Renci.SshNet.Sftp; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + [TestClass] + public class SftpFileStreamTest_OpenAsync_FileModeCreate_FileAccessReadWrite_FileDoesNotExist : SftpFileStreamAsyncTestBase + { + private Random _random; + private string _path; + private FileMode _fileMode; + private FileAccess _fileAccess; + private int _bufferSize; + private uint _readBufferSize; + private uint _writeBufferSize; + private byte[] _handle; + private SftpFileStream _target; + private CancellationToken _cancellationToken; + + protected override void SetupData() + { + base.SetupData(); + + _random = new Random(); + _path = _random.Next().ToString(); + _fileMode = FileMode.Create; + _fileAccess = FileAccess.ReadWrite; + _bufferSize = _random.Next(5, 1000); + _readBufferSize = (uint)_random.Next(5, 1000); + _writeBufferSize = (uint)_random.Next(5, 1000); + _handle = GenerateRandom(_random.Next(1, 10), _random); + _cancellationToken = new CancellationToken(); + } + + protected override void SetupMocks() + { + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestOpenAsync(_path, Flags.Read | Flags.Write | Flags.CreateNewOrOpen | Flags.Truncate, _cancellationToken)) + .ReturnsAsync(_handle); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalReadLength((uint) _bufferSize)) + .Returns(_readBufferSize); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalWriteLength((uint) _bufferSize, _handle)) + .Returns(_writeBufferSize); + } + + protected override async Task ActAsync() + { + _target = await SftpFileStream.OpenAsync(SftpSessionMock.Object, _path, _fileMode, _fileAccess, _bufferSize, _cancellationToken); + } + + [TestMethod] + public void CanReadShouldReturnTrue() + { + Assert.IsTrue(_target.CanRead); + } + + [TestMethod] + public void CanSeekShouldReturnTrue() + { + Assert.IsTrue(_target.CanSeek); + } + + [TestMethod] + public void CanWriteShouldReturnTrue() + { + Assert.IsTrue(_target.CanWrite); + } + + [TestMethod] + public void CanTimeoutShouldReturnTrue() + { + Assert.IsTrue(_target.CanTimeout); + } + + [TestMethod] + public void PositionShouldReturnZero() + { + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); + + var actual = _target.Position; + + Assert.AreEqual(0L, actual); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + } + + [TestMethod] + public async Task ReadShouldStartReadingAtBeginningOfFile() + { + var buffer = new byte[8]; + var data = new byte[] { 5, 4, 3, 2, 1 }; + var expected = new byte[] { 0, 5, 4, 3, 2, 1, 0, 0 }; + + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestReadAsync(_handle, 0UL, _readBufferSize, _cancellationToken)) + .ReturnsAsync(data); + + var actual = await _target.ReadAsync(buffer, 1, data.Length); + + Assert.AreEqual(data.Length, actual); + Assert.IsTrue(buffer.IsEqualTo(expected)); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + SftpSessionMock.Verify(p => p.RequestReadAsync(_handle, 0UL, _readBufferSize, _cancellationToken), Times.Once); + } + + [TestMethod] + public async Task WriteShouldStartWritingAtBeginningOfFile() + { + var buffer = new byte[_writeBufferSize]; + + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestWriteAsync(_handle, 0UL, buffer, 0, buffer.Length, _cancellationToken)) + .Returns(Task.CompletedTask); + + await _target.WriteAsync(buffer, 0, buffer.Length); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + SftpSessionMock.Verify(p => p.RequestWriteAsync(_handle, 0UL, buffer, 0, buffer.Length, _cancellationToken), Times.Once); + } + + [TestMethod] + public void RequestOpenOnSftpSessionShouldBeInvokedOnce() + { + SftpSessionMock.Verify(p => p.RequestOpenAsync(_path, Flags.Read | Flags.Write | Flags.CreateNewOrOpen | Flags.Truncate, _cancellationToken), Times.Once); + } + } +} diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreate_FileAccessReadWrite_FileExists.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreate_FileAccessReadWrite_FileExists.cs new file mode 100644 index 00000000..e2d5d061 --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreate_FileAccessReadWrite_FileExists.cs @@ -0,0 +1,134 @@ +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using Renci.SshNet.Common; +using Renci.SshNet.Sftp; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + [TestClass] + public class SftpFileStreamTest_OpenAsync_FileModeCreate_FileAccessReadWrite_FileExists : SftpFileStreamAsyncTestBase + { + private Random _random; + private string _path; + private FileMode _fileMode; + private FileAccess _fileAccess; + private int _bufferSize; + private uint _readBufferSize; + private uint _writeBufferSize; + private byte[] _handle; + private SftpFileStream _target; + private CancellationToken _cancellationToken; + + protected override void SetupData() + { + base.SetupData(); + + _random = new Random(); + _path = _random.Next().ToString(); + _fileMode = FileMode.Create; + _fileAccess = FileAccess.ReadWrite; + _bufferSize = _random.Next(5, 1000); + _readBufferSize = (uint)_random.Next(5, 1000); + _writeBufferSize = (uint)_random.Next(5, 1000); + _handle = GenerateRandom(_random.Next(1, 10), _random); + _cancellationToken = new CancellationToken(); + } + + protected override void SetupMocks() + { + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestOpenAsync(_path, Flags.Read | Flags.Write | Flags.CreateNewOrOpen | Flags.Truncate, _cancellationToken)) + .ReturnsAsync(_handle); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalReadLength((uint) _bufferSize)) + .Returns(_readBufferSize); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalWriteLength((uint) _bufferSize, _handle)) + .Returns(_writeBufferSize); + } + + protected override async Task ActAsync() + { + _target = await SftpFileStream.OpenAsync(SftpSessionMock.Object, _path, _fileMode, _fileAccess, _bufferSize, _cancellationToken); + } + + [TestMethod] + public void CanReadShouldReturnTrue() + { + Assert.IsTrue(_target.CanRead); + } + + [TestMethod] + public void CanSeekShouldReturnTrue() + { + Assert.IsTrue(_target.CanSeek); + } + + [TestMethod] + public void CanWriteShouldReturnTrue() + { + Assert.IsTrue(_target.CanWrite); + } + + [TestMethod] + public void CanTimeoutShouldReturnTrue() + { + Assert.IsTrue(_target.CanTimeout); + } + + [TestMethod] + public void PositionShouldReturnZero() + { + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + + var actual = _target.Position; + + Assert.AreEqual(0L, actual); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + } + + [TestMethod] + public async Task ReadShouldStartReadingAtBeginningOfFile() + { + var buffer = new byte[8]; + var data = new byte[] { 5, 4, 3, 2, 1 }; + var expected = new byte[] { 0, 5, 4, 3, 2, 1, 0, 0 }; + + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + SftpSessionMock.InSequence(MockSequence).Setup(p => p.RequestReadAsync(_handle, 0UL, _readBufferSize, _cancellationToken)).ReturnsAsync(data); + + var actual = await _target.ReadAsync(buffer, 1, data.Length); + + Assert.AreEqual(data.Length, actual); + Assert.IsTrue(buffer.IsEqualTo(expected)); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + SftpSessionMock.Verify(p => p.RequestReadAsync(_handle, 0UL, _readBufferSize, _cancellationToken), Times.Once); + } + + [TestMethod] + public async Task WriteShouldStartWritingAtBeginningOfFile() + { + var buffer = new byte[_writeBufferSize]; + + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + SftpSessionMock.InSequence(MockSequence).Setup(p => p.RequestWriteAsync(_handle, 0UL, buffer, 0, buffer.Length, _cancellationToken)).Returns(Task.CompletedTask); + + await _target.WriteAsync(buffer, 0, buffer.Length); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + SftpSessionMock.Verify(p => p.RequestWriteAsync(_handle, 0UL, buffer, 0, buffer.Length, _cancellationToken), Times.Once); + } + + [TestMethod] + public void RequestOpenOnSftpSessionShouldBeInvokedOnce() + { + SftpSessionMock.Verify(p => p.RequestOpenAsync(_path, Flags.Read | Flags.Write | Flags.CreateNewOrOpen | Flags.Truncate, _cancellationToken), Times.Once); + } + } +} diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreate_FileAccessWrite_FileDoesNotExist.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreate_FileAccessWrite_FileDoesNotExist.cs new file mode 100644 index 00000000..aa29b739 --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreate_FileAccessWrite_FileDoesNotExist.cs @@ -0,0 +1,134 @@ +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using Renci.SshNet.Sftp; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + [TestClass] + public class SftpFileStreamTest_OpenAsync_FileModeCreate_FileAccessWrite_FileDoesNotExist : SftpFileStreamAsyncTestBase + { + private Random _random; + private string _path; + private FileMode _fileMode; + private FileAccess _fileAccess; + private int _bufferSize; + private uint _readBufferSize; + private uint _writeBufferSize; + private byte[] _handle; + private SftpFileStream _target; + private CancellationToken _cancellationToken; + + protected override void SetupData() + { + base.SetupData(); + + _random = new Random(); + _path = _random.Next().ToString(); + _fileMode = FileMode.Create; + _fileAccess = FileAccess.Write; + _bufferSize = _random.Next(5, 1000); + _readBufferSize = (uint)_random.Next(5, 1000); + _writeBufferSize = (uint)_random.Next(5, 1000); + _handle = GenerateRandom(_random.Next(1, 10), _random); + _cancellationToken = new CancellationToken(); + } + + protected override void SetupMocks() + { + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestOpenAsync(_path, Flags.Write | Flags.CreateNewOrOpen | Flags.Truncate, _cancellationToken)) + .ReturnsAsync(_handle); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalReadLength((uint) _bufferSize)) + .Returns(_readBufferSize); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalWriteLength((uint) _bufferSize, _handle)) + .Returns(_writeBufferSize); + } + + protected override async Task ActAsync() + { + _target = await SftpFileStream.OpenAsync(SftpSessionMock.Object, _path, _fileMode, _fileAccess, _bufferSize, _cancellationToken); + } + + [TestMethod] + public void CanReadShouldReturnFalse() + { + Assert.IsFalse(_target.CanRead); + } + + [TestMethod] + public void CanSeekShouldReturnTrue() + { + Assert.IsTrue(_target.CanSeek); + } + + [TestMethod] + public void CanWriteShouldReturnTrue() + { + Assert.IsTrue(_target.CanWrite); + } + + [TestMethod] + public void CanTimeoutShouldReturnTrue() + { + Assert.IsTrue(_target.CanTimeout); + } + + [TestMethod] + public void PositionShouldReturnZero() + { + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + + var actual = _target.Position; + + Assert.AreEqual(0L, actual); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + } + + [TestMethod] + public async Task ReadShouldThrowNotSupportedException() + { + var buffer = new byte[_readBufferSize]; + + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + + try + { + await _target.ReadAsync(buffer, 0, buffer.Length); + Assert.Fail(); + } + catch (NotSupportedException ex) + { + Assert.IsNull(ex.InnerException); + } + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + } + + [TestMethod] + public async Task WriteShouldStartWritingAtBeginningOfFile() + { + var buffer = new byte[_writeBufferSize]; + + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + SftpSessionMock.InSequence(MockSequence).Setup(p => p.RequestWriteAsync(_handle, 0UL, buffer, 0, buffer.Length, _cancellationToken)).Returns(Task.CompletedTask); + + await _target.WriteAsync(buffer, 0, buffer.Length); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + SftpSessionMock.Verify(p => p.RequestWriteAsync(_handle, 0UL, buffer, 0, buffer.Length, _cancellationToken), Times.Once); + } + + [TestMethod] + public void RequestOpenOnSftpSessionShouldBeInvokedOnceWithTruncateAndOnceWithCreateNew() + { + SftpSessionMock.Verify(p => p.RequestOpenAsync(_path, Flags.Write | Flags.CreateNewOrOpen | Flags.Truncate, _cancellationToken), Times.Once); + } + } +} \ No newline at end of file diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreate_FileAccessWrite_FileExists.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreate_FileAccessWrite_FileExists.cs new file mode 100644 index 00000000..ceb38763 --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreate_FileAccessWrite_FileExists.cs @@ -0,0 +1,145 @@ +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Moq; + +using Renci.SshNet.Sftp; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + [TestClass] + public class SftpFileStreamTest_OpenAsync_FileModeCreate_FileAccessWrite_FileExists : SftpFileStreamAsyncTestBase + { + private Random _random; + private string _path; + private FileMode _fileMode; + private FileAccess _fileAccess; + private int _bufferSize; + private uint _readBufferSize; + private uint _writeBufferSize; + private byte[] _handle; + private SftpFileStream _target; + private CancellationToken _cancellationToken; + + protected override void SetupData() + { + base.SetupData(); + + _random = new Random(); + _path = _random.Next().ToString(); + _fileMode = FileMode.Create; + _fileAccess = FileAccess.Write; + _bufferSize = _random.Next(5, 1000); + _readBufferSize = (uint) _random.Next(5, 1000); + _writeBufferSize = (uint) _random.Next(5, 1000); + _handle = GenerateRandom(_random.Next(1, 10), _random); + _cancellationToken = new CancellationToken(); + } + + protected override void SetupMocks() + { + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestOpenAsync(_path, Flags.Write | Flags.CreateNewOrOpen | Flags.Truncate, _cancellationToken)) + .ReturnsAsync(_handle); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalReadLength((uint) _bufferSize)) + .Returns(_readBufferSize); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalWriteLength((uint) _bufferSize, _handle)) + .Returns(_writeBufferSize); + } + + protected override async Task ActAsync() + { + _target = await SftpFileStream.OpenAsync(SftpSessionMock.Object, _path, _fileMode, _fileAccess, _bufferSize, _cancellationToken); + } + + [TestMethod] + public void CanReadShouldReturnFalse() + { + Assert.IsFalse(_target.CanRead); + } + + [TestMethod] + public void CanSeekShouldReturnTrue() + { + Assert.IsTrue(_target.CanSeek); + } + + [TestMethod] + public void CanWriteShouldReturnTrue() + { + Assert.IsTrue(_target.CanWrite); + } + + [TestMethod] + public void CanTimeoutShouldReturnTrue() + { + Assert.IsTrue(_target.CanTimeout); + } + + [TestMethod] + public void PositionShouldReturnZero() + { + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); + + var actual = _target.Position; + + Assert.AreEqual(0L, actual); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + } + + [TestMethod] + public async Task ReadShouldThrowNotSupportedException() + { + var buffer = new byte[_readBufferSize]; + + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); + + try + { + _ = await _target.ReadAsync(buffer, 0, buffer.Length); + Assert.Fail(); + } + catch (NotSupportedException ex) + { + Assert.IsNull(ex.InnerException); + } + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + } + + [TestMethod] + public async Task WriteShouldStartWritingAtBeginningOfFile() + { + var buffer = new byte[_writeBufferSize]; + + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestWriteAsync(_handle, 0UL, buffer, 0, buffer.Length, _cancellationToken)) + .Returns(Task.CompletedTask); + + await _target.WriteAsync(buffer, 0, buffer.Length); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + SftpSessionMock.Verify(p => p.RequestWriteAsync(_handle, 0UL, buffer, 0, buffer.Length, _cancellationToken), Times.Once); + } + + [TestMethod] + public void RequestOpenOnSftpSessionShouldBeInvokedOnce() + { + SftpSessionMock.Verify(p => p.RequestOpenAsync(_path, Flags.Write | Flags.CreateNewOrOpen | Flags.Truncate, _cancellationToken), Times.Once); + } + } +} diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeInvalid.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeInvalid.cs new file mode 100644 index 00000000..075967ac --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeInvalid.cs @@ -0,0 +1,55 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Renci.SshNet.Sftp; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + [TestClass] + public class SftpFileStreamTest_OpenAsync_FileModeInvalid : SftpFileStreamAsyncTestBase + { + private Random _random; + private string _path; + private FileMode _fileMode; + private FileAccess _fileAccess; + private int _bufferSize; + private ArgumentOutOfRangeException _actualException; + + protected override void SetupData() + { + base.SetupData(); + + _random = new Random(); + _path = _random.Next().ToString(); + _fileMode = 0; + _fileAccess = FileAccess.Read; + _bufferSize = _random.Next(5, 1000); + } + + protected override void SetupMocks() + { + } + + protected override async Task ActAsync() + { + try + { + await SftpFileStream.OpenAsync(SftpSessionMock.Object, _path, _fileMode, _fileAccess, _bufferSize, default); + Assert.Fail(); + } + catch (ArgumentOutOfRangeException ex) + { + _actualException = ex; + } + } + + [TestMethod] + public void CtorShouldHaveThrownArgumentException() + { + Assert.IsNotNull(_actualException); + Assert.IsNull(_actualException.InnerException); + Assert.AreEqual("mode", _actualException.ParamName); + } + } +} diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeOpenOrCreate_FileAccessRead.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeOpenOrCreate_FileAccessRead.cs new file mode 100644 index 00000000..15a40915 --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeOpenOrCreate_FileAccessRead.cs @@ -0,0 +1,139 @@ +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using Renci.SshNet.Common; +using Renci.SshNet.Sftp; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + [TestClass] + public class SftpFileStreamTest_OpenAsync_FileModeOpenOrCreate_FileAccessRead : SftpFileStreamAsyncTestBase + { + private Random _random; + private string _path; + private FileMode _fileMode; + private FileAccess _fileAccess; + private int _bufferSize; + private uint _readBufferSize; + private uint _writeBufferSize; + private byte[] _handle; + private SftpFileStream _target; + private CancellationToken _cancellationToken; + + protected override void SetupData() + { + base.SetupData(); + + _random = new Random(); + _path = _random.Next().ToString(); + _fileMode = FileMode.OpenOrCreate; + _fileAccess = FileAccess.Read; + _bufferSize = _random.Next(5, 1000); + _readBufferSize = (uint) _random.Next(5, 1000); + _writeBufferSize = (uint) _random.Next(5, 1000); + _handle = GenerateRandom(_random.Next(1, 10), _random); + _cancellationToken = new CancellationToken(); + } + + protected override void SetupMocks() + { + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestOpenAsync(_path, Flags.Read | Flags.CreateNewOrOpen, _cancellationToken)) + .ReturnsAsync(_handle); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalReadLength((uint) _bufferSize)) + .Returns(_readBufferSize); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalWriteLength((uint) _bufferSize, _handle)) + .Returns(_writeBufferSize); + } + + protected override async Task ActAsync() + { + _target = await SftpFileStream.OpenAsync(SftpSessionMock.Object, _path, _fileMode, _fileAccess, _bufferSize, _cancellationToken); + } + + [TestMethod] + public void CanReadShouldReturnTrue() + { + Assert.IsTrue(_target.CanRead); + } + + [TestMethod] + public void CanSeekShouldReturnTrue() + { + Assert.IsTrue(_target.CanSeek); + } + + [TestMethod] + public void CanWriteShouldReturnFalse() + { + Assert.IsFalse(_target.CanWrite); + } + + [TestMethod] + public void CanTimeoutShouldReturnTrue() + { + Assert.IsTrue(_target.CanTimeout); + } + + [TestMethod] + public void PositionShouldReturnZero() + { + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + + var actual = _target.Position; + + Assert.AreEqual(0L, actual); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + } + + [TestMethod] + public async Task ReadShouldStartReadingAtBeginningOfFile() + { + var buffer = new byte[8]; + var data = new byte[] { 5, 4, 3, 2, 1 }; + var expected = new byte[] { 0, 5, 4, 3, 2, 1, 0, 0 }; + + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + SftpSessionMock.InSequence(MockSequence).Setup(p => p.RequestReadAsync(_handle, 0UL, _readBufferSize, _cancellationToken)).ReturnsAsync(data); + + var actual = await _target.ReadAsync(buffer, 1, data.Length); + + Assert.AreEqual(data.Length, actual); + Assert.IsTrue(buffer.IsEqualTo(expected)); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + SftpSessionMock.Verify(p => p.RequestReadAsync(_handle, 0UL, _readBufferSize, _cancellationToken), Times.Once); + } + + [TestMethod] + public async Task WriteShouldThrowNotSupportedException() + { + var buffer = new byte[_writeBufferSize]; + + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + + try + { + await _target.WriteAsync(buffer, 0, buffer.Length); + } + catch (NotSupportedException ex) + { + Assert.IsNull(ex.InnerException); + } + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + } + + [TestMethod] + public void RequestOpenOnSftpSessionShouldBeInvokedOnce() + { + SftpSessionMock.Verify(p => p.RequestOpenAsync(_path, Flags.Read | Flags.CreateNewOrOpen, _cancellationToken), Times.Once); + } + } +} \ No newline at end of file diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeOpenOrCreate_FileAccessReadWrite.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeOpenOrCreate_FileAccessReadWrite.cs new file mode 100644 index 00000000..5803444e --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeOpenOrCreate_FileAccessReadWrite.cs @@ -0,0 +1,147 @@ +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Moq; + +using Renci.SshNet.Common; +using Renci.SshNet.Sftp; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + [TestClass] + public class SftpFileStreamTest_OpenAsync_FileModeOpenOrCreate_FileAccessReadWrite : SftpFileStreamAsyncTestBase + { + private Random _random; + private string _path; + private FileMode _fileMode; + private FileAccess _fileAccess; + private int _bufferSize; + private uint _readBufferSize; + private uint _writeBufferSize; + private byte[] _handle; + private SftpFileStream _target; + private CancellationToken _cancellationToken; + + protected override void SetupData() + { + base.SetupData(); + + _random = new Random(); + _path = _random.Next().ToString(); + _fileMode = FileMode.OpenOrCreate; + _fileAccess = FileAccess.ReadWrite; + _bufferSize = _random.Next(5, 1000); + _readBufferSize = (uint)_random.Next(5, 1000); + _writeBufferSize = (uint)_random.Next(5, 1000); + _handle = GenerateRandom(_random.Next(1, 10), _random); + _cancellationToken = new CancellationToken(); + } + + protected override void SetupMocks() + { + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestOpenAsync(_path, Flags.Read | Flags.Write | Flags.CreateNewOrOpen, _cancellationToken)) + .ReturnsAsync(_handle); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalReadLength((uint) _bufferSize)) + .Returns(_readBufferSize); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalWriteLength((uint) _bufferSize, _handle)) + .Returns(_writeBufferSize); + } + + protected override async Task ActAsync() + { + _target = await SftpFileStream.OpenAsync(SftpSessionMock.Object, _path, _fileMode, _fileAccess, _bufferSize, _cancellationToken); + } + + [TestMethod] + public void CanReadShouldReturnTrue() + { + Assert.IsTrue(_target.CanRead); + } + + [TestMethod] + public void CanSeekShouldReturnTrue() + { + Assert.IsTrue(_target.CanSeek); + } + + [TestMethod] + public void CanWriteShouldReturnTrue() + { + Assert.IsTrue(_target.CanWrite); + } + + [TestMethod] + public void CanTimeoutShouldReturnTrue() + { + Assert.IsTrue(_target.CanTimeout); + } + + [TestMethod] + public void PositionShouldReturnZero() + { + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); + + var actual = _target.Position; + + Assert.AreEqual(0L, actual); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + } + + [TestMethod] + public async Task ReadShouldStartReadingAtBeginningOfFile() + { + var buffer = new byte[8]; + var data = new byte[] { 5, 4, 3, 2, 1 }; + var expected = new byte[] { 0, 5, 4, 3, 2, 1, 0, 0 }; + + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestReadAsync(_handle, 0UL, _readBufferSize, _cancellationToken)) + .ReturnsAsync(data); + + var actual = await _target.ReadAsync(buffer, 1, data.Length); + + Assert.AreEqual(data.Length, actual); + Assert.IsTrue(buffer.IsEqualTo(expected)); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + SftpSessionMock.Verify(p => p.RequestReadAsync(_handle, 0UL, _readBufferSize, _cancellationToken), Times.Once); + } + + [TestMethod] + public async Task WriteShouldStartWritingAtBeginningOfFile() + { + var buffer = new byte[_writeBufferSize]; + + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestWriteAsync(_handle, 0UL, buffer, 0, buffer.Length, _cancellationToken)) + .Returns(Task.CompletedTask); + + await _target.WriteAsync(buffer, 0, buffer.Length); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + SftpSessionMock.Verify(p => p.RequestWriteAsync(_handle, 0UL, buffer, 0, buffer.Length, _cancellationToken), Times.Once); + } + + [TestMethod] + public void RequestOpenOnSftpSessionShouldBeInvokedOnce() + { + SftpSessionMock.Verify(p => p.RequestOpenAsync(_path, Flags.Read | Flags.Write | Flags.CreateNewOrOpen, _cancellationToken), Times.Once); + } + } +} diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeOpenOrCreate_FileAccessWrite.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeOpenOrCreate_FileAccessWrite.cs new file mode 100644 index 00000000..0b69f581 --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeOpenOrCreate_FileAccessWrite.cs @@ -0,0 +1,134 @@ +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using Renci.SshNet.Sftp; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + [TestClass] + public class SftpFileStreamTest_OpenAsync_FileModeOpenOrCreate_FileAccessWrite : SftpFileStreamAsyncTestBase + { + private Random _random; + private string _path; + private FileMode _fileMode; + private FileAccess _fileAccess; + private int _bufferSize; + private uint _readBufferSize; + private uint _writeBufferSize; + private byte[] _handle; + private SftpFileStream _target; + private CancellationToken _cancellationToken; + + protected override void SetupData() + { + base.SetupData(); + + _random = new Random(); + _path = _random.Next().ToString(); + _fileMode = FileMode.OpenOrCreate; + _fileAccess = FileAccess.Write; + _bufferSize = _random.Next(5, 1000); + _readBufferSize = (uint) _random.Next(5, 1000); + _writeBufferSize = (uint) _random.Next(5, 1000); + _handle = GenerateRandom(_random.Next(1, 10), _random); + _cancellationToken = new CancellationToken(); + } + + protected override void SetupMocks() + { + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestOpenAsync(_path, Flags.Write | Flags.CreateNewOrOpen, _cancellationToken)) + .ReturnsAsync(_handle); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalReadLength((uint) _bufferSize)) + .Returns(_readBufferSize); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalWriteLength((uint) _bufferSize, _handle)) + .Returns(_writeBufferSize); + } + + protected override async Task ActAsync() + { + _target = await SftpFileStream.OpenAsync(SftpSessionMock.Object, _path, _fileMode, _fileAccess, _bufferSize, _cancellationToken); + } + + [TestMethod] + public void CanReadShouldReturnFalse() + { + Assert.IsFalse(_target.CanRead); + } + + [TestMethod] + public void CanSeekShouldReturnTrue() + { + Assert.IsTrue(_target.CanSeek); + } + + [TestMethod] + public void CanWriteShouldReturnTrue() + { + Assert.IsTrue(_target.CanWrite); + } + + [TestMethod] + public void CanTimeoutShouldReturnTrue() + { + Assert.IsTrue(_target.CanTimeout); + } + + [TestMethod] + public void PositionShouldReturnZero() + { + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + + var actual = _target.Position; + + Assert.AreEqual(0L, actual); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + } + + [TestMethod] + public async Task ReadShouldThrowNotSupportedException() + { + var buffer = new byte[_readBufferSize]; + + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + + try + { + await _target.ReadAsync(buffer, 0, buffer.Length); + Assert.Fail(); + } + catch (NotSupportedException ex) + { + Assert.IsNull(ex.InnerException); + } + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + } + + [TestMethod] + public async Task WriteShouldStartWritingAtBeginningOfFile() + { + var buffer = new byte[_writeBufferSize]; + + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + SftpSessionMock.InSequence(MockSequence).Setup(p => p.RequestWriteAsync(_handle, 0UL, buffer, 0, buffer.Length, _cancellationToken)).Returns(Task.CompletedTask); + + await _target.WriteAsync(buffer, 0, buffer.Length); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + SftpSessionMock.Verify(p => p.RequestWriteAsync(_handle, 0UL, buffer, 0, buffer.Length, _cancellationToken), Times.Once); + } + + [TestMethod] + public void RequestOpenOnSftpSessionShouldBeInvokedOnce() + { + SftpSessionMock.Verify(p => p.RequestOpenAsync(_path, Flags.Write | Flags.CreateNewOrOpen, _cancellationToken), Times.Once); + } + } +} diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeOpen_FileAccessRead.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeOpen_FileAccessRead.cs new file mode 100644 index 00000000..76387888 --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeOpen_FileAccessRead.cs @@ -0,0 +1,140 @@ +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using Renci.SshNet.Common; +using Renci.SshNet.Sftp; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + [TestClass] + public class SftpFileStreamTest_OpenAsync_FileModeOpen_FileAccessRead : SftpFileStreamAsyncTestBase + { + private Random _random; + private string _path; + private FileMode _fileMode; + private FileAccess _fileAccess; + private int _bufferSize; + private uint _readBufferSize; + private uint _writeBufferSize; + private byte[] _handle; + private SftpFileStream _target; + private CancellationToken _cancellationToken; + + protected override void SetupData() + { + base.SetupData(); + + _random = new Random(); + _path = _random.Next().ToString(); + _fileMode = FileMode.Open; + _fileAccess = FileAccess.Read; + _bufferSize = _random.Next(5, 1000); + _readBufferSize = (uint)_random.Next(5, 1000); + _writeBufferSize = (uint)_random.Next(5, 1000); + _handle = GenerateRandom(_random.Next(1, 10), _random); + _cancellationToken = new CancellationToken(); + } + + protected override void SetupMocks() + { + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestOpenAsync(_path, Flags.Read, _cancellationToken)) + .ReturnsAsync(_handle); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalReadLength((uint) _bufferSize)) + .Returns(_readBufferSize); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalWriteLength((uint) _bufferSize, _handle)) + .Returns(_writeBufferSize); + } + + protected override async Task ActAsync() + { + _target = await SftpFileStream.OpenAsync(SftpSessionMock.Object, _path, _fileMode, _fileAccess, _bufferSize, _cancellationToken); + } + + [TestMethod] + public void CanReadShouldReturnTrue() + { + Assert.IsTrue(_target.CanRead); + } + + [TestMethod] + public void CanSeekShouldReturnTrue() + { + Assert.IsTrue(_target.CanSeek); + } + + [TestMethod] + public void CanWriteShouldReturnFalse() + { + Assert.IsFalse(_target.CanWrite); + } + + [TestMethod] + public void CanTimeoutShouldReturnTrue() + { + Assert.IsTrue(_target.CanTimeout); + } + + [TestMethod] + public void PositionShouldReturnZero() + { + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + + var actual = _target.Position; + + Assert.AreEqual(0L, actual); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + } + + [TestMethod] + public async Task ReadShouldStartReadingAtBeginningOfFile() + { + var buffer = new byte[8]; + var data = new byte[] { 5, 4, 3, 2, 1 }; + var expected = new byte[] { 0, 5, 4, 3, 2, 1, 0, 0 }; + + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + SftpSessionMock.InSequence(MockSequence).Setup(p => p.RequestReadAsync(_handle, 0UL, _readBufferSize, _cancellationToken)).ReturnsAsync(data); + + var actual = await _target.ReadAsync(buffer, 1, data.Length, _cancellationToken); + + Assert.AreEqual(data.Length, actual); + Assert.IsTrue(buffer.IsEqualTo(expected)); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + SftpSessionMock.Verify(p => p.RequestReadAsync(_handle, 0UL, _readBufferSize, _cancellationToken), Times.Once); + } + + [TestMethod] + public async Task WriteShouldThrowNotSupportedException() + { + var buffer = new byte[_writeBufferSize]; + + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + + try + { + await _target.WriteAsync(buffer, 0, buffer.Length, _cancellationToken); + } + catch (NotSupportedException ex) + { + Assert.IsNull(ex.InnerException); + Assert.AreEqual("Write not supported.", ex.Message); + } + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + } + + [TestMethod] + public void RequestOpenOnSftpSessionShouldBeInvokedOnce() + { + SftpSessionMock.Verify(p => p.RequestOpenAsync(_path, Flags.Read, _cancellationToken), Times.Once); + } + } +} diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeOpen_FileAccessReadWrite.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeOpen_FileAccessReadWrite.cs new file mode 100644 index 00000000..7a653cb2 --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeOpen_FileAccessReadWrite.cs @@ -0,0 +1,147 @@ +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Moq; + +using Renci.SshNet.Common; +using Renci.SshNet.Sftp; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + [TestClass] + public class SftpFileStreamTest_OpenAsync_FileModeOpen_FileAccessReadWrite : SftpFileStreamAsyncTestBase + { + private Random _random; + private string _path; + private FileMode _fileMode; + private FileAccess _fileAccess; + private int _bufferSize; + private uint _readBufferSize; + private uint _writeBufferSize; + private byte[] _handle; + private SftpFileStream _target; + private CancellationToken _cancellationToken; + + protected override void SetupData() + { + base.SetupData(); + + _random = new Random(); + _path = _random.Next().ToString(); + _fileMode = FileMode.Open; + _fileAccess = FileAccess.ReadWrite; + _bufferSize = _random.Next(5, 1000); + _readBufferSize = (uint) _random.Next(5, 1000); + _writeBufferSize = (uint) _random.Next(5, 1000); + _handle = GenerateRandom(_random.Next(1, 10), _random); + _cancellationToken = new CancellationToken(); + } + + protected override void SetupMocks() + { + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestOpenAsync(_path, Flags.Read | Flags.Write, _cancellationToken)) + .ReturnsAsync(_handle); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalReadLength((uint) _bufferSize)) + .Returns(_readBufferSize); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalWriteLength((uint) _bufferSize, _handle)) + .Returns(_writeBufferSize); + } + + protected override async Task ActAsync() + { + _target = await SftpFileStream.OpenAsync(SftpSessionMock.Object, _path, _fileMode, _fileAccess, _bufferSize, _cancellationToken); + } + + [TestMethod] + public void CanReadShouldReturnTrue() + { + Assert.IsTrue(_target.CanRead); + } + + [TestMethod] + public void CanSeekShouldReturnTrue() + { + Assert.IsTrue(_target.CanSeek); + } + + [TestMethod] + public void CanWriteShouldReturnTrue() + { + Assert.IsTrue(_target.CanWrite); + } + + [TestMethod] + public void CanTimeoutShouldReturnTrue() + { + Assert.IsTrue(_target.CanTimeout); + } + + [TestMethod] + public void PositionShouldReturnZero() + { + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); + + var actual = _target.Position; + + Assert.AreEqual(0L, actual); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + } + + [TestMethod] + public async Task ReadShouldStartReadingAtBeginningOfFile() + { + var buffer = new byte[8]; + var data = new byte[] { 5, 4, 3, 2, 1 }; + var expected = new byte[] { 0, 5, 4, 3, 2, 1, 0, 0 }; + + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestReadAsync(_handle, 0UL, _readBufferSize, _cancellationToken)) + .ReturnsAsync(data); + + var actual = await _target.ReadAsync(buffer, 1, data.Length); + + Assert.AreEqual(data.Length, actual); + Assert.IsTrue(buffer.IsEqualTo(expected)); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + SftpSessionMock.Verify(p => p.RequestReadAsync(_handle, 0UL, _readBufferSize, _cancellationToken), Times.Once); + } + + [TestMethod] + public async Task WriteShouldStartWritingAtBeginningOfFile() + { + var buffer = new byte[_writeBufferSize]; + + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); + _ = SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestWriteAsync(_handle, 0UL, buffer, 0, buffer.Length, _cancellationToken)) + .Returns(Task.CompletedTask); + + await _target.WriteAsync(buffer, 0, buffer.Length); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + SftpSessionMock.Verify(p => p.RequestWriteAsync(_handle, 0UL, buffer, 0, buffer.Length, _cancellationToken), Times.Once); + } + + [TestMethod] + public void RequestOpenOnSftpSessionShouldBeInvokedOnce() + { + SftpSessionMock.Verify(p => p.RequestOpenAsync(_path, Flags.Read | Flags.Write, _cancellationToken), Times.Once); + } + } +} diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeOpen_FileAccessWrite.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeOpen_FileAccessWrite.cs new file mode 100644 index 00000000..db61a2a6 --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeOpen_FileAccessWrite.cs @@ -0,0 +1,134 @@ +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using Renci.SshNet.Sftp; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + [TestClass] + public class SftpFileStreamTest_OpenAsync_FileModeOpen_FileAccessWrite : SftpFileStreamAsyncTestBase + { + private Random _random; + private string _path; + private FileMode _fileMode; + private FileAccess _fileAccess; + private int _bufferSize; + private uint _readBufferSize; + private uint _writeBufferSize; + private byte[] _handle; + private SftpFileStream _target; + private CancellationToken _cancellationToken; + + protected override void SetupData() + { + base.SetupData(); + + _random = new Random(); + _path = _random.Next().ToString(); + _fileMode = FileMode.Open; + _fileAccess = FileAccess.Write; + _bufferSize = _random.Next(5, 1000); + _readBufferSize = (uint) _random.Next(5, 1000); + _writeBufferSize = (uint) _random.Next(5, 1000); + _handle = GenerateRandom(_random.Next(1, 10), _random); + _cancellationToken = new CancellationToken(); + } + + protected override void SetupMocks() + { + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestOpenAsync(_path, Flags.Write, _cancellationToken)) + .ReturnsAsync(_handle); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalReadLength((uint) _bufferSize)) + .Returns(_readBufferSize); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalWriteLength((uint) _bufferSize, _handle)) + .Returns(_writeBufferSize); + } + + protected override async Task ActAsync() + { + _target = await SftpFileStream.OpenAsync(SftpSessionMock.Object, _path, _fileMode, _fileAccess, _bufferSize, _cancellationToken); + } + + [TestMethod] + public void CanReadShouldReturnFalse() + { + Assert.IsFalse(_target.CanRead); + } + + [TestMethod] + public void CanSeekShouldReturnTrue() + { + Assert.IsTrue(_target.CanSeek); + } + + [TestMethod] + public void CanWriteShouldReturnTrue() + { + Assert.IsTrue(_target.CanWrite); + } + + [TestMethod] + public void CanTimeoutShouldReturnTrue() + { + Assert.IsTrue(_target.CanTimeout); + } + + [TestMethod] + public void PositionShouldReturnZero() + { + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + + var actual = _target.Position; + + Assert.AreEqual(0L, actual); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + } + + [TestMethod] + public async Task ReadShouldThrowNotSupportedException() + { + var buffer = new byte[_readBufferSize]; + + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + + try + { + await _target.ReadAsync(buffer, 0, buffer.Length); + Assert.Fail(); + } + catch (NotSupportedException ex) + { + Assert.IsNull(ex.InnerException); + } + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + } + + [TestMethod] + public async Task WriteShouldStartWritingAtBeginningOfFile() + { + var buffer = new byte[_writeBufferSize]; + + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + SftpSessionMock.InSequence(MockSequence).Setup(p => p.RequestWriteAsync(_handle, 0UL, buffer, 0, buffer.Length, _cancellationToken)).Returns(Task.CompletedTask); + + await _target.WriteAsync(buffer, 0, buffer.Length); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + SftpSessionMock.Verify(p => p.RequestWriteAsync(_handle, 0UL, buffer, 0, buffer.Length, _cancellationToken), Times.Once); + } + + [TestMethod] + public void RequestOpenOnSftpSessionShouldBeInvokedOnce() + { + SftpSessionMock.Verify(p => p.RequestOpenAsync(_path, Flags.Write, _cancellationToken), Times.Once); + } + } +} diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeTruncate_FileAccessRead.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeTruncate_FileAccessRead.cs new file mode 100644 index 00000000..660bd1a1 --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeTruncate_FileAccessRead.cs @@ -0,0 +1,58 @@ +using System; +using System.IO; +using System.Threading.Tasks; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Renci.SshNet.Sftp; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + [TestClass] + public class SftpFileStreamTest_OpenAsync_FileModeTruncate_FileAccessRead : SftpFileStreamAsyncTestBase + { + private Random _random; + private string _path; + private FileMode _fileMode; + private FileAccess _fileAccess; + private int _bufferSize; + private ArgumentException _actualException; + + protected override void SetupData() + { + base.SetupData(); + + _random = new Random(); + _path = _random.Next().ToString(); + _fileMode = FileMode.Truncate; + _fileAccess = FileAccess.Read; + _bufferSize = _random.Next(5, 1000); + } + + protected override void SetupMocks() + { + } + + protected override async Task ActAsync() + { + try + { + _ = await SftpFileStream.OpenAsync(SftpSessionMock.Object, _path, _fileMode, _fileAccess, _bufferSize, default); + Assert.Fail(); + } + catch (ArgumentException ex) + { + _actualException = ex; + } + } + + [TestMethod] + public void CtorShouldHaveThrownArgumentException() + { + Assert.IsNotNull(_actualException); + Assert.IsNull(_actualException.InnerException); + Assert.AreEqual(string.Format("Combining {0}: {1} with {2}: {3} is invalid.", nameof(FileMode), _fileMode, nameof(FileAccess), _fileAccess), _actualException.Message); + Assert.IsNull(_actualException.ParamName); + } + } +} diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeTruncate_FileAccessReadWrite.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeTruncate_FileAccessReadWrite.cs new file mode 100644 index 00000000..ecde0214 --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeTruncate_FileAccessReadWrite.cs @@ -0,0 +1,134 @@ +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using Renci.SshNet.Common; +using Renci.SshNet.Sftp; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + [TestClass] + public class SftpFileStreamTest_OpenAsync_FileModeTruncate_FileAccessReadWrite : SftpFileStreamAsyncTestBase + { + private Random _random; + private string _path; + private FileMode _fileMode; + private FileAccess _fileAccess; + private int _bufferSize; + private uint _readBufferSize; + private uint _writeBufferSize; + private byte[] _handle; + private SftpFileStream _target; + private CancellationToken _cancellationToken; + + protected override void SetupData() + { + base.SetupData(); + + _random = new Random(); + _path = _random.Next().ToString(); + _fileMode = FileMode.Truncate; + _fileAccess = FileAccess.ReadWrite; + _bufferSize = _random.Next(5, 1000); + _readBufferSize = (uint) _random.Next(5, 1000); + _writeBufferSize = (uint) _random.Next(5, 1000); + _handle = GenerateRandom(_random.Next(1, 10), _random); + _cancellationToken = new CancellationToken(); + } + + protected override void SetupMocks() + { + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestOpenAsync(_path, Flags.Read | Flags.Write | Flags.Truncate, _cancellationToken)) + .ReturnsAsync(_handle); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalReadLength((uint) _bufferSize)) + .Returns(_readBufferSize); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalWriteLength((uint) _bufferSize, _handle)) + .Returns(_writeBufferSize); + } + + protected override async Task ActAsync() + { + _target = await SftpFileStream.OpenAsync(SftpSessionMock.Object, _path, _fileMode, _fileAccess, _bufferSize, _cancellationToken); + } + + [TestMethod] + public void CanReadShouldReturnTrue() + { + Assert.IsTrue(_target.CanRead); + } + + [TestMethod] + public void CanSeekShouldReturnTrue() + { + Assert.IsTrue(_target.CanSeek); + } + + [TestMethod] + public void CanWriteShouldReturnTrue() + { + Assert.IsTrue(_target.CanWrite); + } + + [TestMethod] + public void CanTimeoutShouldReturnTrue() + { + Assert.IsTrue(_target.CanTimeout); + } + + [TestMethod] + public void PositionShouldReturnZero() + { + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + + var actual = _target.Position; + + Assert.AreEqual(0L, actual); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + } + + [TestMethod] + public async Task ReadShouldStartReadingAtBeginningOfFile() + { + var buffer = new byte[8]; + var data = new byte[] { 5, 4, 3, 2, 1 }; + var expected = new byte[] { 0, 5, 4, 3, 2, 1, 0, 0 }; + + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + SftpSessionMock.InSequence(MockSequence).Setup(p => p.RequestReadAsync(_handle, 0UL, _readBufferSize, _cancellationToken)).ReturnsAsync(data); + + var actual = await _target.ReadAsync(buffer, 1, data.Length); + + Assert.AreEqual(data.Length, actual); + Assert.IsTrue(buffer.IsEqualTo(expected)); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + SftpSessionMock.Verify(p => p.RequestReadAsync(_handle, 0UL, _readBufferSize, _cancellationToken), Times.Once); + } + + [TestMethod] + public async Task WriteShouldStartWritingAtBeginningOfFile() + { + var buffer = new byte[_writeBufferSize]; + + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + SftpSessionMock.InSequence(MockSequence).Setup(p => p.RequestWriteAsync(_handle, 0UL, buffer, 0, buffer.Length, _cancellationToken)).Returns(Task.CompletedTask); + + await _target.WriteAsync(buffer, 0, buffer.Length); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + SftpSessionMock.Verify(p => p.RequestWriteAsync(_handle, 0UL, buffer, 0, buffer.Length, _cancellationToken), Times.Once); + } + + [TestMethod] + public void RequestOpenOnSftpSessionShouldBeInvokedOnce() + { + SftpSessionMock.Verify(p => p.RequestOpenAsync(_path, Flags.Read | Flags.Write | Flags.Truncate, _cancellationToken), Times.Once); + } + } +} diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeTruncate_FileAccessWrite.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeTruncate_FileAccessWrite.cs new file mode 100644 index 00000000..0c06eba6 --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeTruncate_FileAccessWrite.cs @@ -0,0 +1,134 @@ +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using Renci.SshNet.Sftp; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + [TestClass] + public class SftpFileStreamTest_OpenAsync_FileModeTruncate_FileAccessWrite : SftpFileStreamAsyncTestBase + { + private Random _random; + private string _path; + private FileMode _fileMode; + private FileAccess _fileAccess; + private int _bufferSize; + private uint _readBufferSize; + private uint _writeBufferSize; + private byte[] _handle; + private SftpFileStream _target; + private CancellationToken _cancellationToken; + + protected override void SetupData() + { + base.SetupData(); + + _random = new Random(); + _path = _random.Next().ToString(); + _fileMode = FileMode.Truncate; + _fileAccess = FileAccess.Write; + _bufferSize = _random.Next(5, 1000); + _readBufferSize = (uint) _random.Next(5, 1000); + _writeBufferSize = (uint) _random.Next(5, 1000); + _handle = GenerateRandom(_random.Next(1, 10), _random); + _cancellationToken = new CancellationToken(); + } + + protected override void SetupMocks() + { + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestOpenAsync(_path, Flags.Write | Flags.Truncate, _cancellationToken)) + .ReturnsAsync(_handle); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalReadLength((uint) _bufferSize)) + .Returns(_readBufferSize); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalWriteLength((uint) _bufferSize, _handle)) + .Returns(_writeBufferSize); + } + + protected override async Task ActAsync() + { + _target = await SftpFileStream.OpenAsync(SftpSessionMock.Object, _path, _fileMode, _fileAccess, _bufferSize, _cancellationToken); + } + + [TestMethod] + public void CanReadShouldReturnFalse() + { + Assert.IsFalse(_target.CanRead); + } + + [TestMethod] + public void CanSeekShouldReturnTrue() + { + Assert.IsTrue(_target.CanSeek); + } + + [TestMethod] + public void CanWriteShouldReturnTrue() + { + Assert.IsTrue(_target.CanWrite); + } + + [TestMethod] + public void CanTimeoutShouldReturnTrue() + { + Assert.IsTrue(_target.CanTimeout); + } + + [TestMethod] + public void PositionShouldReturnZero() + { + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + + var actual = _target.Position; + + Assert.AreEqual(0L, actual); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + } + + [TestMethod] + public async Task ReadShouldThrowNotSupportedException() + { + var buffer = new byte[_readBufferSize]; + + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + + try + { + await _target.ReadAsync(buffer, 0, buffer.Length); + Assert.Fail(); + } + catch (NotSupportedException ex) + { + Assert.IsNull(ex.InnerException); + } + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + } + + [TestMethod] + public async Task WriteShouldStartWritingAtBeginningOfFile() + { + var buffer = new byte[_writeBufferSize]; + + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + SftpSessionMock.InSequence(MockSequence).Setup(p => p.RequestWriteAsync(_handle, 0UL, buffer, 0, buffer.Length, _cancellationToken)).Returns(Task.CompletedTask); + + await _target.WriteAsync(buffer, 0, buffer.Length); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + SftpSessionMock.Verify(p => p.RequestWriteAsync(_handle, 0UL, buffer, 0, buffer.Length, _cancellationToken), Times.Once); + } + + [TestMethod] + public void RequestOpenOnSftpSessionShouldBeInvokedOnce() + { + SftpSessionMock.Verify(p => p.RequestOpenAsync(_path, Flags.Write | Flags.Truncate, _cancellationToken), Times.Once); + } + } +} \ No newline at end of file diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_ReadAsync_ReadMode_NoDataInReaderBufferAndReadLessBytesFromServerThanCountAndEqualToBufferSize.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_ReadAsync_ReadMode_NoDataInReaderBufferAndReadLessBytesFromServerThanCountAndEqualToBufferSize.cs new file mode 100644 index 00000000..ef8dbebe --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_ReadAsync_ReadMode_NoDataInReaderBufferAndReadLessBytesFromServerThanCountAndEqualToBufferSize.cs @@ -0,0 +1,157 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using Renci.SshNet.Common; +using Renci.SshNet.Sftp; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + [TestClass] + public class SftpFileStreamTest_ReadAsync_ReadMode_NoDataInReaderBufferAndReadLessBytesFromServerThanCountAndEqualToBufferSize : SftpFileStreamAsyncTestBase + { + private string _path; + private SftpFileStream _target; + private byte[] _handle; + private uint _bufferSize; + private uint _readBufferSize; + private uint _writeBufferSize; + private int _actual; + private byte[] _buffer; + private byte[] _serverData1; + private byte[] _serverData2; + private int _serverData1Length; + private int _serverData2Length; + private int _numberOfBytesToRead; + + protected override void SetupData() + { + base.SetupData(); + + var random = new Random(); + _path = random.Next().ToString(); + _handle = GenerateRandom(5, random); + _bufferSize = (uint)random.Next(1, 1000); + _readBufferSize = 20; + _writeBufferSize = 500; + + _numberOfBytesToRead = (int) _readBufferSize + 5; // greather than read buffer size + _buffer = new byte[_numberOfBytesToRead]; + _serverData1Length = (int) _readBufferSize; // equal to read buffer size + _serverData1 = GenerateRandom(_serverData1Length, random); + _serverData2Length = (int) _readBufferSize; // equal to read buffer size + _serverData2 = GenerateRandom(_serverData2Length, random); + + Assert.IsTrue(_serverData1Length < _numberOfBytesToRead && _serverData1Length == _readBufferSize); + } + + protected override void SetupMocks() + { + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestOpenAsync(_path, Flags.Read, default)) + .ReturnsAsync(_handle); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalReadLength(_bufferSize)) + .Returns(_readBufferSize); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalWriteLength(_bufferSize, _handle)) + .Returns(_writeBufferSize); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestReadAsync(_handle, 0UL, _readBufferSize, default)) + .ReturnsAsync(_serverData1); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestReadAsync(_handle, (ulong)_serverData1.Length, _readBufferSize, default)) + .ReturnsAsync(_serverData2); + } + + [TestCleanup] + public void TearDown() + { + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestClose(_handle)); + } + + protected override async Task ArrangeAsync() + { + await base.ArrangeAsync(); + + _target = await SftpFileStream.OpenAsync(SftpSessionMock.Object, + _path, + FileMode.Open, + FileAccess.Read, + (int)_bufferSize, + default); + } + + protected override async Task ActAsync() + { + _actual = await _target.ReadAsync(_buffer, 0, _numberOfBytesToRead, default); + } + + [TestMethod] + public void ReadShouldHaveReturnedTheNumberOfBytesRequested() + { + Assert.AreEqual(_numberOfBytesToRead, _actual); + } + + [TestMethod] + public void ReadShouldHaveWrittenBytesToTheCallerSuppliedBuffer() + { + Assert.IsTrue(_serverData1.IsEqualTo(_buffer.Take(_serverData1Length))); + + var bytesWrittenFromSecondRead = _numberOfBytesToRead - _serverData1Length; + Assert.IsTrue(_serverData2.Take(bytesWrittenFromSecondRead).IsEqualTo(_buffer.Take(_serverData1Length, bytesWrittenFromSecondRead))); + } + + [TestMethod] + public void PositionShouldReturnNumberOfBytesWrittenToCallerProvidedBuffer() + { + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + + Assert.AreEqual(_actual, _target.Position); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(2)); + } + + [TestMethod] + public async Task ReadShouldReturnAllRemaningBytesFromReadBufferWhenCountIsEqualToNumberOfRemainingBytes() + { + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + + var numberOfBytesRemainingInReadBuffer = _serverData1Length + _serverData2Length - _numberOfBytesToRead; + + _buffer = new byte[numberOfBytesRemainingInReadBuffer]; + + var actual = await _target.ReadAsync(_buffer, 0, _buffer.Length); + + Assert.AreEqual(_buffer.Length, actual); + Assert.IsTrue(_serverData2.Take(_numberOfBytesToRead - _serverData1Length, _buffer.Length).IsEqualTo(_buffer)); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(2)); + } + + [TestMethod] + public async Task ReadShouldReturnAllRemaningBytesFromReadBufferAndReadAgainWhenCountIsGreaterThanNumberOfRemainingBytesAndNewReadReturnsZeroBytes() + { + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + SftpSessionMock.InSequence(MockSequence).Setup(p => p.RequestReadAsync(_handle, (ulong)(_serverData1Length + _serverData2Length), _readBufferSize, default)).ReturnsAsync(Array.Empty()); + + var numberOfBytesRemainingInReadBuffer = _serverData1Length + _serverData2Length - _numberOfBytesToRead; + + _buffer = new byte[numberOfBytesRemainingInReadBuffer + 1]; + + var actual = await _target.ReadAsync(_buffer, 0, _buffer.Length); + + Assert.AreEqual(numberOfBytesRemainingInReadBuffer, actual); + Assert.IsTrue(_serverData2.Take(_numberOfBytesToRead - _serverData1Length, numberOfBytesRemainingInReadBuffer).IsEqualTo(_buffer.Take(numberOfBytesRemainingInReadBuffer))); + Assert.AreEqual(0, _buffer[numberOfBytesRemainingInReadBuffer]); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(2)); + SftpSessionMock.Verify(p => p.RequestReadAsync(_handle, (ulong)(_serverData1Length + _serverData2Length), _readBufferSize, default)); + } + } +} \ No newline at end of file diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_ReadAsync_ReadMode_NoDataInReaderBufferAndReadLessBytesFromServerThanCountAndLessThanBufferSize.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_ReadAsync_ReadMode_NoDataInReaderBufferAndReadLessBytesFromServerThanCountAndLessThanBufferSize.cs new file mode 100644 index 00000000..9d07681a --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_ReadAsync_ReadMode_NoDataInReaderBufferAndReadLessBytesFromServerThanCountAndLessThanBufferSize.cs @@ -0,0 +1,150 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using Renci.SshNet.Common; +using Renci.SshNet.Sftp; +using Renci.SshNet.Tests.Common; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + [TestClass] + public class SftpFileStreamTest_ReadAsync_ReadMode_NoDataInReaderBufferAndReadLessBytesFromServerThanCountAndLessThanBufferSize : SftpFileStreamAsyncTestBase + { + private string _path; + private SftpFileStream _target; + private byte[] _handle; + private uint _bufferSize; + private uint _readBufferSize; + private uint _writeBufferSize; + private int _actual; + private byte[] _buffer; + private byte[] _serverData; + private int _serverDataLength; + private int _numberOfBytesToRead; + private byte[] _originalBuffer; + + protected override void SetupData() + { + base.SetupData(); + + var random = new Random(); + _path = random.Next().ToString(); + _handle = GenerateRandom(5, random); + _bufferSize = (uint)random.Next(1, 1000); + _readBufferSize = 20; + _writeBufferSize = 500; + + _numberOfBytesToRead = (int) _readBufferSize + 2; // greater than read buffer size + _originalBuffer = GenerateRandom(_numberOfBytesToRead, random); + _buffer = _originalBuffer.Copy(); + + _serverDataLength = (int) _readBufferSize - 1; // less than read buffer size + _serverData = GenerateRandom(_serverDataLength, random); + + Assert.IsTrue(_serverDataLength < _numberOfBytesToRead && _serverDataLength < _readBufferSize); + } + + protected override void SetupMocks() + { + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestOpenAsync(_path, Flags.Read, default)) + .ReturnsAsync(_handle); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalReadLength(_bufferSize)) + .Returns(_readBufferSize); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalWriteLength(_bufferSize, _handle)) + .Returns(_writeBufferSize); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestReadAsync(_handle, 0UL, _readBufferSize, default)) + .ReturnsAsync(_serverData); + } + + [TestCleanup] + public void TearDown() + { + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestClose(_handle)); + } + + protected override async Task ArrangeAsync() + { + await base.ArrangeAsync(); + + _target = await SftpFileStream.OpenAsync(SftpSessionMock.Object, + _path, + FileMode.Open, + FileAccess.Read, + (int)_bufferSize, + default); + } + + protected override async Task ActAsync() + { + _actual = await _target.ReadAsync(_buffer, 0, _numberOfBytesToRead, default); + } + + [TestMethod] + public void ReadShouldHaveReturnedTheNumberOfBytesReturnedByTheReadFromTheServer() + { + Assert.AreEqual(_serverDataLength, _actual); + } + + [TestMethod] + public void ReadShouldHaveWrittenBytesToTheCallerSuppliedBufferAndRemainingBytesShouldRemainUntouched() + { + Assert.IsTrue(_serverData.IsEqualTo(_buffer.Take(_serverDataLength))); + Assert.IsTrue(_originalBuffer.Take(_serverDataLength, _originalBuffer.Length - _serverDataLength).IsEqualTo(_buffer.Take(_serverDataLength, _buffer.Length - _serverDataLength))); + } + + [TestMethod] + public void PositionShouldReturnNumberOfBytesWrittenToCallerProvidedBuffer() + { + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + + Assert.AreEqual(_actual, _target.Position); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(2)); + } + + [TestMethod] + public async Task SubsequentReadShouldReadAgainFromCurrentPositionFromServerAndReturnZeroWhenServerReturnsZeroBytes() + { + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestReadAsync(_handle, (ulong) _actual, _readBufferSize, default)) + .ReturnsAsync(Array.Empty()); + + var buffer = _originalBuffer.Copy(); + var actual = await _target.ReadAsync(buffer, 0, buffer.Length); + + Assert.AreEqual(0, actual); + Assert.IsTrue(_originalBuffer.IsEqualTo(buffer)); + + SftpSessionMock.Verify(p => p.RequestReadAsync(_handle, (ulong)_actual, _readBufferSize, default), Times.Once); + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(2)); + } + + [TestMethod] + public async Task SubsequentReadShouldReadAgainFromCurrentPositionFromServerAndNotUpdatePositionWhenServerReturnsZeroBytes() + { + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestReadAsync(_handle, (ulong)_actual, _readBufferSize, default)) + .ReturnsAsync(Array.Empty()); + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + + await _target.ReadAsync(new byte[10], 0, 10); + + Assert.AreEqual(_actual, _target.Position); + + SftpSessionMock.Verify(p => p.RequestReadAsync(_handle, (ulong)_actual, _readBufferSize, default), Times.Once); + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(3)); + } + } +} diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_ReadAsync_ReadMode_NoDataInReaderBufferAndReadMoreBytesFromServerThanCount.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_ReadAsync_ReadMode_NoDataInReaderBufferAndReadMoreBytesFromServerThanCount.cs new file mode 100644 index 00000000..83ae8d42 --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_ReadAsync_ReadMode_NoDataInReaderBufferAndReadMoreBytesFromServerThanCount.cs @@ -0,0 +1,141 @@ +using System; +using System.IO; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using Renci.SshNet.Sftp; +using Renci.SshNet.Common; +using System.Threading.Tasks; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + [TestClass] + public class SftpFileStreamTest_ReadAsync_ReadMode_NoDataInReaderBufferAndReadMoreBytesFromServerThanCount : SftpFileStreamAsyncTestBase + { + private string _path; + private SftpFileStream _target; + private byte[] _handle; + private uint _bufferSize; + private uint _readBufferSize; + private uint _writeBufferSize; + private int _actual; + private byte[] _buffer; + private byte[] _serverData; + private int _numberOfBytesToWriteToReadBuffer; + private int _numberOfBytesToRead; + + protected override void SetupData() + { + base.SetupData(); + + var random = new Random(); + _path = random.Next().ToString(); + _handle = GenerateRandom(5, random); + _bufferSize = (uint) random.Next(1, 1000); + _readBufferSize = 20; + _writeBufferSize = 500; + + _numberOfBytesToRead = 20; + _buffer = new byte[_numberOfBytesToRead]; + _numberOfBytesToWriteToReadBuffer = 10; // should be less than _readBufferSize + _serverData = GenerateRandom(_numberOfBytesToRead + _numberOfBytesToWriteToReadBuffer, random); + } + + protected override void SetupMocks() + { + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestOpenAsync(_path, Flags.Read, default)) + .ReturnsAsync(_handle); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalReadLength(_bufferSize)) + .Returns(_readBufferSize); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalWriteLength(_bufferSize, _handle)) + .Returns(_writeBufferSize); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestReadAsync(_handle, 0UL, _readBufferSize, default)) + .ReturnsAsync(_serverData); + } + + [TestCleanup] + public void TearDown() + { + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestClose(_handle)); + } + + protected override async Task ArrangeAsync() + { + await base.ArrangeAsync(); + + _target = await SftpFileStream.OpenAsync(SftpSessionMock.Object, + _path, + FileMode.Open, + FileAccess.Read, + (int)_bufferSize, + default); + } + + protected override async Task ActAsync() + { + _actual = await _target.ReadAsync(_buffer, 0, _numberOfBytesToRead, default); + } + + [TestMethod] + public void ReadShouldHaveReturnedTheNumberOfBytesWrittenToCallerSuppliedBuffer() + { + Assert.AreEqual(_numberOfBytesToRead, _actual); + } + + [TestMethod] + public void ReadShouldHaveWrittenBytesToTheCallerSuppliedBuffer() + { + Assert.IsTrue(_serverData.Take(_actual).IsEqualTo(_buffer)); + } + + [TestMethod] + public void PositionShouldReturnNumberOfBytesWrittenToCallerProvidedBuffer() + { + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + + Assert.AreEqual(_actual, _target.Position); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(2)); + } + + [TestMethod] + public async Task SubsequentReadShouldReturnAllRemaningBytesFromReadBufferWhenCountIsEqualToNumberOfRemainingBytes() + { + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + + var buffer = new byte[_numberOfBytesToWriteToReadBuffer]; + + var actual = await _target.ReadAsync(buffer, 0, _numberOfBytesToWriteToReadBuffer, default); + + Assert.AreEqual(_numberOfBytesToWriteToReadBuffer, actual); + Assert.IsTrue(_serverData.Take(_numberOfBytesToRead, _numberOfBytesToWriteToReadBuffer).IsEqualTo(buffer)); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(2)); + } + + [TestMethod] + public async Task SubsequentReadShouldReturnAllRemaningBytesFromReadBufferAndReadAgainWhenCountIsGreaterThanNumberOfRemainingBytesAndNewReadReturnsZeroBytes() + { + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + SftpSessionMock.InSequence(MockSequence).Setup(p => p.RequestReadAsync(_handle, (ulong)(_serverData.Length), _readBufferSize, default)).ReturnsAsync(Array.Empty()); + + var buffer = new byte[_numberOfBytesToWriteToReadBuffer + 1]; + + var actual = await _target.ReadAsync(buffer, 0, buffer.Length); + + Assert.AreEqual(_numberOfBytesToWriteToReadBuffer, actual); + Assert.IsTrue(_serverData.Take(_numberOfBytesToRead, _numberOfBytesToWriteToReadBuffer).IsEqualTo(buffer.Take(_numberOfBytesToWriteToReadBuffer))); + Assert.AreEqual(0, buffer[_numberOfBytesToWriteToReadBuffer]); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(2)); + SftpSessionMock.Verify(p => p.RequestReadAsync(_handle, (ulong)(_serverData.Length), _readBufferSize, default)); + } + } +} diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Read_ReadMode_NoDataInReaderBufferAndReadLessBytesFromServerThanCountAndEqualToBufferSize.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Read_ReadMode_NoDataInReaderBufferAndReadLessBytesFromServerThanCountAndEqualToBufferSize.cs index 5f6f24be..3932c28a 100644 --- a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Read_ReadMode_NoDataInReaderBufferAndReadLessBytesFromServerThanCountAndEqualToBufferSize.cs +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Read_ReadMode_NoDataInReaderBufferAndReadLessBytesFromServerThanCountAndEqualToBufferSize.cs @@ -136,7 +136,7 @@ namespace Renci.SshNet.Tests.Classes.Sftp public void ReadShouldReturnAllRemaningBytesFromReadBufferAndReadAgainWhenCountIsGreaterThanNumberOfRemainingBytesAndNewReadReturnsZeroBytes() { SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); - SftpSessionMock.InSequence(MockSequence).Setup(p => p.RequestRead(_handle, (ulong)(_serverData1Length + _serverData2Length), _readBufferSize)).Returns(Array.Empty); + SftpSessionMock.InSequence(MockSequence).Setup(p => p.RequestRead(_handle, (ulong)(_serverData1Length + _serverData2Length), _readBufferSize)).Returns(Array.Empty()); var numberOfBytesRemainingInReadBuffer = _serverData1Length + _serverData2Length - _numberOfBytesToRead; diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Read_ReadMode_NoDataInReaderBufferAndReadLessBytesFromServerThanCountAndLessThanBufferSize.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Read_ReadMode_NoDataInReaderBufferAndReadLessBytesFromServerThanCountAndLessThanBufferSize.cs index 85a6679b..794f6c7b 100644 --- a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Read_ReadMode_NoDataInReaderBufferAndReadLessBytesFromServerThanCountAndLessThanBufferSize.cs +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Read_ReadMode_NoDataInReaderBufferAndReadLessBytesFromServerThanCountAndLessThanBufferSize.cs @@ -116,7 +116,7 @@ namespace Renci.SshNet.Tests.Classes.Sftp SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); SftpSessionMock.InSequence(MockSequence) .Setup(p => p.RequestRead(_handle, (ulong) _actual, _readBufferSize)) - .Returns(Array.Empty); + .Returns(Array.Empty()); var buffer = _originalBuffer.Copy(); var actual = _target.Read(buffer, 0, buffer.Length); @@ -134,7 +134,7 @@ namespace Renci.SshNet.Tests.Classes.Sftp SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); SftpSessionMock.InSequence(MockSequence) .Setup(p => p.RequestRead(_handle, (ulong)_actual, _readBufferSize)) - .Returns(Array.Empty); + .Returns(Array.Empty()); SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); _target.Read(new byte[10], 0, 10); diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Read_ReadMode_NoDataInReaderBufferAndReadMoreBytesFromServerThanCount.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Read_ReadMode_NoDataInReaderBufferAndReadMoreBytesFromServerThanCount.cs index 97c66422..1f6f2cf5 100644 --- a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Read_ReadMode_NoDataInReaderBufferAndReadMoreBytesFromServerThanCount.cs +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Read_ReadMode_NoDataInReaderBufferAndReadMoreBytesFromServerThanCount.cs @@ -122,7 +122,7 @@ namespace Renci.SshNet.Tests.Classes.Sftp public void SubsequentReadShouldReturnAllRemaningBytesFromReadBufferAndReadAgainWhenCountIsGreaterThanNumberOfRemainingBytesAndNewReadReturnsZeroBytes() { SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); - SftpSessionMock.InSequence(MockSequence).Setup(p => p.RequestRead(_handle, (ulong)(_serverData.Length), _readBufferSize)).Returns(Array.Empty); + SftpSessionMock.InSequence(MockSequence).Setup(p => p.RequestRead(_handle, (ulong)(_serverData.Length), _readBufferSize)).Returns(Array.Empty()); var buffer = new byte[_numberOfBytesToWriteToReadBuffer + 1]; diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Seek_PositionedAtBeginningOfStream_OriginEndAndOffsetNegative.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Seek_PositionedAtBeginningOfStream_OriginEndAndOffsetNegative.cs new file mode 100644 index 00000000..e47e716e --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Seek_PositionedAtBeginningOfStream_OriginEndAndOffsetNegative.cs @@ -0,0 +1,103 @@ +using System; +using System.IO; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using Renci.SshNet.Sftp; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + [TestClass] + public class SftpFileStreamTest_Seek_PositionedAtBeginningOfStream_OriginEndAndOffsetNegative : SftpFileStreamTestBase + { + private Random _random; + private string _path; + private FileMode _fileMode; + private FileAccess _fileAccess; + private int _bufferSize; + private uint _readBufferSize; + private uint _writeBufferSize; + private int _length; + private byte[] _handle; + private SftpFileStream _target; + private int _offset; + private SftpFileAttributes _attributes; + private long _actual; + + protected override void SetupData() + { + base.SetupData(); + + _random = new Random(); + _path = _random.Next().ToString(); + _fileMode = FileMode.OpenOrCreate; + _fileAccess = FileAccess.Write; + _bufferSize = _random.Next(5, 1000); + _readBufferSize = (uint)_random.Next(5, 1000); + _writeBufferSize = (uint)_random.Next(5, 1000); + _length = _random.Next(5, 10000); + _handle = GenerateRandom(_random.Next(1, 10), _random); + _offset = _random.Next(-_length, -1); + _attributes = SftpFileAttributes.Empty; + } + + protected override void SetupMocks() + { + SftpSessionMock.InSequence(MockSequence) + .Setup(session => session.RequestOpen(_path, Flags.Write | Flags.CreateNewOrOpen, false)) + .Returns(_handle); + SftpSessionMock.InSequence(MockSequence) + .Setup(session => session.CalculateOptimalReadLength((uint)_bufferSize)) + .Returns(_readBufferSize); + SftpSessionMock.InSequence(MockSequence) + .Setup(session => session.CalculateOptimalWriteLength((uint)_bufferSize, _handle)) + .Returns(_writeBufferSize); + SftpSessionMock.InSequence(MockSequence) + .Setup(session => session.IsOpen) + .Returns(true); + SftpSessionMock.InSequence(MockSequence) + .Setup(session => session.RequestFStat(_handle, false)) + .Returns(_attributes); + SftpSessionMock.InSequence(MockSequence) + .Setup(session => session.RequestFSetStat(_handle, _attributes)); + SftpSessionMock.InSequence(MockSequence).Setup(session => session.IsOpen).Returns(true); + SftpSessionMock.InSequence(MockSequence) + .Setup(session => session.RequestFStat(_handle, false)) + .Returns(_attributes); + } + + protected override void Arrange() + { + base.Arrange(); + + _target = new SftpFileStream(SftpSessionMock.Object, _path, _fileMode, _fileAccess, _bufferSize); + _target.SetLength(_length); + } + + protected override void Act() + { + _actual = _target.Seek(_offset, SeekOrigin.End); + } + + [TestMethod] + public void SeekShouldHaveReturnedOffset() + { + Assert.AreEqual(_attributes.Size + _offset, _actual); + } + + [TestMethod] + public void IsOpenOnSftpSessionShouldHaveBeenInvokedTwice() + { + SftpSessionMock.Verify(session => session.IsOpen, Times.Exactly(2)); + } + + [TestMethod] + public void PositionShouldReturnOffset() + { + SftpSessionMock.InSequence(MockSequence).Setup(session => session.IsOpen).Returns(true); + + Assert.AreEqual(_attributes.Size + _offset, _target.Position); + + SftpSessionMock.Verify(session => session.IsOpen, Times.Exactly(3)); + } + } +} diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Seek_PositionedAtBeginningOfStream_OriginEndAndOffsetPositive.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Seek_PositionedAtBeginningOfStream_OriginEndAndOffsetPositive.cs new file mode 100644 index 00000000..506342f1 --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Seek_PositionedAtBeginningOfStream_OriginEndAndOffsetPositive.cs @@ -0,0 +1,103 @@ +using System; +using System.IO; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using Renci.SshNet.Sftp; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + [TestClass] + public class SftpFileStreamTest_Seek_PositionedAtBeginningOfStream_OriginEndAndOffsetPositive : SftpFileStreamTestBase + { + private Random _random; + private string _path; + private FileMode _fileMode; + private FileAccess _fileAccess; + private int _bufferSize; + private uint _readBufferSize; + private uint _writeBufferSize; + private int _length; + private byte[] _handle; + private SftpFileStream _target; + private int _offset; + private SftpFileAttributes _attributes; + private long _actual; + + protected override void SetupData() + { + base.SetupData(); + + _random = new Random(); + _path = _random.Next().ToString(); + _fileMode = FileMode.OpenOrCreate; + _fileAccess = FileAccess.Write; + _bufferSize = _random.Next(5, 1000); + _readBufferSize = (uint)_random.Next(5, 1000); + _writeBufferSize = (uint)_random.Next(5, 1000); + _length = _random.Next(5, 10000); + _handle = GenerateRandom(_random.Next(1, 10), _random); + _offset = _random.Next(1, int.MaxValue); + _attributes = SftpFileAttributes.Empty; + } + + protected override void SetupMocks() + { + SftpSessionMock.InSequence(MockSequence) + .Setup(session => session.RequestOpen(_path, Flags.Write | Flags.CreateNewOrOpen, false)) + .Returns(_handle); + SftpSessionMock.InSequence(MockSequence) + .Setup(session => session.CalculateOptimalReadLength((uint)_bufferSize)) + .Returns(_readBufferSize); + SftpSessionMock.InSequence(MockSequence) + .Setup(session => session.CalculateOptimalWriteLength((uint)_bufferSize, _handle)) + .Returns(_writeBufferSize); + SftpSessionMock.InSequence(MockSequence) + .Setup(session => session.IsOpen) + .Returns(true); + SftpSessionMock.InSequence(MockSequence) + .Setup(session => session.RequestFStat(_handle, false)) + .Returns(_attributes); + SftpSessionMock.InSequence(MockSequence) + .Setup(session => session.RequestFSetStat(_handle, _attributes)); + SftpSessionMock.InSequence(MockSequence).Setup(session => session.IsOpen).Returns(true); + SftpSessionMock.InSequence(MockSequence) + .Setup(session => session.RequestFStat(_handle, false)) + .Returns(_attributes); + } + + protected override void Arrange() + { + base.Arrange(); + + _target = new SftpFileStream(SftpSessionMock.Object, _path, _fileMode, _fileAccess, _bufferSize); + _target.SetLength(_length); + } + + protected override void Act() + { + _actual = _target.Seek(_offset, SeekOrigin.End); + } + + [TestMethod] + public void SeekShouldHaveReturnedOffset() + { + Assert.AreEqual(_attributes.Size + _offset, _actual); + } + + [TestMethod] + public void IsOpenOnSftpSessionShouldHaveBeenInvokedTwice() + { + SftpSessionMock.Verify(session => session.IsOpen, Times.Exactly(2)); + } + + [TestMethod] + public void PositionShouldReturnOffset() + { + SftpSessionMock.InSequence(MockSequence).Setup(session => session.IsOpen).Returns(true); + + Assert.AreEqual(_attributes.Size + _offset, _target.Position); + + SftpSessionMock.Verify(session => session.IsOpen, Times.Exactly(3)); + } + } +} diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Seek_PositionedAtBeginningOfStream_OriginEndAndOffsetZero.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Seek_PositionedAtBeginningOfStream_OriginEndAndOffsetZero.cs new file mode 100644 index 00000000..4791acf8 --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Seek_PositionedAtBeginningOfStream_OriginEndAndOffsetZero.cs @@ -0,0 +1,103 @@ +using System; +using System.IO; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using Renci.SshNet.Sftp; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + [TestClass] + public class SftpFileStreamTest_Seek_PositionedAtBeginningOfStream_OriginEndAndOffsetZero : SftpFileStreamTestBase + { + private Random _random; + private string _path; + private FileMode _fileMode; + private FileAccess _fileAccess; + private int _bufferSize; + private uint _readBufferSize; + private uint _writeBufferSize; + private int _length; + private byte[] _handle; + private SftpFileStream _target; + private int _offset; + private SftpFileAttributes _attributes; + private long _actual; + + protected override void SetupData() + { + base.SetupData(); + + _random = new Random(); + _path = _random.Next().ToString(); + _fileMode = FileMode.OpenOrCreate; + _fileAccess = FileAccess.Write; + _bufferSize = _random.Next(5, 1000); + _readBufferSize = (uint)_random.Next(5, 1000); + _writeBufferSize = (uint)_random.Next(5, 1000); + _length = _random.Next(5, 10000); + _handle = GenerateRandom(_random.Next(1, 10), _random); + _offset = 0; + _attributes = SftpFileAttributes.Empty; + } + + protected override void SetupMocks() + { + SftpSessionMock.InSequence(MockSequence) + .Setup(session => session.RequestOpen(_path, Flags.Write | Flags.CreateNewOrOpen, false)) + .Returns(_handle); + SftpSessionMock.InSequence(MockSequence) + .Setup(session => session.CalculateOptimalReadLength((uint)_bufferSize)) + .Returns(_readBufferSize); + SftpSessionMock.InSequence(MockSequence) + .Setup(session => session.CalculateOptimalWriteLength((uint)_bufferSize, _handle)) + .Returns(_writeBufferSize); + SftpSessionMock.InSequence(MockSequence) + .Setup(session => session.IsOpen) + .Returns(true); + SftpSessionMock.InSequence(MockSequence) + .Setup(session => session.RequestFStat(_handle, false)) + .Returns(_attributes); + SftpSessionMock.InSequence(MockSequence) + .Setup(session => session.RequestFSetStat(_handle, _attributes)); + SftpSessionMock.InSequence(MockSequence).Setup(session => session.IsOpen).Returns(true); + SftpSessionMock.InSequence(MockSequence) + .Setup(session => session.RequestFStat(_handle, false)) + .Returns(_attributes); + } + + protected override void Arrange() + { + base.Arrange(); + + _target = new SftpFileStream(SftpSessionMock.Object, _path, _fileMode, _fileAccess, _bufferSize); + _target.SetLength(_length); + } + + protected override void Act() + { + _actual = _target.Seek(_offset, SeekOrigin.End); + } + + [TestMethod] + public void SeekShouldHaveReturnedSize() + { + Assert.AreEqual(_attributes.Size, _actual); + } + + [TestMethod] + public void IsOpenOnSftpSessionShouldHaveBeenInvokedTwice() + { + SftpSessionMock.Verify(session => session.IsOpen, Times.Exactly(2)); + } + + [TestMethod] + public void PositionShouldReturnSize() + { + SftpSessionMock.InSequence(MockSequence).Setup(session => session.IsOpen).Returns(true); + + Assert.AreEqual(_attributes.Size, _target.Position); + + SftpSessionMock.Verify(session => session.IsOpen, Times.Exactly(3)); + } + } +} diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_DataInReadBuffer_NewLengthLessThanPosition.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_DataInReadBuffer_NewLengthLessThanPosition.cs index a6116339..0709896d 100644 --- a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_DataInReadBuffer_NewLengthLessThanPosition.cs +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_DataInReadBuffer_NewLengthLessThanPosition.cs @@ -137,7 +137,7 @@ namespace Renci.SshNet.Tests.Classes.Sftp SftpSessionMock.InSequence(_sequence).Setup(p => p.IsOpen).Returns(true); SftpSessionMock.InSequence(_sequence) .Setup(p => p.RequestRead(_handle, (uint) _length, _readBufferSize)) - .Returns(Array.Empty); + .Returns(Array.Empty()); var byteRead = _sftpFileStream.ReadByte(); diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_DataInWriteBuffer_NewLengthLessThanPosition.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_DataInWriteBuffer_NewLengthLessThanPosition.cs index 96ad14b1..42a3ce84 100644 --- a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_DataInWriteBuffer_NewLengthLessThanPosition.cs +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_DataInWriteBuffer_NewLengthLessThanPosition.cs @@ -160,7 +160,7 @@ namespace Renci.SshNet.Tests.Classes.Sftp SftpSessionMock.InSequence(_sequence).Setup(p => p.IsOpen).Returns(true); SftpSessionMock.InSequence(_sequence) .Setup(p => p.RequestRead(_handle, (uint) _length, _readBufferSize)) - .Returns(Array.Empty); + .Returns(Array.Empty()); var byteRead = _sftpFileStream.ReadByte(); diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_WriteAsync_SessionOpen_CountGreatherThanTwoTimesTheWriteBufferSize.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_WriteAsync_SessionOpen_CountGreatherThanTwoTimesTheWriteBufferSize.cs new file mode 100644 index 00000000..8fb74b43 --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_WriteAsync_SessionOpen_CountGreatherThanTwoTimesTheWriteBufferSize.cs @@ -0,0 +1,143 @@ +using System; +using System.Globalization; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Moq; + +using Renci.SshNet.Common; +using Renci.SshNet.Sftp; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + [TestClass] + public class SftpFileStreamTest_WriteAsync_SessionOpen_CountGreatherThanTwoTimesTheWriteBufferSize : SftpFileStreamAsyncTestBase + { + private SftpFileStream _target; + private string _path; + private byte[] _handle; + private uint _bufferSize; + private uint _readBufferSize; + private uint _writeBufferSize; + private byte[] _data; + private int _count; + private int _offset; + private Random _random; + private uint _expectedWrittenByteCount; + private int _expectedBufferedByteCount; + private byte[] _expectedBufferedBytes; + private CancellationToken _cancellationToken; + + protected override void SetupData() + { + base.SetupData(); + + _random = new Random(); + _path = _random.Next().ToString(CultureInfo.InvariantCulture); + _handle = GenerateRandom(5, _random); + _bufferSize = (uint)_random.Next(1, 1000); + _readBufferSize = (uint) _random.Next(0, 1000); + _writeBufferSize = (uint) _random.Next(500, 1000); + _data = new byte[(_writeBufferSize * 2) + 15]; + _random.NextBytes(_data); + _offset = _random.Next(1, 5); + // to get multiple SSH_FXP_WRITE messages (and verify the offset is updated correctly), we make sure + // the number of bytes to write is at least two times the write buffer size; we write a few extra bytes to + // ensure the buffer is not empty after the writes so we can verify whether Length, Dispose and Flush + // flush the buffer + _count = ((int) _writeBufferSize * 2) + _random.Next(1, 5); + + _expectedWrittenByteCount = (2 * _writeBufferSize); + _expectedBufferedByteCount = (int)(_count - _expectedWrittenByteCount); + _expectedBufferedBytes = _data.Take(_offset + (int)_expectedWrittenByteCount, _expectedBufferedByteCount); + _cancellationToken = new CancellationToken(); + } + + protected override void SetupMocks() + { + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestOpenAsync(_path, Flags.Write | Flags.CreateNewOrOpen | Flags.Truncate, _cancellationToken)) + .ReturnsAsync(_handle); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalReadLength(_bufferSize)) + .Returns(_readBufferSize); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalWriteLength(_bufferSize, _handle)) + .Returns(_writeBufferSize); + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestWriteAsync(_handle, 0, _data, _offset, (int)_writeBufferSize, _cancellationToken)) + .Returns(Task.CompletedTask); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestWriteAsync(_handle, _writeBufferSize, _data, _offset + (int)_writeBufferSize, (int)_writeBufferSize, _cancellationToken)) + .Returns(Task.CompletedTask); + } + + [TestCleanup] + public void TearDown() + { + if (SftpSessionMock != null) + { + // allow Dispose to complete successfully + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestWriteAsync(_handle, _expectedWrittenByteCount, It.IsAny(), 0, _expectedBufferedByteCount, _cancellationToken)) + .Returns(Task.CompletedTask); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestClose(_handle)); + } + } + + protected override async Task ArrangeAsync() + { + await base.ArrangeAsync(); + + _target = await SftpFileStream.OpenAsync(SftpSessionMock.Object, _path, FileMode.Create, FileAccess.Write, (int) _bufferSize, _cancellationToken); + } + + protected override Task ActAsync() + { + return _target.WriteAsync(_data, _offset, _count); + } + + [TestMethod] + public void RequestWriteOnSftpSessionShouldBeInvokedTwice() + { + SftpSessionMock.Verify(p => p.RequestWriteAsync(_handle, 0, _data, _offset, (int)_writeBufferSize, _cancellationToken), Times.Once); + SftpSessionMock.Verify(p => p.RequestWriteAsync(_handle, _writeBufferSize, _data, _offset + (int)_writeBufferSize, (int)_writeBufferSize, _cancellationToken), Times.Once); + } + + [TestMethod] + public void PositionShouldBeNumberOfBytesWrittenToFileAndNUmberOfBytesInBuffer() + { + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + + Assert.AreEqual(_count, _target.Position); + } + + [TestMethod] + public async Task FlushShouldFlushBuffer() + { + byte[] actualFlushedData = null; + + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestWriteAsync(_handle, _expectedWrittenByteCount, It.IsAny(), 0, _expectedBufferedByteCount, _cancellationToken)) + .Callback((handle, serverFileOffset, data, offset, length, ct) => actualFlushedData = data.Take(offset, length)) + .Returns(Task.CompletedTask); + + await _target.FlushAsync(); + + Assert.IsTrue(actualFlushedData.IsEqualTo(_expectedBufferedBytes)); + + SftpSessionMock.Verify(p => p.RequestWriteAsync(_handle, _expectedWrittenByteCount, It.IsAny(), 0, _expectedBufferedByteCount, _cancellationToken), Times.Once); + } + } +} diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileTest.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileTest.cs index d1716a95..285d4aa5 100644 --- a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileTest.cs +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileTest.cs @@ -1,10 +1,9 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; -using Renci.SshNet.Common; + using Renci.SshNet.Sftp; using Renci.SshNet.Tests.Common; -using Renci.SshNet.Tests.Properties; + using System; -using System.IO; namespace Renci.SshNet.Tests.Classes.Sftp { @@ -14,121 +13,6 @@ namespace Renci.SshNet.Tests.Classes.Sftp [TestClass] public class SftpFileTest : TestBase { - [TestMethod] - [TestCategory("Sftp")] - [TestCategory("integration")] - public void Test_Get_Root_Directory() - { - using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) - { - sftp.Connect(); - var directory = sftp.Get("/"); - - Assert.AreEqual("/", directory.FullName); - Assert.IsTrue(directory.IsDirectory); - Assert.IsFalse(directory.IsRegularFile); - } - } - - [TestMethod] - [TestCategory("Sftp")] - [TestCategory("integration")] - [ExpectedException(typeof(SftpPathNotFoundException))] - public void Test_Get_Invalid_Directory() - { - using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) - { - sftp.Connect(); - - sftp.Get("/xyz"); - } - } - - [TestMethod] - [TestCategory("Sftp")] - [TestCategory("integration")] - public void Test_Get_File() - { - using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) - { - sftp.Connect(); - - sftp.UploadFile(new MemoryStream(), "abc.txt"); - - var file = sftp.Get("abc.txt"); - - Assert.AreEqual("/home/tester/abc.txt", file.FullName); - Assert.IsTrue(file.IsRegularFile); - Assert.IsFalse(file.IsDirectory); - } - } - - [TestMethod] - [TestCategory("Sftp")] - [TestCategory("integration")] - [Description("Test passing null to Get.")] - [ExpectedException(typeof(ArgumentNullException))] - public void Test_Get_File_Null() - { - using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) - { - sftp.Connect(); - - var file = sftp.Get(null); - - sftp.Disconnect(); - } - } - - [TestMethod] - [TestCategory("Sftp")] - [TestCategory("integration")] - public void Test_Get_International_File() - { - using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) - { - sftp.Connect(); - - sftp.UploadFile(new MemoryStream(), "test-üöä-"); - - var file = sftp.Get("test-üöä-"); - - Assert.AreEqual("/home/tester/test-üöä-", file.FullName); - Assert.IsTrue(file.IsRegularFile); - Assert.IsFalse(file.IsDirectory); - } - } - - [TestMethod] - [TestCategory("Sftp")] - [TestCategory("integration")] - public void Test_Sftp_SftpFile_MoveTo() - { - using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) - { - sftp.Connect(); - - string uploadedFileName = Path.GetTempFileName(); - string remoteFileName = Path.GetRandomFileName(); - string newFileName = Path.GetRandomFileName(); - - this.CreateTestFile(uploadedFileName, 1); - - using (var file = File.OpenRead(uploadedFileName)) - { - sftp.UploadFile(file, remoteFileName); - } - - var sftpFile = sftp.Get(remoteFileName); - - sftpFile.MoveTo(newFileName); - - Assert.AreEqual(newFileName, sftpFile.Name); - - sftp.Disconnect(); - } - } - /// ///A test for Delete /// @@ -623,4 +507,4 @@ namespace Renci.SshNet.Tests.Classes.Sftp } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpHandleResponseBuilder.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpHandleResponseBuilder.cs index 4477e2fb..d9f67f61 100644 --- a/src/Renci.SshNet.Tests/Classes/Sftp/SftpHandleResponseBuilder.cs +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpHandleResponseBuilder.cs @@ -1,7 +1,4 @@ -using Renci.SshNet.Sftp; -using Renci.SshNet.Sftp.Responses; -using System.Collections.Generic; -using System.Text; +using Renci.SshNet.Sftp.Responses; namespace Renci.SshNet.Tests.Classes.Sftp { @@ -32,10 +29,10 @@ namespace Renci.SshNet.Tests.Classes.Sftp public SftpHandleResponse Build() { var sftpHandleResponse = new SftpHandleResponse(_protocolVersion) - { - ResponseId = _responseId, - Handle = _handle - }; + { + ResponseId = _responseId, + Handle = _handle + }; return sftpHandleResponse; } } diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpNameResponseBuilder.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpNameResponseBuilder.cs index e0dc9ae5..268f1c5c 100644 --- a/src/Renci.SshNet.Tests/Classes/Sftp/SftpNameResponseBuilder.cs +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpNameResponseBuilder.cs @@ -10,7 +10,7 @@ namespace Renci.SshNet.Tests.Classes.Sftp private uint _responseId; private uint _protocolVersion; private Encoding _encoding; - private List> _files; + private readonly List> _files; public SftpNameResponseBuilder() { @@ -32,7 +32,10 @@ namespace Renci.SshNet.Tests.Classes.Sftp public SftpNameResponseBuilder WithFiles(params KeyValuePair[] files) { for (var i = 0; i < files.Length; i++) + { _files.Add(files[i]); + } + return this; } diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpOpenRequestBuilder.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpOpenRequestBuilder.cs index 00c0efa3..49f785d0 100644 --- a/src/Renci.SshNet.Tests/Classes/Sftp/SftpOpenRequestBuilder.cs +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpOpenRequestBuilder.cs @@ -1,10 +1,9 @@ -using Renci.SshNet.Sftp; +using System; +using System.Text; + +using Renci.SshNet.Sftp; using Renci.SshNet.Sftp.Requests; using Renci.SshNet.Sftp.Responses; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; namespace Renci.SshNet.Tests.Classes.Sftp { diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpSessionTest_Connected_RequestStatVfs.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpSessionTest_Connected_RequestStatVfs.cs index 84cc4565..2d5e0077 100644 --- a/src/Renci.SshNet.Tests/Classes/Sftp/SftpSessionTest_Connected_RequestStatVfs.cs +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpSessionTest_Connected_RequestStatVfs.cs @@ -1,7 +1,10 @@ using System; using System.Text; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; + using Renci.SshNet.Channels; using Renci.SshNet.Common; using Renci.SshNet.Sftp; @@ -97,33 +100,47 @@ namespace Renci.SshNet.Tests.Classes.Sftp #region SftpSession.Connect() - _sessionMock.InSequence(sequence).Setup(p => p.CreateChannelSession()).Returns(_channelSessionMock.Object); - _channelSessionMock.InSequence(sequence).Setup(p => p.Open()); - _channelSessionMock.InSequence(sequence).Setup(p => p.SendSubsystemRequest("sftp")).Returns(true); - _channelSessionMock.InSequence(sequence).Setup(p => p.IsOpen).Returns(true); - _channelSessionMock.InSequence(sequence).Setup(p => p.SendData(_sftpInitRequestBytes)).Callback( - () => - { - _channelSessionMock.Raise(c => c.DataReceived += null, - new ChannelDataEventArgs(0, _sftpVersionResponse.GetBytes())); - }); - _channelSessionMock.InSequence(sequence).Setup(p => p.IsOpen).Returns(true); - _channelSessionMock.InSequence(sequence).Setup(p => p.SendData(_sftpRealPathRequestBytes)).Callback( - () => - { - _channelSessionMock.Raise(c => c.DataReceived += null, - new ChannelDataEventArgs(0, _sftpNameResponse.GetBytes())); - }); + _ = _sessionMock.InSequence(sequence) + .Setup(p => p.CreateChannelSession()) + .Returns(_channelSessionMock.Object); + _ = _channelSessionMock.InSequence(sequence) + .Setup(p => p.Open()); + _ = _channelSessionMock.InSequence(sequence) + .Setup(p => p.SendSubsystemRequest("sftp")) + .Returns(true); + _ = _channelSessionMock.InSequence(sequence) + .Setup(p => p.IsOpen) + .Returns(true); + _ = _channelSessionMock.InSequence(sequence) + .Setup(p => p.SendData(_sftpInitRequestBytes)) + .Callback(() => + { + _channelSessionMock.Raise(c => c.DataReceived += null, + new ChannelDataEventArgs(0, _sftpVersionResponse.GetBytes())); + }); + _ = _channelSessionMock.InSequence(sequence) + .Setup(p => p.IsOpen) + .Returns(true); + _ = _channelSessionMock.InSequence(sequence) + .Setup(p => p.SendData(_sftpRealPathRequestBytes)) + .Callback(() => + { + _channelSessionMock.Raise(c => c.DataReceived += null, + new ChannelDataEventArgs(0, _sftpNameResponse.GetBytes())); + }); #endregion SftpSession.Connect() - _channelSessionMock.InSequence(sequence).Setup(p => p.IsOpen).Returns(true); - _channelSessionMock.InSequence(sequence).Setup(p => p.SendData(_sftpStatVfsRequestBytes)).Callback( - () => - { - _channelSessionMock.Raise(c => c.DataReceived += null, - new ChannelDataEventArgs(0, _sftpStatVfsResponse.GetBytes())); - }); + _ = _channelSessionMock.InSequence(sequence) + .Setup(p => p.IsOpen) + .Returns(true); + _ = _channelSessionMock.InSequence(sequence) + .Setup(p => p.SendData(_sftpStatVfsRequestBytes)) + .Callback(() => + { + _channelSessionMock.Raise(c => c.DataReceived += null, + new ChannelDataEventArgs(0, _sftpStatVfsResponse.GetBytes())); + }); } protected void Arrange() @@ -153,4 +170,4 @@ namespace Renci.SshNet.Tests.Classes.Sftp Assert.AreEqual(_bAvail, _actual.AvailableBlocks); } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpStatVfsRequestBuilder.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpStatVfsRequestBuilder.cs index 71e37bd1..d52b208a 100644 --- a/src/Renci.SshNet.Tests/Classes/Sftp/SftpStatVfsRequestBuilder.cs +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpStatVfsRequestBuilder.cs @@ -1,5 +1,6 @@ using Renci.SshNet.Sftp.Requests; using Renci.SshNet.Sftp.Responses; + using System; using System.Text; diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpStatVfsResponseBuilder.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpStatVfsResponseBuilder.cs index 1331160b..485ebe56 100644 --- a/src/Renci.SshNet.Tests/Classes/Sftp/SftpStatVfsResponseBuilder.cs +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpStatVfsResponseBuilder.cs @@ -3,7 +3,7 @@ using Renci.SshNet.Sftp.Responses; namespace Renci.SshNet.Tests.Classes.Sftp { - internal class SftpStatVfsResponseBuilder + internal sealed class SftpStatVfsResponseBuilder { private uint _protocolVersion; private uint _responseId; @@ -88,9 +88,13 @@ namespace Renci.SshNet.Tests.Classes.Sftp public SftpStatVfsResponseBuilder WithIsReadOnly(bool isReadOnly) { if (isReadOnly) + { _flag &= SftpFileSytemInformation.SSH_FXE_STATVFS_ST_RDONLY; + } else + { _flag |= SftpFileSytemInformation.SSH_FXE_STATVFS_ST_RDONLY; + } return this; } @@ -98,9 +102,13 @@ namespace Renci.SshNet.Tests.Classes.Sftp public SftpStatVfsResponseBuilder WithSupportsSetUid(bool supportsSetUid) { if (supportsSetUid) + { _flag |= SftpFileSytemInformation.SSH_FXE_STATVFS_ST_NOSUID; + } else + { _flag &= SftpFileSytemInformation.SSH_FXE_STATVFS_ST_NOSUID; + } return this; } @@ -133,8 +141,13 @@ namespace Renci.SshNet.Tests.Classes.Sftp } } - internal class StatVfsResponse : SftpExtendedReplyResponse + internal sealed class StatVfsResponse : SftpResponse { + public override SftpMessageTypes SftpMessageType + { + get { return SftpMessageTypes.ExtendedReply; } + } + public SftpFileSytemInformation Information { get; set; } public StatVfsResponse(uint protocolVersion) @@ -147,17 +160,17 @@ namespace Renci.SshNet.Tests.Classes.Sftp base.LoadData(); Information = new SftpFileSytemInformation(ReadUInt64(), // FileSystemBlockSize - ReadUInt64(), // BlockSize - ReadUInt64(), // TotalBlocks - ReadUInt64(), // FreeBlocks - ReadUInt64(), // AvailableBlocks - ReadUInt64(), // TotalNodes - ReadUInt64(), // FreeNodes - ReadUInt64(), // AvailableNodes - ReadUInt64(), // Sid - ReadUInt64(), // Flags - ReadUInt64() // MaxNameLenght - ); + ReadUInt64(), // BlockSize + ReadUInt64(), // TotalBlocks + ReadUInt64(), // FreeBlocks + ReadUInt64(), // AvailableBlocks + ReadUInt64(), // TotalNodes + ReadUInt64(), // FreeNodes + ReadUInt64(), // AvailableNodes + ReadUInt64(), // Sid + ReadUInt64(), // Flags + ReadUInt64() // MaxNameLenght + ); } protected override void SaveData() diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpVersionResponseBuilder.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpVersionResponseBuilder.cs index 85f33cc1..1e4d55ef 100644 --- a/src/Renci.SshNet.Tests/Classes/Sftp/SftpVersionResponseBuilder.cs +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpVersionResponseBuilder.cs @@ -6,7 +6,7 @@ namespace Renci.SshNet.Tests.Classes.Sftp internal class SftpVersionResponseBuilder { private uint _version; - private IDictionary _extensions; + private readonly IDictionary _extensions; public SftpVersionResponseBuilder() { diff --git a/src/Renci.SshNet.Tests/Classes/SftpClientTest.Connect.cs b/src/Renci.SshNet.Tests/Classes/SftpClientTest.Connect.cs index 9ae69f6e..db03567b 100644 --- a/src/Renci.SshNet.Tests/Classes/SftpClientTest.Connect.cs +++ b/src/Renci.SshNet.Tests/Classes/SftpClientTest.Connect.cs @@ -20,7 +20,7 @@ namespace Renci.SshNet.Tests.Classes } catch (SocketException ex) { - Assert.AreEqual(ex.ErrorCode, (int) SocketError.HostNotFound); + Assert.IsTrue(ex.SocketErrorCode is SocketError.HostNotFound or SocketError.TryAgain); } } @@ -38,7 +38,7 @@ namespace Renci.SshNet.Tests.Classes } catch (SocketException ex) { - Assert.AreEqual(ex.ErrorCode, (int)SocketError.HostNotFound); + Assert.IsTrue(ex.SocketErrorCode is SocketError.HostNotFound or SocketError.TryAgain); } } } diff --git a/src/Renci.SshNet.Tests/Classes/SftpClientTest.ConnectAsync.cs b/src/Renci.SshNet.Tests/Classes/SftpClientTest.ConnectAsync.cs new file mode 100644 index 00000000..df7a8f0b --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/SftpClientTest.ConnectAsync.cs @@ -0,0 +1,46 @@ +using System; +using System.Net.Sockets; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Renci.SshNet.Tests.Classes +{ + public partial class SftpClientTest + { + [TestMethod] + public async Task ConnectAsync_HostNameInvalid_ShouldThrowSocketExceptionWithErrorCodeHostNotFound() + { + var connectionInfo = new ConnectionInfo(Guid.NewGuid().ToString("N"), 40, "user", + new KeyboardInteractiveAuthenticationMethod("user")); + var sftpClient = new SftpClient(connectionInfo); + + try + { + await sftpClient.ConnectAsync(default); + Assert.Fail(); + } + catch (SocketException ex) + { + Assert.AreEqual(SocketError.HostNotFound, ex.SocketErrorCode); + } + } + + [TestMethod] + public async Task ConnectAsync_ProxyHostNameInvalid_ShouldThrowSocketExceptionWithErrorCodeHostNotFound() + { + var connectionInfo = new ConnectionInfo("localhost", 40, "user", ProxyTypes.Http, Guid.NewGuid().ToString("N"), 80, + "proxyUser", "proxyPwd", new KeyboardInteractiveAuthenticationMethod("user")); + var sftpClient = new SftpClient(connectionInfo); + + try + { + await sftpClient.ConnectAsync(default); + Assert.Fail(); + } + catch (SocketException ex) + { + Assert.AreEqual(SocketError.HostNotFound, ex.SocketErrorCode); + } + } + } +} \ No newline at end of file diff --git a/src/Renci.SshNet.Tests/Classes/SftpClientTest.CreateDirectory.cs b/src/Renci.SshNet.Tests/Classes/SftpClientTest.CreateDirectory.cs deleted file mode 100644 index 2c6284a6..00000000 --- a/src/Renci.SshNet.Tests/Classes/SftpClientTest.CreateDirectory.cs +++ /dev/null @@ -1,104 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Renci.SshNet.Common; -using Renci.SshNet.Tests.Properties; -using System; - -namespace Renci.SshNet.Tests.Classes -{ - /// - /// Implementation of the SSH File Transfer Protocol (SFTP) over SSH. - /// - public partial class SftpClientTest - { - [TestMethod] - [TestCategory("Sftp")] - [ExpectedException(typeof(SshConnectionException))] - public void Test_Sftp_CreateDirectory_Without_Connecting() - { - using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) - { - sftp.CreateDirectory("test"); - } - } - - [TestMethod] - [TestCategory("Sftp")] - [TestCategory("integration")] - public void Test_Sftp_CreateDirectory_In_Current_Location() - { - using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) - { - sftp.Connect(); - - sftp.CreateDirectory("test"); - - sftp.Disconnect(); - } - } - - [TestMethod] - [TestCategory("Sftp")] - [TestCategory("integration")] - [ExpectedException(typeof(SftpPermissionDeniedException))] - public void Test_Sftp_CreateDirectory_In_Forbidden_Directory() - { - if (Resources.USERNAME == "root") - Assert.Fail("Must not run this test as root!"); - - using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) - { - sftp.Connect(); - - sftp.CreateDirectory("/sbin/test"); - - sftp.Disconnect(); - } - } - - [TestMethod] - [TestCategory("Sftp")] - [TestCategory("integration")] - [ExpectedException(typeof(SftpPathNotFoundException))] - public void Test_Sftp_CreateDirectory_Invalid_Path() - { - using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) - { - sftp.Connect(); - - sftp.CreateDirectory("/abcdefg/abcefg"); - - sftp.Disconnect(); - } - } - - [TestMethod] - [TestCategory("Sftp")] - [TestCategory("integration")] - [ExpectedException(typeof(SshException))] - public void Test_Sftp_CreateDirectory_Already_Exists() - { - using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) - { - sftp.Connect(); - - sftp.CreateDirectory("test"); - - sftp.CreateDirectory("test"); - - sftp.Disconnect(); - } - } - - [TestMethod] - [TestCategory("Sftp")] - [Description("Test passing null to CreateDirectory.")] - [ExpectedException(typeof(ArgumentException))] - public void Test_Sftp_CreateDirectory_Null() - { - using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) - { - sftp.CreateDirectory(null); - } - } - } -} \ No newline at end of file diff --git a/src/Renci.SshNet.Tests/Classes/SftpClientTest.DeleteDirectory.cs b/src/Renci.SshNet.Tests/Classes/SftpClientTest.DeleteDirectory.cs index 5e981afc..d25fb37f 100644 --- a/src/Renci.SshNet.Tests/Classes/SftpClientTest.DeleteDirectory.cs +++ b/src/Renci.SshNet.Tests/Classes/SftpClientTest.DeleteDirectory.cs @@ -1,7 +1,6 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Renci.SshNet.Common; using Renci.SshNet.Tests.Properties; -using System; namespace Renci.SshNet.Tests.Classes { @@ -20,73 +19,5 @@ namespace Renci.SshNet.Tests.Classes sftp.DeleteDirectory("test"); } } - - [TestMethod] - [TestCategory("Sftp")] - [TestCategory("integration")] - [ExpectedException(typeof(SftpPathNotFoundException))] - public void Test_Sftp_DeleteDirectory_Which_Doesnt_Exists() - { - using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) - { - sftp.Connect(); - - sftp.DeleteDirectory("abcdef"); - - sftp.Disconnect(); - } - } - - [TestMethod] - [TestCategory("Sftp")] - [TestCategory("integration")] - [ExpectedException(typeof(SftpPermissionDeniedException))] - public void Test_Sftp_DeleteDirectory_Which_No_Permissions() - { - if (Resources.USERNAME == "root") - Assert.Fail("Must not run this test as root!"); - - using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) - { - sftp.Connect(); - - sftp.DeleteDirectory("/usr"); - - sftp.Disconnect(); - } - } - - [TestMethod] - [TestCategory("Sftp")] - [TestCategory("integration")] - public void Test_Sftp_DeleteDirectory() - { - using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) - { - sftp.Connect(); - - sftp.CreateDirectory("abcdef"); - sftp.DeleteDirectory("abcdef"); - - sftp.Disconnect(); - } - } - - [TestMethod] - [TestCategory("Sftp")] - [TestCategory("integration")] - [Description("Test passing null to DeleteDirectory.")] - [ExpectedException(typeof(ArgumentException))] - public void Test_Sftp_DeleteDirectory_Null() - { - using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) - { - sftp.Connect(); - - sftp.DeleteDirectory(null); - - sftp.Disconnect(); - } - } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.Tests/Classes/SftpClientTest.DeleteFileAsync.cs b/src/Renci.SshNet.Tests/Classes/SftpClientTest.DeleteFileAsync.cs new file mode 100644 index 00000000..9d846437 --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/SftpClientTest.DeleteFileAsync.cs @@ -0,0 +1,25 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Renci.SshNet.Tests.Properties; +using System; +using System.Threading.Tasks; + +namespace Renci.SshNet.Tests.Classes +{ + /// + /// Implementation of the SSH File Transfer Protocol (SFTP) over SSH. + /// + public partial class SftpClientTest + { + [TestMethod] + [TestCategory("Sftp")] + [Description("Test passing null to DeleteFile.")] + [ExpectedException(typeof(ArgumentException))] + public async Task Test_Sftp_DeleteFileAsync_Null() + { + using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) + { + await sftp.DeleteFileAsync(null, default); + } + } + } +} \ No newline at end of file diff --git a/src/Renci.SshNet.Tests/Classes/SftpClientTest.ListDirectory.cs b/src/Renci.SshNet.Tests/Classes/SftpClientTest.ListDirectory.cs index 69f90aef..ceafd4c5 100644 --- a/src/Renci.SshNet.Tests/Classes/SftpClientTest.ListDirectory.cs +++ b/src/Renci.SshNet.Tests/Classes/SftpClientTest.ListDirectory.cs @@ -1,9 +1,8 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Renci.SshNet.Common; using Renci.SshNet.Tests.Properties; -using System; + using System.Diagnostics; -using System.Linq; namespace Renci.SshNet.Tests.Classes { @@ -26,243 +25,5 @@ namespace Renci.SshNet.Tests.Classes } } } - - [TestMethod] - [TestCategory("Sftp")] - [TestCategory("integration")] - [ExpectedException(typeof(SftpPermissionDeniedException))] - public void Test_Sftp_ListDirectory_Permission_Denied() - { - using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) - { - sftp.Connect(); - - var files = sftp.ListDirectory("/root"); - foreach (var file in files) - { - Debug.WriteLine(file.FullName); - } - - sftp.Disconnect(); - } - } - - [TestMethod] - [TestCategory("Sftp")] - [TestCategory("integration")] - [ExpectedException(typeof(SftpPathNotFoundException))] - public void Test_Sftp_ListDirectory_Not_Exists() - { - using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) - { - sftp.Connect(); - - var files = sftp.ListDirectory("/asdfgh"); - foreach (var file in files) - { - Debug.WriteLine(file.FullName); - } - - sftp.Disconnect(); - } - } - - [TestMethod] - [TestCategory("Sftp")] - [TestCategory("integration")] - public void Test_Sftp_ListDirectory_Current() - { - using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) - { - sftp.Connect(); - - var files = sftp.ListDirectory("."); - - Assert.IsTrue(files.Count() > 0); - - foreach (var file in files) - { - Debug.WriteLine(file.FullName); - } - - sftp.Disconnect(); - } - } - - [TestMethod] - [TestCategory("Sftp")] - [TestCategory("integration")] - public void Test_Sftp_ListDirectory_Empty() - { - using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) - { - sftp.Connect(); - - var files = sftp.ListDirectory(string.Empty); - - Assert.IsTrue(files.Count() > 0); - - foreach (var file in files) - { - Debug.WriteLine(file.FullName); - } - - sftp.Disconnect(); - } - } - - [TestMethod] - [TestCategory("Sftp")] - [TestCategory("integration")] - [Description("Test passing null to ListDirectory.")] - [ExpectedException(typeof(ArgumentNullException))] - public void Test_Sftp_ListDirectory_Null() - { - using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) - { - sftp.Connect(); - - var files = sftp.ListDirectory(null); - - Assert.IsTrue(files.Count() > 0); - - foreach (var file in files) - { - Debug.WriteLine(file.FullName); - } - - sftp.Disconnect(); - } - } - - [TestMethod] - [TestCategory("Sftp")] - [TestCategory("integration")] - public void Test_Sftp_ListDirectory_HugeDirectory() - { - using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) - { - sftp.Connect(); - - // Create 10000 directory items - for (int i = 0; i < 10000; i++) - { - sftp.CreateDirectory(string.Format("test_{0}", i)); - Debug.WriteLine("Created " + i); - } - - var files = sftp.ListDirectory("."); - - // Ensure that directory has at least 10000 items - Assert.IsTrue(files.Count() > 10000); - - sftp.Disconnect(); - } - - RemoveAllFiles(); - } - - [TestMethod] - [TestCategory("Sftp")] - [TestCategory("integration")] - public void Test_Sftp_Change_Directory() - { - using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) - { - sftp.Connect(); - - Assert.AreEqual(sftp.WorkingDirectory, "/home/tester"); - - sftp.CreateDirectory("test1"); - - sftp.ChangeDirectory("test1"); - - Assert.AreEqual(sftp.WorkingDirectory, "/home/tester/test1"); - - sftp.CreateDirectory("test1_1"); - sftp.CreateDirectory("test1_2"); - sftp.CreateDirectory("test1_3"); - - var files = sftp.ListDirectory("."); - - Assert.IsTrue(files.First().FullName.StartsWith(string.Format("{0}", sftp.WorkingDirectory))); - - sftp.ChangeDirectory("test1_1"); - - Assert.AreEqual(sftp.WorkingDirectory, "/home/tester/test1/test1_1"); - - sftp.ChangeDirectory("../test1_2"); - - Assert.AreEqual(sftp.WorkingDirectory, "/home/tester/test1/test1_2"); - - sftp.ChangeDirectory(".."); - - Assert.AreEqual(sftp.WorkingDirectory, "/home/tester/test1"); - - sftp.ChangeDirectory(".."); - - Assert.AreEqual(sftp.WorkingDirectory, "/home/tester"); - - files = sftp.ListDirectory("test1/test1_1"); - - Assert.IsTrue(files.First().FullName.StartsWith(string.Format("{0}/test1/test1_1", sftp.WorkingDirectory))); - - sftp.ChangeDirectory("test1/test1_1"); - - Assert.AreEqual(sftp.WorkingDirectory, "/home/tester/test1/test1_1"); - - sftp.ChangeDirectory("/home/tester/test1/test1_1"); - - Assert.AreEqual(sftp.WorkingDirectory, "/home/tester/test1/test1_1"); - - sftp.ChangeDirectory("/home/tester/test1/test1_1/../test1_2"); - - Assert.AreEqual(sftp.WorkingDirectory, "/home/tester/test1/test1_2"); - - sftp.ChangeDirectory("../../"); - - sftp.DeleteDirectory("test1/test1_1"); - sftp.DeleteDirectory("test1/test1_2"); - sftp.DeleteDirectory("test1/test1_3"); - sftp.DeleteDirectory("test1"); - - sftp.Disconnect(); - } - - RemoveAllFiles(); - } - - [TestMethod] - [TestCategory("Sftp")] - [TestCategory("integration")] - [Description("Test passing null to ChangeDirectory.")] - [ExpectedException(typeof(ArgumentNullException))] - public void Test_Sftp_ChangeDirectory_Null() - { - using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) - { - sftp.Connect(); - - sftp.ChangeDirectory(null); - - sftp.Disconnect(); - } - } - - [TestMethod] - [TestCategory("Sftp")] - [TestCategory("integration")] - [Description("Test calling EndListDirectory method more then once.")] - [ExpectedException(typeof(ArgumentException))] - public void Test_Sftp_Call_EndListDirectory_Twice() - { - using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) - { - sftp.Connect(); - var ar = sftp.BeginListDirectory("/", null, null); - var result = sftp.EndListDirectory(ar); - var result1 = sftp.EndListDirectory(ar); - } - } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.Tests/Classes/SftpClientTest.cs b/src/Renci.SshNet.Tests/Classes/SftpClientTest.cs index f562f66b..9e1c1f3c 100644 --- a/src/Renci.SshNet.Tests/Classes/SftpClientTest.cs +++ b/src/Renci.SshNet.Tests/Classes/SftpClientTest.cs @@ -1,11 +1,9 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Renci.SshNet.Sftp; using Renci.SshNet.Tests.Common; -using Renci.SshNet.Tests.Properties; using System; using System.Collections.Generic; using System.IO; -using System.Security.Cryptography; using System.Text; namespace Renci.SshNet.Tests.Classes @@ -94,7 +92,7 @@ namespace Renci.SshNet.Tests.Classes catch (ArgumentOutOfRangeException ex) { Assert.IsNull(ex.InnerException); - Assert.AreEqual("The timeout must represent a value between -1 and Int32.MaxValue, inclusive." + Environment.NewLine + "Parameter name: " + ex.ParamName, ex.Message); + ArgumentExceptionAssert.MessageEquals("The timeout must represent a value between -1 and Int32.MaxValue, inclusive.", ex); Assert.AreEqual("value", ex.ParamName); } } @@ -113,7 +111,7 @@ namespace Renci.SshNet.Tests.Classes catch (ArgumentOutOfRangeException ex) { Assert.IsNull(ex.InnerException); - Assert.AreEqual("The timeout must represent a value between -1 and Int32.MaxValue, inclusive." + Environment.NewLine + "Parameter name: " + ex.ParamName, ex.Message); + ArgumentExceptionAssert.MessageEquals("The timeout must represent a value between -1 and Int32.MaxValue, inclusive.", ex); Assert.AreEqual("value", ex.ParamName); } } @@ -562,8 +560,8 @@ namespace Renci.SshNet.Tests.Classes ConnectionInfo connectionInfo = null; // TODO: Initialize to an appropriate value SftpClient target = new SftpClient(connectionInfo); // TODO: Initialize to an appropriate value IAsyncResult asyncResult = null; // TODO: Initialize to an appropriate value - IEnumerable expected = null; // TODO: Initialize to an appropriate value - IEnumerable actual; + IEnumerable expected = null; // TODO: Initialize to an appropriate value + IEnumerable actual; actual = target.EndListDirectory(asyncResult); Assert.AreEqual(expected, actual); Assert.Inconclusive("Verify the correctness of this test method."); @@ -702,8 +700,8 @@ namespace Renci.SshNet.Tests.Classes ConnectionInfo connectionInfo = null; // TODO: Initialize to an appropriate value SftpClient target = new SftpClient(connectionInfo); // TODO: Initialize to an appropriate value string path = string.Empty; // TODO: Initialize to an appropriate value - SftpFile expected = null; // TODO: Initialize to an appropriate value - SftpFile actual; + ISftpFile expected = null; // TODO: Initialize to an appropriate value + ISftpFile actual; actual = target.Get(path); Assert.AreEqual(expected, actual); Assert.Inconclusive("Verify the correctness of this test method."); @@ -802,8 +800,8 @@ namespace Renci.SshNet.Tests.Classes SftpClient target = new SftpClient(connectionInfo); // TODO: Initialize to an appropriate value string path = string.Empty; // TODO: Initialize to an appropriate value Action listCallback = null; // TODO: Initialize to an appropriate value - IEnumerable expected = null; // TODO: Initialize to an appropriate value - IEnumerable actual; + IEnumerable expected = null; // TODO: Initialize to an appropriate value + IEnumerable actual; actual = target.ListDirectory(path, listCallback); Assert.AreEqual(expected, actual); Assert.Inconclusive("Verify the correctness of this test method."); @@ -1355,60 +1353,5 @@ namespace Renci.SshNet.Tests.Classes actual = target.WorkingDirectory; Assert.Inconclusive("Verify the correctness of this test method."); } - - protected static string CalculateMD5(string fileName) - { - using (FileStream file = new FileStream(fileName, FileMode.Open)) - { - MD5 md5 = new MD5CryptoServiceProvider(); - byte[] retVal = md5.ComputeHash(file); - file.Close(); - - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < retVal.Length; i++) - { - sb.Append(retVal[i].ToString("x2")); - } - return sb.ToString(); - } - } - - private static void RemoveAllFiles() - { - using (var client = new SshClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) - { - client.Connect(); - client.RunCommand("rm -rf *"); - client.Disconnect(); - } - } - - /// - /// Helper class to help with upload and download testing - /// - private class TestInfo - { - public string RemoteFileName { get; set; } - - public string UploadedFileName { get; set; } - - public string DownloadedFileName { get; set; } - - //public ulong UploadedBytes { get; set; } - - //public ulong DownloadedBytes { get; set; } - - public FileStream UploadedFile { get; set; } - - public FileStream DownloadedFile { get; set; } - - public string UploadedHash { get; set; } - - public string DownloadedHash { get; set; } - - public SftpUploadAsyncResult UploadResult { get; set; } - - public SftpDownloadAsyncResult DownloadResult { get; set; } - } } } diff --git a/src/Renci.SshNet.Tests/Classes/SftpClientTestBase.cs b/src/Renci.SshNet.Tests/Classes/SftpClientTestBase.cs index 11657962..e0ca2c91 100644 --- a/src/Renci.SshNet.Tests/Classes/SftpClientTestBase.cs +++ b/src/Renci.SshNet.Tests/Classes/SftpClientTestBase.cs @@ -5,15 +5,15 @@ namespace Renci.SshNet.Tests.Classes { public abstract class SftpClientTestBase : BaseClientTestBase { - internal Mock _sftpResponseFactoryMock { get; private set; } - internal Mock _sftpSessionMock { get; private set; } + internal Mock SftpResponseFactoryMock { get; private set; } + internal Mock SftpSessionMock { get; private set; } protected override void CreateMocks() { base.CreateMocks(); - _sftpResponseFactoryMock = new Mock(MockBehavior.Strict); - _sftpSessionMock = new Mock(MockBehavior.Strict); + SftpResponseFactoryMock = new Mock(MockBehavior.Strict); + SftpSessionMock = new Mock(MockBehavior.Strict); } } } diff --git a/src/Renci.SshNet.Tests/Classes/SftpClientTest_Connect_SftpSessionConnectFailure.cs b/src/Renci.SshNet.Tests/Classes/SftpClientTest_Connect_SftpSessionConnectFailure.cs index b3bcb8e8..80c050a8 100644 --- a/src/Renci.SshNet.Tests/Classes/SftpClientTest_Connect_SftpSessionConnectFailure.cs +++ b/src/Renci.SshNet.Tests/Classes/SftpClientTest_Connect_SftpSessionConnectFailure.cs @@ -1,12 +1,14 @@ using System; +using System.Linq; using System.Reflection; using System.Threading; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; + using Renci.SshNet.Common; -using Renci.SshNet.Connection; using Renci.SshNet.Security; -using Renci.SshNet.Sftp; namespace Renci.SshNet.Tests.Classes { @@ -28,34 +30,34 @@ namespace Renci.SshNet.Tests.Classes { var sequence = new MockSequence(); - _serviceFactoryMock.InSequence(sequence) - .Setup(p => p.CreateSocketFactory()) - .Returns(_socketFactoryMock.Object); - _serviceFactoryMock.InSequence(sequence) - .Setup(p => p.CreateSession(_connectionInfo, _socketFactoryMock.Object)) - .Returns(_sessionMock.Object); - _sessionMock.InSequence(sequence) - .Setup(p => p.Connect()); - _serviceFactoryMock.InSequence(sequence) - .Setup(p => p.CreateSftpResponseFactory()) - .Returns(_sftpResponseFactoryMock.Object); - _serviceFactoryMock.InSequence(sequence) - .Setup(p => p.CreateSftpSession(_sessionMock.Object, -1, _connectionInfo.Encoding, _sftpResponseFactoryMock.Object)) - .Returns(_sftpSessionMock.Object); - _sftpSessionMock.InSequence(sequence) - .Setup(p => p.Connect()) - .Throws(_sftpSessionConnectionException); - _sftpSessionMock.InSequence(sequence) + _ = ServiceFactoryMock.InSequence(sequence) + .Setup(p => p.CreateSocketFactory()) + .Returns(SocketFactoryMock.Object); + _ = ServiceFactoryMock.InSequence(sequence) + .Setup(p => p.CreateSession(_connectionInfo, SocketFactoryMock.Object)) + .Returns(SessionMock.Object); + _ = SessionMock.InSequence(sequence) + .Setup(p => p.Connect()); + _ = ServiceFactoryMock.InSequence(sequence) + .Setup(p => p.CreateSftpResponseFactory()) + .Returns(SftpResponseFactoryMock.Object); + _ = ServiceFactoryMock.InSequence(sequence) + .Setup(p => p.CreateSftpSession(SessionMock.Object, -1, _connectionInfo.Encoding, SftpResponseFactoryMock.Object)) + .Returns(SftpSessionMock.Object); + _ = SftpSessionMock.InSequence(sequence) + .Setup(p => p.Connect()) + .Throws(_sftpSessionConnectionException); + _ = SftpSessionMock.InSequence(sequence) + .Setup(p => p.Dispose()); + _ = SessionMock.InSequence(sequence) .Setup(p => p.Dispose()); - _sessionMock.InSequence(sequence) - .Setup(p => p.Dispose()); } protected override void Arrange() { base.Arrange(); - _sftpClient = new SftpClient(_connectionInfo, false, _serviceFactoryMock.Object); + _sftpClient = new SftpClient(_connectionInfo, false, ServiceFactoryMock.Object); } protected override void Act() @@ -97,7 +99,7 @@ namespace Renci.SshNet.Tests.Classes _sftpClient.ErrorOccurred += (sender, args) => Interlocked.Increment(ref errorOccurredSignalCount); - _sessionMock.Raise(p => p.ErrorOccured += null, new ExceptionEventArgs(new Exception())); + SessionMock.Raise(p => p.ErrorOccured += null, new ExceptionEventArgs(new Exception())); Assert.AreEqual(0, errorOccurredSignalCount); } @@ -109,7 +111,7 @@ namespace Renci.SshNet.Tests.Classes _sftpClient.HostKeyReceived += (sender, args) => Interlocked.Increment(ref hostKeyReceivedSignalCount); - _sessionMock.Raise(p => p.HostKeyReceived += null, new HostKeyEventArgs(GetKeyHostAlgorithm())); + SessionMock.Raise(p => p.HostKeyReceived += null, new HostKeyEventArgs(GetKeyHostAlgorithm())); Assert.AreEqual(0, hostKeyReceivedSignalCount); } @@ -121,7 +123,7 @@ namespace Renci.SshNet.Tests.Classes using (var s = executingAssembly.GetManifestResourceStream(string.Format("Renci.SshNet.Tests.Data.{0}", "Key.RSA.txt"))) { var privateKey = new PrivateKeyFile(s); - return (KeyHostAlgorithm)privateKey.HostKey; + return (KeyHostAlgorithm)privateKey.HostKeyAlgorithms.First(); } } } diff --git a/src/Renci.SshNet.Tests/Classes/SftpClientTest_Dispose_Connected.cs b/src/Renci.SshNet.Tests/Classes/SftpClientTest_Dispose_Connected.cs index 239c8493..fe8ef9f6 100644 --- a/src/Renci.SshNet.Tests/Classes/SftpClientTest_Dispose_Connected.cs +++ b/src/Renci.SshNet.Tests/Classes/SftpClientTest_Dispose_Connected.cs @@ -1,7 +1,8 @@ using System; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; -using Renci.SshNet.Sftp; namespace Renci.SshNet.Tests.Classes { @@ -16,31 +17,38 @@ namespace Renci.SshNet.Tests.Classes { _connectionInfo = new ConnectionInfo("host", "user", new NoneAuthenticationMethod("userauth")); _operationTimeout = new Random().Next(1000, 10000); - _sftpClient = new SftpClient(_connectionInfo, false, _serviceFactoryMock.Object); - _sftpClient.OperationTimeout = TimeSpan.FromMilliseconds(_operationTimeout); + _sftpClient = new SftpClient(_connectionInfo, false, ServiceFactoryMock.Object) + { + OperationTimeout = TimeSpan.FromMilliseconds(_operationTimeout) + }; } protected override void SetupMocks() { var sequence = new MockSequence(); - _serviceFactoryMock.InSequence(sequence) - .Setup(p => p.CreateSocketFactory()) - .Returns(_socketFactoryMock.Object); - _serviceFactoryMock.InSequence(sequence) - .Setup(p => p.CreateSession(_connectionInfo, _socketFactoryMock.Object)) - .Returns(_sessionMock.Object); - _sessionMock.InSequence(sequence).Setup(p => p.Connect()); - _serviceFactoryMock.InSequence(sequence) - .Setup(p => p.CreateSftpResponseFactory()) - .Returns(_sftpResponseFactoryMock.Object); - _serviceFactoryMock.InSequence(sequence) - .Setup(p => p.CreateSftpSession(_sessionMock.Object, _operationTimeout, _connectionInfo.Encoding, _sftpResponseFactoryMock.Object)) - .Returns(_sftpSessionMock.Object); - _sftpSessionMock.InSequence(sequence).Setup(p => p.Connect()); - _sessionMock.InSequence(sequence).Setup(p => p.OnDisconnecting()); - _sftpSessionMock.InSequence(sequence).Setup(p => p.Dispose()); - _sessionMock.InSequence(sequence).Setup(p => p.Dispose()); + _ = ServiceFactoryMock.InSequence(sequence) + .Setup(p => p.CreateSocketFactory()) + .Returns(SocketFactoryMock.Object); + _ = ServiceFactoryMock.InSequence(sequence) + .Setup(p => p.CreateSession(_connectionInfo, SocketFactoryMock.Object)) + .Returns(SessionMock.Object); + _ = SessionMock.InSequence(sequence) + .Setup(p => p.Connect()); + _ = ServiceFactoryMock.InSequence(sequence) + .Setup(p => p.CreateSftpResponseFactory()) + .Returns(SftpResponseFactoryMock.Object); + _ = ServiceFactoryMock.InSequence(sequence) + .Setup(p => p.CreateSftpSession(SessionMock.Object, _operationTimeout, _connectionInfo.Encoding, SftpResponseFactoryMock.Object)) + .Returns(SftpSessionMock.Object); + _ = SftpSessionMock.InSequence(sequence) + .Setup(p => p.Connect()); + _ = SessionMock.InSequence(sequence) + .Setup(p => p.OnDisconnecting()); + _ = SftpSessionMock.InSequence(sequence) + .Setup(p => p.Dispose()); + _ = SessionMock.InSequence(sequence) + .Setup(p => p.Dispose()); } protected override void Arrange() @@ -58,58 +66,58 @@ namespace Renci.SshNet.Tests.Classes [TestMethod] public void CreateSftpMessageFactoryOnServiceFactoryShouldBeInvokedOnce() { - _serviceFactoryMock.Verify(p => p.CreateSftpResponseFactory(), Times.Once); + ServiceFactoryMock.Verify(p => p.CreateSftpResponseFactory(), Times.Once); } [TestMethod] public void CreateSftpSessionOnServiceFactoryShouldBeInvokedOnce() { - _serviceFactoryMock.Verify( - p => p.CreateSftpSession(_sessionMock.Object, _operationTimeout, _connectionInfo.Encoding, _sftpResponseFactoryMock.Object), + ServiceFactoryMock.Verify( + p => p.CreateSftpSession(SessionMock.Object, _operationTimeout, _connectionInfo.Encoding, SftpResponseFactoryMock.Object), Times.Once); } [TestMethod] public void CreateSocketFactoryOnServiceFactoryShouldBeInvokedOnce() { - _serviceFactoryMock.Verify(p => p.CreateSocketFactory(), Times.Once); + ServiceFactoryMock.Verify(p => p.CreateSocketFactory(), Times.Once); } [TestMethod] public void CreateSessionOnServiceFactoryShouldBeInvokedOnce() { - _serviceFactoryMock.Verify(p => p.CreateSession(_connectionInfo, _socketFactoryMock.Object), + ServiceFactoryMock.Verify(p => p.CreateSession(_connectionInfo, SocketFactoryMock.Object), Times.Once); } [TestMethod] public void DisconnectOnNetConfSessionShouldNeverBeInvoked() { - _sftpSessionMock.Verify(p => p.Disconnect(), Times.Never); + SftpSessionMock.Verify(p => p.Disconnect(), Times.Never); } [TestMethod] public void DisconnectOnSessionShouldNeverBeInvoked() { - _sessionMock.Verify(p => p.Disconnect(), Times.Never); + SessionMock.Verify(p => p.Disconnect(), Times.Never); } [TestMethod] public void DisposeOnNetConfSessionShouldBeInvokedOnce() { - _sftpSessionMock.Verify(p => p.Dispose(), Times.Once); + SftpSessionMock.Verify(p => p.Dispose(), Times.Once); } [TestMethod] public void DisposeOnSessionShouldBeInvokedOnce() { - _sessionMock.Verify(p => p.Dispose(), Times.Once); + SessionMock.Verify(p => p.Dispose(), Times.Once); } [TestMethod] public void OnDisconnectingOnSessionShouldBeInvokedOnce() { - _sessionMock.Verify(p => p.OnDisconnecting(), Times.Once); + SessionMock.Verify(p => p.OnDisconnecting(), Times.Once); } } } diff --git a/src/Renci.SshNet.Tests/Classes/SftpClientTest_Dispose_Disconnected.cs b/src/Renci.SshNet.Tests/Classes/SftpClientTest_Dispose_Disconnected.cs index 192c5e95..0f8d0a46 100644 --- a/src/Renci.SshNet.Tests/Classes/SftpClientTest_Dispose_Disconnected.cs +++ b/src/Renci.SshNet.Tests/Classes/SftpClientTest_Dispose_Disconnected.cs @@ -1,7 +1,8 @@ using System; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; -using Renci.SshNet.Sftp; namespace Renci.SshNet.Tests.Classes { @@ -17,31 +18,38 @@ namespace Renci.SshNet.Tests.Classes { _connectionInfo = new ConnectionInfo("host", "user", new NoneAuthenticationMethod("userauth")); _operationTimeout = new Random().Next(1000, 10000); - _sftpClient = new SftpClient(_connectionInfo, false, _serviceFactoryMock.Object); - _sftpClient.OperationTimeout = TimeSpan.FromMilliseconds(_operationTimeout); + _sftpClient = new SftpClient(_connectionInfo, false, ServiceFactoryMock.Object) + { + OperationTimeout = TimeSpan.FromMilliseconds(_operationTimeout) + }; } protected override void SetupMocks() { var sequence = new MockSequence(); - _serviceFactoryMock.InSequence(sequence) - .Setup(p => p.CreateSocketFactory()) - .Returns(_socketFactoryMock.Object); - _serviceFactoryMock.InSequence(sequence) - .Setup(p => p.CreateSession(_connectionInfo, _socketFactoryMock.Object)) - .Returns(_sessionMock.Object); - _sessionMock.InSequence(sequence).Setup(p => p.Connect()); - _serviceFactoryMock.InSequence(sequence) - .Setup(p => p.CreateSftpResponseFactory()) - .Returns(_sftpResponseFactoryMock.Object); - _serviceFactoryMock.InSequence(sequence) - .Setup(p => p.CreateSftpSession(_sessionMock.Object, _operationTimeout, _connectionInfo.Encoding, _sftpResponseFactoryMock.Object)) - .Returns(_sftpSessionMock.Object); - _sftpSessionMock.InSequence(sequence).Setup(p => p.Connect()); - _sessionMock.InSequence(sequence).Setup(p => p.OnDisconnecting()); - _sftpSessionMock.InSequence(sequence).Setup(p => p.Dispose()); - _sessionMock.InSequence(sequence).Setup(p => p.Dispose()); + _ = ServiceFactoryMock.InSequence(sequence) + .Setup(p => p.CreateSocketFactory()) + .Returns(SocketFactoryMock.Object); + _ = ServiceFactoryMock.InSequence(sequence) + .Setup(p => p.CreateSession(_connectionInfo, SocketFactoryMock.Object)) + .Returns(SessionMock.Object); + _ = SessionMock.InSequence(sequence) + .Setup(p => p.Connect()); + _ = ServiceFactoryMock.InSequence(sequence) + .Setup(p => p.CreateSftpResponseFactory()) + .Returns(SftpResponseFactoryMock.Object); + _ = ServiceFactoryMock.InSequence(sequence) + .Setup(p => p.CreateSftpSession(SessionMock.Object, _operationTimeout, _connectionInfo.Encoding, SftpResponseFactoryMock.Object)) + .Returns(SftpSessionMock.Object); + _ = SftpSessionMock.InSequence(sequence) + .Setup(p => p.Connect()); + _ = SessionMock.InSequence(sequence) + .Setup(p => p.OnDisconnecting()); + _ = SftpSessionMock.InSequence(sequence) + .Setup(p => p.Dispose()); + _ = SessionMock.InSequence(sequence) + .Setup(p => p.Dispose()); } protected override void Arrange() @@ -60,58 +68,58 @@ namespace Renci.SshNet.Tests.Classes [TestMethod] public void CreateSftpMessageFactoryOnServiceFactoryShouldBeInvokedOnce() { - _serviceFactoryMock.Verify(p => p.CreateSftpResponseFactory(), Times.Once); + ServiceFactoryMock.Verify(p => p.CreateSftpResponseFactory(), Times.Once); } [TestMethod] public void CreateSftpSessionOnServiceFactoryShouldBeInvokedOnce() { - _serviceFactoryMock.Verify( - p => p.CreateSftpSession(_sessionMock.Object, _operationTimeout, _connectionInfo.Encoding, _sftpResponseFactoryMock.Object), + ServiceFactoryMock.Verify( + p => p.CreateSftpSession(SessionMock.Object, _operationTimeout, _connectionInfo.Encoding, SftpResponseFactoryMock.Object), Times.Once); } [TestMethod] public void CreateSocketFactoryOnServiceFactoryShouldBeInvokedOnce() { - _serviceFactoryMock.Verify(p => p.CreateSocketFactory(), Times.Once); + ServiceFactoryMock.Verify(p => p.CreateSocketFactory(), Times.Once); } [TestMethod] public void CreateSessionOnServiceFactoryShouldBeInvokedOnce() { - _serviceFactoryMock.Verify(p => p.CreateSession(_connectionInfo, _socketFactoryMock.Object), + ServiceFactoryMock.Verify(p => p.CreateSession(_connectionInfo, SocketFactoryMock.Object), Times.Once); } [TestMethod] public void DisconnectOnNetConfSessionShouldNeverBeInvoked() { - _sftpSessionMock.Verify(p => p.Disconnect(), Times.Never); + SftpSessionMock.Verify(p => p.Disconnect(), Times.Never); } [TestMethod] public void DisconnectOnSessionShouldNeverBeInvoked() { - _sessionMock.Verify(p => p.Disconnect(), Times.Never); + SessionMock.Verify(p => p.Disconnect(), Times.Never); } [TestMethod] public void DisposeOnNetConfSessionShouldBeInvokedOnce() { - _sftpSessionMock.Verify(p => p.Dispose(), Times.Once); + SftpSessionMock.Verify(p => p.Dispose(), Times.Once); } [TestMethod] public void DisposeOnSessionShouldBeInvokedOnce() { - _sessionMock.Verify(p => p.Dispose(), Times.Once); + SessionMock.Verify(p => p.Dispose(), Times.Once); } [TestMethod] public void OnDisconnectingOnSessionShouldBeInvokedOnce() { - _sessionMock.Verify(p => p.OnDisconnecting(), Times.Once); + SessionMock.Verify(p => p.OnDisconnecting(), Times.Once); } } } diff --git a/src/Renci.SshNet.Tests/Classes/SftpClientTest_Dispose_Disposed.cs b/src/Renci.SshNet.Tests/Classes/SftpClientTest_Dispose_Disposed.cs index 11e00ad0..09208b8b 100644 --- a/src/Renci.SshNet.Tests/Classes/SftpClientTest_Dispose_Disposed.cs +++ b/src/Renci.SshNet.Tests/Classes/SftpClientTest_Dispose_Disposed.cs @@ -1,7 +1,8 @@ using System; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; -using Renci.SshNet.Sftp; namespace Renci.SshNet.Tests.Classes { @@ -16,31 +17,38 @@ namespace Renci.SshNet.Tests.Classes { _connectionInfo = new ConnectionInfo("host", "user", new NoneAuthenticationMethod("userauth")); _operationTimeout = new Random().Next(1000, 10000); - _sftpClient = new SftpClient(_connectionInfo, false, _serviceFactoryMock.Object); - _sftpClient.OperationTimeout = TimeSpan.FromMilliseconds(_operationTimeout); + _sftpClient = new SftpClient(_connectionInfo, false, ServiceFactoryMock.Object) + { + OperationTimeout = TimeSpan.FromMilliseconds(_operationTimeout) + }; } protected override void SetupMocks() { var sequence = new MockSequence(); - _serviceFactoryMock.InSequence(sequence) - .Setup(p => p.CreateSocketFactory()) - .Returns(_socketFactoryMock.Object); - _serviceFactoryMock.InSequence(sequence) - .Setup(p => p.CreateSession(_connectionInfo, _socketFactoryMock.Object)) - .Returns(_sessionMock.Object); - _sessionMock.InSequence(sequence).Setup(p => p.Connect()); - _serviceFactoryMock.InSequence(sequence) - .Setup(p => p.CreateSftpResponseFactory()) - .Returns(_sftpResponseFactoryMock.Object); - _serviceFactoryMock.InSequence(sequence) - .Setup(p => p.CreateSftpSession(_sessionMock.Object, _operationTimeout, _connectionInfo.Encoding, _sftpResponseFactoryMock.Object)) - .Returns(_sftpSessionMock.Object); - _sftpSessionMock.InSequence(sequence).Setup(p => p.Connect()); - _sessionMock.InSequence(sequence).Setup(p => p.OnDisconnecting()); - _sftpSessionMock.InSequence(sequence).Setup(p => p.Dispose()); - _sessionMock.InSequence(sequence).Setup(p => p.Dispose()); + _ = ServiceFactoryMock.InSequence(sequence) + .Setup(p => p.CreateSocketFactory()) + .Returns(SocketFactoryMock.Object); + _ = ServiceFactoryMock.InSequence(sequence) + .Setup(p => p.CreateSession(_connectionInfo, SocketFactoryMock.Object)) + .Returns(SessionMock.Object); + _ = SessionMock.InSequence(sequence) + .Setup(p => p.Connect()); + _ = ServiceFactoryMock.InSequence(sequence) + .Setup(p => p.CreateSftpResponseFactory()) + .Returns(SftpResponseFactoryMock.Object); + _ = ServiceFactoryMock.InSequence(sequence) + .Setup(p => p.CreateSftpSession(SessionMock.Object, _operationTimeout, _connectionInfo.Encoding, SftpResponseFactoryMock.Object)) + .Returns(SftpSessionMock.Object); + _ = SftpSessionMock.InSequence(sequence) + .Setup(p => p.Connect()); + _ = SessionMock.InSequence(sequence) + .Setup(p => p.OnDisconnecting()); + _ = SftpSessionMock.InSequence(sequence) + .Setup(p => p.Dispose()); + _ = SessionMock.InSequence(sequence) + .Setup(p => p.Dispose()); } protected override void Arrange() @@ -59,58 +67,58 @@ namespace Renci.SshNet.Tests.Classes [TestMethod] public void CreateSftpMessageFactoryOnServiceFactoryShouldBeInvokedOnce() { - _serviceFactoryMock.Verify(p => p.CreateSftpResponseFactory(), Times.Once); + ServiceFactoryMock.Verify(p => p.CreateSftpResponseFactory(), Times.Once); } [TestMethod] public void CreateSftpSessionOnServiceFactoryShouldBeInvokedOnce() { - _serviceFactoryMock.Verify( - p => p.CreateSftpSession(_sessionMock.Object, _operationTimeout, _connectionInfo.Encoding, _sftpResponseFactoryMock.Object), + ServiceFactoryMock.Verify( + p => p.CreateSftpSession(SessionMock.Object, _operationTimeout, _connectionInfo.Encoding, SftpResponseFactoryMock.Object), Times.Once); } [TestMethod] public void CreateSocketFactoryOnServiceFactoryShouldBeInvokedOnce() { - _serviceFactoryMock.Verify(p => p.CreateSocketFactory(), Times.Once); + ServiceFactoryMock.Verify(p => p.CreateSocketFactory(), Times.Once); } [TestMethod] public void CreateSessionOnServiceFactoryShouldBeInvokedOnce() { - _serviceFactoryMock.Verify(p => p.CreateSession(_connectionInfo, _socketFactoryMock.Object), + ServiceFactoryMock.Verify(p => p.CreateSession(_connectionInfo, SocketFactoryMock.Object), Times.Once); } [TestMethod] public void DisconnectOnNetConfSessionShouldNeverBeInvoked() { - _sftpSessionMock.Verify(p => p.Disconnect(), Times.Never); + SftpSessionMock.Verify(p => p.Disconnect(), Times.Never); } [TestMethod] public void DisconnectOnSessionShouldNeverBeInvoked() { - _sessionMock.Verify(p => p.Disconnect(), Times.Never); + SessionMock.Verify(p => p.Disconnect(), Times.Never); } [TestMethod] public void DisposeOnNetConfSessionShouldBeInvokedOnce() { - _sftpSessionMock.Verify(p => p.Dispose(), Times.Once); + SftpSessionMock.Verify(p => p.Dispose(), Times.Once); } [TestMethod] public void DisposeOnSessionShouldBeInvokedOnce() { - _sessionMock.Verify(p => p.Dispose(), Times.Once); + SessionMock.Verify(p => p.Dispose(), Times.Once); } [TestMethod] public void OnDisconnectingOnSessionShouldBeInvokedOnce() { - _sessionMock.Verify(p => p.OnDisconnecting(), Times.Once); + SessionMock.Verify(p => p.OnDisconnecting(), Times.Once); } } } diff --git a/src/Renci.SshNet.Tests/Classes/SftpClientTest_Finalize_Connected.cs b/src/Renci.SshNet.Tests/Classes/SftpClientTest_Finalize_Connected.cs index 033f0ea0..f98f6dad 100644 --- a/src/Renci.SshNet.Tests/Classes/SftpClientTest_Finalize_Connected.cs +++ b/src/Renci.SshNet.Tests/Classes/SftpClientTest_Finalize_Connected.cs @@ -1,7 +1,8 @@ using System; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; -using Renci.SshNet.Sftp; namespace Renci.SshNet.Tests.Classes { @@ -17,23 +18,25 @@ namespace Renci.SshNet.Tests.Classes { _connectionInfo = new ConnectionInfo("host", "user", new NoneAuthenticationMethod("userauth")); _operationTimeout = new Random().Next(1000, 10000); - _sftpClient = new SftpClient(_connectionInfo, false, _serviceFactoryMock.Object); - _sftpClient.OperationTimeout = TimeSpan.FromMilliseconds(_operationTimeout); + _sftpClient = new SftpClient(_connectionInfo, false, ServiceFactoryMock.Object) + { + OperationTimeout = TimeSpan.FromMilliseconds(_operationTimeout) + }; _sftpClientWeakRefence = new WeakReference(_sftpClient); } protected override void SetupMocks() { - _serviceFactoryMock.Setup(p => p.CreateSocketFactory()) - .Returns(_socketFactoryMock.Object); - _serviceFactoryMock.Setup(p => p.CreateSession(_connectionInfo, _socketFactoryMock.Object)) - .Returns(_sessionMock.Object); - _sessionMock.Setup(p => p.Connect()); - _serviceFactoryMock.Setup(p => p.CreateSftpResponseFactory()) - .Returns(_sftpResponseFactoryMock.Object); - _serviceFactoryMock.Setup(p => p.CreateSftpSession(_sessionMock.Object, _operationTimeout, _connectionInfo.Encoding, _sftpResponseFactoryMock.Object)) - .Returns(_sftpSessionMock.Object); - _sftpSessionMock.Setup(p => p.Connect()); + _ = ServiceFactoryMock.Setup(p => p.CreateSocketFactory()) + .Returns(SocketFactoryMock.Object); + _ = ServiceFactoryMock.Setup(p => p.CreateSession(_connectionInfo, SocketFactoryMock.Object)) + .Returns(SessionMock.Object); + _ = SessionMock.Setup(p => p.Connect()); + _ = ServiceFactoryMock.Setup(p => p.CreateSftpResponseFactory()) + .Returns(SftpResponseFactoryMock.Object); + _ = ServiceFactoryMock.Setup(p => p.CreateSftpSession(SessionMock.Object, _operationTimeout, _connectionInfo.Encoding, SftpResponseFactoryMock.Object)) + .Returns(SftpSessionMock.Object); + _ = SftpSessionMock.Setup(p => p.Connect()); } protected override void Arrange() @@ -60,7 +63,7 @@ namespace Renci.SshNet.Tests.Classes // Since we recreated the mocks, this test has no value // We'll leaving ths test just in case we have a solution that does not require us // to recreate the mocks - _sftpSessionMock.Verify(p => p.Disconnect(), Times.Never); + SftpSessionMock.Verify(p => p.Disconnect(), Times.Never); } [TestMethod] @@ -69,7 +72,7 @@ namespace Renci.SshNet.Tests.Classes // Since we recreated the mocks, this test has no value // We'll leaving ths test just in case we have a solution that does not require us // to recreate the mocks - _sftpSessionMock.Verify(p => p.Dispose(), Times.Never); + SftpSessionMock.Verify(p => p.Dispose(), Times.Never); } [TestMethod] diff --git a/src/Renci.SshNet.Tests/Classes/ShellStreamTest_Write_WriteBufferEmptyAndWriteMoreBytesThanBufferSize.cs b/src/Renci.SshNet.Tests/Classes/ShellStreamTest_Write_WriteBufferEmptyAndWriteMoreBytesThanBufferSize.cs index 4f1fa869..941c3f7b 100644 --- a/src/Renci.SshNet.Tests/Classes/ShellStreamTest_Write_WriteBufferEmptyAndWriteMoreBytesThanBufferSize.cs +++ b/src/Renci.SshNet.Tests/Classes/ShellStreamTest_Write_WriteBufferEmptyAndWriteMoreBytesThanBufferSize.cs @@ -51,7 +51,7 @@ namespace Renci.SshNet.Tests.Classes _terminalModes = new Dictionary(); _bufferSize = random.Next(100, 1000); - _data = CryptoAbstraction.GenerateRandom(_bufferSize * 2 + 10); + _data = CryptoAbstraction.GenerateRandom((_bufferSize * 2) + 10); _offset = 0; _count = _data.Length; @@ -70,32 +70,32 @@ namespace Renci.SshNet.Tests.Classes { _mockSequence = new MockSequence(); - _sessionMock.InSequence(_mockSequence) - .Setup(p => p.ConnectionInfo) - .Returns(_connectionInfoMock.Object); - _connectionInfoMock.InSequence(_mockSequence) - .Setup(p => p.Encoding) - .Returns(new UTF8Encoding()); - _sessionMock.InSequence(_mockSequence) - .Setup(p => p.CreateChannelSession()) - .Returns(_channelSessionMock.Object); - _channelSessionMock.InSequence(_mockSequence) - .Setup(p => p.Open()); - _channelSessionMock.InSequence(_mockSequence) - .Setup(p => p.SendPseudoTerminalRequest(_terminalName, - _widthColumns, - _heightRows, - _widthPixels, - _heightPixels, - _terminalModes)) - .Returns(true); - _channelSessionMock.InSequence(_mockSequence) - .Setup(p => p.SendShellRequest()) - .Returns(true); - _channelSessionMock.InSequence(_mockSequence) - .Setup(p => p.SendData(_expectedBytesSent1)); - _channelSessionMock.InSequence(_mockSequence) - .Setup(p => p.SendData(_expectedBytesSent2)); + _ = _sessionMock.InSequence(_mockSequence) + .Setup(p => p.ConnectionInfo) + .Returns(_connectionInfoMock.Object); + _ = _connectionInfoMock.InSequence(_mockSequence) + .Setup(p => p.Encoding) + .Returns(new UTF8Encoding()); + _ = _sessionMock.InSequence(_mockSequence) + .Setup(p => p.CreateChannelSession()) + .Returns(_channelSessionMock.Object); + _ = _channelSessionMock.InSequence(_mockSequence) + .Setup(p => p.Open()); + _ = _channelSessionMock.InSequence(_mockSequence) + .Setup(p => p.SendPseudoTerminalRequest(_terminalName, + _widthColumns, + _heightRows, + _widthPixels, + _heightPixels, + _terminalModes)) + .Returns(true); + _ = _channelSessionMock.InSequence(_mockSequence) + .Setup(p => p.SendShellRequest()) + .Returns(true); + _ = _channelSessionMock.InSequence(_mockSequence) + .Setup(p => p.SendData(_expectedBytesSent1)); + _ = _channelSessionMock.InSequence(_mockSequence) + .Setup(p => p.SendData(_expectedBytesSent2)); } private void Arrange() @@ -129,14 +129,14 @@ namespace Renci.SshNet.Tests.Classes [TestMethod] public void FlushShouldSendRemaningBytesToServer() { - var expectedBytesSent = _data.Take(_bufferSize * 2, _data.Length - _bufferSize * 2); + var expectedBytesSent = _data.Take(_bufferSize * 2, _data.Length - (_bufferSize * 2)); - _channelSessionMock.InSequence(_mockSequence) - .Setup(p => p.SendData(expectedBytesSent)); + _ = _channelSessionMock.InSequence(_mockSequence) + .Setup(p => p.SendData(expectedBytesSent)); _shellStream.Flush(); _channelSessionMock.Verify(p => p.SendData(expectedBytesSent), Times.Once); } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.Tests/Classes/ShellStreamTest_Write_WriteBufferEmptyAndWriteZeroBytes.cs b/src/Renci.SshNet.Tests/Classes/ShellStreamTest_Write_WriteBufferEmptyAndWriteZeroBytes.cs index 05e63a19..94d55ca8 100644 --- a/src/Renci.SshNet.Tests/Classes/ShellStreamTest_Write_WriteBufferEmptyAndWriteZeroBytes.cs +++ b/src/Renci.SshNet.Tests/Classes/ShellStreamTest_Write_WriteBufferEmptyAndWriteZeroBytes.cs @@ -1,9 +1,11 @@ using System; using System.Collections.Generic; using System.Text; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; -using Renci.SshNet.Abstractions; + using Renci.SshNet.Channels; using Renci.SshNet.Common; @@ -64,28 +66,28 @@ namespace Renci.SshNet.Tests.Classes { _mockSequence = new MockSequence(); - _sessionMock.InSequence(_mockSequence) - .Setup(p => p.ConnectionInfo) - .Returns(_connectionInfoMock.Object); - _connectionInfoMock.InSequence(_mockSequence) - .Setup(p => p.Encoding) - .Returns(new UTF8Encoding()); - _sessionMock.InSequence(_mockSequence) - .Setup(p => p.CreateChannelSession()) - .Returns(_channelSessionMock.Object); - _channelSessionMock.InSequence(_mockSequence) - .Setup(p => p.Open()); - _channelSessionMock.InSequence(_mockSequence) - .Setup(p => p.SendPseudoTerminalRequest(_terminalName, - _widthColumns, - _heightRows, - _widthPixels, - _heightPixels, - _terminalModes)) - .Returns(true); - _channelSessionMock.InSequence(_mockSequence) - .Setup(p => p.SendShellRequest()) - .Returns(true); + _ = _sessionMock.InSequence(_mockSequence) + .Setup(p => p.ConnectionInfo) + .Returns(_connectionInfoMock.Object); + _ = _connectionInfoMock.InSequence(_mockSequence) + .Setup(p => p.Encoding) + .Returns(new UTF8Encoding()); + _ = _sessionMock.InSequence(_mockSequence) + .Setup(p => p.CreateChannelSession()) + .Returns(_channelSessionMock.Object); + _ = _channelSessionMock.InSequence(_mockSequence) + .Setup(p => p.Open()); + _ = _channelSessionMock.InSequence(_mockSequence) + .Setup(p => p.SendPseudoTerminalRequest(_terminalName, + _widthColumns, + _heightRows, + _widthPixels, + _heightPixels, + _terminalModes)) + .Returns(true); + _ = _channelSessionMock.InSequence(_mockSequence) + .Setup(p => p.SendShellRequest()) + .Returns(true); } private void Arrange() @@ -123,4 +125,4 @@ namespace Renci.SshNet.Tests.Classes _channelSessionMock.Verify(p => p.SendData(It.IsAny()), Times.Never); } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.Tests/Classes/SshClientTest.cs b/src/Renci.SshNet.Tests/Classes/SshClientTest.cs index 78f1db6c..8ee74c2c 100644 --- a/src/Renci.SshNet.Tests/Classes/SshClientTest.cs +++ b/src/Renci.SshNet.Tests/Classes/SshClientTest.cs @@ -2,11 +2,10 @@ using Renci.SshNet.Common; using Renci.SshNet.Tests.Common; using Renci.SshNet.Tests.Properties; -using System; + using System.Collections.Generic; using System.IO; using System.Text; -using System.Linq; namespace Renci.SshNet.Tests.Classes { @@ -16,247 +15,6 @@ namespace Renci.SshNet.Tests.Classes [TestClass] public class SshClientTest : TestBase { - [TestMethod] - [TestCategory("Authentication")] - [TestCategory("integration")] - public void Test_Connect_Using_Correct_Password() - { - var host = Resources.HOST; - var username = Resources.USERNAME; - var password = Resources.PASSWORD; - - #region Example SshClient(host, username) Connect - using (var client = new SshClient(host, username, password)) - { - client.Connect(); - // Do something here - client.Disconnect(); - } - #endregion - } - - [TestMethod] - [TestCategory("Authentication")] - [TestCategory("integration")] - public void Test_Connect_Handle_HostKeyReceived() - { - var host = Resources.HOST; - var username = Resources.USERNAME; - var password = Resources.PASSWORD; - var hostKeyValidated = false; - - #region Example SshClient Connect HostKeyReceived - using (var client = new SshClient(host, username, password)) - { - client.HostKeyReceived += delegate(object sender, HostKeyEventArgs e) - { - hostKeyValidated = true; - - if (e.FingerPrint.SequenceEqual(new byte[] { 0x00, 0x01, 0x02, 0x03 })) - { - e.CanTrust = true; - } - else - { - e.CanTrust = false; - } - }; - client.Connect(); - // Do something here - client.Disconnect(); - } - #endregion - - Assert.IsTrue(hostKeyValidated); - } - - [TestMethod] - [TestCategory("Authentication")] - [TestCategory("integration")] - public void Test_Connect_Timeout() - { - var host = Resources.HOST; - var username = Resources.USERNAME; - var password = Resources.PASSWORD; - - #region Example SshClient Connect Timeout - var connectionInfo = new PasswordConnectionInfo(host, username, password); - - connectionInfo.Timeout = TimeSpan.FromSeconds(30); - - using (var client = new SshClient(connectionInfo)) - { - client.Connect(); - // Do something here - client.Disconnect(); - } - #endregion - Assert.Inconclusive(); - } - - [TestMethod] - [TestCategory("Authentication")] - [TestCategory("integration")] - public void Test_Connect_Handle_ErrorOccurred() - { - var host = Resources.HOST; - var username = Resources.USERNAME; - var password = Resources.PASSWORD; - var exceptionOccured = false; - - #region Example SshClient Connect ErrorOccurred - using (var client = new SshClient(host, username, password)) - { - client.ErrorOccurred += delegate(object sender, ExceptionEventArgs e) - { - Console.WriteLine("Error occured: " + e.Exception); - exceptionOccured = true; - }; - - client.Connect(); - // Do something here - client.Disconnect(); - } - #endregion - Assert.IsTrue(exceptionOccured); - } - - [TestMethod] - [TestCategory("Authentication")] - [TestCategory("integration")] - [ExpectedException(typeof(SshAuthenticationException))] - public void Test_Connect_Using_Invalid_Password() - { - using (var client = new SshClient(Resources.HOST, Resources.USERNAME, "invalid password")) - { - client.Connect(); - client.Disconnect(); - } - } - - [TestMethod] - [TestCategory("Authentication")] - [TestCategory("integration")] - public void Test_Connect_Using_Rsa_Key_Without_PassPhrase() - { - var host = Resources.HOST; - var username = Resources.USERNAME; - MemoryStream keyFileStream = new MemoryStream(Encoding.ASCII.GetBytes(Resources.RSA_KEY_WITHOUT_PASS)); - - #region Example SshClient(host, username) Connect PrivateKeyFile - using (var client = new SshClient(host, username, new PrivateKeyFile(keyFileStream))) - { - client.Connect(); - client.Disconnect(); - } - #endregion - } - - [TestMethod] - [TestCategory("Authentication")] - [TestCategory("integration")] - public void Test_Connect_Using_RsaKey_With_PassPhrase() - { - var host = Resources.HOST; - var username = Resources.USERNAME; - var passphrase = Resources.PASSWORD; - MemoryStream keyFileStream = new MemoryStream(Encoding.ASCII.GetBytes(Resources.RSA_KEY_WITH_PASS)); - - #region Example SshClient(host, username) Connect PrivateKeyFile PassPhrase - using (var client = new SshClient(host, username, new PrivateKeyFile(keyFileStream, passphrase))) - { - client.Connect(); - client.Disconnect(); - } - #endregion - } - - [TestMethod] - [TestCategory("Authentication")] - [TestCategory("integration")] - [ExpectedException(typeof(SshPassPhraseNullOrEmptyException))] - public void Test_Connect_Using_Key_With_Empty_PassPhrase() - { - MemoryStream keyFileStream = new MemoryStream(Encoding.ASCII.GetBytes(Resources.RSA_KEY_WITH_PASS)); - using (var client = new SshClient(Resources.HOST, Resources.USERNAME, new PrivateKeyFile(keyFileStream, null))) - { - client.Connect(); - client.Disconnect(); - } - } - - [TestMethod] - [TestCategory("Authentication")] - [TestCategory("integration")] - public void Test_Connect_Using_DsaKey_Without_PassPhrase() - { - MemoryStream keyFileStream = new MemoryStream(Encoding.ASCII.GetBytes(Resources.DSA_KEY_WITHOUT_PASS)); - using (var client = new SshClient(Resources.HOST, Resources.USERNAME, new PrivateKeyFile(keyFileStream))) - { - client.Connect(); - client.Disconnect(); - } - } - - [TestMethod] - [TestCategory("Authentication")] - [TestCategory("integration")] - public void Test_Connect_Using_DsaKey_With_PassPhrase() - { - MemoryStream keyFileStream = new MemoryStream(Encoding.ASCII.GetBytes(Resources.DSA_KEY_WITH_PASS)); - using (var client = new SshClient(Resources.HOST, Resources.USERNAME, new PrivateKeyFile(keyFileStream, Resources.PASSWORD))) - { - client.Connect(); - client.Disconnect(); - } - } - - [TestMethod] - [TestCategory("Authentication")] - [TestCategory("integration")] - [ExpectedException(typeof(SshAuthenticationException))] - public void Test_Connect_Using_Invalid_PrivateKey() - { - MemoryStream keyFileStream = new MemoryStream(Encoding.ASCII.GetBytes(Resources.INVALID_KEY)); - using (var client = new SshClient(Resources.HOST, Resources.USERNAME, new PrivateKeyFile(keyFileStream))) - { - client.Connect(); - client.Disconnect(); - } - } - - [TestMethod] - [TestCategory("Authentication")] - [TestCategory("integration")] - public void Test_Connect_Using_Multiple_PrivateKeys() - { - using (var client = new SshClient(Resources.HOST, Resources.USERNAME, - new PrivateKeyFile(new MemoryStream(Encoding.ASCII.GetBytes(Resources.INVALID_KEY))), - new PrivateKeyFile(new MemoryStream(Encoding.ASCII.GetBytes(Resources.DSA_KEY_WITH_PASS)), Resources.PASSWORD), - new PrivateKeyFile(new MemoryStream(Encoding.ASCII.GetBytes(Resources.RSA_KEY_WITH_PASS)), Resources.PASSWORD), - new PrivateKeyFile(new MemoryStream(Encoding.ASCII.GetBytes(Resources.RSA_KEY_WITHOUT_PASS))), - new PrivateKeyFile(new MemoryStream(Encoding.ASCII.GetBytes(Resources.DSA_KEY_WITHOUT_PASS))) - )) - { - client.Connect(); - client.Disconnect(); - } - } - - [TestMethod] - [TestCategory("Authentication")] - [TestCategory("integration")] - public void Test_Connect_Then_Reconnect() - { - using (var client = new SshClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) - { - client.Connect(); - client.Disconnect(); - client.Connect(); - client.Disconnect(); - } - } - [TestMethod] public void CreateShellStream1_NeverConnected() { @@ -942,4 +700,4 @@ namespace Renci.SshNet.Tests.Classes } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.Tests/Classes/SshClientTest_CreateShellStream_TerminalNameAndColumnsAndRowsAndWidthAndHeightAndBufferSizeAndTerminalModes_Connected.cs b/src/Renci.SshNet.Tests/Classes/SshClientTest_CreateShellStream_TerminalNameAndColumnsAndRowsAndWidthAndHeightAndBufferSizeAndTerminalModes_Connected.cs index df1ea835..4dc5a206 100644 --- a/src/Renci.SshNet.Tests/Classes/SshClientTest_CreateShellStream_TerminalNameAndColumnsAndRowsAndWidthAndHeightAndBufferSizeAndTerminalModes_Connected.cs +++ b/src/Renci.SshNet.Tests/Classes/SshClientTest_CreateShellStream_TerminalNameAndColumnsAndRowsAndWidthAndHeightAndBufferSizeAndTerminalModes_Connected.cs @@ -43,16 +43,16 @@ namespace Renci.SshNet.Tests.Classes { var sequence = new MockSequence(); - _serviceFactoryMock.InSequence(sequence) + ServiceFactoryMock.InSequence(sequence) .Setup(p => p.CreateSocketFactory()) - .Returns(_socketFactoryMock.Object); - _serviceFactoryMock.InSequence(sequence) - .Setup(p => p.CreateSession(_connectionInfo, _socketFactoryMock.Object)) - .Returns(_sessionMock.Object); - _sessionMock.InSequence(sequence) + .Returns(SocketFactoryMock.Object); + ServiceFactoryMock.InSequence(sequence) + .Setup(p => p.CreateSession(_connectionInfo, SocketFactoryMock.Object)) + .Returns(SessionMock.Object); + SessionMock.InSequence(sequence) .Setup(p => p.Connect()); - _serviceFactoryMock.InSequence(sequence) - .Setup(p => p.CreateShellStream(_sessionMock.Object, + ServiceFactoryMock.InSequence(sequence) + .Setup(p => p.CreateShellStream(SessionMock.Object, _terminalName, _widthColumns, _heightRows, @@ -67,7 +67,7 @@ namespace Renci.SshNet.Tests.Classes { base.Arrange(); - _sshClient = new SshClient(_connectionInfo, false, _serviceFactoryMock.Object); + _sshClient = new SshClient(_connectionInfo, false, ServiceFactoryMock.Object); _sshClient.Connect(); } @@ -85,7 +85,7 @@ namespace Renci.SshNet.Tests.Classes [TestMethod] public void CreateShellStreamOnServiceFactoryShouldBeInvokedOnce() { - _serviceFactoryMock.Verify(p => p.CreateShellStream(_sessionMock.Object, + ServiceFactoryMock.Verify(p => p.CreateShellStream(SessionMock.Object, _terminalName, _widthColumns, _heightRows, diff --git a/src/Renci.SshNet.Tests/Classes/SshClientTest_CreateShellStream_TerminalNameAndColumnsAndRowsAndWidthAndHeightAndBufferSize_Connected.cs b/src/Renci.SshNet.Tests/Classes/SshClientTest_CreateShellStream_TerminalNameAndColumnsAndRowsAndWidthAndHeightAndBufferSize_Connected.cs index 337f9bce..861c8c72 100644 --- a/src/Renci.SshNet.Tests/Classes/SshClientTest_CreateShellStream_TerminalNameAndColumnsAndRowsAndWidthAndHeightAndBufferSize_Connected.cs +++ b/src/Renci.SshNet.Tests/Classes/SshClientTest_CreateShellStream_TerminalNameAndColumnsAndRowsAndWidthAndHeightAndBufferSize_Connected.cs @@ -1,9 +1,10 @@ using System; -using System.Collections.Generic; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; + using Renci.SshNet.Channels; -using Renci.SshNet.Common; namespace Renci.SshNet.Tests.Classes { @@ -41,31 +42,31 @@ namespace Renci.SshNet.Tests.Classes { var sequence = new MockSequence(); - _serviceFactoryMock.InSequence(sequence) - .Setup(p => p.CreateSocketFactory()) - .Returns(_socketFactoryMock.Object); - _serviceFactoryMock.InSequence(sequence) - .Setup(p => p.CreateSession(_connectionInfo, _socketFactoryMock.Object)) - .Returns(_sessionMock.Object); - _sessionMock.InSequence(sequence) - .Setup(p => p.Connect()); - _serviceFactoryMock.InSequence(sequence) - .Setup(p => p.CreateShellStream(_sessionMock.Object, - _terminalName, - _widthColumns, - _heightRows, - _widthPixels, - _heightPixels, - null, - _bufferSize)) - .Returns(_expected); + _ = ServiceFactoryMock.InSequence(sequence) + .Setup(p => p.CreateSocketFactory()) + .Returns(SocketFactoryMock.Object); + _ = ServiceFactoryMock.InSequence(sequence) + .Setup(p => p.CreateSession(_connectionInfo, SocketFactoryMock.Object)) + .Returns(SessionMock.Object); + _ = SessionMock.InSequence(sequence) + .Setup(p => p.Connect()); + _ = ServiceFactoryMock.InSequence(sequence) + .Setup(p => p.CreateShellStream(SessionMock.Object, + _terminalName, + _widthColumns, + _heightRows, + _widthPixels, + _heightPixels, + null, + _bufferSize)) + .Returns(_expected); } protected override void Arrange() { base.Arrange(); - _sshClient = new SshClient(_connectionInfo, false, _serviceFactoryMock.Object); + _sshClient = new SshClient(_connectionInfo, false, ServiceFactoryMock.Object); _sshClient.Connect(); } @@ -82,7 +83,7 @@ namespace Renci.SshNet.Tests.Classes [TestMethod] public void CreateShellStreamOnServiceFactoryShouldBeInvokedOnce() { - _serviceFactoryMock.Verify(p => p.CreateShellStream(_sessionMock.Object, + ServiceFactoryMock.Verify(p => p.CreateShellStream(SessionMock.Object, _terminalName, _widthColumns, _heightRows, @@ -105,20 +106,20 @@ namespace Renci.SshNet.Tests.Classes var sessionMock = new Mock(MockBehavior.Loose); var channelSessionMock = new Mock(MockBehavior.Strict); - sessionMock.Setup(p => p.ConnectionInfo) - .Returns(new ConnectionInfo("A", "B", new PasswordAuthenticationMethod("A", "B"))); - sessionMock.Setup(p => p.CreateChannelSession()) - .Returns(channelSessionMock.Object); - channelSessionMock.Setup(p => p.Open()); - channelSessionMock.Setup(p => p.SendPseudoTerminalRequest(_terminalName, + _ = sessionMock.Setup(p => p.ConnectionInfo) + .Returns(new ConnectionInfo("A", "B", new PasswordAuthenticationMethod("A", "B"))); + _ = sessionMock.Setup(p => p.CreateChannelSession()) + .Returns(channelSessionMock.Object); + _ = channelSessionMock.Setup(p => p.Open()); + _ = channelSessionMock.Setup(p => p.SendPseudoTerminalRequest(_terminalName, _widthColumns, _heightRows, _widthPixels, _heightPixels, null)) - .Returns(true); - channelSessionMock.Setup(p => p.SendShellRequest()) - .Returns(true); + .Returns(true); + _ = channelSessionMock.Setup(p => p.SendShellRequest()) + .Returns(true); return new ShellStream(sessionMock.Object, _terminalName, diff --git a/src/Renci.SshNet.Tests/Classes/SshClientTest_Disconnect_ForwardedPortStarted.cs b/src/Renci.SshNet.Tests/Classes/SshClientTest_Disconnect_ForwardedPortStarted.cs index 6b1259f0..23451264 100644 --- a/src/Renci.SshNet.Tests/Classes/SshClientTest_Disconnect_ForwardedPortStarted.cs +++ b/src/Renci.SshNet.Tests/Classes/SshClientTest_Disconnect_ForwardedPortStarted.cs @@ -27,24 +27,24 @@ namespace Renci.SshNet.Tests.Classes { var sequence = new MockSequence(); - _serviceFactoryMock.InSequence(sequence) + ServiceFactoryMock.InSequence(sequence) .Setup(p => p.CreateSocketFactory()) - .Returns(_socketFactoryMock.Object); - _serviceFactoryMock.InSequence(sequence) - .Setup(p => p.CreateSession(_connectionInfo, _socketFactoryMock.Object)) - .Returns(_sessionMock.Object); - _sessionMock.InSequence(sequence).Setup(p => p.Connect()); + .Returns(SocketFactoryMock.Object); + ServiceFactoryMock.InSequence(sequence) + .Setup(p => p.CreateSession(_connectionInfo, SocketFactoryMock.Object)) + .Returns(SessionMock.Object); + SessionMock.InSequence(sequence).Setup(p => p.Connect()); _forwardedPortMock.InSequence(sequence).Setup(p => p.Start()); - _sessionMock.InSequence(sequence).Setup(p => p.OnDisconnecting()); + SessionMock.InSequence(sequence).Setup(p => p.OnDisconnecting()); _forwardedPortMock.InSequence(sequence).Setup(p => p.Stop()); - _sessionMock.InSequence(sequence).Setup(p => p.Dispose()); + SessionMock.InSequence(sequence).Setup(p => p.Dispose()); } protected override void Arrange() { base.Arrange(); - _sshClient = new SshClient(_connectionInfo, false, _serviceFactoryMock.Object); + _sshClient = new SshClient(_connectionInfo, false, ServiceFactoryMock.Object); _sshClient.Connect(); _sshClient.AddForwardedPort(_forwardedPortMock.Object); @@ -71,13 +71,13 @@ namespace Renci.SshNet.Tests.Classes [TestMethod] public void DisconnectOnSessionShouldNeverBeInvoked() { - _sessionMock.Verify(p => p.Disconnect(), Times.Never); + SessionMock.Verify(p => p.Disconnect(), Times.Never); } [TestMethod] public void DisposeOnSessionShouldBeInvokedOnce() { - _sessionMock.Verify(p => p.Dispose(), Times.Once); + SessionMock.Verify(p => p.Dispose(), Times.Once); } } } diff --git a/src/Renci.SshNet.Tests/Classes/SshClientTest_Dispose_Connected.cs b/src/Renci.SshNet.Tests/Classes/SshClientTest_Dispose_Connected.cs index 55108df6..fd10b893 100644 --- a/src/Renci.SshNet.Tests/Classes/SshClientTest_Dispose_Connected.cs +++ b/src/Renci.SshNet.Tests/Classes/SshClientTest_Dispose_Connected.cs @@ -18,22 +18,22 @@ namespace Renci.SshNet.Tests.Classes { var sequence = new MockSequence(); - _serviceFactoryMock.InSequence(sequence) + ServiceFactoryMock.InSequence(sequence) .Setup(p => p.CreateSocketFactory()) - .Returns(_socketFactoryMock.Object); - _serviceFactoryMock.InSequence(sequence) - .Setup(p => p.CreateSession(_connectionInfo, _socketFactoryMock.Object)) - .Returns(_sessionMock.Object); - _sessionMock.InSequence(sequence).Setup(p => p.Connect()); - _sessionMock.InSequence(sequence).Setup(p => p.OnDisconnecting()); - _sessionMock.InSequence(sequence).Setup(p => p.Dispose()); + .Returns(SocketFactoryMock.Object); + ServiceFactoryMock.InSequence(sequence) + .Setup(p => p.CreateSession(_connectionInfo, SocketFactoryMock.Object)) + .Returns(SessionMock.Object); + SessionMock.InSequence(sequence).Setup(p => p.Connect()); + SessionMock.InSequence(sequence).Setup(p => p.OnDisconnecting()); + SessionMock.InSequence(sequence).Setup(p => p.Dispose()); } protected override void Arrange() { base.Arrange(); - _sshClient = new SshClient(_connectionInfo, false, _serviceFactoryMock.Object); + _sshClient = new SshClient(_connectionInfo, false, ServiceFactoryMock.Object); _sshClient.Connect(); } @@ -45,32 +45,32 @@ namespace Renci.SshNet.Tests.Classes [TestMethod] public void CreateSocketFactoryOnServiceFactoryShouldBeInvokedOnce() { - _serviceFactoryMock.Verify(p => p.CreateSocketFactory(), Times.Once); + ServiceFactoryMock.Verify(p => p.CreateSocketFactory(), Times.Once); } [TestMethod] public void CreateSessionOnServiceFactoryShouldBeInvokedOnce() { - _serviceFactoryMock.Verify(p => p.CreateSession(_connectionInfo, _socketFactoryMock.Object), + ServiceFactoryMock.Verify(p => p.CreateSession(_connectionInfo, SocketFactoryMock.Object), Times.Once); } [TestMethod] public void DisconnectOnSessionShouldNeverBeInvoked() { - _sessionMock.Verify(p => p.Disconnect(), Times.Never); + SessionMock.Verify(p => p.Disconnect(), Times.Never); } [TestMethod] public void DisposeOnSessionShouldBeInvokedOnce() { - _sessionMock.Verify(p => p.Dispose(), Times.Once); + SessionMock.Verify(p => p.Dispose(), Times.Once); } [TestMethod] public void OnDisconnectingOnSessionShouldBeInvokedOnce() { - _sessionMock.Verify(p => p.OnDisconnecting(), Times.Once); + SessionMock.Verify(p => p.OnDisconnecting(), Times.Once); } } } diff --git a/src/Renci.SshNet.Tests/Classes/SshClientTest_Dispose_Disconnected.cs b/src/Renci.SshNet.Tests/Classes/SshClientTest_Dispose_Disconnected.cs index 1d78561b..bae18feb 100644 --- a/src/Renci.SshNet.Tests/Classes/SshClientTest_Dispose_Disconnected.cs +++ b/src/Renci.SshNet.Tests/Classes/SshClientTest_Dispose_Disconnected.cs @@ -18,22 +18,22 @@ namespace Renci.SshNet.Tests.Classes { var sequence = new MockSequence(); - _serviceFactoryMock.InSequence(sequence) + ServiceFactoryMock.InSequence(sequence) .Setup(p => p.CreateSocketFactory()) - .Returns(_socketFactoryMock.Object); - _serviceFactoryMock.InSequence(sequence) - .Setup(p => p.CreateSession(_connectionInfo, _socketFactoryMock.Object)) - .Returns(_sessionMock.Object); - _sessionMock.InSequence(sequence).Setup(p => p.Connect()); - _sessionMock.InSequence(sequence).Setup(p => p.OnDisconnecting()); - _sessionMock.InSequence(sequence).Setup(p => p.Dispose()); + .Returns(SocketFactoryMock.Object); + ServiceFactoryMock.InSequence(sequence) + .Setup(p => p.CreateSession(_connectionInfo, SocketFactoryMock.Object)) + .Returns(SessionMock.Object); + SessionMock.InSequence(sequence).Setup(p => p.Connect()); + SessionMock.InSequence(sequence).Setup(p => p.OnDisconnecting()); + SessionMock.InSequence(sequence).Setup(p => p.Dispose()); } protected override void Arrange() { base.Arrange(); - _sshClient = new SshClient(_connectionInfo, false, _serviceFactoryMock.Object); + _sshClient = new SshClient(_connectionInfo, false, ServiceFactoryMock.Object); _sshClient.Connect(); _sshClient.Disconnect(); } @@ -46,32 +46,32 @@ namespace Renci.SshNet.Tests.Classes [TestMethod] public void CreateSocketFactoryOnServiceFactoryShouldBeInvokedOnce() { - _serviceFactoryMock.Verify(p => p.CreateSocketFactory(), Times.Once); + ServiceFactoryMock.Verify(p => p.CreateSocketFactory(), Times.Once); } [TestMethod] public void CreateSessionOnServiceFactoryShouldBeInvokedOnce() { - _serviceFactoryMock.Verify(p => p.CreateSession(_connectionInfo, _socketFactoryMock.Object), + ServiceFactoryMock.Verify(p => p.CreateSession(_connectionInfo, SocketFactoryMock.Object), Times.Once); } [TestMethod] public void DisconnectOnSessionShouldNeverBeInvoked() { - _sessionMock.Verify(p => p.Disconnect(), Times.Never); + SessionMock.Verify(p => p.Disconnect(), Times.Never); } [TestMethod] public void DisposeOnSessionShouldBeInvokedOnce() { - _sessionMock.Verify(p => p.Dispose(), Times.Once); + SessionMock.Verify(p => p.Dispose(), Times.Once); } [TestMethod] public void OnDisconnectingOnSessionShouldBeInvokedOnce() { - _sessionMock.Verify(p => p.OnDisconnecting(), Times.Once); + SessionMock.Verify(p => p.OnDisconnecting(), Times.Once); } } } diff --git a/src/Renci.SshNet.Tests/Classes/SshClientTest_Dispose_Disposed.cs b/src/Renci.SshNet.Tests/Classes/SshClientTest_Dispose_Disposed.cs index 54e07be9..c4dcf868 100644 --- a/src/Renci.SshNet.Tests/Classes/SshClientTest_Dispose_Disposed.cs +++ b/src/Renci.SshNet.Tests/Classes/SshClientTest_Dispose_Disposed.cs @@ -18,22 +18,22 @@ namespace Renci.SshNet.Tests.Classes { var sequence = new MockSequence(); - _serviceFactoryMock.InSequence(sequence) + ServiceFactoryMock.InSequence(sequence) .Setup(p => p.CreateSocketFactory()) - .Returns(_socketFactoryMock.Object); - _serviceFactoryMock.InSequence(sequence) - .Setup(p => p.CreateSession(_connectionInfo, _socketFactoryMock.Object)) - .Returns(_sessionMock.Object); - _sessionMock.InSequence(sequence).Setup(p => p.Connect()); - _sessionMock.InSequence(sequence).Setup(p => p.OnDisconnecting()); - _sessionMock.InSequence(sequence).Setup(p => p.Dispose()); + .Returns(SocketFactoryMock.Object); + ServiceFactoryMock.InSequence(sequence) + .Setup(p => p.CreateSession(_connectionInfo, SocketFactoryMock.Object)) + .Returns(SessionMock.Object); + SessionMock.InSequence(sequence).Setup(p => p.Connect()); + SessionMock.InSequence(sequence).Setup(p => p.OnDisconnecting()); + SessionMock.InSequence(sequence).Setup(p => p.Dispose()); } protected override void Arrange() { base.Arrange(); - _sshClient = new SshClient(_connectionInfo, false, _serviceFactoryMock.Object); + _sshClient = new SshClient(_connectionInfo, false, ServiceFactoryMock.Object); _sshClient.Connect(); _sshClient.Dispose(); } @@ -46,32 +46,32 @@ namespace Renci.SshNet.Tests.Classes [TestMethod] public void CreateSocketFactoryOnServiceFactoryShouldBeInvokedOnce() { - _serviceFactoryMock.Verify(p => p.CreateSocketFactory(), Times.Once); + ServiceFactoryMock.Verify(p => p.CreateSocketFactory(), Times.Once); } [TestMethod] public void CreateSessionOnServiceFactoryShouldBeInvokedOnce() { - _serviceFactoryMock.Verify(p => p.CreateSession(_connectionInfo, _socketFactoryMock.Object), + ServiceFactoryMock.Verify(p => p.CreateSession(_connectionInfo, SocketFactoryMock.Object), Times.Once); } [TestMethod] public void DisconnectOnSessionShouldNeverBeInvoked() { - _sessionMock.Verify(p => p.Disconnect(), Times.Never); + SessionMock.Verify(p => p.Disconnect(), Times.Never); } [TestMethod] public void DisposeOnSessionShouldBeInvokedOnce() { - _sessionMock.Verify(p => p.Dispose(), Times.Once); + SessionMock.Verify(p => p.Dispose(), Times.Once); } [TestMethod] public void OnDisconnectingOnSessionShouldBeInvokedOnce() { - _sessionMock.Verify(p => p.OnDisconnecting(), Times.Once); + SessionMock.Verify(p => p.OnDisconnecting(), Times.Once); } } } diff --git a/src/Renci.SshNet.Tests/Classes/SshClientTest_Dispose_ForwardedPortStarted.cs b/src/Renci.SshNet.Tests/Classes/SshClientTest_Dispose_ForwardedPortStarted.cs index 0b6da3cd..213e11ef 100644 --- a/src/Renci.SshNet.Tests/Classes/SshClientTest_Dispose_ForwardedPortStarted.cs +++ b/src/Renci.SshNet.Tests/Classes/SshClientTest_Dispose_ForwardedPortStarted.cs @@ -28,24 +28,24 @@ namespace Renci.SshNet.Tests.Classes { var sequence = new MockSequence(); - _serviceFactoryMock.InSequence(sequence) + ServiceFactoryMock.InSequence(sequence) .Setup(p => p.CreateSocketFactory()) - .Returns(_socketFactoryMock.Object); - _serviceFactoryMock.InSequence(sequence) - .Setup(p => p.CreateSession(_connectionInfo, _socketFactoryMock.Object)) - .Returns(_sessionMock.Object); - _sessionMock.InSequence(sequence).Setup(p => p.Connect()); + .Returns(SocketFactoryMock.Object); + ServiceFactoryMock.InSequence(sequence) + .Setup(p => p.CreateSession(_connectionInfo, SocketFactoryMock.Object)) + .Returns(SessionMock.Object); + SessionMock.InSequence(sequence).Setup(p => p.Connect()); _forwardedPortMock.InSequence(sequence).Setup(p => p.Start()); - _sessionMock.InSequence(sequence).Setup(p => p.OnDisconnecting()); + SessionMock.InSequence(sequence).Setup(p => p.OnDisconnecting()); _forwardedPortMock.InSequence(sequence).Setup(p => p.Stop()); - _sessionMock.InSequence(sequence).Setup(p => p.Dispose()); + SessionMock.InSequence(sequence).Setup(p => p.Dispose()); } protected override void Arrange() { base.Arrange(); - _sshClient = new SshClient(_connectionInfo, false, _serviceFactoryMock.Object); + _sshClient = new SshClient(_connectionInfo, false, ServiceFactoryMock.Object); _sshClient.Connect(); _sshClient.AddForwardedPort(_forwardedPortMock.Object); @@ -86,13 +86,13 @@ namespace Renci.SshNet.Tests.Classes [TestMethod] public void DisconnectOnSessionShouldNeverBeInvoked() { - _sessionMock.Verify(p => p.Disconnect(), Times.Never); + SessionMock.Verify(p => p.Disconnect(), Times.Never); } [TestMethod] public void DisposeOnSessionShouldBeInvokedOnce() { - _sessionMock.Verify(p => p.Dispose(), Times.Once); + SessionMock.Verify(p => p.Dispose(), Times.Once); } } } diff --git a/src/Renci.SshNet.Tests/Classes/SshCommandTest.cs b/src/Renci.SshNet.Tests/Classes/SshCommandTest.cs index cfd68b07..3f545095 100644 --- a/src/Renci.SshNet.Tests/Classes/SshCommandTest.cs +++ b/src/Renci.SshNet.Tests/Classes/SshCommandTest.cs @@ -3,13 +3,7 @@ using Renci.SshNet.Common; using Renci.SshNet.Tests.Common; using Renci.SshNet.Tests.Properties; using System; -using System.IO; using System.Text; -using System.Threading; -#if FEATURE_TPL -using System.Diagnostics; -using System.Threading.Tasks; -#endif // FEATURE_TPL namespace Renci.SshNet.Tests.Classes { @@ -31,476 +25,6 @@ namespace Renci.SshNet.Tests.Classes } } - [TestMethod] - [TestCategory("integration")] - public void Test_Run_SingleCommand() - { - var host = Resources.HOST; - var username = Resources.USERNAME; - var password = Resources.PASSWORD; - - using (var client = new SshClient(host, username, password)) - { - #region Example SshCommand RunCommand Result - client.Connect(); - - var testValue = Guid.NewGuid().ToString(); - var command = client.RunCommand(string.Format("echo {0}", testValue)); - var result = command.Result; - result = result.Substring(0, result.Length - 1); // Remove \n character returned by command - - client.Disconnect(); - #endregion - - Assert.IsTrue(result.Equals(testValue)); - } - } - - [TestMethod] - [TestCategory("integration")] - public void Test_Execute_SingleCommand() - { - var host = Resources.HOST; - var username = Resources.USERNAME; - var password = Resources.PASSWORD; - - using (var client = new SshClient(host, username, password)) - { - #region Example SshCommand CreateCommand Execute - client.Connect(); - - var testValue = Guid.NewGuid().ToString(); - var command = string.Format("echo {0}", testValue); - var cmd = client.CreateCommand(command); - var result = cmd.Execute(); - result = result.Substring(0, result.Length - 1); // Remove \n character returned by command - - client.Disconnect(); - #endregion - - Assert.IsTrue(result.Equals(testValue)); - } - } - - [TestMethod] - [TestCategory("integration")] - public void Test_Execute_OutputStream() - { - var host = Resources.HOST; - var username = Resources.USERNAME; - var password = Resources.PASSWORD; - - using (var client = new SshClient(host, username, password)) - { - #region Example SshCommand CreateCommand Execute OutputStream - client.Connect(); - - var cmd = client.CreateCommand("ls -l"); // very long list - var asynch = cmd.BeginExecute(); - - var reader = new StreamReader(cmd.OutputStream); - - while (!asynch.IsCompleted) - { - var result = reader.ReadToEnd(); - if (string.IsNullOrEmpty(result)) - continue; - Console.Write(result); - } - cmd.EndExecute(asynch); - - client.Disconnect(); - #endregion - - Assert.Inconclusive(); - } - } - - [TestMethod] - [TestCategory("integration")] - public void Test_Execute_ExtendedOutputStream() - { - var host = Resources.HOST; - var username = Resources.USERNAME; - var password = Resources.PASSWORD; - - using (var client = new SshClient(host, username, password)) - { - #region Example SshCommand CreateCommand Execute ExtendedOutputStream - - client.Connect(); - var cmd = client.CreateCommand("echo 12345; echo 654321 >&2"); - var result = cmd.Execute(); - - Console.Write(result); - - var reader = new StreamReader(cmd.ExtendedOutputStream); - Console.WriteLine("DEBUG:"); - Console.Write(reader.ReadToEnd()); - - client.Disconnect(); - - #endregion - - Assert.Inconclusive(); - } - } - - [TestMethod] - [TestCategory("integration")] - [ExpectedException(typeof(SshOperationTimeoutException))] - public void Test_Execute_Timeout() - { - using (var client = new SshClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) - { - #region Example SshCommand CreateCommand Execute CommandTimeout - client.Connect(); - var cmd = client.CreateCommand("sleep 10s"); - cmd.CommandTimeout = TimeSpan.FromSeconds(5); - cmd.Execute(); - client.Disconnect(); - #endregion - } - } - - [TestMethod] - [TestCategory("integration")] - public void Test_Execute_Infinite_Timeout() - { - using (var client = new SshClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) - { - client.Connect(); - var cmd = client.CreateCommand("sleep 10s"); - cmd.Execute(); - client.Disconnect(); - } - } - - [TestMethod] - [TestCategory("integration")] - public void Test_Execute_InvalidCommand() - { - using (var client = new SshClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) - { - client.Connect(); - - var cmd = client.CreateCommand(";"); - cmd.Execute(); - if (string.IsNullOrEmpty(cmd.Error)) - { - Assert.Fail("Operation should fail"); - } - Assert.IsTrue(cmd.ExitStatus > 0); - - client.Disconnect(); - } - } - - [TestMethod] - [TestCategory("integration")] - public void Test_Execute_InvalidCommand_Then_Execute_ValidCommand() - { - using (var client = new SshClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) - { - client.Connect(); - var cmd = client.CreateCommand(";"); - cmd.Execute(); - if (string.IsNullOrEmpty(cmd.Error)) - { - Assert.Fail("Operation should fail"); - } - Assert.IsTrue(cmd.ExitStatus > 0); - - var result = ExecuteTestCommand(client); - - client.Disconnect(); - - Assert.IsTrue(result); - } - } - - [TestMethod] - [TestCategory("integration")] - public void Test_Execute_Command_with_ExtendedOutput() - { - using (var client = new SshClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) - { - client.Connect(); - var cmd = client.CreateCommand("echo 12345; echo 654321 >&2"); - cmd.Execute(); - - //var extendedData = Encoding.ASCII.GetString(cmd.ExtendedOutputStream.ToArray()); - var extendedData = new StreamReader(cmd.ExtendedOutputStream, Encoding.ASCII).ReadToEnd(); - client.Disconnect(); - - Assert.AreEqual("12345\n", cmd.Result); - Assert.AreEqual("654321\n", extendedData); - } - } - - [TestMethod] - [TestCategory("integration")] - public void Test_Execute_Command_Reconnect_Execute_Command() - { - using (var client = new SshClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) - { - client.Connect(); - var result = ExecuteTestCommand(client); - Assert.IsTrue(result); - - client.Disconnect(); - client.Connect(); - result = ExecuteTestCommand(client); - Assert.IsTrue(result); - client.Disconnect(); - } - } - - [TestMethod] - [TestCategory("integration")] - public void Test_Execute_Command_ExitStatus() - { - using (var client = new SshClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) - { - #region Example SshCommand RunCommand ExitStatus - client.Connect(); - - var cmd = client.RunCommand("exit 128"); - - Console.WriteLine(cmd.ExitStatus); - - client.Disconnect(); - #endregion - - Assert.IsTrue(cmd.ExitStatus == 128); - } - } - - [TestMethod] - [TestCategory("integration")] - public void Test_Execute_Command_Asynchronously() - { - using (var client = new SshClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) - { - client.Connect(); - - var cmd = client.CreateCommand("sleep 5s; echo 'test'"); - var asyncResult = cmd.BeginExecute(null, null); - while (!asyncResult.IsCompleted) - { - Thread.Sleep(100); - } - - cmd.EndExecute(asyncResult); - - Assert.IsTrue(cmd.Result == "test\n"); - - client.Disconnect(); - } - } - - [TestMethod] - [TestCategory("integration")] - public void Test_Execute_Command_Asynchronously_With_Error() - { - using (var client = new SshClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) - { - client.Connect(); - - var cmd = client.CreateCommand("sleep 5s; ;"); - var asyncResult = cmd.BeginExecute(null, null); - while (!asyncResult.IsCompleted) - { - Thread.Sleep(100); - } - - cmd.EndExecute(asyncResult); - - Assert.IsFalse(string.IsNullOrEmpty(cmd.Error)); - - client.Disconnect(); - } - } - - [TestMethod] - [TestCategory("integration")] - public void Test_Execute_Command_Asynchronously_With_Callback() - { - using (var client = new SshClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) - { - client.Connect(); - - var callbackCalled = false; - - var cmd = client.CreateCommand("sleep 5s; echo 'test'"); - var asyncResult = cmd.BeginExecute(new AsyncCallback((s) => - { - callbackCalled = true; - }), null); - while (!asyncResult.IsCompleted) - { - Thread.Sleep(100); - } - - cmd.EndExecute(asyncResult); - - Assert.IsTrue(callbackCalled); - - client.Disconnect(); - } - } - - [TestMethod] - [TestCategory("integration")] - public void Test_Execute_Command_Asynchronously_With_Callback_On_Different_Thread() - { - using (var client = new SshClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) - { - client.Connect(); - - var currentThreadId = Thread.CurrentThread.ManagedThreadId; - int callbackThreadId = 0; - - var cmd = client.CreateCommand("sleep 5s; echo 'test'"); - var asyncResult = cmd.BeginExecute(new AsyncCallback((s) => - { - callbackThreadId = Thread.CurrentThread.ManagedThreadId; - }), null); - while (!asyncResult.IsCompleted) - { - Thread.Sleep(100); - } - - cmd.EndExecute(asyncResult); - - Assert.AreNotEqual(currentThreadId, callbackThreadId); - - client.Disconnect(); - } - } - - /// - /// Tests for Issue 563. - /// - [WorkItem(563), TestMethod] - [TestCategory("integration")] - public void Test_Execute_Command_Same_Object_Different_Commands() - { - using (var client = new SshClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) - { - client.Connect(); - var cmd = client.CreateCommand("echo 12345"); - cmd.Execute(); - Assert.AreEqual("12345\n", cmd.Result); - cmd.Execute("echo 23456"); - Assert.AreEqual("23456\n", cmd.Result); - client.Disconnect(); - } - } - - [TestMethod] - [TestCategory("integration")] - public void Test_Get_Result_Without_Execution() - { - using (var client = new SshClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) - { - client.Connect(); - var cmd = client.CreateCommand("ls -l"); - - Assert.IsTrue(string.IsNullOrEmpty(cmd.Result)); - client.Disconnect(); - } - } - - [TestMethod] - [TestCategory("integration")] - public void Test_Get_Error_Without_Execution() - { - using (var client = new SshClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) - { - client.Connect(); - var cmd = client.CreateCommand("ls -l"); - - Assert.IsTrue(string.IsNullOrEmpty(cmd.Error)); - client.Disconnect(); - } - } - - [WorkItem(703), TestMethod] - [ExpectedException(typeof(ArgumentException))] - [TestCategory("integration")] - public void Test_EndExecute_Before_BeginExecute() - { - using (var client = new SshClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) - { - client.Connect(); - var cmd = client.CreateCommand("ls -l"); - cmd.EndExecute(null); - client.Disconnect(); - } - } - - /// - ///A test for BeginExecute - /// - [TestMethod()] - [TestCategory("integration")] - public void BeginExecuteTest() - { - string expected = "123\n"; - string result; - - using (var client = new SshClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) - { - #region Example SshCommand CreateCommand BeginExecute IsCompleted EndExecute - - client.Connect(); - - var cmd = client.CreateCommand("sleep 15s;echo 123"); // Perform long running task - - var asynch = cmd.BeginExecute(); - - while (!asynch.IsCompleted) - { - // Waiting for command to complete... - Thread.Sleep(2000); - } - result = cmd.EndExecute(asynch); - client.Disconnect(); - - #endregion - - Assert.IsNotNull(asynch); - Assert.AreEqual(expected, result); - } - } - - [TestMethod] - [TestCategory("integration")] - public void Test_Execute_Invalid_Command() - { - using (var client = new SshClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) - { - #region Example SshCommand CreateCommand Error - - client.Connect(); - - var cmd = client.CreateCommand(";"); - cmd.Execute(); - if (!string.IsNullOrEmpty(cmd.Error)) - { - Console.WriteLine(cmd.Error); - } - - client.Disconnect(); - - #endregion - - Assert.Inconclusive(); - } - } - - /// ///A test for BeginExecute /// @@ -626,100 +150,6 @@ namespace Renci.SshNet.Tests.Classes Assert.Inconclusive("Verify the correctness of this test method."); } -#if FEATURE_TPL - [TestMethod] - [TestCategory("integration")] - public void Test_MultipleThread_Example_MultipleConnections() - { - var host = Resources.HOST; - var username = Resources.USERNAME; - var password = Resources.PASSWORD; - - try - { -#region Example SshCommand RunCommand Parallel - System.Threading.Tasks.Parallel.For(0, 10000, - () => - { - var client = new SshClient(host, username, password); - client.Connect(); - return client; - }, - (int counter, ParallelLoopState pls, SshClient client) => - { - var result = client.RunCommand("echo 123"); - Debug.WriteLine(string.Format("TestMultipleThreadMultipleConnections #{0}", counter)); - return client; - }, - (SshClient client) => - { - client.Disconnect(); - client.Dispose(); - } - ); -#endregion - - } - catch (Exception exp) - { - Assert.Fail(exp.ToString()); - } - } - - [TestMethod] - [TestCategory("integration")] - public void Test_MultipleThread_10000_MultipleConnections() - { - try - { - System.Threading.Tasks.Parallel.For(0, 10000, - () => - { - var client = new SshClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD); - client.Connect(); - return client; - }, - (int counter, ParallelLoopState pls, SshClient client) => - { - var result = ExecuteTestCommand(client); - Debug.WriteLine(string.Format("TestMultipleThreadMultipleConnections #{0}", counter)); - Assert.IsTrue(result); - return client; - }, - (SshClient client) => - { - client.Disconnect(); - client.Dispose(); - } - ); - } - catch (Exception exp) - { - Assert.Fail(exp.ToString()); - } - } - - [TestMethod] - [TestCategory("integration")] - public void Test_MultipleThread_10000_MultipleSessions() - { - using (var client = new SshClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) - { - client.Connect(); - System.Threading.Tasks.Parallel.For(0, 10000, - (counter) => - { - var result = ExecuteTestCommand(client); - Debug.WriteLine(string.Format("TestMultipleThreadMultipleConnections #{0}", counter)); - Assert.IsTrue(result); - } - ); - - client.Disconnect(); - } - } -#endif // FEATURE_TPL - private static bool ExecuteTestCommand(SshClient s) { var testValue = Guid.NewGuid().ToString(); @@ -730,4 +160,4 @@ namespace Renci.SshNet.Tests.Classes return result.Equals(testValue); } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.Tests/Classes/SshCommandTest_EndExecute_AsyncResultFromOtherInstance.cs b/src/Renci.SshNet.Tests/Classes/SshCommandTest_EndExecute_AsyncResultFromOtherInstance.cs index b312a205..5584786c 100644 --- a/src/Renci.SshNet.Tests/Classes/SshCommandTest_EndExecute_AsyncResultFromOtherInstance.cs +++ b/src/Renci.SshNet.Tests/Classes/SshCommandTest_EndExecute_AsyncResultFromOtherInstance.cs @@ -1,8 +1,11 @@ using System; using System.Globalization; using System.Text; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; + using Renci.SshNet.Channels; using Renci.SshNet.Tests.Common; @@ -39,15 +42,26 @@ namespace Renci.SshNet.Tests.Classes _asyncResultB = null; var seq = new MockSequence(); - _sessionMock.InSequence(seq).Setup(p => p.CreateChannelSession()).Returns(_channelSessionAMock.Object); - _channelSessionAMock.InSequence(seq).Setup(p => p.Open()); - _channelSessionAMock.InSequence(seq).Setup(p => p.SendExecRequest(_commandText)).Returns(true); - _sessionMock.InSequence(seq).Setup(p => p.CreateChannelSession()).Returns(_channelSessionBMock.Object); - _channelSessionBMock.InSequence(seq).Setup(p => p.Open()); - _channelSessionBMock.InSequence(seq).Setup(p => p.SendExecRequest(_commandText)).Returns(true); + + _ = _sessionMock.InSequence(seq) + .Setup(p => p.CreateChannelSession()) + .Returns(_channelSessionAMock.Object); + _ = _channelSessionAMock.InSequence(seq) + .Setup(p => p.Open()); + _ = _channelSessionAMock.InSequence(seq) + .Setup(p => p.SendExecRequest(_commandText)) + .Returns(true); + _ = _sessionMock.InSequence(seq) + .Setup(p => p.CreateChannelSession()) + .Returns(_channelSessionBMock.Object); + _ = _channelSessionBMock.InSequence(seq) + .Setup(p => p.Open()); + _ = _channelSessionBMock.InSequence(seq) + .Setup(p => p.SendExecRequest(_commandText)) + .Returns(true); _sshCommandA = new SshCommand(_sessionMock.Object, _commandText, _encoding); - _sshCommandA.BeginExecute(); + _ = _sshCommandA.BeginExecute(); _sshCommandB = new SshCommand(_sessionMock.Object, _commandText, _encoding); _asyncResultB = _sshCommandB.BeginExecute(); @@ -57,7 +71,7 @@ namespace Renci.SshNet.Tests.Classes { try { - _sshCommandA.EndExecute(_asyncResultB); + _ = _sshCommandA.EndExecute(_asyncResultB); Assert.Fail(); } catch (ArgumentException ex) @@ -71,7 +85,7 @@ namespace Renci.SshNet.Tests.Classes { Assert.IsNotNull(_actualException); Assert.IsNull(_actualException.InnerException); - Assert.AreEqual(string.Format("The {0} object was not returned from the corresponding asynchronous method on this class.", typeof(IAsyncResult).Name), _actualException.Message); + Assert.AreEqual(string.Format("The {0} object was not returned from the corresponding asynchronous method on this class.", nameof(IAsyncResult)), _actualException.Message); Assert.IsNull(_actualException.ParamName); } } diff --git a/src/Renci.SshNet.Tests/Classes/SubsystemSessionStub.cs b/src/Renci.SshNet.Tests/Classes/SubsystemSessionStub.cs index 241b4910..e7fab595 100644 --- a/src/Renci.SshNet.Tests/Classes/SubsystemSessionStub.cs +++ b/src/Renci.SshNet.Tests/Classes/SubsystemSessionStub.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; -using System.Text; using System.Threading; + using Renci.SshNet.Common; namespace Renci.SshNet.Tests.Classes @@ -29,10 +29,12 @@ namespace Renci.SshNet.Tests.Classes protected override void OnChannelOpen() { - Interlocked.Increment(ref _onChannelOpenInvocationCount); + _ = Interlocked.Increment(ref _onChannelOpenInvocationCount); if (OnChannelOpenException != null) + { throw OnChannelOpenException; + } } protected override void OnDataReceived(byte[] data) @@ -40,7 +42,9 @@ namespace Renci.SshNet.Tests.Classes OnDataReceivedInvocations.Add(new ChannelDataEventArgs(0, data)); if (OnDataReceivedException != null) + { throw OnDataReceivedException; + } } } } diff --git a/src/Renci.SshNet.Tests/Classes/SubsystemSession_Connect_Disconnected.cs b/src/Renci.SshNet.Tests/Classes/SubsystemSession_Connect_Disconnected.cs index 1d15dd6e..273ceef4 100644 --- a/src/Renci.SshNet.Tests/Classes/SubsystemSession_Connect_Disconnected.cs +++ b/src/Renci.SshNet.Tests/Classes/SubsystemSession_Connect_Disconnected.cs @@ -1,9 +1,11 @@ using System; using System.Collections.Generic; using System.Globalization; -using System.Text; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; + using Renci.SshNet.Channels; using Renci.SshNet.Common; @@ -42,18 +44,29 @@ namespace Renci.SshNet.Tests.Classes _channelAfterDisconnectMock = new Mock(MockBehavior.Strict); _sequence = new MockSequence(); - _sessionMock.InSequence(_sequence).Setup(p => p.CreateChannelSession()).Returns(_channelBeforeDisconnectMock.Object); - _channelBeforeDisconnectMock.InSequence(_sequence).Setup(p => p.Open()); - _channelBeforeDisconnectMock.InSequence(_sequence).Setup(p => p.SendSubsystemRequest(_subsystemName)).Returns(true); - _channelBeforeDisconnectMock.InSequence(_sequence).Setup(p => p.Dispose()); - _sessionMock.InSequence(_sequence).Setup(p => p.CreateChannelSession()).Returns(_channelAfterDisconnectMock.Object); - _channelAfterDisconnectMock.InSequence(_sequence).Setup(p => p.Open()); - _channelAfterDisconnectMock.InSequence(_sequence).Setup(p => p.SendSubsystemRequest(_subsystemName)).Returns(true); - _subsystemSession = new SubsystemSessionStub( - _sessionMock.Object, - _subsystemName, - _operationTimeout); + _ = _sessionMock.InSequence(_sequence) + .Setup(p => p.CreateChannelSession()) + .Returns(_channelBeforeDisconnectMock.Object); + _ = _channelBeforeDisconnectMock.InSequence(_sequence) + .Setup(p => p.Open()); + _ = _channelBeforeDisconnectMock.InSequence(_sequence) + .Setup(p => p.SendSubsystemRequest(_subsystemName)) + .Returns(true); + _ = _channelBeforeDisconnectMock.InSequence(_sequence) + .Setup(p => p.Dispose()); + _ = _sessionMock.InSequence(_sequence) + .Setup(p => p.CreateChannelSession()) + .Returns(_channelAfterDisconnectMock.Object); + _ = _channelAfterDisconnectMock.InSequence(_sequence) + .Setup(p => p.Open()); + _ = _channelAfterDisconnectMock.InSequence(_sequence) + .Setup(p => p.SendSubsystemRequest(_subsystemName)) + .Returns(true); + + _subsystemSession = new SubsystemSessionStub(_sessionMock.Object, + _subsystemName, + _operationTimeout); _subsystemSession.Disconnected += (sender, args) => _disconnectedRegister.Add(args); _subsystemSession.ErrorOccurred += (sender, args) => _errorOccurredRegister.Add(args); _subsystemSession.Connect(); @@ -80,7 +93,9 @@ namespace Renci.SshNet.Tests.Classes [TestMethod] public void IsOpenShouldReturnTrueWhenChannelIsOpen() { - _channelAfterDisconnectMock.InSequence(_sequence).Setup(p => p.IsOpen).Returns(true); + _ = _channelAfterDisconnectMock.InSequence(_sequence) + .Setup(p => p.IsOpen) + .Returns(true); Assert.IsTrue(_subsystemSession.IsOpen); @@ -90,7 +105,9 @@ namespace Renci.SshNet.Tests.Classes [TestMethod] public void IsOpenShouldReturnFalseWhenChannelIsNotOpen() { - _channelAfterDisconnectMock.InSequence(_sequence).Setup(p => p.IsOpen).Returns(false); + _ = _channelAfterDisconnectMock.InSequence(_sequence) + .Setup(p => p.IsOpen) + .Returns(false); Assert.IsFalse(_subsystemSession.IsOpen); diff --git a/src/Renci.SshNet.Tests/Classes/SubsystemSession_Connect_Disposed.cs b/src/Renci.SshNet.Tests/Classes/SubsystemSession_Connect_Disposed.cs index 11e6b0c6..2d90ce74 100644 --- a/src/Renci.SshNet.Tests/Classes/SubsystemSession_Connect_Disposed.cs +++ b/src/Renci.SshNet.Tests/Classes/SubsystemSession_Connect_Disposed.cs @@ -1,9 +1,11 @@ using System; using System.Collections.Generic; using System.Globalization; -using System.Text; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; + using Renci.SshNet.Channels; using Renci.SshNet.Common; @@ -40,14 +42,19 @@ namespace Renci.SshNet.Tests.Classes _channelMock = new Mock(MockBehavior.Strict); var sequence = new MockSequence(); - _sessionMock.InSequence(sequence).Setup(p => p.CreateChannelSession()).Returns(_channelMock.Object); - _channelMock.InSequence(sequence).Setup(p => p.Open()); - _channelMock.InSequence(sequence).Setup(p => p.SendSubsystemRequest(_subsystemName)).Returns(true); - _subsystemSession = new SubsystemSessionStub( - _sessionMock.Object, - _subsystemName, - _operationTimeout); + _ = _sessionMock.InSequence(sequence) + .Setup(p => p.CreateChannelSession()) + .Returns(_channelMock.Object); + _ = _channelMock.InSequence(sequence) + .Setup(p => p.Open()); + _ = _channelMock.InSequence(sequence) + .Setup(p => p.SendSubsystemRequest(_subsystemName)) + .Returns(true); + + _subsystemSession = new SubsystemSessionStub(_sessionMock.Object, + _subsystemName, + _operationTimeout); _subsystemSession.Disconnected += (sender, args) => _disconnectedRegister.Add(args); _subsystemSession.ErrorOccurred += (sender, args) => _errorOccurredRegister.Add(args); _subsystemSession.Dispose(); @@ -58,6 +65,7 @@ namespace Renci.SshNet.Tests.Classes try { _subsystemSession.Connect(); + Assert.Fail(); } catch (ObjectDisposedException ex) { diff --git a/src/Renci.SshNet.Tests/Classes/SubsystemSession_Connect_NeverConnected.cs b/src/Renci.SshNet.Tests/Classes/SubsystemSession_Connect_NeverConnected.cs index d784c7ae..23bd273e 100644 --- a/src/Renci.SshNet.Tests/Classes/SubsystemSession_Connect_NeverConnected.cs +++ b/src/Renci.SshNet.Tests/Classes/SubsystemSession_Connect_NeverConnected.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Renci.SshNet.Tests.Classes +namespace Renci.SshNet.Tests.Classes { class SubsystemSession_Connect_NeverConnected { diff --git a/src/Renci.SshNet.Tests/Classes/SubsystemSession_Disconnect_Connected.cs b/src/Renci.SshNet.Tests/Classes/SubsystemSession_Disconnect_Connected.cs index f5d07813..bf1b3b78 100644 --- a/src/Renci.SshNet.Tests/Classes/SubsystemSession_Disconnect_Connected.cs +++ b/src/Renci.SshNet.Tests/Classes/SubsystemSession_Disconnect_Connected.cs @@ -1,9 +1,11 @@ using System; using System.Collections.Generic; using System.Globalization; -using System.Text; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; + using Renci.SshNet.Channels; using Renci.SshNet.Common; @@ -40,10 +42,17 @@ namespace Renci.SshNet.Tests.Classes _channelMock = new Mock(MockBehavior.Strict); _sequence = new MockSequence(); - _sessionMock.InSequence(_sequence).Setup(p => p.CreateChannelSession()).Returns(_channelMock.Object); - _channelMock.InSequence(_sequence).Setup(p => p.Open()); - _channelMock.InSequence(_sequence).Setup(p => p.SendSubsystemRequest(_subsystemName)).Returns(true); - _channelMock.InSequence(_sequence).Setup(p => p.Dispose()); + + _ = _sessionMock.InSequence(_sequence) + .Setup(p => p.CreateChannelSession()) + .Returns(_channelMock.Object); + _ = _channelMock.InSequence(_sequence) + .Setup(p => p.Open()); + _ = _channelMock.InSequence(_sequence) + .Setup(p => p.SendSubsystemRequest(_subsystemName)) + .Returns(true); + _ = _channelMock.InSequence(_sequence) + .Setup(p => p.Dispose()); _subsystemSession = new SubsystemSessionStub( _sessionMock.Object, diff --git a/src/Renci.SshNet.Tests/Classes/SubsystemSession_Disconnect_Disposed.cs b/src/Renci.SshNet.Tests/Classes/SubsystemSession_Disconnect_Disposed.cs index 21116a78..4ee558c3 100644 --- a/src/Renci.SshNet.Tests/Classes/SubsystemSession_Disconnect_Disposed.cs +++ b/src/Renci.SshNet.Tests/Classes/SubsystemSession_Disconnect_Disposed.cs @@ -1,9 +1,11 @@ using System; using System.Collections.Generic; using System.Globalization; -using System.Text; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; + using Renci.SshNet.Channels; using Renci.SshNet.Common; @@ -39,15 +41,21 @@ namespace Renci.SshNet.Tests.Classes _channelMock = new Mock(MockBehavior.Strict); var sequence = new MockSequence(); - _sessionMock.InSequence(sequence).Setup(p => p.CreateChannelSession()).Returns(_channelMock.Object); - _channelMock.InSequence(sequence).Setup(p => p.Open()); - _channelMock.InSequence(sequence).Setup(p => p.SendSubsystemRequest(_subsystemName)).Returns(true); - _channelMock.InSequence(sequence).Setup(p => p.Dispose()); - _subsystemSession = new SubsystemSessionStub( - _sessionMock.Object, - _subsystemName, - _operationTimeout); + _ = _sessionMock.InSequence(sequence) + .Setup(p => p.CreateChannelSession()) + .Returns(_channelMock.Object); + _ = _channelMock.InSequence(sequence) + .Setup(p => p.Open()); + _ = _channelMock.InSequence(sequence) + .Setup(p => p.SendSubsystemRequest(_subsystemName)) + .Returns(true); + _ = _channelMock.InSequence(sequence) + .Setup(p => p.Dispose()); + + _subsystemSession = new SubsystemSessionStub(_sessionMock.Object, + _subsystemName, + _operationTimeout); _subsystemSession.Disconnected += (sender, args) => _disconnectedRegister.Add(args); _subsystemSession.ErrorOccurred += (sender, args) => _errorOccurredRegister.Add(args); _subsystemSession.Connect(); diff --git a/src/Renci.SshNet.Tests/Classes/SubsystemSession_Disconnect_NeverConnected.cs b/src/Renci.SshNet.Tests/Classes/SubsystemSession_Disconnect_NeverConnected.cs index 71b161a5..a7edaf9f 100644 --- a/src/Renci.SshNet.Tests/Classes/SubsystemSession_Disconnect_NeverConnected.cs +++ b/src/Renci.SshNet.Tests/Classes/SubsystemSession_Disconnect_NeverConnected.cs @@ -1,9 +1,11 @@ using System; using System.Collections.Generic; using System.Globalization; -using System.Text; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; + using Renci.SshNet.Common; namespace Renci.SshNet.Tests.Classes @@ -35,10 +37,9 @@ namespace Renci.SshNet.Tests.Classes _sessionMock = new Mock(MockBehavior.Strict); - _subsystemSession = new SubsystemSessionStub( - _sessionMock.Object, - _subsystemName, - _operationTimeout); + _subsystemSession = new SubsystemSessionStub(_sessionMock.Object, + _subsystemName, + _operationTimeout); _subsystemSession.Disconnected += (sender, args) => _disconnectedRegister.Add(args); _subsystemSession.ErrorOccurred += (sender, args) => _errorOccurredRegister.Add(args); } diff --git a/src/Renci.SshNet.Tests/Classes/SubsystemSession_Dispose_Connected.cs b/src/Renci.SshNet.Tests/Classes/SubsystemSession_Dispose_Connected.cs index f89da65c..3de5b14a 100644 --- a/src/Renci.SshNet.Tests/Classes/SubsystemSession_Dispose_Connected.cs +++ b/src/Renci.SshNet.Tests/Classes/SubsystemSession_Dispose_Connected.cs @@ -1,9 +1,11 @@ using System; using System.Collections.Generic; using System.Globalization; -using System.Text; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; + using Renci.SshNet.Channels; using Renci.SshNet.Common; @@ -39,15 +41,13 @@ namespace Renci.SshNet.Tests.Classes _channelMock = new Mock(MockBehavior.Strict); var sequence = new MockSequence(); - _sessionMock.InSequence(sequence).Setup(p => p.CreateChannelSession()).Returns(_channelMock.Object); - _channelMock.InSequence(sequence).Setup(p => p.Open()); - _channelMock.InSequence(sequence).Setup(p => p.SendSubsystemRequest(_subsystemName)).Returns(true); - _channelMock.InSequence(sequence).Setup(p => p.Dispose()); - _subsystemSession = new SubsystemSessionStub( - _sessionMock.Object, - _subsystemName, - _operationTimeout); + _ = _sessionMock.InSequence(sequence).Setup(p => p.CreateChannelSession()).Returns(_channelMock.Object); + _ = _channelMock.InSequence(sequence).Setup(p => p.Open()); + _ = _channelMock.InSequence(sequence).Setup(p => p.SendSubsystemRequest(_subsystemName)).Returns(true); + _ = _channelMock.InSequence(sequence).Setup(p => p.Dispose()); + + _subsystemSession = new SubsystemSessionStub(_sessionMock.Object, _subsystemName, _operationTimeout); _subsystemSession.Disconnected += (sender, args) => _disconnectedRegister.Add(args); _subsystemSession.ErrorOccurred += (sender, args) => _errorOccurredRegister.Add(args); _subsystemSession.Connect(); diff --git a/src/Renci.SshNet.Tests/Classes/SubsystemSession_Dispose_Disconnected.cs b/src/Renci.SshNet.Tests/Classes/SubsystemSession_Dispose_Disconnected.cs index c304ed43..eaae69dc 100644 --- a/src/Renci.SshNet.Tests/Classes/SubsystemSession_Dispose_Disconnected.cs +++ b/src/Renci.SshNet.Tests/Classes/SubsystemSession_Dispose_Disconnected.cs @@ -1,9 +1,11 @@ using System; using System.Collections.Generic; using System.Globalization; -using System.Text; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; + using Renci.SshNet.Channels; using Renci.SshNet.Common; @@ -39,10 +41,17 @@ namespace Renci.SshNet.Tests.Classes _channelMock = new Mock(MockBehavior.Strict); var sequence = new MockSequence(); - _sessionMock.InSequence(sequence).Setup(p => p.CreateChannelSession()).Returns(_channelMock.Object); - _channelMock.InSequence(sequence).Setup(p => p.Open()); - _channelMock.InSequence(sequence).Setup(p => p.SendSubsystemRequest(_subsystemName)).Returns(true); - _channelMock.InSequence(sequence).Setup(p => p.Dispose()); + + _ = _sessionMock.InSequence(sequence) + .Setup(p => p.CreateChannelSession()) + .Returns(_channelMock.Object); + _ = _channelMock.InSequence(sequence) + .Setup(p => p.Open()); + _ = _channelMock.InSequence(sequence) + .Setup(p => p.SendSubsystemRequest(_subsystemName)) + .Returns(true); + _ = _channelMock.InSequence(sequence) + .Setup(p => p.Dispose()); _subsystemSession = new SubsystemSessionStub( _sessionMock.Object, diff --git a/src/Renci.SshNet.Tests/Classes/SubsystemSession_Dispose_Disposed.cs b/src/Renci.SshNet.Tests/Classes/SubsystemSession_Dispose_Disposed.cs index d2f93dec..e0da3678 100644 --- a/src/Renci.SshNet.Tests/Classes/SubsystemSession_Dispose_Disposed.cs +++ b/src/Renci.SshNet.Tests/Classes/SubsystemSession_Dispose_Disposed.cs @@ -1,9 +1,11 @@ using System; using System.Collections.Generic; using System.Globalization; -using System.Text; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; + using Renci.SshNet.Channels; using Renci.SshNet.Common; @@ -39,10 +41,17 @@ namespace Renci.SshNet.Tests.Classes _channelMock = new Mock(MockBehavior.Strict); var sequence = new MockSequence(); - _sessionMock.InSequence(sequence).Setup(p => p.CreateChannelSession()).Returns(_channelMock.Object); - _channelMock.InSequence(sequence).Setup(p => p.Open()); - _channelMock.InSequence(sequence).Setup(p => p.SendSubsystemRequest(_subsystemName)).Returns(true); - _channelMock.InSequence(sequence).Setup(p => p.Dispose()); + + _ = _sessionMock.InSequence(sequence) + .Setup(p => p.CreateChannelSession()) + .Returns(_channelMock.Object); + _ = _channelMock.InSequence(sequence) + .Setup(p => p.Open()); + _ = _channelMock.InSequence(sequence) + .Setup(p => p.SendSubsystemRequest(_subsystemName)) + .Returns(true); + _ = _channelMock.InSequence(sequence) + .Setup(p => p.Dispose()); _subsystemSession = new SubsystemSessionStub( _sessionMock.Object, diff --git a/src/Renci.SshNet.Tests/Classes/SubsystemSession_Dispose_NeverConnected.cs b/src/Renci.SshNet.Tests/Classes/SubsystemSession_Dispose_NeverConnected.cs index 83fab899..96e768c9 100644 --- a/src/Renci.SshNet.Tests/Classes/SubsystemSession_Dispose_NeverConnected.cs +++ b/src/Renci.SshNet.Tests/Classes/SubsystemSession_Dispose_NeverConnected.cs @@ -1,10 +1,11 @@ using System; using System.Collections.Generic; using System.Globalization; -using System.Text; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; -using Renci.SshNet.Channels; + using Renci.SshNet.Common; namespace Renci.SshNet.Tests.Classes @@ -29,6 +30,7 @@ namespace Renci.SshNet.Tests.Classes protected void Arrange() { var random = new Random(); + _subsystemName = random.Next().ToString(CultureInfo.InvariantCulture); _operationTimeout = 30000; _disconnectedRegister = new List(); @@ -36,10 +38,9 @@ namespace Renci.SshNet.Tests.Classes _sessionMock = new Mock(MockBehavior.Strict); - _subsystemSession = new SubsystemSessionStub( - _sessionMock.Object, - _subsystemName, - _operationTimeout); + _subsystemSession = new SubsystemSessionStub(_sessionMock.Object, + _subsystemName, + _operationTimeout); _subsystemSession.Disconnected += (sender, args) => _disconnectedRegister.Add(args); _subsystemSession.ErrorOccurred += (sender, args) => _errorOccurredRegister.Add(args); } diff --git a/src/Renci.SshNet.Tests/Classes/SubsystemSession_OnChannelException_Disposed.cs b/src/Renci.SshNet.Tests/Classes/SubsystemSession_OnChannelException_Disposed.cs index d9b3a162..9f6f054e 100644 --- a/src/Renci.SshNet.Tests/Classes/SubsystemSession_OnChannelException_Disposed.cs +++ b/src/Renci.SshNet.Tests/Classes/SubsystemSession_OnChannelException_Disposed.cs @@ -1,9 +1,11 @@ using System; using System.Collections.Generic; using System.Globalization; -using System.Text; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; + using Renci.SshNet.Channels; using Renci.SshNet.Common; @@ -41,10 +43,17 @@ namespace Renci.SshNet.Tests.Classes _channelMock = new Mock(MockBehavior.Strict); var sequence = new MockSequence(); - _sessionMock.InSequence(sequence).Setup(p => p.CreateChannelSession()).Returns(_channelMock.Object); - _channelMock.InSequence(sequence).Setup(p => p.Open()); - _channelMock.InSequence(sequence).Setup(p => p.SendSubsystemRequest(_subsystemName)).Returns(true); - _channelMock.InSequence(sequence).Setup(p => p.Dispose()); + + _ = _sessionMock.InSequence(sequence) + .Setup(p => p.CreateChannelSession()) + .Returns(_channelMock.Object); + _ = _channelMock.InSequence(sequence) + .Setup(p => p.Open()); + _ = _channelMock.InSequence(sequence) + .Setup(p => p.SendSubsystemRequest(_subsystemName)) + .Returns(true); + _ = _channelMock.InSequence(sequence) + .Setup(p => p.Dispose()); _subsystemSession = new SubsystemSessionStub( _sessionMock.Object, diff --git a/src/Renci.SshNet.Tests/Classes/SubsystemSession_OnSessionDisconnected_Connected.cs b/src/Renci.SshNet.Tests/Classes/SubsystemSession_OnSessionDisconnected_Connected.cs index b6851cfc..f689d801 100644 --- a/src/Renci.SshNet.Tests/Classes/SubsystemSession_OnSessionDisconnected_Connected.cs +++ b/src/Renci.SshNet.Tests/Classes/SubsystemSession_OnSessionDisconnected_Connected.cs @@ -1,9 +1,11 @@ using System; using System.Collections.Generic; using System.Globalization; -using System.Text; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; + using Renci.SshNet.Channels; using Renci.SshNet.Common; @@ -40,14 +42,19 @@ namespace Renci.SshNet.Tests.Classes _channelMock = new Mock(MockBehavior.Strict); _sequence = new MockSequence(); - _sessionMock.InSequence(_sequence).Setup(p => p.CreateChannelSession()).Returns(_channelMock.Object); - _channelMock.InSequence(_sequence).Setup(p => p.Open()); - _channelMock.InSequence(_sequence).Setup(p => p.SendSubsystemRequest(_subsystemName)).Returns(true); - _subsystemSession = new SubsystemSessionStub( - _sessionMock.Object, - _subsystemName, - _operationTimeout); + _ = _sessionMock.InSequence(_sequence) + .Setup(p => p.CreateChannelSession()) + .Returns(_channelMock.Object); + _ = _channelMock.InSequence(_sequence) + .Setup(p => p.Open()); + _ = _channelMock.InSequence(_sequence) + .Setup(p => p.SendSubsystemRequest(_subsystemName)) + .Returns(true); + + _subsystemSession = new SubsystemSessionStub(_sessionMock.Object, + _subsystemName, + _operationTimeout); _subsystemSession.Disconnected += (sender, args) => _disconnectedRegister.Add(args); _subsystemSession.ErrorOccurred += (sender, args) => _errorOccurredRegister.Add(args); _subsystemSession.Connect(); @@ -73,7 +80,9 @@ namespace Renci.SshNet.Tests.Classes [TestMethod] public void IsOpenShouldReturnTrueWhenChannelIsOpen() { - _channelMock.InSequence(_sequence).Setup(p => p.IsOpen).Returns(true); + _ = _channelMock.InSequence(_sequence) + .Setup(p => p.IsOpen) + .Returns(true); Assert.IsTrue(_subsystemSession.IsOpen); @@ -83,7 +92,9 @@ namespace Renci.SshNet.Tests.Classes [TestMethod] public void IsOpenShouldReturnFalseWhenChannelIsNotOpen() { - _channelMock.InSequence(_sequence).Setup(p => p.IsOpen).Returns(false); + _ = _channelMock.InSequence(_sequence) + .Setup(p => p.IsOpen) + .Returns(false); Assert.IsFalse(_subsystemSession.IsOpen); diff --git a/src/Renci.SshNet.Tests/Classes/SubsystemSession_OnSessionDisconnected_Disposed.cs b/src/Renci.SshNet.Tests/Classes/SubsystemSession_OnSessionDisconnected_Disposed.cs index 7a90eba4..177bbff3 100644 --- a/src/Renci.SshNet.Tests/Classes/SubsystemSession_OnSessionDisconnected_Disposed.cs +++ b/src/Renci.SshNet.Tests/Classes/SubsystemSession_OnSessionDisconnected_Disposed.cs @@ -1,9 +1,11 @@ using System; using System.Collections.Generic; using System.Globalization; -using System.Text; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; + using Renci.SshNet.Channels; using Renci.SshNet.Common; @@ -39,15 +41,21 @@ namespace Renci.SshNet.Tests.Classes _channelMock = new Mock(MockBehavior.Strict); var sequence = new MockSequence(); - _sessionMock.InSequence(sequence).Setup(p => p.CreateChannelSession()).Returns(_channelMock.Object); - _channelMock.InSequence(sequence).Setup(p => p.Open()); - _channelMock.InSequence(sequence).Setup(p => p.SendSubsystemRequest(_subsystemName)).Returns(true); - _channelMock.InSequence(sequence).Setup(p => p.Dispose()); - _subsystemSession = new SubsystemSessionStub( - _sessionMock.Object, - _subsystemName, - _operationTimeout); + _ = _sessionMock.InSequence(sequence) + .Setup(p => p.CreateChannelSession()) + .Returns(_channelMock.Object); + _ = _channelMock.InSequence(sequence) + .Setup(p => p.Open()); + _ = _channelMock.InSequence(sequence) + .Setup(p => p.SendSubsystemRequest(_subsystemName)) + .Returns(true); + _ = _channelMock.InSequence(sequence) + .Setup(p => p.Dispose()); + + _subsystemSession = new SubsystemSessionStub(_sessionMock.Object, + _subsystemName, + _operationTimeout); _subsystemSession.Disconnected += (sender, args) => _disconnectedRegister.Add(args); _subsystemSession.ErrorOccurred += (sender, args) => _errorOccurredRegister.Add(args); _subsystemSession.Connect(); diff --git a/src/Renci.SshNet.Tests/Classes/SubsystemSession_SendData_Disconnected.cs b/src/Renci.SshNet.Tests/Classes/SubsystemSession_SendData_Disconnected.cs index 6d60a61b..92754621 100644 --- a/src/Renci.SshNet.Tests/Classes/SubsystemSession_SendData_Disconnected.cs +++ b/src/Renci.SshNet.Tests/Classes/SubsystemSession_SendData_Disconnected.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Renci.SshNet.Tests.Classes +namespace Renci.SshNet.Tests.Classes { class SubsystemSession_SendData_Disconnected { diff --git a/src/Renci.SshNet.Tests/Common/ArgumentExceptionAssert.cs b/src/Renci.SshNet.Tests/Common/ArgumentExceptionAssert.cs new file mode 100644 index 00000000..5caee457 --- /dev/null +++ b/src/Renci.SshNet.Tests/Common/ArgumentExceptionAssert.cs @@ -0,0 +1,15 @@ +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Renci.SshNet.Tests.Common +{ + public static class ArgumentExceptionAssert + { + public static void MessageEquals(string expected, ArgumentException exception) + { + var newMessage = new ArgumentException(expected, exception.ParamName); + + Assert.AreEqual(newMessage.Message, exception.Message); + } + } +} \ No newline at end of file diff --git a/src/Renci.SshNet.Tests/Common/ArrayBuilder.cs b/src/Renci.SshNet.Tests/Common/ArrayBuilder.cs index cff2e4c3..9f9f7d65 100644 --- a/src/Renci.SshNet.Tests/Common/ArrayBuilder.cs +++ b/src/Renci.SshNet.Tests/Common/ArrayBuilder.cs @@ -4,7 +4,7 @@ namespace Renci.SshNet.Tests.Common { public class ArrayBuilder { - private List _buffer; + private readonly List _buffer; public ArrayBuilder() { @@ -19,7 +19,10 @@ namespace Renci.SshNet.Tests.Common public ArrayBuilder Add(T[] array, int index, int length) { for (var i = 0; i < length; i++) + { _buffer.Add(array[index + i]); + } + return this; } diff --git a/src/Renci.SshNet.Tests/Common/AsyncSocketListener.cs b/src/Renci.SshNet.Tests/Common/AsyncSocketListener.cs index 5512b15c..0f7dac81 100644 --- a/src/Renci.SshNet.Tests/Common/AsyncSocketListener.cs +++ b/src/Renci.SshNet.Tests/Common/AsyncSocketListener.cs @@ -3,9 +3,6 @@ using System.Collections.Generic; using System.Net; using System.Net.Sockets; using System.Threading; -#if !FEATURE_SOCKET_DISPOSE -using Renci.SshNet.Common; -#endif // !FEATURE_SOCKET_DISPOSE namespace Renci.SshNet.Tests.Common { @@ -13,11 +10,11 @@ namespace Renci.SshNet.Tests.Common { private readonly IPEndPoint _endPoint; private readonly ManualResetEvent _acceptCallbackDone; - private List _connectedClients; + private readonly List _connectedClients; + private readonly object _syncLock; private Socket _listener; private Thread _receiveThread; private bool _started; - private object _syncLock; private string _stackTrace; public delegate void BytesReceivedHandler(byte[] bytesReceived, Socket socket); @@ -43,7 +40,7 @@ namespace Renci.SshNet.Tests.Common /// /// to invoke on the that is used /// to handle the communication with the remote host, when the remote host has closed the connection; otherwise, - /// . The default is . + /// . The default is . /// public bool ShutdownRemoteCommunicationSocket { get; set; } @@ -87,10 +84,7 @@ namespace Renci.SshNet.Tests.Common _connectedClients.Clear(); } - if (_listener != null) - { - _listener.Dispose(); - } + _listener?.Dispose(); if (_receiveThread != null) { @@ -107,19 +101,31 @@ namespace Renci.SshNet.Tests.Common private void StartListener(object state) { - var listener = (Socket)state; - while (_started) + try { - _acceptCallbackDone.Reset(); - listener.BeginAccept(AcceptCallback, listener); - _acceptCallbackDone.WaitOne(); + var listener = (Socket)state; + while (_started) + { + _ = _acceptCallbackDone.Reset(); + _ = listener.BeginAccept(AcceptCallback, listener); + _ = _acceptCallbackDone.WaitOne(); + } + } + catch (Exception ex) + { + // On .NET framework when Thread throws an exception then unit tests + // were executed without any problem. + // On new .NET exceptions from Thread breaks unit tests session. + Console.Error.WriteLine("[{0}] Failure in StartListener: {1}", + typeof(AsyncSocketListener).FullName, + ex); } } private void AcceptCallback(IAsyncResult ar) { // Signal the main thread to continue - _acceptCallbackDone.Set(); + _ = _acceptCallbackDone.Set(); // Get the socket that listens for inbound connections var listener = (Socket)ar.AsyncState; @@ -131,21 +137,38 @@ namespace Renci.SshNet.Tests.Common { handler = listener.EndAccept(ar); } - catch (ObjectDisposedException ex) + catch (SocketException ex) { // The listener is stopped through a Dispose() call, which in turn causes - // Socket.EndAccept(IAsyncResult) to throw an ObjectDisposedException + // Socket.EndAccept(...) to throw a SocketException or + // ObjectDisposedException // - // Since we consider this ObjectDisposedException normal when the listener - // is being stopped, we only write a message to stderr if the listener - // is considered to be up and running + // Since we consider such an exception normal when the listener is being + // stopped, we only write a message to stderr if the listener is considered + // to be up and running if (_started) { Console.Error.WriteLine("[{0}] Failure accepting new connection: {1}", - typeof(AsyncSocketListener).FullName, - ex); + typeof(AsyncSocketListener).FullName, + ex); + } + return; + } + catch (ObjectDisposedException ex) + { + // The listener is stopped through a Dispose() call, which in turn causes + // Socket.EndAccept(IAsyncResult) to throw a SocketException or + // ObjectDisposedException + // + // Since we consider such an exception normal when the listener is being + // stopped, we only write a message to stderr if the listener is considered + // to be up and running + if (_started) + { + Console.Error.WriteLine("[{0}] Failure accepting new connection: {1}", + typeof(AsyncSocketListener).FullName, + ex); } - return; } @@ -162,16 +185,33 @@ namespace Renci.SshNet.Tests.Common try { - handler.BeginReceive(state.Buffer, 0, state.Buffer.Length, 0, ReadCallback, state); + _ =handler.BeginReceive(state.Buffer, 0, state.Buffer.Length, 0, ReadCallback, state); + } + catch (SocketException ex) + { + // The listener is stopped through a Dispose() call, which in turn causes + // Socket.BeginReceive(...) to throw a SocketException or + // ObjectDisposedException + // + // Since we consider such an exception normal when the listener is being + // stopped, we only write a message to stderr if the listener is considered + // to be up and running + if (_started) + { + Console.Error.WriteLine("[{0}] Failure receiving new data: {1}", + typeof(AsyncSocketListener).FullName, + ex); + } } catch (ObjectDisposedException ex) { // The listener is stopped through a Dispose() call, which in turn causes - // Socket.BeginReceive(...) to throw an ObjectDisposedException + // Socket.BeginReceive(...) to throw a SocketException or + // ObjectDisposedException // - // Since we consider this ObjectDisposedException normal when the listener - // is being stopped, we only write a message to stderr if the listener - // is considered to be up and running + // Since we consider such an exception normal when the listener is being + // stopped, we only write a message to stderr if the listener is considered + // to be up and running if (_started) { Console.Error.WriteLine("[{0}] Failure receiving new data: {1}", @@ -192,7 +232,11 @@ namespace Renci.SshNet.Tests.Common try { // Read data from the client socket. - bytesRead = handler.EndReceive(ar); + bytesRead = handler.EndReceive(ar, out var errorCode); + if (errorCode != SocketError.Success) + { + bytesRead = 0; + } } catch (SocketException ex) { @@ -229,6 +273,43 @@ namespace Renci.SshNet.Tests.Common return; } + void ConnectionDisconnected() + { + SignalDisconnected(handler); + + if (ShutdownRemoteCommunicationSocket) + { + lock (_syncLock) + { + if (!_started) + { + return; + } + + try + { + handler.Shutdown(SocketShutdown.Send); + handler.Close(); + } + catch (SocketException ex) when (ex.SocketErrorCode == SocketError.ConnectionReset) + { + // On .NET 7 we got Socker Exception with ConnectionReset from Shutdown method + // when the socket is disposed + } + catch (SocketException ex) + { + throw new Exception("Exception in ReadCallback: " + ex.SocketErrorCode + " " + _stackTrace, ex); + } + catch (Exception ex) + { + throw new Exception("Exception in ReadCallback: " + _stackTrace, ex); + } + + _ = _connectedClients.Remove(handler); + } + } + } + if (bytesRead > 0) { var bytesReceived = new byte[bytesRead]; @@ -237,7 +318,12 @@ namespace Renci.SshNet.Tests.Common try { - handler.BeginReceive(state.Buffer, 0, state.Buffer.Length, 0, ReadCallback, state); + _ = handler.BeginReceive(state.Buffer, 0, state.Buffer.Length, 0, ReadCallback, state); + } + catch (ObjectDisposedException) + { + // TODO On .NET 7, sometimes we get ObjectDisposedException when _started but only on appveyor, locally it works + ConnectionDisconnected(); } catch (SocketException ex) { @@ -252,55 +338,23 @@ namespace Renci.SshNet.Tests.Common } else { - SignalDisconnected(handler); - - if (ShutdownRemoteCommunicationSocket) - { - lock (_syncLock) - { - if (!_started) - { - return; - } - try - { - handler.Shutdown(SocketShutdown.Send); - handler.Close(); - } - catch (SocketException ex) - { - throw new Exception("Exception in ReadCallback: " + ex.SocketErrorCode + " " + _stackTrace, ex); - } - catch (Exception ex) - { - throw new Exception("Exception in ReadCallback: " + _stackTrace, ex); - } - - _connectedClients.Remove(handler); - } - } + ConnectionDisconnected(); } } private void SignalBytesReceived(byte[] bytesReceived, Socket client) { - var subscribers = BytesReceived; - if (subscribers != null) - subscribers(bytesReceived, client); + BytesReceived?.Invoke(bytesReceived, client); } private void SignalConnected(Socket client) { - var subscribers = Connected; - if (subscribers != null) - subscribers(client); + Connected?.Invoke(client); } private void SignalDisconnected(Socket client) { - var subscribers = Disconnected; - if (subscribers != null) - subscribers(client); + Disconnected?.Invoke(client); } private static void DrainSocket(Socket socket) diff --git a/src/Renci.SshNet.Tests/Common/DictionaryAssert.cs b/src/Renci.SshNet.Tests/Common/DictionaryAssert.cs index 0df1c504..ec45fb61 100644 --- a/src/Renci.SshNet.Tests/Common/DictionaryAssert.cs +++ b/src/Renci.SshNet.Tests/Common/DictionaryAssert.cs @@ -8,22 +8,29 @@ namespace Renci.SshNet.Tests.Common public static void AreEqual(IDictionary expected, IDictionary actual) { if (ReferenceEquals(expected, actual)) + { return; + } if (expected == null) + { throw new AssertFailedException("Expected dictionary to be null, but was not null."); + } if (actual == null) + { throw new AssertFailedException("Expected dictionary not to be null, but was null."); + } if (expected.Count != actual.Count) + { throw new AssertFailedException(string.Format("Expected dictionary to contain {0} entries, but was {1}.", expected.Count, actual.Count)); + } foreach (var expectedEntry in expected) { - TValue actualValue; - if (!actual.TryGetValue(expectedEntry.Key, out actualValue)) + if (!actual.TryGetValue(expectedEntry.Key, out var actualValue)) { throw new AssertFailedException(string.Format("Dictionary contains no entry with key '{0}'.", expectedEntry.Key)); } diff --git a/src/Renci.SshNet.Tests/Common/Extensions.cs b/src/Renci.SshNet.Tests/Common/Extensions.cs index 4e88a790..2c2e04be 100644 --- a/src/Renci.SshNet.Tests/Common/Extensions.cs +++ b/src/Renci.SshNet.Tests/Common/Extensions.cs @@ -10,11 +10,15 @@ namespace Renci.SshNet.Tests.Common public static string AsString(this IList exceptionEvents) { if (exceptionEvents.Count == 0) + { return string.Empty; + } - string reportedExceptions = string.Empty; + var reportedExceptions = string.Empty; foreach (var exceptionEvent in exceptionEvents) + { reportedExceptions += exceptionEvent.Exception.ToString(); + } return reportedExceptions; } diff --git a/src/Renci.SshNet.Tests/Common/HttpProxyStub.cs b/src/Renci.SshNet.Tests/Common/HttpProxyStub.cs index d7bdba50..7cc8c471 100644 --- a/src/Renci.SshNet.Tests/Common/HttpProxyStub.cs +++ b/src/Renci.SshNet.Tests/Common/HttpProxyStub.cs @@ -24,7 +24,10 @@ namespace Renci.SshNet.Tests.Common get { if (_httpRequestParser == null) + { throw new InvalidOperationException("The proxy is not started."); + } + return _httpRequestParser.HttpRequest; } } @@ -45,8 +48,7 @@ namespace Renci.SshNet.Tests.Common public void Stop() { - if (_listener != null) - _listener.Stop(); + _listener?.Stop(); } public void Dispose() @@ -62,7 +64,10 @@ namespace Renci.SshNet.Tests.Common if (_httpRequestParser.CurrentState == HttpRequestParser.State.Content) { foreach (var response in Responses) - socket.Send(response); + { + _ = socket.Send(response); + } + socket.Shutdown(SocketShutdown.Send); } } @@ -150,11 +155,16 @@ namespace Renci.SshNet.Tests.Common { var buffer = _buffer.ToArray(); var bytesInLine = buffer.Length; + // when the previous byte was a CR, then do not include it in line if (buffer.Length > 0 && buffer[buffer.Length - 1] == '\r') + { bytesInLine -= 1; + } + // clear the buffer _buffer.Clear(); + // move position up one position as we've processed the current byte position++; return Encoding.ASCII.GetString(buffer, 0, bytesInLine); @@ -166,4 +176,4 @@ namespace Renci.SshNet.Tests.Common } } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.Tests/Common/SftpFileAttributesBuilder.cs b/src/Renci.SshNet.Tests/Common/SftpFileAttributesBuilder.cs index 2cf1b11a..92521316 100644 --- a/src/Renci.SshNet.Tests/Common/SftpFileAttributesBuilder.cs +++ b/src/Renci.SshNet.Tests/Common/SftpFileAttributesBuilder.cs @@ -64,23 +64,27 @@ namespace Renci.SshNet.Tests.Common public SftpFileAttributes Build() { if (_lastAccessTime == null) + { _lastAccessTime = DateTime.SpecifyKind(DateTime.MinValue, DateTimeKind.Utc); + } else if (_lastAccessTime.Value.Kind != DateTimeKind.Utc) + { _lastAccessTime = _lastAccessTime.Value.ToUniversalTime(); + } if (_lastWriteTime == null) + { _lastWriteTime = DateTime.SpecifyKind(DateTime.MinValue, DateTimeKind.Utc); + } else if (_lastWriteTime.Value.Kind != DateTimeKind.Utc) + { _lastWriteTime = _lastWriteTime.Value.ToUniversalTime(); + } - if (_size == null) - _size = 0; - if (_userId == null) - _userId = 0; - if (_groupId == null) - _groupId = 0; - if (_permissions == null) - _permissions = 0; + _size ??= 0; + _userId ??= 0; + _groupId ??= 0; + _permissions ??= 0; return new SftpFileAttributes(_lastAccessTime.Value, _lastWriteTime.Value, diff --git a/src/Renci.SshNet.Tests/Common/TestBase.cs b/src/Renci.SshNet.Tests/Common/TestBase.cs index 4ae86fb4..5973aa4a 100644 --- a/src/Renci.SshNet.Tests/Common/TestBase.cs +++ b/src/Renci.SshNet.Tests/Common/TestBase.cs @@ -49,9 +49,9 @@ namespace Renci.SshNet.Tests.Common } } - protected Stream GetData(string name) + protected static Stream GetData(string name) { return ExecutingAssembly.GetManifestResourceStream(string.Format("Renci.SshNet.Tests.Data.{0}", name)); } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj b/src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj index d7ee81e9..aa9d79a2 100644 --- a/src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj +++ b/src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj @@ -1,69 +1,26 @@  - true - ..\Renci.SshNet.snk + net462;net6.0;net7.0 + + $(NoWarn);CS1591 - - net35;net40;netcoreapp2.1;netcoreapp2.2 - - - net35;net40;netcoreapp2.1;netcoreapp2.2;netcoreapp3.0 - - - - - - - FEATURE_THREAD_COUNTDOWNEVENT;FEATURE_TPL - - - FEATURE_THREAD_COUNTDOWNEVENT;FEATURE_TPL - - - FEATURE_THREAD_COUNTDOWNEVENT;FEATURE_TPL - - - FEATURE_THREAD_COUNTDOWNEVENT;FEATURE_TPL - - - - - - - - - - - - - - - - - - - - + - - - $(MSBuildProgramFiles32)\Microsoft Visual Studio\2017\Enterprise\Common7\IDE\PublicAssemblies\Microsoft.VisualStudio.QualityTools.UnitTestFramework.dll - $(MSTestV1UnitTestFrameworkAssemblyCandidate) - - $(MSBuildProgramFiles32)\Microsoft Visual Studio\2017\Professional\Common7\IDE\PublicAssemblies\Microsoft.VisualStudio.QualityTools.UnitTestFramework.dll - $(MSTestV1UnitTestFrameworkAssemblyCandidate) - - $(MSBuildProgramFiles32)\Microsoft Visual Studio\2017\Community\Common7\IDE\PublicAssemblies\Microsoft.VisualStudio.QualityTools.UnitTestFramework.dll - $(MSTestV1UnitTestFrameworkAssemblyCandidate) - - $(MSBuildProgramFiles32)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\PublicAssemblies\Microsoft.VisualStudio.QualityTools.UnitTestFramework.dll @@ -79,41 +36,12 @@ $(MSTestV1UnitTestFrameworkAssemblyCandidate) - - - $(MSTestV1UnitTestFrameworkAssembly) - - - - - - $(MSTestV1UnitTestFrameworkAssembly) - - - - - - - - 2.1.0 - - - - - - - - 2.1.0 - - - - - - - - 2.1.0 - - + + + + + + diff --git a/src/Renci.SshNet.UAP10/Properties/AssemblyInfo.cs b/src/Renci.SshNet.UAP10/Properties/AssemblyInfo.cs deleted file mode 100644 index f71ba679..00000000 --- a/src/Renci.SshNet.UAP10/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; -using System.Runtime.CompilerServices; - -[assembly: AssemblyTitle("SSH.NET UAP 10.0")] -[assembly: InternalsVisibleTo("Renci.SshNet.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f9194e1eb66b7e2575aaee115ee1d27bc100920e7150e43992d6f668f9737de8b9c7ae892b62b8a36dd1d57929ff1541665d101dc476d6e02390846efae7e5186eec409710fdb596e3f83740afef0d4443055937649bc5a773175b61c57615dac0f0fd10f52b52fedf76c17474cc567b3f7a79de95dde842509fb39aaf69c6c2")] -[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] - -// https://github.com/dotnet/corefx/issues/7274 -//[assembly: Guid("4EE4F2DC-208D-42B2-B286-5E5DEC1DD766")] \ No newline at end of file diff --git a/src/Renci.SshNet.UAP10/Properties/Renci.SshNet.UAP10.rd.xml b/src/Renci.SshNet.UAP10/Properties/Renci.SshNet.UAP10.rd.xml deleted file mode 100644 index ba5e1b0a..00000000 --- a/src/Renci.SshNet.UAP10/Properties/Renci.SshNet.UAP10.rd.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - diff --git a/src/Renci.SshNet.UAP10/Renci.SshNet.UAP10.csproj b/src/Renci.SshNet.UAP10/Renci.SshNet.UAP10.csproj deleted file mode 100644 index d0eb423a..00000000 --- a/src/Renci.SshNet.UAP10/Renci.SshNet.UAP10.csproj +++ /dev/null @@ -1,1523 +0,0 @@ - - - - - Debug - AnyCPU - {EC212E04-A372-4B95-B45B-C0D4A739EF80} - Library - Properties - Renci.SshNet - Renci.SshNet - en-US - UAP - 10.0.10240.0 - 10.0.10240.0 - 14 - 512 - {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - - - AnyCPU - true - full - false - bin\Debug\ - TRACE;DEBUG;FEATURE_STRINGBUILDER_CLEAR;FEATURE_HASHALGORITHM_DISPOSE;FEATURE_DATAGRAMSOCKET;FEATURE_SOCKET_EAP;FEATURE_SOCKET_DISPOSE;FEATURE_STREAM_TAP;FEATURE_THREAD_COUNTDOWNEVENT;FEATURE_THREAD_TAP;FEATURE_WAITHANDLE_DISPOSE;FEATURE_MEMORYSTREAM_TRYGETBUFFER;FEATURE_REFLECTION_TYPEINFO;FEATURE_ENCODING_ASCII - prompt - 4 - bin\Debug\Renci.SshNet.xml - true - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE;FEATURE_STRINGBUILDER_CLEAR;FEATURE_HASHALGORITHM_DISPOSE;FEATURE_DATAGRAMSOCKET;FEATURE_SOCKET_EAP;FEATURE_SOCKET_DISPOSE;FEATURE_STREAM_TAP;FEATURE_THREAD_COUNTDOWNEVENT;FEATURE_THREAD_TAP;FEATURE_WAITHANDLE_DISPOSE;FEATURE_MEMORYSTREAM_TRYGETBUFFER;FEATURE_REFLECTION_TYPEINFO;FEATURE_ENCODING_ASCII - prompt - 4 - bin\Release\Renci.SshNet.xml - true - - - x86 - true - bin\x86\Debug\ - TRACE;DEBUG;FEATURE_DATAGRAMSOCKET;FEATURE_SOCKET_EAP;FEATURE_SOCKET_DISPOSE;FEATURE_STREAM_TAP;FEATURE_THREAD_COUNTDOWNEVENT;FEATURE_THREAD_TAP;FEATURE_WAITHANDLE_DISPOSE;FEATURE_MEMORYSTREAM_TRYGETBUFFER;FEATURE_REFLECTION_TYPEINFO;FEATURE_ENCODING_ASCII - ;2008 - full - x86 - false - prompt - - - x86 - bin\x86\Release\ - TRACE;FEATURE_DATAGRAMSOCKET;FEATURE_SOCKET_EAP;FEATURE_SOCKET_DISPOSE;FEATURE_STREAM_TAP;FEATURE_THREAD_COUNTDOWNEVENT;FEATURE_THREAD_TAP;FEATURE_WAITHANDLE_DISPOSE;FEATURE_MEMORYSTREAM_TRYGETBUFFER;FEATURE_REFLECTION_TYPEINFO;FEATURE_ENCODING_ASCII - true - ;2008 - pdbonly - x86 - false - prompt - - - ARM - true - bin\ARM\Debug\ - TRACE;DEBUG;FEATURE_DATAGRAMSOCKET;FEATURE_SOCKET_EAP;FEATURE_SOCKET_DISPOSE;FEATURE_STREAM_TAP;FEATURE_THREAD_COUNTDOWNEVENT;FEATURE_THREAD_TAP;FEATURE_WAITHANDLE_DISPOSE;FEATURE_MEMORYSTREAM_TRYGETBUFFER;FEATURE_REFLECTION_TYPEINFO;FEATURE_ENCODING_ASCII - ;2008 - full - ARM - false - prompt - - - ARM - bin\ARM\Release\ - TRACE;FEATURE_DATAGRAMSOCKET;FEATURE_SOCKET_EAP;FEATURE_SOCKET_DISPOSE;FEATURE_STREAM_TAP;FEATURE_THREAD_COUNTDOWNEVENT;FEATURE_THREAD_TAP;FEATURE_WAITHANDLE_DISPOSE;FEATURE_MEMORYSTREAM_TRYGETBUFFER;FEATURE_REFLECTION_TYPEINFO;FEATURE_ENCODING_ASCII - true - ;2008 - pdbonly - ARM - false - prompt - - - x64 - true - bin\x64\Debug\ - TRACE;DEBUG;FEATURE_DATAGRAMSOCKET;FEATURE_SOCKET_EAP;FEATURE_SOCKET_DISPOSE;FEATURE_STREAM_TAP;FEATURE_THREAD_COUNTDOWNEVENT;FEATURE_THREAD_TAP;FEATURE_WAITHANDLE_DISPOSE;FEATURE_MEMORYSTREAM_TRYGETBUFFER;FEATURE_REFLECTION_TYPEINFO;FEATURE_ENCODING_ASCII - ;2008 - full - x64 - false - prompt - - - x64 - bin\x64\Release\ - TRACE;FEATURE_DATAGRAMSOCKET;FEATURE_SOCKET_EAP;FEATURE_SOCKET_DISPOSE;FEATURE_STREAM_TAP;FEATURE_THREAD_COUNTDOWNEVENT;FEATURE_THREAD_TAP;FEATURE_WAITHANDLE_DISPOSE;FEATURE_MEMORYSTREAM_TRYGETBUFFER;FEATURE_REFLECTION_TYPEINFO;FEATURE_ENCODING_ASCII - true - ;2008 - pdbonly - x64 - false - prompt - - - - - - - - Abstractions\CryptoAbstraction.cs - - - Abstractions\DiagnosticAbstraction.cs - - - Abstractions\DnsAbstraction.cs - - - Abstractions\FileSystemAbstraction.cs - - - Abstractions\ReflectionAbstraction.cs - - - Abstractions\SocketAbstraction.cs - - - Abstractions\ThreadAbstraction.cs - - - AuthenticationMethod.cs - - - AuthenticationResult.cs - - - BaseClient.cs - - - Channels\Channel.cs - - - Channels\ChannelDirectTcpip.cs - - - Channels\ChannelForwardedTcpip.cs - - - Channels\ChannelSession.cs - - - Channels\ChannelTypes.cs - - - Channels\ClientChannel.cs - - - Channels\IChannel.cs - - - Channels\IChannelDirectTcpip.cs - - - Channels\IChannelForwardedTcpip.cs - - - Channels\IChannelSession.cs - - - Channels\ServerChannel.cs - - - CipherInfo.cs - - - ClientAuthentication.cs - - - CommandAsyncResult.cs - - - Common\Array.cs - - - Common\ASCIIEncoding.cs - - - Common\AsyncResult.cs - - - Common\AuthenticationBannerEventArgs.cs - - - Common\AuthenticationEventArgs.cs - - - Common\AuthenticationPasswordChangeEventArgs.cs - - - Common\AuthenticationPrompt.cs - - - Common\AuthenticationPromptEventArgs.cs - - - Common\BigInteger.cs - - - Common\ChannelDataEventArgs.cs - - - Common\ChannelEventArgs.cs - - - Common\ChannelExtendedDataEventArgs.cs - - - Common\ChannelOpenConfirmedEventArgs.cs - - - Common\ChannelOpenFailedEventArgs.cs - - - Common\ChannelRequestEventArgs.cs - - - Common\CountdownEvent.cs - - - Common\DerData.cs - - - Common\ExceptionEventArgs.cs - - - Common\Extensions.cs - - - Common\HostKeyEventArgs.cs - - - Common\NetConfServerException.cs - - - Common\ObjectIdentifier.cs - - - Common\Pack.cs - - - Common\PacketDump.cs - - - Common\PipeStream.cs - - - Common\PortForwardEventArgs.cs - - - Common\PosixPath.cs - - - Common\ProxyException.cs - - - Common\ScpDownloadEventArgs.cs - - - Common\ScpException.cs - - - Common\ScpUploadEventArgs.cs - - - Common\SemaphoreLight.cs - - - Common\SftpPathNotFoundException.cs - - - Common\SftpPermissionDeniedException.cs - - - Common\ShellDataEventArgs.cs - - - Common\SshAuthenticationException.cs - - - Common\SshConnectionException.cs - - - Common\SshData.cs - - - Common\SshDataStream.cs - - - Common\SshException.cs - - - Common\SshOperationTimeoutException.cs - - - Common\SshPassPhraseNullOrEmptyException.cs - - - Common\TerminalModes.cs - - - Compression\CompressionMode.cs - - - Compression\Compressor.cs - - - Compression\Zlib.cs - - - Compression\ZlibOpenSsh.cs - - - Compression\ZlibStream.cs - - - ConnectionInfo.cs - - - Connection\ConnectorBase.cs - - - Connection\DirectConnector.cs - - - Connection\HttpConnector.cs - - - Connection\IConnector.cs - - - Connection\IProtocolVersionExchange.cs - - - Connection\ISocketFactory.cs - - - Connection\ProtocolVersionExchange.cs - - - Connection\SocketFactory.cs - - - Connection\Socks4Connector.cs - - - Connection\Socks5Connector.cs - - - Connection\SshIdentification.cs - - - ExpectAction.cs - - - ExpectAsyncResult.cs - - - ForwardedPort.cs - - - ForwardedPortDynamic.cs - - - ForwardedPortDynamic.NET.cs - - - ForwardedPortLocal.cs - - - ForwardedPortLocal.NET.cs - - - ForwardedPortRemote.cs - - - ForwardedPortStatus.cs - - - HashInfo.cs - - - IAuthenticationMethod.cs - - - IClientAuthentication.cs - - - IConnectionInfo.cs - - - IForwardedPort.cs - - - IRemotePathTransformation.cs - - - IServiceFactory.cs - - - IServiceFactory.NET.cs - - - ISession.cs - - - ISftpClient.cs - - - ISubsystemSession.cs - - - KeyboardInteractiveAuthenticationMethod.cs - - - KeyboardInteractiveConnectionInfo.cs - - - MessageEventArgs.cs - - - Messages\Authentication\BannerMessage.cs - - - Messages\Authentication\FailureMessage.cs - - - Messages\Authentication\InformationRequestMessage.cs - - - Messages\Authentication\InformationResponseMessage.cs - - - Messages\Authentication\PasswordChangeRequiredMessage.cs - - - Messages\Authentication\PublicKeyMessage.cs - - - Messages\Authentication\RequestMessage.cs - - - Messages\Authentication\RequestMessageHost.cs - - - Messages\Authentication\RequestMessageKeyboardInteractive.cs - - - Messages\Authentication\RequestMessageNone.cs - - - Messages\Authentication\RequestMessagePassword.cs - - - Messages\Authentication\RequestMessagePublicKey.cs - - - Messages\Authentication\SuccessMessage.cs - - - Messages\Connection\CancelTcpIpForwardGlobalRequestMessage.cs - - - Messages\Connection\ChannelCloseMessage.cs - - - Messages\Connection\ChannelDataMessage.cs - - - Messages\Connection\ChannelEofMessage.cs - - - Messages\Connection\ChannelExtendedDataMessage.cs - - - Messages\Connection\ChannelFailureMessage.cs - - - Messages\Connection\ChannelMessage.cs - - - Messages\Connection\ChannelOpenConfirmationMessage.cs - - - Messages\Connection\ChannelOpenFailureMessage.cs - - - Messages\Connection\ChannelOpenFailureReasons.cs - - - Messages\Connection\ChannelOpen\ChannelOpenInfo.cs - - - Messages\Connection\ChannelOpen\ChannelOpenMessage.cs - - - Messages\Connection\ChannelOpen\DirectTcpipChannelInfo.cs - - - Messages\Connection\ChannelOpen\ForwardedTcpipChannelInfo.cs - - - Messages\Connection\ChannelOpen\SessionChannelOpenInfo.cs - - - Messages\Connection\ChannelOpen\X11ChannelOpenInfo.cs - - - Messages\Connection\ChannelRequest\BreakRequestInfo.cs - - - Messages\Connection\ChannelRequest\ChannelRequestMessage.cs - - - Messages\Connection\ChannelRequest\EndOfWriteRequestInfo.cs - - - Messages\Connection\ChannelRequest\EnvironmentVariableRequestInfo.cs - - - Messages\Connection\ChannelRequest\ExecRequestInfo.cs - - - Messages\Connection\ChannelRequest\ExitSignalRequestInfo.cs - - - Messages\Connection\ChannelRequest\ExitStatusRequestInfo.cs - - - Messages\Connection\ChannelRequest\KeepAliveRequestInfo.cs - - - Messages\Connection\ChannelRequest\PseudoTerminalInfo.cs - - - Messages\Connection\ChannelRequest\RequestInfo.cs - - - Messages\Connection\ChannelRequest\ShellRequestInfo.cs - - - Messages\Connection\ChannelRequest\SignalRequestInfo.cs - - - Messages\Connection\ChannelRequest\SubsystemRequestInfo.cs - - - Messages\Connection\ChannelRequest\WindowChangeRequestInfo.cs - - - Messages\Connection\ChannelRequest\X11ForwardingRequestInfo.cs - - - Messages\Connection\ChannelRequest\XonXoffRequestInfo.cs - - - Messages\Connection\ChannelSuccessMessage.cs - - - Messages\Connection\ChannelWindowAdjustMessage.cs - - - Messages\Connection\GlobalRequestMessage.cs - - - Messages\Connection\GlobalRequestName.cs - - - Messages\Connection\RequestFailureMessage.cs - - - Messages\Connection\RequestSuccessMessage.cs - - - Messages\Connection\TcpIpForwardGlobalRequestMessage.cs - - - Messages\Message.cs - - - Messages\MessageAttribute.cs - - - Messages\ServiceName.cs - - - Messages\Transport\DebugMessage.cs - - - Messages\Transport\DisconnectMessage.cs - - - Messages\Transport\DisconnectReason.cs - - - Messages\Transport\IgnoreMessage.cs - - - Messages\Transport\IKeyExchangedAllowed.cs - - - Messages\Transport\KeyExchangeDhGroupExchangeGroup.cs - - - Messages\Transport\KeyExchangeDhGroupExchangeInit.cs - - - Messages\Transport\KeyExchangeDhGroupExchangeReply.cs - - - Messages\Transport\KeyExchangeDhGroupExchangeRequest.cs - - - Messages\Transport\KeyExchangeDhInitMessage.cs - - - Messages\Transport\KeyExchangeDhReplyMessage.cs - - - Messages\Transport\KeyExchangeEcdhInitMessage.cs - - - Messages\Transport\KeyExchangeEcdhReplyMessage.cs - - - Messages\Transport\KeyExchangeInitMessage.cs - - - Messages\Transport\NewKeysMessage.cs - - - Messages\Transport\ServiceAcceptMessage.cs - - - Messages\Transport\ServiceRequestMessage.cs - - - Messages\Transport\UnimplementedMessage.cs - - - NetConfClient.cs - - - Netconf\INetConfSession.cs - - - Netconf\NetConfSession.cs - - - NoneAuthenticationMethod.cs - - - PasswordAuthenticationMethod.cs - - - PasswordConnectionInfo.cs - - - PrivateKeyAuthenticationMethod.cs - - - PrivateKeyConnectionInfo.cs - - - PrivateKeyFile.cs - - - Properties\CommonAssemblyInfo.cs - - - ProxyTypes.cs - - - RemotePathDoubleQuoteTransformation.cs - - - RemotePathNoneTransformation.cs - - - RemotePathShellQuoteTransformation.cs - - - RemotePathTransformation.cs - - - ScpClient.cs - - - ScpClient.NET.cs - - - Security\Algorithm.cs - - - Security\Cryptography\BouncyCastle\asn1\sec\SECNamedCurves.cs - - - Security\Cryptography\BouncyCastle\asn1\x9\X9Curve.cs - - - Security\Cryptography\BouncyCastle\asn1\x9\X9ECParameters.cs - - - Security\Cryptography\BouncyCastle\asn1\x9\X9ECParametersHolder.cs - - - Security\Cryptography\BouncyCastle\asn1\x9\X9ECPoint.cs - - - Security\Cryptography\BouncyCastle\crypto\agreement\ECDHCBasicAgreement.cs - - - Security\Cryptography\BouncyCastle\crypto\AsymmetricCipherKeyPair.cs - - - Security\Cryptography\BouncyCastle\crypto\AsymmetricKeyParameter.cs - - - Security\Cryptography\BouncyCastle\crypto\digests\GeneralDigest.cs - - - Security\Cryptography\BouncyCastle\crypto\digests\Sha256Digest.cs - - - Security\Cryptography\BouncyCastle\crypto\generators\ECKeyPairGenerator.cs - - - Security\Cryptography\BouncyCastle\crypto\IAsymmetricCipherKeyPairGenerator.cs - - - Security\Cryptography\BouncyCastle\crypto\IDigest.cs - - - Security\Cryptography\BouncyCastle\crypto\KeyGenerationParameters.cs - - - Security\Cryptography\BouncyCastle\crypto\parameters\ECDomainParameters.cs - - - Security\Cryptography\BouncyCastle\crypto\parameters\ECKeyGenerationParameters.cs - - - Security\Cryptography\BouncyCastle\crypto\parameters\ECKeyParameters.cs - - - Security\Cryptography\BouncyCastle\crypto\parameters\ECPrivateKeyParameters.cs - - - Security\Cryptography\BouncyCastle\crypto\parameters\ECPublicKeyParameters.cs - - - Security\Cryptography\BouncyCastle\crypto\prng\CryptoApiRandomGenerator.cs - - - Security\Cryptography\BouncyCastle\crypto\prng\DigestRandomGenerator.cs - - - Security\Cryptography\BouncyCastle\crypto\prng\IRandomGenerator.cs - - - Security\Cryptography\BouncyCastle\crypto\util\Pack.cs - - - Security\Cryptography\BouncyCastle\math\BigInteger.cs - - - Security\Cryptography\BouncyCastle\math\ec\abc\SimpleBigDecimal.cs - - - Security\Cryptography\BouncyCastle\math\ec\abc\Tnaf.cs - - - Security\Cryptography\BouncyCastle\math\ec\abc\ZTauElement.cs - - - Security\Cryptography\BouncyCastle\math\ec\ECAlgorithms.cs - - - Security\Cryptography\BouncyCastle\math\ec\ECCurve.cs - - - Security\Cryptography\BouncyCastle\math\ec\ECFieldElement.cs - - - Security\Cryptography\BouncyCastle\math\ec\ECLookupTable.cs - - - Security\Cryptography\BouncyCastle\math\ec\ECPoint.cs - - - Security\Cryptography\BouncyCastle\math\ec\ECPointMap.cs - - - Security\Cryptography\BouncyCastle\math\ec\endo\ECEndomorphism.cs - - - Security\Cryptography\BouncyCastle\math\ec\endo\GlvEndomorphism.cs - - - Security\Cryptography\BouncyCastle\math\ec\LongArray.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\AbstractECMultiplier.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\ECMultiplier.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\FixedPointCombMultiplier.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\FixedPointPreCompInfo.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\FixedPointUtilities.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\GlvMultiplier.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\IPreCompCallback.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\PreCompInfo.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\ValidityPreCompInfo.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\WNafL2RMultiplier.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\WNafPreCompInfo.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\WNafUtilities.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\WTauNafMultiplier.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\WTauNafPreCompInfo.cs - - - Security\Cryptography\BouncyCastle\math\field\FiniteFields.cs - - - Security\Cryptography\BouncyCastle\math\field\GenericPolynomialExtensionField.cs - - - Security\Cryptography\BouncyCastle\math\field\GF2Polynomial.cs - - - Security\Cryptography\BouncyCastle\math\field\IExtensionField.cs - - - Security\Cryptography\BouncyCastle\math\field\IFiniteField.cs - - - Security\Cryptography\BouncyCastle\math\field\IPolynomial.cs - - - Security\Cryptography\BouncyCastle\math\field\IPolynomialExtensionField.cs - - - Security\Cryptography\BouncyCastle\math\field\PrimeField.cs - - - Security\Cryptography\BouncyCastle\math\raw\Mod.cs - - - Security\Cryptography\BouncyCastle\math\raw\Nat.cs - - - Security\Cryptography\BouncyCastle\security\DigestUtilities.cs - - - Security\Cryptography\BouncyCastle\security\SecureRandom.cs - - - Security\Cryptography\BouncyCastle\security\SecurityUtilityException.cs - - - Security\Cryptography\BouncyCastle\util\Arrays.cs - - - Security\Cryptography\BouncyCastle\util\BigIntegers.cs - - - Security\Cryptography\BouncyCastle\util\encoders\Hex.cs - - - Security\Cryptography\BouncyCastle\util\encoders\HexEncoder.cs - - - Security\Cryptography\BouncyCastle\util\IMemoable.cs - - - Security\Cryptography\BouncyCastle\util\Integers.cs - - - Security\Cryptography\BouncyCastle\util\MemoableResetException.cs - - - Security\Cryptography\BouncyCastle\util\Times.cs - - - Security\CertificateHostAlgorithm.cs - - - Security\Cryptography\Chaos.NaCl\CryptoBytes.cs - - - Security\Cryptography\Chaos.NaCl\Ed25519.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Array16.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Array8.cs - - - Security\Cryptography\Chaos.NaCl\Internal\ByteIntegerConverter.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\base.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\base2.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\d.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\d2.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_0.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_1.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_add.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_cmov.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_cswap.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_frombytes.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_invert.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_isnegative.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_isnonzero.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_mul.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_mul121666.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_neg.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_pow22523.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_sq.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_sq2.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_sub.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_tobytes.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\FieldElement.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_add.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_double_scalarmult.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_frombytes.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_madd.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_msub.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p1p1_to_p2.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p1p1_to_p3.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p2_0.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p2_dbl.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p3_0.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p3_dbl.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p3_tobytes.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p3_to_cached.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p3_to_p2.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_precomp_0.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_scalarmult_base.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_sub.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_tobytes.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\GroupElement.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\keypair.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\open.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\scalarmult.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\sc_clamp.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\sc_mul_add.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\sc_reduce.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\sign.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\sqrtm1.cs - - - Security\Cryptography\Chaos.NaCl\Internal\InternalAssert.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Poly1305Donna.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Salsa\Salsa20.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Salsa\SalsaCore.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Sha512Internal.cs - - - Security\Cryptography\Chaos.NaCl\MontgomeryCurve25519.cs - - - Security\Cryptography\Chaos.NaCl\Sha512.cs - - - Security\Cryptography\AsymmetricCipher.cs - - - Security\Cryptography\Bcrypt.cs - - - Security\Cryptography\BlockCipher.cs - - - Security\Cryptography\Cipher.cs - - - Security\Cryptography\CipherDigitalSignature.cs - - - Security\Cryptography\Ciphers\AesCipher.cs - - - Security\Cryptography\Ciphers\Arc4Cipher.cs - - - Security\Cryptography\Ciphers\BlowfishCipher.cs - - - Security\Cryptography\Ciphers\CastCipher.cs - - - Security\Cryptography\Ciphers\CipherMode.cs - - - Security\Cryptography\Ciphers\CipherPadding.cs - - - Security\Cryptography\Ciphers\DesCipher.cs - - - Security\Cryptography\Ciphers\Modes\CbcCipherMode.cs - - - Security\Cryptography\Ciphers\Modes\CfbCipherMode.cs - - - Security\Cryptography\Ciphers\Modes\CtrCipherMode.cs - - - Security\Cryptography\Ciphers\Modes\OfbCipherMode.cs - - - Security\Cryptography\Ciphers\Paddings\PKCS5Padding.cs - - - Security\Cryptography\Ciphers\Paddings\PKCS7Padding.cs - - - Security\Cryptography\Ciphers\RsaCipher.cs - - - Security\Cryptography\Ciphers\SerpentCipher.cs - - - Security\Cryptography\Ciphers\TripleDesCipher.cs - - - Security\Cryptography\Ciphers\TwofishCipher.cs - - - Security\Cryptography\DigitalSignature.cs - - - Security\Cryptography\DsaDigitalSignature.cs - - - Security\Cryptography\DsaKey.cs - - - Security\Cryptography\ED25519DigitalSignature.cs - - - Security\Cryptography\ED25519Key.cs - - - Security\Cryptography\HMACMD5.cs - - - Security\Cryptography\HMACSHA1.cs - - - Security\Cryptography\HMACSHA256.cs - - - Security\Cryptography\HMACSHA384.cs - - - Security\Cryptography\HMACSHA512.cs - - - Security\Cryptography\Key.cs - - - Security\Cryptography\EcdsaDigitalSignature.cs - - - Security\Cryptography\EcdsaKey.cs - - - Security\Cryptography\RsaDigitalSignature.cs - - - Security\Cryptography\RsaKey.cs - - - Security\Cryptography\StreamCipher.cs - - - Security\Cryptography\SymmetricCipher.cs - - - Security\GroupExchangeHashData.cs - - - Security\HostAlgorithm.cs - - - Security\IKeyExchange.cs - - - Security\KeyExchange.cs - - - Security\KeyExchangeDiffieHellman.cs - - - Security\KeyExchangeDiffieHellmanGroup14Sha1.cs - - - Security\KeyExchangeDiffieHellmanGroup14Sha256.cs - - - Security\KeyExchangeDiffieHellmanGroup16Sha512.cs - - - Security\KeyExchangeDiffieHellmanGroup1Sha1.cs - - - Security\KeyExchangeDiffieHellmanGroupExchangeSha1.cs - - - Security\KeyExchangeDiffieHellmanGroupExchangeSha256.cs - - - Security\KeyExchangeDiffieHellmanGroupExchangeShaBase.cs - - - Security\KeyExchangeDiffieHellmanGroupSha1.cs - - - Security\KeyExchangeDiffieHellmanGroupSha256.cs - - - Security\KeyExchangeDiffieHellmanGroupSha512.cs - - - Security\KeyExchangeDiffieHellmanGroupShaBase.cs - - - Security\KeyExchangeEC.cs - - - Security\KeyExchangeECCurve25519.cs - - - Security\KeyExchangeECDH.cs - - - Security\KeyExchangeECDH256.cs - - - Security\KeyExchangeECDH384.cs - - - Security\KeyExchangeECDH521.cs - - - Security\KeyExchangeHash.cs - - - Security\KeyHostAlgorithm.cs - - - ServiceFactory.cs - - - ServiceFactory.NET.cs - - - Session.cs - - - SftpClient.cs - - - Sftp\Flags.cs - - - Sftp\ISftpFileReader.cs - - - Sftp\ISftpResponseFactory.cs - - - Sftp\ISftpSession.cs - - - Sftp\Requests\ExtendedRequests\FStatVfsRequest.cs - - - Sftp\Requests\ExtendedRequests\HardLinkRequest.cs - - - Sftp\Requests\ExtendedRequests\PosixRenameRequest.cs - - - Sftp\Requests\ExtendedRequests\StatVfsRequest.cs - - - Sftp\Requests\SftpBlockRequest.cs - - - Sftp\Requests\SftpCloseRequest.cs - - - Sftp\Requests\SftpExtendedRequest.cs - - - Sftp\Requests\SftpFSetStatRequest.cs - - - Sftp\Requests\SftpFStatRequest.cs - - - Sftp\Requests\SftpInitRequest.cs - - - Sftp\Requests\SftpLinkRequest.cs - - - Sftp\Requests\SftpLStatRequest.cs - - - Sftp\Requests\SftpMkDirRequest.cs - - - Sftp\Requests\SftpOpenDirRequest.cs - - - Sftp\Requests\SftpOpenRequest.cs - - - Sftp\Requests\SftpReadDirRequest.cs - - - Sftp\Requests\SftpReadLinkRequest.cs - - - Sftp\Requests\SftpReadRequest.cs - - - Sftp\Requests\SftpRealPathRequest.cs - - - Sftp\Requests\SftpRemoveRequest.cs - - - Sftp\Requests\SftpRenameRequest.cs - - - Sftp\Requests\SftpRequest.cs - - - Sftp\Requests\SftpRmDirRequest.cs - - - Sftp\Requests\SftpSetStatRequest.cs - - - Sftp\Requests\SftpStatRequest.cs - - - Sftp\Requests\SftpSymLinkRequest.cs - - - Sftp\Requests\SftpUnblockRequest.cs - - - Sftp\Requests\SftpWriteRequest.cs - - - Sftp\Responses\ExtendedReplies\ExtendedReplyInfo.cs - - - Sftp\Responses\ExtendedReplies\StatVfsReplyInfo.cs - - - Sftp\Responses\SftpAttrsResponse.cs - - - Sftp\Responses\SftpDataResponse.cs - - - Sftp\Responses\SftpExtendedReplyResponse.cs - - - Sftp\Responses\SftpHandleResponse.cs - - - Sftp\Responses\SftpNameResponse.cs - - - Sftp\Responses\SftpResponse.cs - - - Sftp\Responses\SftpStatusResponse.cs - - - Sftp\Responses\SftpVersionResponse.cs - - - Sftp\SftpCloseAsyncResult.cs - - - Sftp\SftpDownloadAsyncResult.cs - - - Sftp\SftpFile.cs - - - Sftp\SftpFileAttributes.cs - - - Sftp\SftpFileReader.cs - - - Sftp\SftpFileStream.cs - - - Sftp\SftpFileSystemInformation.cs - - - Sftp\SftpListDirectoryAsyncResult.cs - - - Sftp\SftpMessage.cs - - - Sftp\SftpMessageTypes.cs - - - Sftp\SftpOpenAsyncResult.cs - - - Sftp\SftpReadAsyncResult.cs - - - Sftp\SftpRealPathAsyncResult.cs - - - Sftp\SftpResponseFactory.cs - - - Sftp\SftpSession.cs - - - Sftp\SFtpStatAsyncResult.cs - - - Sftp\SftpSynchronizeDirectoriesAsyncResult.cs - - - Sftp\SftpUploadAsyncResult.cs - - - Sftp\StatusCodes.cs - - - Shell.cs - - - ShellStream.cs - - - SshClient.cs - - - SshCommand.cs - - - SshMessageFactory.cs - - - SubsystemSession.cs - - - - - - 14.0 - - - - \ No newline at end of file diff --git a/src/Renci.SshNet.UAP10/project.json b/src/Renci.SshNet.UAP10/project.json deleted file mode 100644 index 6916d5e6..00000000 --- a/src/Renci.SshNet.UAP10/project.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "dependencies": { - "Microsoft.NETCore.UniversalWindowsPlatform": "5.2.0", - "SshNet.Security.Cryptography": "1.2.0", - "System.Xml.XPath.XmlDocument": "4.0.1" - }, - "frameworks": { - "uap10.0": {} - }, - "runtimes": { - "win10-arm": {}, - "win10-arm-aot": {}, - "win10-x86": {}, - "win10-x86-aot": {}, - "win10-x64": {}, - "win10-x64-aot": {} - } -} \ No newline at end of file diff --git a/src/Renci.SshNet.VS2012.sln b/src/Renci.SshNet.VS2012.sln deleted file mode 100644 index a80b1908..00000000 --- a/src/Renci.SshNet.VS2012.sln +++ /dev/null @@ -1,108 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2012 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Renci.SshNet.Silverlight", "Renci.SshNet.Silverlight\Renci.SshNet.Silverlight.csproj", "{77C294BB-1DC2-49DC-BE16-963F8F22794D}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Renci.SshNet.WindowsPhone", "Renci.SshNet.WindowsPhone\Renci.SshNet.WindowsPhone.csproj", "{3AD3EDF0-702E-4A91-8735-DCE4659AA54C}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Renci.SshNet.Silverlight5", "Renci.SshNet.Silverlight5\Renci.SshNet.Silverlight5.csproj", "{E367F791-C1EC-4181-912A-2943CAC6B3BC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Renci.SshNet.WindowsPhone8", "Renci.SshNet.WindowsPhone8\Renci.SshNet.WindowsPhone8.csproj", "{4A6CA785-1C8A-47FE-98C0-30C675A9328B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{2D6CAE62-D053-476F-9BDD-2B1F27FA9C5D}" - ProjectSection(SolutionItems) = preProject - ..\build\build.proj = ..\build\build.proj - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "nuget", "nuget", "{94EE3919-19FA-4D9B-8DA9-249050B15232}" - ProjectSection(SolutionItems) = preProject - ..\build\nuget\SSH.NET.nuspec = ..\build\nuget\SSH.NET.nuspec - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "sandcastle", "sandcastle", "{A6C3FFD3-16A5-44D3-8C1F-3613D6DD17D1}" - ProjectSection(SolutionItems) = preProject - ..\build\sandcastle\SSH.NET.shfbproj = ..\build\sandcastle\SSH.NET.shfbproj - EndProjectSection -EndProject -Global - GlobalSection(TestCaseManagementSettings) = postSolution - CategoryFile = Renci.SshNet1.vsmdi - EndGlobalSection - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Debug|ARM = Debug|ARM - Debug|Mixed Platforms = Debug|Mixed Platforms - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|Any CPU = Release|Any CPU - Release|ARM = Release|ARM - Release|Mixed Platforms = Release|Mixed Platforms - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {77C294BB-1DC2-49DC-BE16-963F8F22794D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77C294BB-1DC2-49DC-BE16-963F8F22794D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77C294BB-1DC2-49DC-BE16-963F8F22794D}.Debug|ARM.ActiveCfg = Debug|Any CPU - {77C294BB-1DC2-49DC-BE16-963F8F22794D}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {77C294BB-1DC2-49DC-BE16-963F8F22794D}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {77C294BB-1DC2-49DC-BE16-963F8F22794D}.Debug|x64.ActiveCfg = Debug|Any CPU - {77C294BB-1DC2-49DC-BE16-963F8F22794D}.Debug|x86.ActiveCfg = Debug|Any CPU - {77C294BB-1DC2-49DC-BE16-963F8F22794D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77C294BB-1DC2-49DC-BE16-963F8F22794D}.Release|Any CPU.Build.0 = Release|Any CPU - {77C294BB-1DC2-49DC-BE16-963F8F22794D}.Release|ARM.ActiveCfg = Release|Any CPU - {77C294BB-1DC2-49DC-BE16-963F8F22794D}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {77C294BB-1DC2-49DC-BE16-963F8F22794D}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {77C294BB-1DC2-49DC-BE16-963F8F22794D}.Release|x64.ActiveCfg = Release|Any CPU - {77C294BB-1DC2-49DC-BE16-963F8F22794D}.Release|x86.ActiveCfg = Release|Any CPU - {3AD3EDF0-702E-4A91-8735-DCE4659AA54C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3AD3EDF0-702E-4A91-8735-DCE4659AA54C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3AD3EDF0-702E-4A91-8735-DCE4659AA54C}.Debug|ARM.ActiveCfg = Debug|Any CPU - {3AD3EDF0-702E-4A91-8735-DCE4659AA54C}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {3AD3EDF0-702E-4A91-8735-DCE4659AA54C}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {3AD3EDF0-702E-4A91-8735-DCE4659AA54C}.Debug|x64.ActiveCfg = Debug|Any CPU - {3AD3EDF0-702E-4A91-8735-DCE4659AA54C}.Debug|x86.ActiveCfg = Debug|Any CPU - {3AD3EDF0-702E-4A91-8735-DCE4659AA54C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3AD3EDF0-702E-4A91-8735-DCE4659AA54C}.Release|Any CPU.Build.0 = Release|Any CPU - {3AD3EDF0-702E-4A91-8735-DCE4659AA54C}.Release|ARM.ActiveCfg = Release|Any CPU - {3AD3EDF0-702E-4A91-8735-DCE4659AA54C}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {3AD3EDF0-702E-4A91-8735-DCE4659AA54C}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {3AD3EDF0-702E-4A91-8735-DCE4659AA54C}.Release|x64.ActiveCfg = Release|Any CPU - {3AD3EDF0-702E-4A91-8735-DCE4659AA54C}.Release|x86.ActiveCfg = Release|Any CPU - {E367F791-C1EC-4181-912A-2943CAC6B3BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E367F791-C1EC-4181-912A-2943CAC6B3BC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E367F791-C1EC-4181-912A-2943CAC6B3BC}.Debug|ARM.ActiveCfg = Debug|Any CPU - {E367F791-C1EC-4181-912A-2943CAC6B3BC}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {E367F791-C1EC-4181-912A-2943CAC6B3BC}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {E367F791-C1EC-4181-912A-2943CAC6B3BC}.Debug|x64.ActiveCfg = Debug|Any CPU - {E367F791-C1EC-4181-912A-2943CAC6B3BC}.Debug|x86.ActiveCfg = Debug|Any CPU - {E367F791-C1EC-4181-912A-2943CAC6B3BC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E367F791-C1EC-4181-912A-2943CAC6B3BC}.Release|Any CPU.Build.0 = Release|Any CPU - {E367F791-C1EC-4181-912A-2943CAC6B3BC}.Release|ARM.ActiveCfg = Release|Any CPU - {E367F791-C1EC-4181-912A-2943CAC6B3BC}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {E367F791-C1EC-4181-912A-2943CAC6B3BC}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {E367F791-C1EC-4181-912A-2943CAC6B3BC}.Release|x64.ActiveCfg = Release|Any CPU - {E367F791-C1EC-4181-912A-2943CAC6B3BC}.Release|x86.ActiveCfg = Release|Any CPU - {4A6CA785-1C8A-47FE-98C0-30C675A9328B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4A6CA785-1C8A-47FE-98C0-30C675A9328B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4A6CA785-1C8A-47FE-98C0-30C675A9328B}.Debug|ARM.ActiveCfg = Debug|Any CPU - {4A6CA785-1C8A-47FE-98C0-30C675A9328B}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {4A6CA785-1C8A-47FE-98C0-30C675A9328B}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {4A6CA785-1C8A-47FE-98C0-30C675A9328B}.Debug|x64.ActiveCfg = Debug|Any CPU - {4A6CA785-1C8A-47FE-98C0-30C675A9328B}.Debug|x86.ActiveCfg = Debug|Any CPU - {4A6CA785-1C8A-47FE-98C0-30C675A9328B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4A6CA785-1C8A-47FE-98C0-30C675A9328B}.Release|Any CPU.Build.0 = Release|Any CPU - {4A6CA785-1C8A-47FE-98C0-30C675A9328B}.Release|ARM.ActiveCfg = Release|Any CPU - {4A6CA785-1C8A-47FE-98C0-30C675A9328B}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {4A6CA785-1C8A-47FE-98C0-30C675A9328B}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {4A6CA785-1C8A-47FE-98C0-30C675A9328B}.Release|x64.ActiveCfg = Release|Any CPU - {4A6CA785-1C8A-47FE-98C0-30C675A9328B}.Release|x86.ActiveCfg = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {94EE3919-19FA-4D9B-8DA9-249050B15232} = {2D6CAE62-D053-476F-9BDD-2B1F27FA9C5D} - {A6C3FFD3-16A5-44D3-8C1F-3613D6DD17D1} = {2D6CAE62-D053-476F-9BDD-2B1F27FA9C5D} - EndGlobalSection -EndGlobal diff --git a/src/Renci.SshNet.VS2015.sln b/src/Renci.SshNet.VS2015.sln deleted file mode 100644 index 81fa7155..00000000 --- a/src/Renci.SshNet.VS2015.sln +++ /dev/null @@ -1,130 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.25420.1 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{2D6CAE62-D053-476F-9BDD-2B1F27FA9C5D}" - ProjectSection(SolutionItems) = preProject - ..\build\build.proj = ..\build\build.proj - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "nuget", "nuget", "{94EE3919-19FA-4D9B-8DA9-249050B15232}" - ProjectSection(SolutionItems) = preProject - ..\build\nuget\SSH.NET.nuspec = ..\build\nuget\SSH.NET.nuspec - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "sandcastle", "sandcastle", "{A6C3FFD3-16A5-44D3-8C1F-3613D6DD17D1}" - ProjectSection(SolutionItems) = preProject - ..\build\sandcastle\SSH.NET.shfbproj = ..\build\sandcastle\SSH.NET.shfbproj - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Renci.SshNet.Silverlight5", "Renci.SshNet.Silverlight5\Renci.SshNet.Silverlight5.csproj", "{E367F791-C1EC-4181-912A-2943CAC6B3BC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Renci.SshNet.WindowsPhone8", "Renci.SshNet.WindowsPhone8\Renci.SshNet.WindowsPhone8.csproj", "{4A6CA785-1C8A-47FE-98C0-30C675A9328B}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Renci.SshNet.UAP10", "Renci.SshNet.UAP10\Renci.SshNet.UAP10.csproj", "{EC212E04-A372-4B95-B45B-C0D4A739EF80}" -EndProject -Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Renci.SshNet.Shared.Tests", "..\test\Renci.SshNet.Shared.Tests\Renci.SshNet.Shared.Tests.shproj", "{FAE3948F-A438-458E-8E0E-7F6E39A5DD8A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Renci.SshNet.WindowsPhone8.Tests", "..\test\Renci.SshNet.WindowsPhone8.Tests\Renci.SshNet.WindowsPhone8.Tests.csproj", "{26F0D644-B3EF-47DF-8040-E9E4B2E63884}" -EndProject -Global - GlobalSection(SharedMSBuildProjectFiles) = preSolution - ..\test\Renci.SshNet.Shared.Tests\Renci.SshNet.Shared.Tests.projitems*{26f0d644-b3ef-47df-8040-e9e4b2e63884}*SharedItemsImports = 4 - ..\test\Renci.SshNet.Shared.Tests\Renci.SshNet.Shared.Tests.projitems*{fae3948f-a438-458e-8e0e-7f6e39a5dd8a}*SharedItemsImports = 13 - EndGlobalSection - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Debug|ARM = Debug|ARM - Debug|Mixed Platforms = Debug|Mixed Platforms - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|Any CPU = Release|Any CPU - Release|ARM = Release|ARM - Release|Mixed Platforms = Release|Mixed Platforms - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {E367F791-C1EC-4181-912A-2943CAC6B3BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E367F791-C1EC-4181-912A-2943CAC6B3BC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E367F791-C1EC-4181-912A-2943CAC6B3BC}.Debug|ARM.ActiveCfg = Debug|Any CPU - {E367F791-C1EC-4181-912A-2943CAC6B3BC}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {E367F791-C1EC-4181-912A-2943CAC6B3BC}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {E367F791-C1EC-4181-912A-2943CAC6B3BC}.Debug|x64.ActiveCfg = Debug|Any CPU - {E367F791-C1EC-4181-912A-2943CAC6B3BC}.Debug|x86.ActiveCfg = Debug|Any CPU - {E367F791-C1EC-4181-912A-2943CAC6B3BC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E367F791-C1EC-4181-912A-2943CAC6B3BC}.Release|Any CPU.Build.0 = Release|Any CPU - {E367F791-C1EC-4181-912A-2943CAC6B3BC}.Release|ARM.ActiveCfg = Release|Any CPU - {E367F791-C1EC-4181-912A-2943CAC6B3BC}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {E367F791-C1EC-4181-912A-2943CAC6B3BC}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {E367F791-C1EC-4181-912A-2943CAC6B3BC}.Release|x64.ActiveCfg = Release|Any CPU - {E367F791-C1EC-4181-912A-2943CAC6B3BC}.Release|x86.ActiveCfg = Release|Any CPU - {4A6CA785-1C8A-47FE-98C0-30C675A9328B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4A6CA785-1C8A-47FE-98C0-30C675A9328B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4A6CA785-1C8A-47FE-98C0-30C675A9328B}.Debug|ARM.ActiveCfg = Debug|Any CPU - {4A6CA785-1C8A-47FE-98C0-30C675A9328B}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {4A6CA785-1C8A-47FE-98C0-30C675A9328B}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {4A6CA785-1C8A-47FE-98C0-30C675A9328B}.Debug|x64.ActiveCfg = Debug|Any CPU - {4A6CA785-1C8A-47FE-98C0-30C675A9328B}.Debug|x86.ActiveCfg = Debug|Any CPU - {4A6CA785-1C8A-47FE-98C0-30C675A9328B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4A6CA785-1C8A-47FE-98C0-30C675A9328B}.Release|Any CPU.Build.0 = Release|Any CPU - {4A6CA785-1C8A-47FE-98C0-30C675A9328B}.Release|ARM.ActiveCfg = Release|Any CPU - {4A6CA785-1C8A-47FE-98C0-30C675A9328B}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {4A6CA785-1C8A-47FE-98C0-30C675A9328B}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {4A6CA785-1C8A-47FE-98C0-30C675A9328B}.Release|x64.ActiveCfg = Release|Any CPU - {4A6CA785-1C8A-47FE-98C0-30C675A9328B}.Release|x86.ActiveCfg = Release|Any CPU - {EC212E04-A372-4B95-B45B-C0D4A739EF80}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EC212E04-A372-4B95-B45B-C0D4A739EF80}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EC212E04-A372-4B95-B45B-C0D4A739EF80}.Debug|ARM.ActiveCfg = Debug|ARM - {EC212E04-A372-4B95-B45B-C0D4A739EF80}.Debug|ARM.Build.0 = Debug|ARM - {EC212E04-A372-4B95-B45B-C0D4A739EF80}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 - {EC212E04-A372-4B95-B45B-C0D4A739EF80}.Debug|Mixed Platforms.Build.0 = Debug|x86 - {EC212E04-A372-4B95-B45B-C0D4A739EF80}.Debug|x64.ActiveCfg = Debug|x64 - {EC212E04-A372-4B95-B45B-C0D4A739EF80}.Debug|x64.Build.0 = Debug|x64 - {EC212E04-A372-4B95-B45B-C0D4A739EF80}.Debug|x86.ActiveCfg = Debug|x86 - {EC212E04-A372-4B95-B45B-C0D4A739EF80}.Debug|x86.Build.0 = Debug|x86 - {EC212E04-A372-4B95-B45B-C0D4A739EF80}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EC212E04-A372-4B95-B45B-C0D4A739EF80}.Release|Any CPU.Build.0 = Release|Any CPU - {EC212E04-A372-4B95-B45B-C0D4A739EF80}.Release|ARM.ActiveCfg = Release|ARM - {EC212E04-A372-4B95-B45B-C0D4A739EF80}.Release|ARM.Build.0 = Release|ARM - {EC212E04-A372-4B95-B45B-C0D4A739EF80}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {EC212E04-A372-4B95-B45B-C0D4A739EF80}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {EC212E04-A372-4B95-B45B-C0D4A739EF80}.Release|x64.ActiveCfg = Release|x64 - {EC212E04-A372-4B95-B45B-C0D4A739EF80}.Release|x64.Build.0 = Release|x64 - {EC212E04-A372-4B95-B45B-C0D4A739EF80}.Release|x86.ActiveCfg = Release|x86 - {EC212E04-A372-4B95-B45B-C0D4A739EF80}.Release|x86.Build.0 = Release|x86 - {26F0D644-B3EF-47DF-8040-E9E4B2E63884}.Debug|Any CPU.ActiveCfg = Debug|x86 - {26F0D644-B3EF-47DF-8040-E9E4B2E63884}.Debug|ARM.ActiveCfg = Debug|ARM - {26F0D644-B3EF-47DF-8040-E9E4B2E63884}.Debug|ARM.Build.0 = Debug|ARM - {26F0D644-B3EF-47DF-8040-E9E4B2E63884}.Debug|ARM.Deploy.0 = Debug|ARM - {26F0D644-B3EF-47DF-8040-E9E4B2E63884}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 - {26F0D644-B3EF-47DF-8040-E9E4B2E63884}.Debug|Mixed Platforms.Build.0 = Debug|x86 - {26F0D644-B3EF-47DF-8040-E9E4B2E63884}.Debug|Mixed Platforms.Deploy.0 = Debug|x86 - {26F0D644-B3EF-47DF-8040-E9E4B2E63884}.Debug|x64.ActiveCfg = Debug|x86 - {26F0D644-B3EF-47DF-8040-E9E4B2E63884}.Debug|x86.ActiveCfg = Debug|x86 - {26F0D644-B3EF-47DF-8040-E9E4B2E63884}.Debug|x86.Build.0 = Debug|x86 - {26F0D644-B3EF-47DF-8040-E9E4B2E63884}.Debug|x86.Deploy.0 = Debug|x86 - {26F0D644-B3EF-47DF-8040-E9E4B2E63884}.Release|Any CPU.ActiveCfg = Release|x86 - {26F0D644-B3EF-47DF-8040-E9E4B2E63884}.Release|ARM.ActiveCfg = Release|ARM - {26F0D644-B3EF-47DF-8040-E9E4B2E63884}.Release|ARM.Build.0 = Release|ARM - {26F0D644-B3EF-47DF-8040-E9E4B2E63884}.Release|ARM.Deploy.0 = Release|ARM - {26F0D644-B3EF-47DF-8040-E9E4B2E63884}.Release|Mixed Platforms.ActiveCfg = Release|x86 - {26F0D644-B3EF-47DF-8040-E9E4B2E63884}.Release|Mixed Platforms.Build.0 = Release|x86 - {26F0D644-B3EF-47DF-8040-E9E4B2E63884}.Release|Mixed Platforms.Deploy.0 = Release|x86 - {26F0D644-B3EF-47DF-8040-E9E4B2E63884}.Release|x64.ActiveCfg = Release|x86 - {26F0D644-B3EF-47DF-8040-E9E4B2E63884}.Release|x86.ActiveCfg = Release|x86 - {26F0D644-B3EF-47DF-8040-E9E4B2E63884}.Release|x86.Build.0 = Release|x86 - {26F0D644-B3EF-47DF-8040-E9E4B2E63884}.Release|x86.Deploy.0 = Release|x86 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {94EE3919-19FA-4D9B-8DA9-249050B15232} = {2D6CAE62-D053-476F-9BDD-2B1F27FA9C5D} - {A6C3FFD3-16A5-44D3-8C1F-3613D6DD17D1} = {2D6CAE62-D053-476F-9BDD-2B1F27FA9C5D} - EndGlobalSection - GlobalSection(TestCaseManagementSettings) = postSolution - CategoryFile = Renci.SshNet1.vsmdi - EndGlobalSection -EndGlobal diff --git a/src/Renci.SshNet.VS2015.sln.DotSettings b/src/Renci.SshNet.VS2015.sln.DotSettings deleted file mode 100644 index 15b1b217..00000000 --- a/src/Renci.SshNet.VS2015.sln.DotSettings +++ /dev/null @@ -1,22 +0,0 @@ - - DO_NOT_SHOW - DO_NOT_SHOW - DO_NOT_SHOW - DO_NOT_SHOW - DO_NOT_SHOW - SUGGESTION - WARNING - DO_NOT_SHOW - DO_NOT_SHOW - DO_NOT_SHOW - True - True - True - NEXT_LINE_SHIFTED_2 - CHOP_IF_LONG - HMACMD - HMACSHA - True - True - True - integration,LongRunning \ No newline at end of file diff --git a/src/Renci.SshNet.VS2017.sln b/src/Renci.SshNet.VS2017.sln deleted file mode 100644 index 9f1ce5e3..00000000 --- a/src/Renci.SshNet.VS2017.sln +++ /dev/null @@ -1,82 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26014.0 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{2D6CAE62-D053-476F-9BDD-2B1F27FA9C5D}" - ProjectSection(SolutionItems) = preProject - ..\build\build.cmd = ..\build\build.cmd - ..\build\build.proj = ..\build\build.proj - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "nuget", "nuget", "{94EE3919-19FA-4D9B-8DA9-249050B15232}" - ProjectSection(SolutionItems) = preProject - ..\build\nuget\SSH.NET.nuspec = ..\build\nuget\SSH.NET.nuspec - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "sandcastle", "sandcastle", "{A6C3FFD3-16A5-44D3-8C1F-3613D6DD17D1}" - ProjectSection(SolutionItems) = preProject - ..\build\sandcastle\SSH.NET.shfbproj = ..\build\sandcastle\SSH.NET.shfbproj - EndProjectSection -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Renci.SshNet", "Renci.SshNet\Renci.SshNet.csproj", "{2F5F8C90-0BD1-424F-997C-7BC6280919D1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Renci.SshNet.Tests", "Renci.SshNet.Tests\Renci.SshNet.Tests.csproj", "{C45379B9-17B1-4E89-BC2E-6D41726413E8}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Debug|ARM = Debug|ARM - Debug|Mixed Platforms = Debug|Mixed Platforms - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|Any CPU = Release|Any CPU - Release|ARM = Release|ARM - Release|Mixed Platforms = Release|Mixed Platforms - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|ARM.ActiveCfg = Debug|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|x64.ActiveCfg = Debug|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|x86.ActiveCfg = Debug|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|Any CPU.Build.0 = Release|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|ARM.ActiveCfg = Release|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|x64.ActiveCfg = Release|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|x86.ActiveCfg = Release|Any CPU - {C45379B9-17B1-4E89-BC2E-6D41726413E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C45379B9-17B1-4E89-BC2E-6D41726413E8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C45379B9-17B1-4E89-BC2E-6D41726413E8}.Debug|ARM.ActiveCfg = Debug|Any CPU - {C45379B9-17B1-4E89-BC2E-6D41726413E8}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {C45379B9-17B1-4E89-BC2E-6D41726413E8}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {C45379B9-17B1-4E89-BC2E-6D41726413E8}.Debug|x64.ActiveCfg = Debug|Any CPU - {C45379B9-17B1-4E89-BC2E-6D41726413E8}.Debug|x86.ActiveCfg = Debug|Any CPU - {C45379B9-17B1-4E89-BC2E-6D41726413E8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C45379B9-17B1-4E89-BC2E-6D41726413E8}.Release|Any CPU.Build.0 = Release|Any CPU - {C45379B9-17B1-4E89-BC2E-6D41726413E8}.Release|ARM.ActiveCfg = Release|Any CPU - {C45379B9-17B1-4E89-BC2E-6D41726413E8}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {C45379B9-17B1-4E89-BC2E-6D41726413E8}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {C45379B9-17B1-4E89-BC2E-6D41726413E8}.Release|x64.ActiveCfg = Release|Any CPU - {C45379B9-17B1-4E89-BC2E-6D41726413E8}.Release|x86.ActiveCfg = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {94EE3919-19FA-4D9B-8DA9-249050B15232} = {2D6CAE62-D053-476F-9BDD-2B1F27FA9C5D} - {A6C3FFD3-16A5-44D3-8C1F-3613D6DD17D1} = {2D6CAE62-D053-476F-9BDD-2B1F27FA9C5D} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {C3D130B3-A070-4B12-A10F-E3E44D6ACEE2} - EndGlobalSection - GlobalSection(TestCaseManagementSettings) = postSolution - CategoryFile = Renci.SshNet1.vsmdi - EndGlobalSection -EndGlobal diff --git a/src/Renci.SshNet.VS2019.sln b/src/Renci.SshNet.VS2019.sln deleted file mode 100644 index bc1f6f6d..00000000 --- a/src/Renci.SshNet.VS2019.sln +++ /dev/null @@ -1,82 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.29521.150 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{2D6CAE62-D053-476F-9BDD-2B1F27FA9C5D}" - ProjectSection(SolutionItems) = preProject - ..\build\build.cmd = ..\build\build.cmd - ..\build\build.proj = ..\build\build.proj - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "nuget", "nuget", "{94EE3919-19FA-4D9B-8DA9-249050B15232}" - ProjectSection(SolutionItems) = preProject - ..\build\nuget\SSH.NET.nuspec = ..\build\nuget\SSH.NET.nuspec - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "sandcastle", "sandcastle", "{A6C3FFD3-16A5-44D3-8C1F-3613D6DD17D1}" - ProjectSection(SolutionItems) = preProject - ..\build\sandcastle\SSH.NET.shfbproj = ..\build\sandcastle\SSH.NET.shfbproj - EndProjectSection -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Renci.SshNet", "Renci.SshNet\Renci.SshNet.csproj", "{2F5F8C90-0BD1-424F-997C-7BC6280919D1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Renci.SshNet.Tests", "Renci.SshNet.Tests\Renci.SshNet.Tests.csproj", "{C45379B9-17B1-4E89-BC2E-6D41726413E8}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Debug|ARM = Debug|ARM - Debug|Mixed Platforms = Debug|Mixed Platforms - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|Any CPU = Release|Any CPU - Release|ARM = Release|ARM - Release|Mixed Platforms = Release|Mixed Platforms - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|ARM.ActiveCfg = Debug|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|x64.ActiveCfg = Debug|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|x86.ActiveCfg = Debug|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|Any CPU.Build.0 = Release|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|ARM.ActiveCfg = Release|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|x64.ActiveCfg = Release|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|x86.ActiveCfg = Release|Any CPU - {C45379B9-17B1-4E89-BC2E-6D41726413E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C45379B9-17B1-4E89-BC2E-6D41726413E8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C45379B9-17B1-4E89-BC2E-6D41726413E8}.Debug|ARM.ActiveCfg = Debug|Any CPU - {C45379B9-17B1-4E89-BC2E-6D41726413E8}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {C45379B9-17B1-4E89-BC2E-6D41726413E8}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {C45379B9-17B1-4E89-BC2E-6D41726413E8}.Debug|x64.ActiveCfg = Debug|Any CPU - {C45379B9-17B1-4E89-BC2E-6D41726413E8}.Debug|x86.ActiveCfg = Debug|Any CPU - {C45379B9-17B1-4E89-BC2E-6D41726413E8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C45379B9-17B1-4E89-BC2E-6D41726413E8}.Release|Any CPU.Build.0 = Release|Any CPU - {C45379B9-17B1-4E89-BC2E-6D41726413E8}.Release|ARM.ActiveCfg = Release|Any CPU - {C45379B9-17B1-4E89-BC2E-6D41726413E8}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {C45379B9-17B1-4E89-BC2E-6D41726413E8}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {C45379B9-17B1-4E89-BC2E-6D41726413E8}.Release|x64.ActiveCfg = Release|Any CPU - {C45379B9-17B1-4E89-BC2E-6D41726413E8}.Release|x86.ActiveCfg = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {94EE3919-19FA-4D9B-8DA9-249050B15232} = {2D6CAE62-D053-476F-9BDD-2B1F27FA9C5D} - {A6C3FFD3-16A5-44D3-8C1F-3613D6DD17D1} = {2D6CAE62-D053-476F-9BDD-2B1F27FA9C5D} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {BAD6019D-4AF7-4E15-99A0-8036E16FC0E5} - EndGlobalSection - GlobalSection(TestCaseManagementSettings) = postSolution - CategoryFile = Renci.SshNet1.vsmdi - EndGlobalSection -EndGlobal diff --git a/src/Renci.SshNet.WindowsPhone/Properties/AssemblyInfo.cs b/src/Renci.SshNet.WindowsPhone/Properties/AssemblyInfo.cs deleted file mode 100644 index f61fb134..00000000 --- a/src/Renci.SshNet.WindowsPhone/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,5 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -[assembly: AssemblyTitle("SSH.NET Windows Phone 7.1")] -[assembly: Guid("b044a9d9-fe40-4d7e-b198-c142ab9721f0")] diff --git a/src/Renci.SshNet.WindowsPhone/Renci.SshNet.WindowsPhone.csproj b/src/Renci.SshNet.WindowsPhone/Renci.SshNet.WindowsPhone.csproj deleted file mode 100644 index 317dbc84..00000000 --- a/src/Renci.SshNet.WindowsPhone/Renci.SshNet.WindowsPhone.csproj +++ /dev/null @@ -1,1432 +0,0 @@ - - - - Debug - AnyCPU - 10.0.20506 - 2.0 - {3AD3EDF0-702E-4A91-8735-DCE4659AA54C} - {C089C8C0-30E0-4E22-80C0-CE093F111A43};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} - Library - Properties - Renci.SshNet - Renci.SshNet - v4.0 - $(TargetFrameworkVersion) - WindowsPhone71 - Silverlight - false - true - true - - - true - full - false - Bin\Debug - TRACE;DEBUG;FEATURE_DEVICEINFORMATION_APM;FEATURE_MEMORYSTREAM_GETBUFFER;FEATURE_REGEX_COMPILE;FEATURE_RNG_CSP;FEATURE_SOCKET_EAP;FEATURE_SOCKET_DISPOSE;FEATURE_STREAM_APM;FEATURE_DEVICEINFORMATION_APM;FEATURE_THREAD_THREADPOOL;FEATURE_THREAD_SLEEP;FEATURE_WAITHANDLE_DISPOSE;FEATURE_HASH_SHA1;FEATURE_HASH_SHA256;FEATURE_HMAC_SHA1;FEATURE_HMAC_SHA256 - true - true - prompt - 4 - Bin\Debug\Renci.SshNet.xml - - - none - true - Bin\Release - TRACE;FEATURE_DEVICEINFORMATION_APM;FEATURE_MEMORYSTREAM_GETBUFFER;FEATURE_REGEX_COMPILE;FEATURE_RNG_CSP;FEATURE_SOCKET_EAP;FEATURE_SOCKET_DISPOSE;FEATURE_STREAM_APM;FEATURE_DEVICEINFORMATION_APM;FEATURE_THREAD_THREADPOOL;FEATURE_THREAD_SLEEP;FEATURE_WAITHANDLE_DISPOSE;FEATURE_HASH_SHA1;FEATURE_HASH_SHA256;FEATURE_HMAC_SHA1;FEATURE_HMAC_SHA256 - true - true - prompt - 4 - Bin\Release\Renci.SshNet.xml - 1591 - - - - - False - ..\..\packages\SshNet.Security.Cryptography.1.2.0\lib\wp71\SshNet.Security.Cryptography.dll - - - - - - - Abstractions\CryptoAbstraction.cs - - - Abstractions\DiagnosticAbstraction.cs - - - Abstractions\DnsAbstraction.cs - - - Abstractions\FileSystemAbstraction.cs - - - Abstractions\ReflectionAbstraction.cs - - - Abstractions\SocketAbstraction.cs - - - Abstractions\ThreadAbstraction.cs - - - AuthenticationMethod.cs - - - AuthenticationResult.cs - - - BaseClient.cs - - - Channels\Channel.cs - - - Channels\ChannelDirectTcpip.cs - - - Channels\ChannelForwardedTcpip.cs - - - Channels\ChannelSession.cs - - - Channels\ChannelTypes.cs - - - Channels\ClientChannel.cs - - - Channels\IChannel.cs - - - Channels\IChannelDirectTcpip.cs - - - Channels\IChannelForwardedTcpip.cs - - - Channels\IChannelSession.cs - - - Channels\ServerChannel.cs - - - CipherInfo.cs - - - ClientAuthentication.cs - - - CommandAsyncResult.cs - - - Common\Array.cs - - - Common\ASCIIEncoding.cs - - - Common\AsyncResult.cs - - - Common\AuthenticationBannerEventArgs.cs - - - Common\AuthenticationEventArgs.cs - - - Common\AuthenticationPasswordChangeEventArgs.cs - - - Common\AuthenticationPrompt.cs - - - Common\AuthenticationPromptEventArgs.cs - - - Common\BigInteger.cs - - - Common\ChannelDataEventArgs.cs - - - Common\ChannelEventArgs.cs - - - Common\ChannelExtendedDataEventArgs.cs - - - Common\ChannelOpenConfirmedEventArgs.cs - - - Common\ChannelOpenFailedEventArgs.cs - - - Common\ChannelRequestEventArgs.cs - - - Common\CountdownEvent.cs - - - Common\DerData.cs - - - Common\ExceptionEventArgs.cs - - - Common\Extensions.cs - - - Common\HostKeyEventArgs.cs - - - Common\ObjectIdentifier.cs - - - Common\Pack.cs - - - Common\PacketDump.cs - - - Common\PipeStream.cs - - - Common\PortForwardEventArgs.cs - - - Common\PosixPath.cs - - - Common\ProxyException.cs - - - Common\ScpDownloadEventArgs.cs - - - Common\ScpException.cs - - - Common\ScpUploadEventArgs.cs - - - Common\SemaphoreLight.cs - - - Common\SftpPathNotFoundException.cs - - - Common\SftpPermissionDeniedException.cs - - - Common\ShellDataEventArgs.cs - - - Common\SshAuthenticationException.cs - - - Common\SshConnectionException.cs - - - Common\SshData.cs - - - Common\SshDataStream.cs - - - Common\SshException.cs - - - Common\SshOperationTimeoutException.cs - - - Common\SshPassPhraseNullOrEmptyException.cs - - - Common\TerminalModes.cs - - - Compression\CompressionMode.cs - - - Compression\Compressor.cs - - - Compression\Zlib.cs - - - Compression\ZlibOpenSsh.cs - - - Compression\ZlibStream.cs - - - ConnectionInfo.cs - - - Connection\ConnectorBase.cs - - - Connection\DirectConnector.cs - - - Connection\HttpConnector.cs - - - Connection\IConnector.cs - - - Connection\IProtocolVersionExchange.cs - - - Connection\ISocketFactory.cs - - - Connection\ProtocolVersionExchange.cs - - - Connection\SocketFactory.cs - - - Connection\Socks4Connector.cs - - - Connection\Socks5Connector.cs - - - Connection\SshIdentification.cs - - - ExpectAction.cs - - - ExpectAsyncResult.cs - - - ForwardedPort.cs - - - ForwardedPortDynamic.cs - - - ForwardedPortLocal.cs - - - ForwardedPortRemote.cs - - - ForwardedPortStatus.cs - - - HashInfo.cs - - - IAuthenticationMethod.cs - - - IClientAuthentication.cs - - - IConnectionInfo.cs - - - IForwardedPort.cs - - - IRemotePathTransformation.cs - - - IServiceFactory.cs - - - ISession.cs - - - ISubsystemSession.cs - - - KeyboardInteractiveAuthenticationMethod.cs - - - KeyboardInteractiveConnectionInfo.cs - - - MessageEventArgs.cs - - - Messages\Authentication\BannerMessage.cs - - - Messages\Authentication\FailureMessage.cs - - - Messages\Authentication\InformationRequestMessage.cs - - - Messages\Authentication\InformationResponseMessage.cs - - - Messages\Authentication\PasswordChangeRequiredMessage.cs - - - Messages\Authentication\PublicKeyMessage.cs - - - Messages\Authentication\RequestMessage.cs - - - Messages\Authentication\RequestMessageHost.cs - - - Messages\Authentication\RequestMessageKeyboardInteractive.cs - - - Messages\Authentication\RequestMessageNone.cs - - - Messages\Authentication\RequestMessagePassword.cs - - - Messages\Authentication\RequestMessagePublicKey.cs - - - Messages\Authentication\SuccessMessage.cs - - - Messages\Connection\CancelTcpIpForwardGlobalRequestMessage.cs - - - Messages\Connection\ChannelCloseMessage.cs - - - Messages\Connection\ChannelDataMessage.cs - - - Messages\Connection\ChannelEofMessage.cs - - - Messages\Connection\ChannelExtendedDataMessage.cs - - - Messages\Connection\ChannelFailureMessage.cs - - - Messages\Connection\ChannelMessage.cs - - - Messages\Connection\ChannelOpenConfirmationMessage.cs - - - Messages\Connection\ChannelOpenFailureMessage.cs - - - Messages\Connection\ChannelOpenFailureReasons.cs - - - Messages\Connection\ChannelOpen\ChannelOpenInfo.cs - - - Messages\Connection\ChannelOpen\ChannelOpenMessage.cs - - - Messages\Connection\ChannelOpen\DirectTcpipChannelInfo.cs - - - Messages\Connection\ChannelOpen\ForwardedTcpipChannelInfo.cs - - - Messages\Connection\ChannelOpen\SessionChannelOpenInfo.cs - - - Messages\Connection\ChannelOpen\X11ChannelOpenInfo.cs - - - Messages\Connection\ChannelRequest\BreakRequestInfo.cs - - - Messages\Connection\ChannelRequest\ChannelRequestMessage.cs - - - Messages\Connection\ChannelRequest\EndOfWriteRequestInfo.cs - - - Messages\Connection\ChannelRequest\EnvironmentVariableRequestInfo.cs - - - Messages\Connection\ChannelRequest\ExecRequestInfo.cs - - - Messages\Connection\ChannelRequest\ExitSignalRequestInfo.cs - - - Messages\Connection\ChannelRequest\ExitStatusRequestInfo.cs - - - Messages\Connection\ChannelRequest\KeepAliveRequestInfo.cs - - - Messages\Connection\ChannelRequest\PseudoTerminalInfo.cs - - - Messages\Connection\ChannelRequest\RequestInfo.cs - - - Messages\Connection\ChannelRequest\ShellRequestInfo.cs - - - Messages\Connection\ChannelRequest\SignalRequestInfo.cs - - - Messages\Connection\ChannelRequest\SubsystemRequestInfo.cs - - - Messages\Connection\ChannelRequest\WindowChangeRequestInfo.cs - - - Messages\Connection\ChannelRequest\X11ForwardingRequestInfo.cs - - - Messages\Connection\ChannelRequest\XonXoffRequestInfo.cs - - - Messages\Connection\ChannelSuccessMessage.cs - - - Messages\Connection\ChannelWindowAdjustMessage.cs - - - Messages\Connection\GlobalRequestMessage.cs - - - Messages\Connection\GlobalRequestName.cs - - - Messages\Connection\RequestFailureMessage.cs - - - Messages\Connection\RequestSuccessMessage.cs - - - Messages\Connection\TcpIpForwardGlobalRequestMessage.cs - - - Messages\Message.cs - - - Messages\MessageAttribute.cs - - - Messages\ServiceName.cs - - - Messages\Transport\DebugMessage.cs - - - Messages\Transport\DisconnectMessage.cs - - - Messages\Transport\DisconnectReason.cs - - - Messages\Transport\IgnoreMessage.cs - - - Messages\Transport\IKeyExchangedAllowed.cs - - - Messages\Transport\KeyExchangeDhGroupExchangeGroup.cs - - - Messages\Transport\KeyExchangeDhGroupExchangeInit.cs - - - Messages\Transport\KeyExchangeDhGroupExchangeReply.cs - - - Messages\Transport\KeyExchangeDhGroupExchangeRequest.cs - - - Messages\Transport\KeyExchangeDhInitMessage.cs - - - Messages\Transport\KeyExchangeDhReplyMessage.cs - - - Messages\Transport\KeyExchangeEcdhInitMessage.cs - - - Messages\Transport\KeyExchangeEcdhReplyMessage.cs - - - Messages\Transport\KeyExchangeInitMessage.cs - - - Messages\Transport\NewKeysMessage.cs - - - Messages\Transport\ServiceAcceptMessage.cs - - - Messages\Transport\ServiceRequestMessage.cs - - - Messages\Transport\UnimplementedMessage.cs - - - NoneAuthenticationMethod.cs - - - PasswordAuthenticationMethod.cs - - - PasswordConnectionInfo.cs - - - PrivateKeyAuthenticationMethod.cs - - - PrivateKeyConnectionInfo.cs - - - PrivateKeyFile.cs - - - ProxyTypes.cs - - - RemotePathDoubleQuoteTransformation.cs - - - RemotePathNoneTransformation.cs - - - RemotePathShellQuoteTransformation.cs - - - RemotePathTransformation.cs - - - ScpClient.cs - - - Security\Algorithm.cs - - - Security\Cryptography\BouncyCastle\asn1\sec\SECNamedCurves.cs - - - Security\Cryptography\BouncyCastle\asn1\x9\X9Curve.cs - - - Security\Cryptography\BouncyCastle\asn1\x9\X9ECParameters.cs - - - Security\Cryptography\BouncyCastle\asn1\x9\X9ECParametersHolder.cs - - - Security\Cryptography\BouncyCastle\asn1\x9\X9ECPoint.cs - - - Security\Cryptography\BouncyCastle\crypto\agreement\ECDHCBasicAgreement.cs - - - Security\Cryptography\BouncyCastle\crypto\AsymmetricCipherKeyPair.cs - - - Security\Cryptography\BouncyCastle\crypto\AsymmetricKeyParameter.cs - - - Security\Cryptography\BouncyCastle\crypto\digests\GeneralDigest.cs - - - Security\Cryptography\BouncyCastle\crypto\digests\Sha256Digest.cs - - - Security\Cryptography\BouncyCastle\crypto\generators\ECKeyPairGenerator.cs - - - Security\Cryptography\BouncyCastle\crypto\IAsymmetricCipherKeyPairGenerator.cs - - - Security\Cryptography\BouncyCastle\crypto\IDigest.cs - - - Security\Cryptography\BouncyCastle\crypto\KeyGenerationParameters.cs - - - Security\Cryptography\BouncyCastle\crypto\parameters\ECDomainParameters.cs - - - Security\Cryptography\BouncyCastle\crypto\parameters\ECKeyGenerationParameters.cs - - - Security\Cryptography\BouncyCastle\crypto\parameters\ECKeyParameters.cs - - - Security\Cryptography\BouncyCastle\crypto\parameters\ECPrivateKeyParameters.cs - - - Security\Cryptography\BouncyCastle\crypto\parameters\ECPublicKeyParameters.cs - - - Security\Cryptography\BouncyCastle\crypto\prng\CryptoApiRandomGenerator.cs - - - Security\Cryptography\BouncyCastle\crypto\prng\DigestRandomGenerator.cs - - - Security\Cryptography\BouncyCastle\crypto\prng\IRandomGenerator.cs - - - Security\Cryptography\BouncyCastle\crypto\util\Pack.cs - - - Security\Cryptography\BouncyCastle\math\BigInteger.cs - - - Security\Cryptography\BouncyCastle\math\ec\abc\SimpleBigDecimal.cs - - - Security\Cryptography\BouncyCastle\math\ec\abc\Tnaf.cs - - - Security\Cryptography\BouncyCastle\math\ec\abc\ZTauElement.cs - - - Security\Cryptography\BouncyCastle\math\ec\ECAlgorithms.cs - - - Security\Cryptography\BouncyCastle\math\ec\ECCurve.cs - - - Security\Cryptography\BouncyCastle\math\ec\ECFieldElement.cs - - - Security\Cryptography\BouncyCastle\math\ec\ECLookupTable.cs - - - Security\Cryptography\BouncyCastle\math\ec\ECPoint.cs - - - Security\Cryptography\BouncyCastle\math\ec\ECPointMap.cs - - - Security\Cryptography\BouncyCastle\math\ec\endo\ECEndomorphism.cs - - - Security\Cryptography\BouncyCastle\math\ec\endo\GlvEndomorphism.cs - - - Security\Cryptography\BouncyCastle\math\ec\LongArray.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\AbstractECMultiplier.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\ECMultiplier.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\FixedPointCombMultiplier.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\FixedPointPreCompInfo.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\FixedPointUtilities.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\GlvMultiplier.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\IPreCompCallback.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\PreCompInfo.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\ValidityPreCompInfo.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\WNafL2RMultiplier.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\WNafPreCompInfo.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\WNafUtilities.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\WTauNafMultiplier.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\WTauNafPreCompInfo.cs - - - Security\Cryptography\BouncyCastle\math\field\FiniteFields.cs - - - Security\Cryptography\BouncyCastle\math\field\GenericPolynomialExtensionField.cs - - - Security\Cryptography\BouncyCastle\math\field\GF2Polynomial.cs - - - Security\Cryptography\BouncyCastle\math\field\IExtensionField.cs - - - Security\Cryptography\BouncyCastle\math\field\IFiniteField.cs - - - Security\Cryptography\BouncyCastle\math\field\IPolynomial.cs - - - Security\Cryptography\BouncyCastle\math\field\IPolynomialExtensionField.cs - - - Security\Cryptography\BouncyCastle\math\field\PrimeField.cs - - - Security\Cryptography\BouncyCastle\math\raw\Mod.cs - - - Security\Cryptography\BouncyCastle\math\raw\Nat.cs - - - Security\Cryptography\BouncyCastle\security\DigestUtilities.cs - - - Security\Cryptography\BouncyCastle\security\SecureRandom.cs - - - Security\Cryptography\BouncyCastle\security\SecurityUtilityException.cs - - - Security\Cryptography\BouncyCastle\util\Arrays.cs - - - Security\Cryptography\BouncyCastle\util\BigIntegers.cs - - - Security\Cryptography\BouncyCastle\util\encoders\Hex.cs - - - Security\Cryptography\BouncyCastle\util\encoders\HexEncoder.cs - - - Security\Cryptography\BouncyCastle\util\IMemoable.cs - - - Security\Cryptography\BouncyCastle\util\Integers.cs - - - Security\Cryptography\BouncyCastle\util\MemoableResetException.cs - - - Security\Cryptography\BouncyCastle\util\Times.cs - - - Security\CertificateHostAlgorithm.cs - - - Security\Cryptography\Chaos.NaCl\CryptoBytes.cs - - - Security\Cryptography\Chaos.NaCl\Ed25519.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Array16.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Array8.cs - - - Security\Cryptography\Chaos.NaCl\Internal\ByteIntegerConverter.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\base.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\base2.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\d.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\d2.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_0.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_1.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_add.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_cmov.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_cswap.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_frombytes.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_invert.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_isnegative.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_isnonzero.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_mul.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_mul121666.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_neg.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_pow22523.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_sq.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_sq2.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_sub.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_tobytes.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\FieldElement.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_add.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_double_scalarmult.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_frombytes.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_madd.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_msub.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p1p1_to_p2.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p1p1_to_p3.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p2_0.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p2_dbl.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p3_0.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p3_dbl.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p3_tobytes.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p3_to_cached.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p3_to_p2.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_precomp_0.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_scalarmult_base.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_sub.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_tobytes.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\GroupElement.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\keypair.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\open.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\scalarmult.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\sc_clamp.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\sc_mul_add.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\sc_reduce.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\sign.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\sqrtm1.cs - - - Security\Cryptography\Chaos.NaCl\Internal\InternalAssert.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Poly1305Donna.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Salsa\Salsa20.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Salsa\SalsaCore.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Sha512Internal.cs - - - Security\Cryptography\Chaos.NaCl\MontgomeryCurve25519.cs - - - Security\Cryptography\Chaos.NaCl\Sha512.cs - - - Security\Cryptography\AsymmetricCipher.cs - - - Security\Cryptography\Bcrypt.cs - - - Security\Cryptography\BlockCipher.cs - - - Security\Cryptography\Cipher.cs - - - Security\Cryptography\CipherDigitalSignature.cs - - - Security\Cryptography\Ciphers\AesCipher.cs - - - Security\Cryptography\Ciphers\Arc4Cipher.cs - - - Security\Cryptography\Ciphers\BlowfishCipher.cs - - - Security\Cryptography\Ciphers\CastCipher.cs - - - Security\Cryptography\Ciphers\CipherMode.cs - - - Security\Cryptography\Ciphers\CipherPadding.cs - - - Security\Cryptography\Ciphers\DesCipher.cs - - - Security\Cryptography\Ciphers\Modes\CbcCipherMode.cs - - - Security\Cryptography\Ciphers\Modes\CfbCipherMode.cs - - - Security\Cryptography\Ciphers\Modes\CtrCipherMode.cs - - - Security\Cryptography\Ciphers\Modes\OfbCipherMode.cs - - - Security\Cryptography\Ciphers\Paddings\PKCS7Padding.cs - - - Security\Cryptography\Ciphers\RsaCipher.cs - - - Security\Cryptography\Ciphers\SerpentCipher.cs - - - Security\Cryptography\Ciphers\TripleDesCipher.cs - - - Security\Cryptography\Ciphers\TwofishCipher.cs - - - Security\Cryptography\DigitalSignature.cs - - - Security\Cryptography\DsaDigitalSignature.cs - - - Security\Cryptography\DsaKey.cs - - - Security\Cryptography\ED25519DigitalSignature.cs - - - Security\Cryptography\ED25519Key.cs - - - Security\Cryptography\HMACMD5.cs - - - Security\Cryptography\HMACSHA1.cs - - - Security\Cryptography\HMACSHA256.cs - - - Security\Cryptography\HMACSHA384.cs - - - Security\Cryptography\HMACSHA512.cs - - - Security\Cryptography\Key.cs - - - Security\Cryptography\RsaDigitalSignature.cs - - - Security\Cryptography\RsaKey.cs - - - Security\Cryptography\StreamCipher.cs - - - Security\Cryptography\SymmetricCipher.cs - - - Security\GroupExchangeHashData.cs - - - Security\HostAlgorithm.cs - - - Security\IKeyExchange.cs - - - Security\KeyExchange.cs - - - Security\KeyExchangeDiffieHellman.cs - - - Security\KeyExchangeDiffieHellmanGroup14Sha1.cs - - - Security\KeyExchangeDiffieHellmanGroup14Sha256.cs - - - Security\KeyExchangeDiffieHellmanGroup16Sha512.cs - - - Security\KeyExchangeDiffieHellmanGroup1Sha1.cs - - - Security\KeyExchangeDiffieHellmanGroupExchangeSha1.cs - - - Security\KeyExchangeDiffieHellmanGroupExchangeSha256.cs - - - Security\KeyExchangeDiffieHellmanGroupExchangeShaBase.cs - - - Security\KeyExchangeDiffieHellmanGroupSha1.cs - - - Security\KeyExchangeDiffieHellmanGroupSha256.cs - - - Security\KeyExchangeDiffieHellmanGroupSha512.cs - - - Security\KeyExchangeDiffieHellmanGroupShaBase.cs - - - Security\KeyExchangeEC.cs - - - Security\KeyExchangeECCurve25519.cs - - - Security\KeyExchangeECDH.cs - - - Security\KeyExchangeECDH256.cs - - - Security\KeyExchangeECDH384.cs - - - Security\KeyExchangeECDH521.cs - - - Security\KeyExchangeHash.cs - - - Security\KeyHostAlgorithm.cs - - - ServiceFactory.cs - - - Session.cs - - - SftpClient.cs - - - ISftpClient.cs - - - Sftp\Flags.cs - - - Sftp\ISftpFileReader.cs - - - Sftp\ISftpResponseFactory.cs - - - Sftp\ISftpSession.cs - - - Sftp\Requests\ExtendedRequests\FStatVfsRequest.cs - - - Sftp\Requests\ExtendedRequests\HardLinkRequest.cs - - - Sftp\Requests\ExtendedRequests\PosixRenameRequest.cs - - - Sftp\Requests\ExtendedRequests\StatVfsRequest.cs - - - Sftp\Requests\SftpBlockRequest.cs - - - Sftp\Requests\SftpCloseRequest.cs - - - Sftp\Requests\SftpExtendedRequest.cs - - - Sftp\Requests\SftpFSetStatRequest.cs - - - Sftp\Requests\SftpFStatRequest.cs - - - Sftp\Requests\SftpInitRequest.cs - - - Sftp\Requests\SftpLinkRequest.cs - - - Sftp\Requests\SftpLStatRequest.cs - - - Sftp\Requests\SftpMkDirRequest.cs - - - Sftp\Requests\SftpOpenDirRequest.cs - - - Sftp\Requests\SftpOpenRequest.cs - - - Sftp\Requests\SftpReadDirRequest.cs - - - Sftp\Requests\SftpReadLinkRequest.cs - - - Sftp\Requests\SftpReadRequest.cs - - - Sftp\Requests\SftpRealPathRequest.cs - - - Sftp\Requests\SftpRemoveRequest.cs - - - Sftp\Requests\SftpRenameRequest.cs - - - Sftp\Requests\SftpRequest.cs - - - Sftp\Requests\SftpRmDirRequest.cs - - - Sftp\Requests\SftpSetStatRequest.cs - - - Sftp\Requests\SftpStatRequest.cs - - - Sftp\Requests\SftpSymLinkRequest.cs - - - Sftp\Requests\SftpUnblockRequest.cs - - - Sftp\Requests\SftpWriteRequest.cs - - - Sftp\Responses\ExtendedReplies\ExtendedReplyInfo.cs - - - Sftp\Responses\ExtendedReplies\StatVfsReplyInfo.cs - - - Sftp\Responses\SftpAttrsResponse.cs - - - Sftp\Responses\SftpDataResponse.cs - - - Sftp\Responses\SftpExtendedReplyResponse.cs - - - Sftp\Responses\SftpHandleResponse.cs - - - Sftp\Responses\SftpNameResponse.cs - - - Sftp\Responses\SftpResponse.cs - - - Sftp\Responses\SftpStatusResponse.cs - - - Sftp\Responses\SftpVersionResponse.cs - - - Sftp\SftpCloseAsyncResult.cs - - - Sftp\SftpDownloadAsyncResult.cs - - - Sftp\SftpFile.cs - - - Sftp\SftpFileAttributes.cs - - - Sftp\SftpFileReader.cs - - - Sftp\SftpFileStream.cs - - - Sftp\SftpFileSystemInformation.cs - - - Sftp\SftpListDirectoryAsyncResult.cs - - - Sftp\SftpMessage.cs - - - Sftp\SftpMessageTypes.cs - - - Sftp\SftpOpenAsyncResult.cs - - - Sftp\SftpReadAsyncResult.cs - - - Sftp\SftpRealPathAsyncResult.cs - - - Sftp\SftpResponseFactory.cs - - - Sftp\SftpSession.cs - - - Sftp\SFtpStatAsyncResult.cs - - - Sftp\SftpSynchronizeDirectoriesAsyncResult.cs - - - Sftp\SftpUploadAsyncResult.cs - - - Sftp\StatusCodes.cs - - - Shell.cs - - - ShellStream.cs - - - SshClient.cs - - - SshCommand.cs - - - SshMessageFactory.cs - - - SubsystemSession.cs - - - - Properties\CommonAssemblyInfo.cs - - - - - Designer - - - - - - - \ No newline at end of file diff --git a/src/Renci.SshNet.WindowsPhone/packages.config b/src/Renci.SshNet.WindowsPhone/packages.config deleted file mode 100644 index a9e0cd0e..00000000 --- a/src/Renci.SshNet.WindowsPhone/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/src/Renci.SshNet.WindowsPhone8/Properties/AssemblyInfo.cs b/src/Renci.SshNet.WindowsPhone8/Properties/AssemblyInfo.cs deleted file mode 100644 index 3db5746c..00000000 --- a/src/Renci.SshNet.WindowsPhone8/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -[assembly: AssemblyTitle("SSH.NET Windows Phone 8.0")] -[assembly: Guid("b044a9d9-fe40-4d7e-b198-c142ab9721f0")] - -[assembly: InternalsVisibleTo("Renci.SshNet.Tests")] \ No newline at end of file diff --git a/src/Renci.SshNet.WindowsPhone8/Renci.SshNet.WindowsPhone8.csproj b/src/Renci.SshNet.WindowsPhone8/Renci.SshNet.WindowsPhone8.csproj deleted file mode 100644 index f34c1d8e..00000000 --- a/src/Renci.SshNet.WindowsPhone8/Renci.SshNet.WindowsPhone8.csproj +++ /dev/null @@ -1,1493 +0,0 @@ - - - - Debug - AnyCPU - 10.0.20506 - 2.0 - {4A6CA785-1C8A-47FE-98C0-30C675A9328B} - {C089C8C0-30E0-4E22-80C0-CE093F111A43};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} - Library - Properties - Renci.SshNet - Renci.SshNet - v8.0 - WindowsPhone - false - true - true - 11.0 - - - true - full - false - Bin\Debug - TRACE;DEBUG;FEATURE_REGEX_COMPILE;FEATURE_RNG_CSP;FEATURE_SOCKET_EAP;FEATURE_SOCKET_DISPOSE;FEATURE_STREAM_APM;FEATURE_STREAM_TAP;FEATURE_DEVICEINFORMATION_APM;FEATURE_THREAD_COUNTDOWNEVENT;FEATURE_THREAD_THREADPOOL;FEATURE_THREAD_SLEEP;FEATURE_WAITHANDLE_DISPOSE;FEATURE_MEMORYSTREAM_GETBUFFER;FEATURE_HASH_SHA1_MANAGED;FEATURE_HASH_SHA256_MANAGED;FEATURE_HMAC_SHA1;FEATURE_HMAC_SHA256 - true - true - prompt - 4 - false - Bin\Debug\Renci.SshNet.xml - true - - - none - true - Bin\Release - TRACE;FEATURE_REGEX_COMPILE;FEATURE_RNG_CSP;FEATURE_SOCKET_EAP;FEATURE_SOCKET_DISPOSE;FEATURE_STREAM_APM;FEATURE_STREAM_TAP;FEATURE_DEVICEINFORMATION_APM;FEATURE_THREAD_COUNTDOWNEVENT;FEATURE_THREAD_THREADPOOL;FEATURE_THREAD_SLEEP;FEATURE_WAITHANDLE_DISPOSE;FEATURE_MEMORYSTREAM_GETBUFFER;FEATURE_HASH_SHA1_MANAGED;FEATURE_HASH_SHA256_MANAGED;FEATURE_HMAC_SHA1;FEATURE_HMAC_SHA256 - true - true - prompt - 4 - false - Bin\Release\Renci.SshNet.xml - - - true - - - true - Bin\x86\Debug - TRACE;DEBUG;FEATURE_REGEX_COMPILE;FEATURE_RNG_CSP;FEATURE_SOCKET_EAP;FEATURE_SOCKET_DISPOSE;FEATURE_STREAM_APM;FEATURE_STREAM_TAP;FEATURE_DEVICEINFORMATION_APM;FEATURE_THREAD_COUNTDOWNEVENT;FEATURE_THREAD_THREADPOOL;FEATURE_THREAD_SLEEP;FEATURE_WAITHANDLE_DISPOSE;FEATURE_MEMORYSTREAM_GETBUFFER;FEATURE_HASH_SHA1_MANAGED;FEATURE_HASH_SHA256_MANAGED;FEATURE_HMAC_SHA1;FEATURE_HMAC_SHA256 - true - full - - - prompt - MinimumRecommendedRules.ruleset - false - - - Bin\x86\Release - TRACE;FEATURE_REGEX_COMPILE;FEATURE_RNG_CSP;FEATURE_SOCKET_EAP;FEATURE_SOCKET_DISPOSE;FEATURE_STREAM_APM;FEATURE_STREAM_TAP;FEATURE_DEVICEINFORMATION_APM;FEATURE_THREAD_COUNTDOWNEVENT;FEATURE_THREAD_THREADPOOL;FEATURE_THREAD_SLEEP;FEATURE_WAITHANDLE_DISPOSE;FEATURE_MEMORYSTREAM_GETBUFFER;FEATURE_HASH_SHA1_MANAGED;FEATURE_HASH_SHA256_MANAGED;FEATURE_HMAC_SHA1;FEATURE_HMAC_SHA256 - true - true - pdbonly - - - prompt - MinimumRecommendedRules.ruleset - - - true - Bin\ARM\Debug - TRACE;DEBUG;FEATURE_REGEX_COMPILE;FEATURE_RNG_CSP;FEATURE_SOCKET_EAP;FEATURE_SOCKET_DISPOSE;FEATURE_STREAM_APM;FEATURE_STREAM_TAP;FEATURE_DEVICEINFORMATION_APM;FEATURE_THREAD_COUNTDOWNEVENT;FEATURE_THREAD_THREADPOOL;FEATURE_THREAD_SLEEP;FEATURE_WAITHANDLE_DISPOSE;FEATURE_MEMORYSTREAM_GETBUFFER;FEATURE_HASH_SHA1_MANAGED;FEATURE_HASH_SHA256_MANAGED;FEATURE_HMAC_SHA1;FEATURE_HMAC_SHA256 - true - full - - - prompt - MinimumRecommendedRules.ruleset - false - - - Bin\ARM\Release - TRACE;FEATURE_REGEX_COMPILE;FEATURE_RNG_CSP;FEATURE_SOCKET_EAP;FEATURE_SOCKET_DISPOSE;FEATURE_STREAM_APM;FEATURE_STREAM_TAP;FEATURE_DEVICEINFORMATION_APM;FEATURE_THREAD_COUNTDOWNEVENT;FEATURE_THREAD_THREADPOOL;FEATURE_THREAD_SLEEP;FEATURE_WAITHANDLE_DISPOSE;FEATURE_MEMORYSTREAM_GETBUFFER;FEATURE_HASH_SHA1_MANAGED;FEATURE_HASH_SHA256_MANAGED;FEATURE_HMAC_SHA1;FEATURE_HMAC_SHA256 - true - true - pdbonly - - - prompt - MinimumRecommendedRules.ruleset - - - - Abstractions\CryptoAbstraction.cs - - - Abstractions\DiagnosticAbstraction.cs - - - Abstractions\DnsAbstraction.cs - - - Abstractions\FileSystemAbstraction.cs - - - Abstractions\ReflectionAbstraction.cs - - - Abstractions\SocketAbstraction.cs - - - Abstractions\ThreadAbstraction.cs - - - AuthenticationMethod.cs - - - AuthenticationResult.cs - - - BaseClient.cs - - - Channels\Channel.cs - - - Channels\ChannelDirectTcpip.cs - - - Channels\ChannelForwardedTcpip.cs - - - Channels\ChannelSession.cs - - - Channels\ChannelTypes.cs - - - Channels\ClientChannel.cs - - - Channels\IChannel.cs - - - Channels\IChannelDirectTcpip.cs - - - Channels\IChannelForwardedTcpip.cs - - - Channels\IChannelSession.cs - - - Channels\ServerChannel.cs - - - CipherInfo.cs - - - ClientAuthentication.cs - - - CommandAsyncResult.cs - - - Common\Array.cs - - - Common\ASCIIEncoding.cs - - - Common\AsyncResult.cs - - - Common\AuthenticationBannerEventArgs.cs - - - Common\AuthenticationEventArgs.cs - - - Common\AuthenticationPasswordChangeEventArgs.cs - - - Common\AuthenticationPrompt.cs - - - Common\AuthenticationPromptEventArgs.cs - - - Common\BigInteger.cs - - - Common\ChannelDataEventArgs.cs - - - Common\ChannelEventArgs.cs - - - Common\ChannelExtendedDataEventArgs.cs - - - Common\ChannelOpenConfirmedEventArgs.cs - - - Common\ChannelOpenFailedEventArgs.cs - - - Common\ChannelRequestEventArgs.cs - - - Common\CountdownEvent.cs - - - Common\DerData.cs - - - Common\ExceptionEventArgs.cs - - - Common\Extensions.cs - - - Common\HostKeyEventArgs.cs - - - Common\ObjectIdentifier.cs - - - Common\Pack.cs - - - Common\PacketDump.cs - - - Common\PipeStream.cs - - - Common\PortForwardEventArgs.cs - - - Common\PosixPath.cs - - - Common\ProxyException.cs - - - Common\ScpDownloadEventArgs.cs - - - Common\ScpException.cs - - - Common\ScpUploadEventArgs.cs - - - Common\SemaphoreLight.cs - - - Common\SftpPathNotFoundException.cs - - - Common\SftpPermissionDeniedException.cs - - - Common\ShellDataEventArgs.cs - - - Common\SshAuthenticationException.cs - - - Common\SshConnectionException.cs - - - Common\SshData.cs - - - Common\SshDataStream.cs - - - Common\SshException.cs - - - Common\SshOperationTimeoutException.cs - - - Common\SshPassPhraseNullOrEmptyException.cs - - - Common\TerminalModes.cs - - - Compression\CompressionMode.cs - - - Compression\Compressor.cs - - - Compression\Zlib.cs - - - Compression\ZlibOpenSsh.cs - - - Compression\ZlibStream.cs - - - ConnectionInfo.cs - - - Connection\ConnectorBase.cs - - - Connection\DirectConnector.cs - - - Connection\HttpConnector.cs - - - Connection\IConnector.cs - - - Connection\IProtocolVersionExchange.cs - - - Connection\ISocketFactory.cs - - - Connection\ProtocolVersionExchange.cs - - - Connection\SocketFactory.cs - - - Connection\Socks4Connector.cs - - - Connection\Socks5Connector.cs - - - Connection\SshIdentification.cs - - - ExpectAction.cs - - - ExpectAsyncResult.cs - - - ForwardedPort.cs - - - ForwardedPortDynamic.cs - - - ForwardedPortDynamic.NET.cs - - - ForwardedPortLocal.cs - - - ForwardedPortLocal.NET.cs - - - ForwardedPortRemote.cs - - - ForwardedPortStatus.cs - - - HashInfo.cs - - - IAuthenticationMethod.cs - - - IClientAuthentication.cs - - - IConnectionInfo.cs - - - IForwardedPort.cs - - - IRemotePathTransformation.cs - - - IServiceFactory.cs - - - ISession.cs - - - ISftpClient.cs - - - ISubsystemSession.cs - - - KeyboardInteractiveAuthenticationMethod.cs - - - KeyboardInteractiveConnectionInfo.cs - - - MessageEventArgs.cs - - - Messages\Authentication\BannerMessage.cs - - - Messages\Authentication\FailureMessage.cs - - - Messages\Authentication\InformationRequestMessage.cs - - - Messages\Authentication\InformationResponseMessage.cs - - - Messages\Authentication\PasswordChangeRequiredMessage.cs - - - Messages\Authentication\PublicKeyMessage.cs - - - Messages\Authentication\RequestMessage.cs - - - Messages\Authentication\RequestMessageHost.cs - - - Messages\Authentication\RequestMessageKeyboardInteractive.cs - - - Messages\Authentication\RequestMessageNone.cs - - - Messages\Authentication\RequestMessagePassword.cs - - - Messages\Authentication\RequestMessagePublicKey.cs - - - Messages\Authentication\SuccessMessage.cs - - - Messages\Connection\CancelTcpIpForwardGlobalRequestMessage.cs - - - Messages\Connection\ChannelCloseMessage.cs - - - Messages\Connection\ChannelDataMessage.cs - - - Messages\Connection\ChannelEofMessage.cs - - - Messages\Connection\ChannelExtendedDataMessage.cs - - - Messages\Connection\ChannelFailureMessage.cs - - - Messages\Connection\ChannelMessage.cs - - - Messages\Connection\ChannelOpenConfirmationMessage.cs - - - Messages\Connection\ChannelOpenFailureMessage.cs - - - Messages\Connection\ChannelOpenFailureReasons.cs - - - Messages\Connection\ChannelOpen\ChannelOpenInfo.cs - - - Messages\Connection\ChannelOpen\ChannelOpenMessage.cs - - - Messages\Connection\ChannelOpen\DirectTcpipChannelInfo.cs - - - Messages\Connection\ChannelOpen\ForwardedTcpipChannelInfo.cs - - - Messages\Connection\ChannelOpen\SessionChannelOpenInfo.cs - - - Messages\Connection\ChannelOpen\X11ChannelOpenInfo.cs - - - Messages\Connection\ChannelRequest\BreakRequestInfo.cs - - - Messages\Connection\ChannelRequest\ChannelRequestMessage.cs - - - Messages\Connection\ChannelRequest\EndOfWriteRequestInfo.cs - - - Messages\Connection\ChannelRequest\EnvironmentVariableRequestInfo.cs - - - Messages\Connection\ChannelRequest\ExecRequestInfo.cs - - - Messages\Connection\ChannelRequest\ExitSignalRequestInfo.cs - - - Messages\Connection\ChannelRequest\ExitStatusRequestInfo.cs - - - Messages\Connection\ChannelRequest\KeepAliveRequestInfo.cs - - - Messages\Connection\ChannelRequest\PseudoTerminalInfo.cs - - - Messages\Connection\ChannelRequest\RequestInfo.cs - - - Messages\Connection\ChannelRequest\ShellRequestInfo.cs - - - Messages\Connection\ChannelRequest\SignalRequestInfo.cs - - - Messages\Connection\ChannelRequest\SubsystemRequestInfo.cs - - - Messages\Connection\ChannelRequest\WindowChangeRequestInfo.cs - - - Messages\Connection\ChannelRequest\X11ForwardingRequestInfo.cs - - - Messages\Connection\ChannelRequest\XonXoffRequestInfo.cs - - - Messages\Connection\ChannelSuccessMessage.cs - - - Messages\Connection\ChannelWindowAdjustMessage.cs - - - Messages\Connection\GlobalRequestMessage.cs - - - Messages\Connection\GlobalRequestName.cs - - - Messages\Connection\RequestFailureMessage.cs - - - Messages\Connection\RequestSuccessMessage.cs - - - Messages\Connection\TcpIpForwardGlobalRequestMessage.cs - - - Messages\Message.cs - - - Messages\MessageAttribute.cs - - - Messages\ServiceName.cs - - - Messages\Transport\DebugMessage.cs - - - Messages\Transport\DisconnectMessage.cs - - - Messages\Transport\DisconnectReason.cs - - - Messages\Transport\IgnoreMessage.cs - - - Messages\Transport\IKeyExchangedAllowed.cs - - - Messages\Transport\KeyExchangeDhGroupExchangeGroup.cs - - - Messages\Transport\KeyExchangeDhGroupExchangeInit.cs - - - Messages\Transport\KeyExchangeDhGroupExchangeReply.cs - - - Messages\Transport\KeyExchangeDhGroupExchangeRequest.cs - - - Messages\Transport\KeyExchangeDhInitMessage.cs - - - Messages\Transport\KeyExchangeDhReplyMessage.cs - - - Messages\Transport\KeyExchangeEcdhInitMessage.cs - - - Messages\Transport\KeyExchangeEcdhReplyMessage.cs - - - Messages\Transport\KeyExchangeInitMessage.cs - - - Messages\Transport\NewKeysMessage.cs - - - Messages\Transport\ServiceAcceptMessage.cs - - - Messages\Transport\ServiceRequestMessage.cs - - - Messages\Transport\UnimplementedMessage.cs - - - NoneAuthenticationMethod.cs - - - PasswordAuthenticationMethod.cs - - - PasswordConnectionInfo.cs - - - PrivateKeyAuthenticationMethod.cs - - - PrivateKeyConnectionInfo.cs - - - PrivateKeyFile.cs - - - ProxyTypes.cs - - - RemotePathDoubleQuoteTransformation.cs - - - RemotePathNoneTransformation.cs - - - RemotePathShellQuoteTransformation.cs - - - RemotePathTransformation.cs - - - ScpClient.cs - - - Security\Algorithm.cs - - - Security\Cryptography\BouncyCastle\asn1\sec\SECNamedCurves.cs - - - Security\Cryptography\BouncyCastle\asn1\x9\X9Curve.cs - - - Security\Cryptography\BouncyCastle\asn1\x9\X9ECParameters.cs - - - Security\Cryptography\BouncyCastle\asn1\x9\X9ECParametersHolder.cs - - - Security\Cryptography\BouncyCastle\asn1\x9\X9ECPoint.cs - - - Security\Cryptography\BouncyCastle\crypto\agreement\ECDHCBasicAgreement.cs - - - Security\Cryptography\BouncyCastle\crypto\AsymmetricCipherKeyPair.cs - - - Security\Cryptography\BouncyCastle\crypto\AsymmetricKeyParameter.cs - - - Security\Cryptography\BouncyCastle\crypto\digests\GeneralDigest.cs - - - Security\Cryptography\BouncyCastle\crypto\digests\Sha256Digest.cs - - - Security\Cryptography\BouncyCastle\crypto\generators\ECKeyPairGenerator.cs - - - Security\Cryptography\BouncyCastle\crypto\IAsymmetricCipherKeyPairGenerator.cs - - - Security\Cryptography\BouncyCastle\crypto\IDigest.cs - - - Security\Cryptography\BouncyCastle\crypto\KeyGenerationParameters.cs - - - Security\Cryptography\BouncyCastle\crypto\parameters\ECDomainParameters.cs - - - Security\Cryptography\BouncyCastle\crypto\parameters\ECKeyGenerationParameters.cs - - - Security\Cryptography\BouncyCastle\crypto\parameters\ECKeyParameters.cs - - - Security\Cryptography\BouncyCastle\crypto\parameters\ECPrivateKeyParameters.cs - - - Security\Cryptography\BouncyCastle\crypto\parameters\ECPublicKeyParameters.cs - - - Security\Cryptography\BouncyCastle\crypto\prng\CryptoApiRandomGenerator.cs - - - Security\Cryptography\BouncyCastle\crypto\prng\DigestRandomGenerator.cs - - - Security\Cryptography\BouncyCastle\crypto\prng\IRandomGenerator.cs - - - Security\Cryptography\BouncyCastle\crypto\util\Pack.cs - - - Security\Cryptography\BouncyCastle\math\BigInteger.cs - - - Security\Cryptography\BouncyCastle\math\ec\abc\SimpleBigDecimal.cs - - - Security\Cryptography\BouncyCastle\math\ec\abc\Tnaf.cs - - - Security\Cryptography\BouncyCastle\math\ec\abc\ZTauElement.cs - - - Security\Cryptography\BouncyCastle\math\ec\ECAlgorithms.cs - - - Security\Cryptography\BouncyCastle\math\ec\ECCurve.cs - - - Security\Cryptography\BouncyCastle\math\ec\ECFieldElement.cs - - - Security\Cryptography\BouncyCastle\math\ec\ECLookupTable.cs - - - Security\Cryptography\BouncyCastle\math\ec\ECPoint.cs - - - Security\Cryptography\BouncyCastle\math\ec\ECPointMap.cs - - - Security\Cryptography\BouncyCastle\math\ec\endo\ECEndomorphism.cs - - - Security\Cryptography\BouncyCastle\math\ec\endo\GlvEndomorphism.cs - - - Security\Cryptography\BouncyCastle\math\ec\LongArray.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\AbstractECMultiplier.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\ECMultiplier.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\FixedPointCombMultiplier.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\FixedPointPreCompInfo.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\FixedPointUtilities.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\GlvMultiplier.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\IPreCompCallback.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\PreCompInfo.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\ValidityPreCompInfo.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\WNafL2RMultiplier.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\WNafPreCompInfo.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\WNafUtilities.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\WTauNafMultiplier.cs - - - Security\Cryptography\BouncyCastle\math\ec\multiplier\WTauNafPreCompInfo.cs - - - Security\Cryptography\BouncyCastle\math\field\FiniteFields.cs - - - Security\Cryptography\BouncyCastle\math\field\GenericPolynomialExtensionField.cs - - - Security\Cryptography\BouncyCastle\math\field\GF2Polynomial.cs - - - Security\Cryptography\BouncyCastle\math\field\IExtensionField.cs - - - Security\Cryptography\BouncyCastle\math\field\IFiniteField.cs - - - Security\Cryptography\BouncyCastle\math\field\IPolynomial.cs - - - Security\Cryptography\BouncyCastle\math\field\IPolynomialExtensionField.cs - - - Security\Cryptography\BouncyCastle\math\field\PrimeField.cs - - - Security\Cryptography\BouncyCastle\math\raw\Mod.cs - - - Security\Cryptography\BouncyCastle\math\raw\Nat.cs - - - Security\Cryptography\BouncyCastle\security\DigestUtilities.cs - - - Security\Cryptography\BouncyCastle\security\SecureRandom.cs - - - Security\Cryptography\BouncyCastle\security\SecurityUtilityException.cs - - - Security\Cryptography\BouncyCastle\util\Arrays.cs - - - Security\Cryptography\BouncyCastle\util\BigIntegers.cs - - - Security\Cryptography\BouncyCastle\util\encoders\Hex.cs - - - Security\Cryptography\BouncyCastle\util\encoders\HexEncoder.cs - - - Security\Cryptography\BouncyCastle\util\IMemoable.cs - - - Security\Cryptography\BouncyCastle\util\Integers.cs - - - Security\Cryptography\BouncyCastle\util\MemoableResetException.cs - - - Security\Cryptography\BouncyCastle\util\Times.cs - - - Security\CertificateHostAlgorithm.cs - - - Security\Cryptography\Chaos.NaCl\CryptoBytes.cs - - - Security\Cryptography\Chaos.NaCl\Ed25519.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Array16.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Array8.cs - - - Security\Cryptography\Chaos.NaCl\Internal\ByteIntegerConverter.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\base.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\base2.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\d.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\d2.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_0.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_1.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_add.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_cmov.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_cswap.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_frombytes.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_invert.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_isnegative.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_isnonzero.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_mul.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_mul121666.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_neg.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_pow22523.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_sq.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_sq2.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_sub.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_tobytes.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\FieldElement.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_add.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_double_scalarmult.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_frombytes.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_madd.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_msub.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p1p1_to_p2.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p1p1_to_p3.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p2_0.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p2_dbl.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p3_0.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p3_dbl.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p3_tobytes.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p3_to_cached.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p3_to_p2.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_precomp_0.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_scalarmult_base.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_sub.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_tobytes.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\GroupElement.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\keypair.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\open.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\scalarmult.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\sc_clamp.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\sc_mul_add.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\sc_reduce.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\sign.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\sqrtm1.cs - - - Security\Cryptography\Chaos.NaCl\Internal\InternalAssert.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Poly1305Donna.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Salsa\Salsa20.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Salsa\SalsaCore.cs - - - Security\Cryptography\Chaos.NaCl\Internal\Sha512Internal.cs - - - Security\Cryptography\Chaos.NaCl\MontgomeryCurve25519.cs - - - Security\Cryptography\Chaos.NaCl\Sha512.cs - - - Security\Cryptography\AsymmetricCipher.cs - - - Security\Cryptography\Bcrypt.cs - - - Security\Cryptography\BlockCipher.cs - - - Security\Cryptography\Cipher.cs - - - Security\Cryptography\CipherDigitalSignature.cs - - - Security\Cryptography\Ciphers\AesCipher.cs - - - Security\Cryptography\Ciphers\Arc4Cipher.cs - - - Security\Cryptography\Ciphers\BlowfishCipher.cs - - - Security\Cryptography\Ciphers\CastCipher.cs - - - Security\Cryptography\Ciphers\CipherMode.cs - - - Security\Cryptography\Ciphers\CipherPadding.cs - - - Security\Cryptography\Ciphers\DesCipher.cs - - - Security\Cryptography\Ciphers\Modes\CbcCipherMode.cs - - - Security\Cryptography\Ciphers\Modes\CfbCipherMode.cs - - - Security\Cryptography\Ciphers\Modes\CtrCipherMode.cs - - - Security\Cryptography\Ciphers\Modes\OfbCipherMode.cs - - - Security\Cryptography\Ciphers\Paddings\PKCS7Padding.cs - - - Security\Cryptography\Ciphers\RsaCipher.cs - - - Security\Cryptography\Ciphers\SerpentCipher.cs - - - Security\Cryptography\Ciphers\TripleDesCipher.cs - - - Security\Cryptography\Ciphers\TwofishCipher.cs - - - Security\Cryptography\DigitalSignature.cs - - - Security\Cryptography\DsaDigitalSignature.cs - - - Security\Cryptography\DsaKey.cs - - - Security\Cryptography\ED25519DigitalSignature.cs - - - Security\Cryptography\ED25519Key.cs - - - Security\Cryptography\HMACMD5.cs - - - Security\Cryptography\HMACSHA1.cs - - - Security\Cryptography\HMACSHA256.cs - - - Security\Cryptography\HMACSHA384.cs - - - Security\Cryptography\HMACSHA512.cs - - - Security\Cryptography\Key.cs - - - Security\Cryptography\EcdsaDigitalSignature.cs - - - Security\Cryptography\EcdsaKey.cs - - - Security\Cryptography\RsaDigitalSignature.cs - - - Security\Cryptography\RsaKey.cs - - - Security\Cryptography\StreamCipher.cs - - - Security\Cryptography\SymmetricCipher.cs - - - Security\GroupExchangeHashData.cs - - - Security\HostAlgorithm.cs - - - Security\IKeyExchange.cs - - - Security\KeyExchange.cs - - - Security\KeyExchangeDiffieHellman.cs - - - Security\KeyExchangeDiffieHellmanGroup14Sha1.cs - - - Security\KeyExchangeDiffieHellmanGroup14Sha256.cs - - - Security\KeyExchangeDiffieHellmanGroup16Sha512.cs - - - Security\KeyExchangeDiffieHellmanGroup1Sha1.cs - - - Security\KeyExchangeDiffieHellmanGroupExchangeSha1.cs - - - Security\KeyExchangeDiffieHellmanGroupExchangeSha256.cs - - - Security\KeyExchangeDiffieHellmanGroupExchangeShaBase.cs - - - Security\KeyExchangeDiffieHellmanGroupSha1.cs - - - Security\KeyExchangeDiffieHellmanGroupSha256.cs - - - Security\KeyExchangeDiffieHellmanGroupSha512.cs - - - Security\KeyExchangeDiffieHellmanGroupShaBase.cs - - - Security\KeyExchangeEC.cs - - - Security\KeyExchangeECCurve25519.cs - - - Security\KeyExchangeECDH.cs - - - Security\KeyExchangeECDH256.cs - - - Security\KeyExchangeECDH384.cs - - - Security\KeyExchangeECDH521.cs - - - Security\KeyExchangeHash.cs - - - Security\KeyHostAlgorithm.cs - - - ServiceFactory.cs - - - Session.cs - - - SftpClient.cs - - - Sftp\Flags.cs - - - Sftp\ISftpFileReader.cs - - - Sftp\ISftpResponseFactory.cs - - - Sftp\ISftpSession.cs - - - Sftp\Requests\ExtendedRequests\FStatVfsRequest.cs - - - Sftp\Requests\ExtendedRequests\HardLinkRequest.cs - - - Sftp\Requests\ExtendedRequests\PosixRenameRequest.cs - - - Sftp\Requests\ExtendedRequests\StatVfsRequest.cs - - - Sftp\Requests\SftpBlockRequest.cs - - - Sftp\Requests\SftpCloseRequest.cs - - - Sftp\Requests\SftpExtendedRequest.cs - - - Sftp\Requests\SftpFSetStatRequest.cs - - - Sftp\Requests\SftpFStatRequest.cs - - - Sftp\Requests\SftpInitRequest.cs - - - Sftp\Requests\SftpLinkRequest.cs - - - Sftp\Requests\SftpLStatRequest.cs - - - Sftp\Requests\SftpMkDirRequest.cs - - - Sftp\Requests\SftpOpenDirRequest.cs - - - Sftp\Requests\SftpOpenRequest.cs - - - Sftp\Requests\SftpReadDirRequest.cs - - - Sftp\Requests\SftpReadLinkRequest.cs - - - Sftp\Requests\SftpReadRequest.cs - - - Sftp\Requests\SftpRealPathRequest.cs - - - Sftp\Requests\SftpRemoveRequest.cs - - - Sftp\Requests\SftpRenameRequest.cs - - - Sftp\Requests\SftpRequest.cs - - - Sftp\Requests\SftpRmDirRequest.cs - - - Sftp\Requests\SftpSetStatRequest.cs - - - Sftp\Requests\SftpStatRequest.cs - - - Sftp\Requests\SftpSymLinkRequest.cs - - - Sftp\Requests\SftpUnblockRequest.cs - - - Sftp\Requests\SftpWriteRequest.cs - - - Sftp\Responses\ExtendedReplies\ExtendedReplyInfo.cs - - - Sftp\Responses\ExtendedReplies\StatVfsReplyInfo.cs - - - Sftp\Responses\SftpAttrsResponse.cs - - - Sftp\Responses\SftpDataResponse.cs - - - Sftp\Responses\SftpExtendedReplyResponse.cs - - - Sftp\Responses\SftpHandleResponse.cs - - - Sftp\Responses\SftpNameResponse.cs - - - Sftp\Responses\SftpResponse.cs - - - Sftp\Responses\SftpStatusResponse.cs - - - Sftp\Responses\SftpVersionResponse.cs - - - Sftp\SftpCloseAsyncResult.cs - - - Sftp\SftpDownloadAsyncResult.cs - - - Sftp\SftpFile.cs - - - Sftp\SftpFileAttributes.cs - - - Sftp\SftpFileReader.cs - - - Sftp\SftpFileStream.cs - - - Sftp\SftpFileSystemInformation.cs - - - Sftp\SftpListDirectoryAsyncResult.cs - - - Sftp\SftpMessage.cs - - - Sftp\SftpMessageTypes.cs - - - Sftp\SftpOpenAsyncResult.cs - - - Sftp\SftpReadAsyncResult.cs - - - Sftp\SftpRealPathAsyncResult.cs - - - Sftp\SftpResponseFactory.cs - - - Sftp\SftpSession.cs - - - Sftp\SFtpStatAsyncResult.cs - - - Sftp\SftpSynchronizeDirectoriesAsyncResult.cs - - - Sftp\SftpUploadAsyncResult.cs - - - Sftp\StatusCodes.cs - - - Shell.cs - - - ShellStream.cs - - - SshClient.cs - - - SshCommand.cs - - - SshMessageFactory.cs - - - SubsystemSession.cs - - - - Properties\CommonAssemblyInfo.cs - - - - - - - - ..\..\packages\SshNet.Security.Cryptography.1.2.0\lib\wp8\SshNet.Security.Cryptography.dll - True - - - - - - - - - - - \ No newline at end of file diff --git a/src/Renci.SshNet.WindowsPhone8/packages.config b/src/Renci.SshNet.WindowsPhone8/packages.config deleted file mode 100644 index a571ea4f..00000000 --- a/src/Renci.SshNet.WindowsPhone8/packages.config +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/Renci.SshNet.sln b/src/Renci.SshNet.sln new file mode 100644 index 00000000..259ae16e --- /dev/null +++ b/src/Renci.SshNet.sln @@ -0,0 +1,174 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.33326.253 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{2D6CAE62-D053-476F-9BDD-2B1F27FA9C5D}" + ProjectSection(SolutionItems) = preProject + ..\build\build.cmd = ..\build\build.cmd + ..\build\build.proj = ..\build\build.proj + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "nuget", "nuget", "{94EE3919-19FA-4D9B-8DA9-249050B15232}" + ProjectSection(SolutionItems) = preProject + ..\build\nuget\SSH.NET.nuspec = ..\build\nuget\SSH.NET.nuspec + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "sandcastle", "sandcastle", "{A6C3FFD3-16A5-44D3-8C1F-3613D6DD17D1}" + ProjectSection(SolutionItems) = preProject + ..\build\sandcastle\SSH.NET.shfbproj = ..\build\sandcastle\SSH.NET.shfbproj + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Renci.SshNet", "Renci.SshNet\Renci.SshNet.csproj", "{2F5F8C90-0BD1-424F-997C-7BC6280919D1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Renci.SshNet.Tests", "Renci.SshNet.Tests\Renci.SshNet.Tests.csproj", "{C45379B9-17B1-4E89-BC2E-6D41726413E8}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{04E8CC26-116E-4116-9558-7ED542548E70}" + ProjectSection(SolutionItems) = preProject + ..\.editorconfig = ..\.editorconfig + ..\.gitattributes = ..\.gitattributes + ..\.gitignore = ..\.gitignore + ..\appveyor.yml = ..\appveyor.yml + ..\CODEOWNERS = ..\CODEOWNERS + ..\Directory.Build.props = ..\Directory.Build.props + ..\LICENSE = ..\LICENSE + ..\README.md = ..\README.md + ..\THIRD-PARTY-NOTICES.TXT = ..\THIRD-PARTY-NOTICES.TXT + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{D21A4D03-0AC2-4613-BB6D-74D2D16A72CC}" + ProjectSection(SolutionItems) = preProject + ..\test\.editorconfig = ..\test\.editorconfig + ..\test\Directory.Build.props = ..\test\Directory.Build.props + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Renci.SshNet.Benchmarks", "Renci.SshNet.Benchmarks\Renci.SshNet.Benchmarks.csproj", "{A8C83FF2-B733-4A01-8D4E-D6DA2D420484}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Renci.SshNet.IntegrationTests", "Renci.SshNet.IntegrationTests\Renci.SshNet.IntegrationTests.csproj", "{EEF98046-729C-419E-932D-4E569073C8CC}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Renci.SshNet.TestTools.OpenSSH", "Renci.SshNet.TestTools.OpenSSH\Renci.SshNet.TestTools.OpenSSH.csproj", "{78239046-2019-494E-B6EC-240AF787E4D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{E8A42832-1183-4E66-9141-DEBA662374DF}" + ProjectSection(SolutionItems) = preProject + global.json = global.json + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|ARM = Debug|ARM + Debug|Mixed Platforms = Debug|Mixed Platforms + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|ARM = Release|ARM + Release|Mixed Platforms = Release|Mixed Platforms + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|ARM.ActiveCfg = Debug|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|x64.ActiveCfg = Debug|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|x86.ActiveCfg = Debug|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|Any CPU.Build.0 = Release|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|ARM.ActiveCfg = Release|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|x64.ActiveCfg = Release|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|x86.ActiveCfg = Release|Any CPU + {C45379B9-17B1-4E89-BC2E-6D41726413E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C45379B9-17B1-4E89-BC2E-6D41726413E8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C45379B9-17B1-4E89-BC2E-6D41726413E8}.Debug|ARM.ActiveCfg = Debug|Any CPU + {C45379B9-17B1-4E89-BC2E-6D41726413E8}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {C45379B9-17B1-4E89-BC2E-6D41726413E8}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {C45379B9-17B1-4E89-BC2E-6D41726413E8}.Debug|x64.ActiveCfg = Debug|Any CPU + {C45379B9-17B1-4E89-BC2E-6D41726413E8}.Debug|x86.ActiveCfg = Debug|Any CPU + {C45379B9-17B1-4E89-BC2E-6D41726413E8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C45379B9-17B1-4E89-BC2E-6D41726413E8}.Release|Any CPU.Build.0 = Release|Any CPU + {C45379B9-17B1-4E89-BC2E-6D41726413E8}.Release|ARM.ActiveCfg = Release|Any CPU + {C45379B9-17B1-4E89-BC2E-6D41726413E8}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {C45379B9-17B1-4E89-BC2E-6D41726413E8}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {C45379B9-17B1-4E89-BC2E-6D41726413E8}.Release|x64.ActiveCfg = Release|Any CPU + {C45379B9-17B1-4E89-BC2E-6D41726413E8}.Release|x86.ActiveCfg = Release|Any CPU + {A8C83FF2-B733-4A01-8D4E-D6DA2D420484}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A8C83FF2-B733-4A01-8D4E-D6DA2D420484}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A8C83FF2-B733-4A01-8D4E-D6DA2D420484}.Debug|ARM.ActiveCfg = Debug|Any CPU + {A8C83FF2-B733-4A01-8D4E-D6DA2D420484}.Debug|ARM.Build.0 = Debug|Any CPU + {A8C83FF2-B733-4A01-8D4E-D6DA2D420484}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {A8C83FF2-B733-4A01-8D4E-D6DA2D420484}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {A8C83FF2-B733-4A01-8D4E-D6DA2D420484}.Debug|x64.ActiveCfg = Debug|Any CPU + {A8C83FF2-B733-4A01-8D4E-D6DA2D420484}.Debug|x64.Build.0 = Debug|Any CPU + {A8C83FF2-B733-4A01-8D4E-D6DA2D420484}.Debug|x86.ActiveCfg = Debug|Any CPU + {A8C83FF2-B733-4A01-8D4E-D6DA2D420484}.Debug|x86.Build.0 = Debug|Any CPU + {A8C83FF2-B733-4A01-8D4E-D6DA2D420484}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A8C83FF2-B733-4A01-8D4E-D6DA2D420484}.Release|Any CPU.Build.0 = Release|Any CPU + {A8C83FF2-B733-4A01-8D4E-D6DA2D420484}.Release|ARM.ActiveCfg = Release|Any CPU + {A8C83FF2-B733-4A01-8D4E-D6DA2D420484}.Release|ARM.Build.0 = Release|Any CPU + {A8C83FF2-B733-4A01-8D4E-D6DA2D420484}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {A8C83FF2-B733-4A01-8D4E-D6DA2D420484}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {A8C83FF2-B733-4A01-8D4E-D6DA2D420484}.Release|x64.ActiveCfg = Release|Any CPU + {A8C83FF2-B733-4A01-8D4E-D6DA2D420484}.Release|x64.Build.0 = Release|Any CPU + {A8C83FF2-B733-4A01-8D4E-D6DA2D420484}.Release|x86.ActiveCfg = Release|Any CPU + {A8C83FF2-B733-4A01-8D4E-D6DA2D420484}.Release|x86.Build.0 = Release|Any CPU + {EEF98046-729C-419E-932D-4E569073C8CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EEF98046-729C-419E-932D-4E569073C8CC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EEF98046-729C-419E-932D-4E569073C8CC}.Debug|ARM.ActiveCfg = Debug|Any CPU + {EEF98046-729C-419E-932D-4E569073C8CC}.Debug|ARM.Build.0 = Debug|Any CPU + {EEF98046-729C-419E-932D-4E569073C8CC}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {EEF98046-729C-419E-932D-4E569073C8CC}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {EEF98046-729C-419E-932D-4E569073C8CC}.Debug|x64.ActiveCfg = Debug|Any CPU + {EEF98046-729C-419E-932D-4E569073C8CC}.Debug|x64.Build.0 = Debug|Any CPU + {EEF98046-729C-419E-932D-4E569073C8CC}.Debug|x86.ActiveCfg = Debug|Any CPU + {EEF98046-729C-419E-932D-4E569073C8CC}.Debug|x86.Build.0 = Debug|Any CPU + {EEF98046-729C-419E-932D-4E569073C8CC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EEF98046-729C-419E-932D-4E569073C8CC}.Release|Any CPU.Build.0 = Release|Any CPU + {EEF98046-729C-419E-932D-4E569073C8CC}.Release|ARM.ActiveCfg = Release|Any CPU + {EEF98046-729C-419E-932D-4E569073C8CC}.Release|ARM.Build.0 = Release|Any CPU + {EEF98046-729C-419E-932D-4E569073C8CC}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {EEF98046-729C-419E-932D-4E569073C8CC}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {EEF98046-729C-419E-932D-4E569073C8CC}.Release|x64.ActiveCfg = Release|Any CPU + {EEF98046-729C-419E-932D-4E569073C8CC}.Release|x64.Build.0 = Release|Any CPU + {EEF98046-729C-419E-932D-4E569073C8CC}.Release|x86.ActiveCfg = Release|Any CPU + {EEF98046-729C-419E-932D-4E569073C8CC}.Release|x86.Build.0 = Release|Any CPU + {78239046-2019-494E-B6EC-240AF787E4D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {78239046-2019-494E-B6EC-240AF787E4D0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {78239046-2019-494E-B6EC-240AF787E4D0}.Debug|ARM.ActiveCfg = Debug|Any CPU + {78239046-2019-494E-B6EC-240AF787E4D0}.Debug|ARM.Build.0 = Debug|Any CPU + {78239046-2019-494E-B6EC-240AF787E4D0}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {78239046-2019-494E-B6EC-240AF787E4D0}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {78239046-2019-494E-B6EC-240AF787E4D0}.Debug|x64.ActiveCfg = Debug|Any CPU + {78239046-2019-494E-B6EC-240AF787E4D0}.Debug|x64.Build.0 = Debug|Any CPU + {78239046-2019-494E-B6EC-240AF787E4D0}.Debug|x86.ActiveCfg = Debug|Any CPU + {78239046-2019-494E-B6EC-240AF787E4D0}.Debug|x86.Build.0 = Debug|Any CPU + {78239046-2019-494E-B6EC-240AF787E4D0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {78239046-2019-494E-B6EC-240AF787E4D0}.Release|Any CPU.Build.0 = Release|Any CPU + {78239046-2019-494E-B6EC-240AF787E4D0}.Release|ARM.ActiveCfg = Release|Any CPU + {78239046-2019-494E-B6EC-240AF787E4D0}.Release|ARM.Build.0 = Release|Any CPU + {78239046-2019-494E-B6EC-240AF787E4D0}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {78239046-2019-494E-B6EC-240AF787E4D0}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {78239046-2019-494E-B6EC-240AF787E4D0}.Release|x64.ActiveCfg = Release|Any CPU + {78239046-2019-494E-B6EC-240AF787E4D0}.Release|x64.Build.0 = Release|Any CPU + {78239046-2019-494E-B6EC-240AF787E4D0}.Release|x86.ActiveCfg = Release|Any CPU + {78239046-2019-494E-B6EC-240AF787E4D0}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {94EE3919-19FA-4D9B-8DA9-249050B15232} = {2D6CAE62-D053-476F-9BDD-2B1F27FA9C5D} + {A6C3FFD3-16A5-44D3-8C1F-3613D6DD17D1} = {2D6CAE62-D053-476F-9BDD-2B1F27FA9C5D} + {D21A4D03-0AC2-4613-BB6D-74D2D16A72CC} = {04E8CC26-116E-4116-9558-7ED542548E70} + {E8A42832-1183-4E66-9141-DEBA662374DF} = {04E8CC26-116E-4116-9558-7ED542548E70} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {BAD6019D-4AF7-4E15-99A0-8036E16FC0E5} + EndGlobalSection + GlobalSection(TestCaseManagementSettings) = postSolution + CategoryFile = Renci.SshNet1.vsmdi + EndGlobalSection +EndGlobal diff --git a/src/Renci.SshNet/.editorconfig b/src/Renci.SshNet/.editorconfig new file mode 100644 index 00000000..5da8db71 --- /dev/null +++ b/src/Renci.SshNet/.editorconfig @@ -0,0 +1,38 @@ +[*.cs] + +#### SYSLIB diagnostics #### + +# SYSLIB1045: Use 'GeneratedRegexAttribute' to generate the regular expression implementation at compile-time +# +# TODO: Remove this when https://github.com/sshnet/SSH.NET/issues/1131 is implemented. +dotnet_diagnostic.SYSLIB1045.severity = none + +### StyleCop Analyzers rules ### + +# SA1202: Elements must be ordered by access +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1202.md +dotnet_diagnostic.SA1202.severity = none + +#### Meziantou.Analyzer rules #### + +# MA0053: Make class sealed +# https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0053.md +MA0053.public_class_should_be_sealed = false + +#### .NET Compiler Platform analysers rules #### + +# CA1031: Do not catch general exception types +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1031 +dotnet_diagnostic.CA1031.severity = none + +# CA2213: Disposable fields should be disposed +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca2213 +dotnet_diagnostic.CA2213.severity = none + +# IDE0004: Types that own disposable fields should be disposable +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0004 +dotnet_diagnostic.IDE0004.severity = none + +# IDE0048: Add parentheses for clarity +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0047 +dotnet_diagnostic.IDE0048.severity = none diff --git a/src/Renci.SshNet/Abstractions/CryptoAbstraction.cs b/src/Renci.SshNet/Abstractions/CryptoAbstraction.cs index ff9e50a5..45a62447 100644 --- a/src/Renci.SshNet/Abstractions/CryptoAbstraction.cs +++ b/src/Renci.SshNet/Abstractions/CryptoAbstraction.cs @@ -5,12 +5,10 @@ namespace Renci.SshNet.Abstractions { internal static class CryptoAbstraction { -#if FEATURE_RNG_CREATE || FEATURE_RNG_CSP private static readonly System.Security.Cryptography.RandomNumberGenerator Randomizer = CreateRandomNumberGenerator(); -#endif /// - /// Generates a array of the specified length, and fills it with a + /// Generates a array of the specified length, and fills it with a /// cryptographically strong random sequence of values. /// /// The length of the array generate. @@ -31,111 +29,50 @@ namespace Renci.SshNet.Abstractions /// public static void GenerateRandom(byte[] data) { -#if FEATURE_RNG_CREATE || FEATURE_RNG_CSP Randomizer.GetBytes(data); -#else - if(data == null) - throw new ArgumentNullException("data"); - - var buffer = Windows.Security.Cryptography.CryptographicBuffer.GenerateRandom((uint) data.Length); - System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeBufferExtensions.CopyTo(buffer, data); -#endif } -#if FEATURE_RNG_CREATE || FEATURE_RNG_CSP public static System.Security.Cryptography.RandomNumberGenerator CreateRandomNumberGenerator() { -#if FEATURE_RNG_CREATE return System.Security.Cryptography.RandomNumberGenerator.Create(); -#elif FEATURE_RNG_CSP - return new System.Security.Cryptography.RNGCryptoServiceProvider(); -#else -#error Creation of RandomNumberGenerator is not implemented. -#endif } -#endif // FEATURE_RNG_CREATE || FEATURE_RNG_CSP -#if FEATURE_HASH_MD5 public static System.Security.Cryptography.MD5 CreateMD5() { +#pragma warning disable CA5351 // Do not use broken cryptographic algorithms return System.Security.Cryptography.MD5.Create(); +#pragma warning restore CA5351 // Do not use broken cryptographic algorithms } -#else - public static global::SshNet.Security.Cryptography.MD5 CreateMD5() - { - return new global::SshNet.Security.Cryptography.MD5(); - } -#endif // FEATURE_HASH_MD5 -#if FEATURE_HASH_SHA1_CREATE || FEATURE_HASH_SHA1_MANAGED public static System.Security.Cryptography.SHA1 CreateSHA1() { -#if FEATURE_HASH_SHA1_CREATE +#pragma warning disable CA5350 // Do not use weak cryptographic algorithms return System.Security.Cryptography.SHA1.Create(); -#elif FEATURE_HASH_SHA1_MANAGED - return new System.Security.Cryptography.SHA1Managed(); -#endif +#pragma warning restore CA5350 // Do not use weak cryptographic algorithms } -#else - public static global::SshNet.Security.Cryptography.SHA1 CreateSHA1() - { - return new global::SshNet.Security.Cryptography.SHA1(); - } -#endif -#if FEATURE_HASH_SHA256_CREATE || FEATURE_HASH_SHA256_MANAGED public static System.Security.Cryptography.SHA256 CreateSHA256() { -#if FEATURE_HASH_SHA256_CREATE return System.Security.Cryptography.SHA256.Create(); -#elif FEATURE_HASH_SHA256_MANAGED - return new System.Security.Cryptography.SHA256Managed(); -#endif } -#else - public static global::SshNet.Security.Cryptography.SHA256 CreateSHA256() - { - return new global::SshNet.Security.Cryptography.SHA256(); - } -#endif -#if FEATURE_HASH_SHA384_CREATE || FEATURE_HASH_SHA384_MANAGED public static System.Security.Cryptography.SHA384 CreateSHA384() { -#if FEATURE_HASH_SHA384_CREATE return System.Security.Cryptography.SHA384.Create(); -#elif FEATURE_HASH_SHA384_MANAGED - return new System.Security.Cryptography.SHA384Managed(); -#endif } -#else - public static global::SshNet.Security.Cryptography.SHA384 CreateSHA384() - { - return new global::SshNet.Security.Cryptography.SHA384(); - } -#endif -#if FEATURE_HASH_SHA512_CREATE || FEATURE_HASH_SHA512_MANAGED public static System.Security.Cryptography.SHA512 CreateSHA512() { -#if FEATURE_HASH_SHA512_CREATE return System.Security.Cryptography.SHA512.Create(); -#elif FEATURE_HASH_SHA512_MANAGED - return new System.Security.Cryptography.SHA512Managed(); -#endif } -#else - public static global::SshNet.Security.Cryptography.SHA512 CreateSHA512() - { - return new global::SshNet.Security.Cryptography.SHA512(); - } -#endif #if FEATURE_HASH_RIPEMD160_CREATE || FEATURE_HASH_RIPEMD160_MANAGED public static System.Security.Cryptography.RIPEMD160 CreateRIPEMD160() { #if FEATURE_HASH_RIPEMD160_CREATE +#pragma warning disable CA5350 // Do not use weak cryptographic algorithms return System.Security.Cryptography.RIPEMD160.Create(); +#pragma warning restore CA5350 // Do not use weak cryptographic algorithms #else return new System.Security.Cryptography.RIPEMD160Managed(); #endif @@ -147,51 +84,34 @@ namespace Renci.SshNet.Abstractions } #endif // FEATURE_HASH_RIPEMD160 -#if FEATURE_HMAC_MD5 public static System.Security.Cryptography.HMACMD5 CreateHMACMD5(byte[] key) { +#pragma warning disable CA5351 // Do not use broken cryptographic algorithms return new System.Security.Cryptography.HMACMD5(key); +#pragma warning restore CA5351 // Do not use broken cryptographic algorithms } public static HMACMD5 CreateHMACMD5(byte[] key, int hashSize) { +#pragma warning disable CA5351 // Do not use broken cryptographic algorithms return new HMACMD5(key, hashSize); - } -#else - public static global::SshNet.Security.Cryptography.HMACMD5 CreateHMACMD5(byte[] key) - { - return new global::SshNet.Security.Cryptography.HMACMD5(key); +#pragma warning restore CA5351 // Do not use broken cryptographic algorithms } - public static global::SshNet.Security.Cryptography.HMACMD5 CreateHMACMD5(byte[] key, int hashSize) - { - return new global::SshNet.Security.Cryptography.HMACMD5(key, hashSize); - } -#endif // FEATURE_HMAC_MD5 - -#if FEATURE_HMAC_SHA1 public static System.Security.Cryptography.HMACSHA1 CreateHMACSHA1(byte[] key) { +#pragma warning disable CA5350 // Do not use weak cryptographic algorithms return new System.Security.Cryptography.HMACSHA1(key); +#pragma warning restore CA5350 // Do not use weak cryptographic algorithms } public static HMACSHA1 CreateHMACSHA1(byte[] key, int hashSize) { +#pragma warning disable CA5350 // Do not use weak cryptographic algorithms return new HMACSHA1(key, hashSize); - } -#else - public static global::SshNet.Security.Cryptography.HMACSHA1 CreateHMACSHA1(byte[] key) - { - return new global::SshNet.Security.Cryptography.HMACSHA1(key); +#pragma warning restore CA5350 // Do not use weak cryptographic algorithms } - public static global::SshNet.Security.Cryptography.HMACSHA1 CreateHMACSHA1(byte[] key, int hashSize) - { - return new global::SshNet.Security.Cryptography.HMACSHA1(key, hashSize); - } -#endif // FEATURE_HMAC_SHA1 - -#if FEATURE_HMAC_SHA256 public static System.Security.Cryptography.HMACSHA256 CreateHMACSHA256(byte[] key) { return new System.Security.Cryptography.HMACSHA256(key); @@ -201,19 +121,7 @@ namespace Renci.SshNet.Abstractions { return new HMACSHA256(key, hashSize); } -#else - public static global::SshNet.Security.Cryptography.HMACSHA256 CreateHMACSHA256(byte[] key) - { - return new global::SshNet.Security.Cryptography.HMACSHA256(key); - } - public static global::SshNet.Security.Cryptography.HMACSHA256 CreateHMACSHA256(byte[] key, int hashSize) - { - return new global::SshNet.Security.Cryptography.HMACSHA256(key, hashSize); - } -#endif // FEATURE_HMAC_SHA256 - -#if FEATURE_HMAC_SHA384 public static System.Security.Cryptography.HMACSHA384 CreateHMACSHA384(byte[] key) { return new System.Security.Cryptography.HMACSHA384(key); @@ -223,19 +131,7 @@ namespace Renci.SshNet.Abstractions { return new HMACSHA384(key, hashSize); } -#else - public static global::SshNet.Security.Cryptography.HMACSHA384 CreateHMACSHA384(byte[] key) - { - return new global::SshNet.Security.Cryptography.HMACSHA384(key); - } - public static global::SshNet.Security.Cryptography.HMACSHA384 CreateHMACSHA384(byte[] key, int hashSize) - { - return new global::SshNet.Security.Cryptography.HMACSHA384(key, hashSize); - } -#endif // FEATURE_HMAC_SHA384 - -#if FEATURE_HMAC_SHA512 public static System.Security.Cryptography.HMACSHA512 CreateHMACSHA512(byte[] key) { return new System.Security.Cryptography.HMACSHA512(key); @@ -245,22 +141,13 @@ namespace Renci.SshNet.Abstractions { return new HMACSHA512(key, hashSize); } -#else - public static global::SshNet.Security.Cryptography.HMACSHA512 CreateHMACSHA512(byte[] key) - { - return new global::SshNet.Security.Cryptography.HMACSHA512(key); - } - - public static global::SshNet.Security.Cryptography.HMACSHA512 CreateHMACSHA512(byte[] key, int hashSize) - { - return new global::SshNet.Security.Cryptography.HMACSHA512(key, hashSize); - } -#endif // FEATURE_HMAC_SHA512 #if FEATURE_HMAC_RIPEMD160 public static System.Security.Cryptography.HMACRIPEMD160 CreateHMACRIPEMD160(byte[] key) { +#pragma warning disable CA5350 // Do not use weak cryptographic algorithms return new System.Security.Cryptography.HMACRIPEMD160(key); +#pragma warning restore CA5350 // Do not use weak cryptographic algorithms } #else public static global::SshNet.Security.Cryptography.HMACRIPEMD160 CreateHMACRIPEMD160(byte[] key) diff --git a/src/Renci.SshNet/Abstractions/DiagnosticAbstraction.cs b/src/Renci.SshNet/Abstractions/DiagnosticAbstraction.cs index 6fc1308e..18275e72 100644 --- a/src/Renci.SshNet/Abstractions/DiagnosticAbstraction.cs +++ b/src/Renci.SshNet/Abstractions/DiagnosticAbstraction.cs @@ -1,14 +1,9 @@ using System.Diagnostics; -#if FEATURE_DIAGNOSTICS_TRACESOURCE -using System.Threading; -#endif // FEATURE_DIAGNOSTICS_TRACESOURCE namespace Renci.SshNet.Abstractions { internal static class DiagnosticAbstraction { -#if FEATURE_DIAGNOSTICS_TRACESOURCE - private static readonly SourceSwitch SourceSwitch = new SourceSwitch("SshNetSwitch"); public static bool IsEnabled(TraceEventType traceEventType) @@ -22,14 +17,17 @@ namespace Renci.SshNet.Abstractions #else new TraceSource("SshNet.Logging"); #endif // DEBUG -#endif // FEATURE_DIAGNOSTICS_TRACESOURCE [Conditional("DEBUG")] public static void Log(string text) { -#if FEATURE_DIAGNOSTICS_TRACESOURCE - Loggging.TraceEvent(TraceEventType.Verbose, Thread.CurrentThread.ManagedThreadId, text); -#endif // FEATURE_DIAGNOSTICS_TRACESOURCE + Loggging.TraceEvent(TraceEventType.Verbose, +#if NET6_0_OR_GREATER + System.Environment.CurrentManagedThreadId, +#else + System.Threading.Thread.CurrentThread.ManagedThreadId, +#endif // NET6_0_OR_GREATER + text); } } } diff --git a/src/Renci.SshNet/Abstractions/DnsAbstraction.cs b/src/Renci.SshNet/Abstractions/DnsAbstraction.cs index 0af35aee..707521ea 100644 --- a/src/Renci.SshNet/Abstractions/DnsAbstraction.cs +++ b/src/Renci.SshNet/Abstractions/DnsAbstraction.cs @@ -1,6 +1,7 @@ using System; using System.Net; using System.Net.Sockets; +using System.Threading.Tasks; #if FEATURE_DNS_SYNC #elif FEATURE_DNS_APM @@ -87,5 +88,20 @@ namespace Renci.SshNet.Abstractions #endif // FEATURE_DEVICEINFORMATION_APM #endif } + + /// + /// Returns the Internet Protocol (IP) addresses for the specified host. + /// + /// The host name or IP address to resolve + /// + /// A task with result of an array of type that holds the IP addresses for the host that + /// is specified by the parameter. + /// + /// is null. + /// An error is encountered when resolving . + public static Task GetHostAddressesAsync(string hostNameOrAddress) + { + return Dns.GetHostAddressesAsync(hostNameOrAddress); + } } } diff --git a/src/Renci.SshNet/Abstractions/FileSystemAbstraction.cs b/src/Renci.SshNet/Abstractions/FileSystemAbstraction.cs deleted file mode 100644 index 1bce391e..00000000 --- a/src/Renci.SshNet/Abstractions/FileSystemAbstraction.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; - -namespace Renci.SshNet.Abstractions -{ - internal class FileSystemAbstraction - { - /// - /// Returns an enumerable collection of file information that matches a search pattern. - /// - /// - /// The search string to match against the names of files. - /// - /// An enumerable collection of files that matches . - /// - /// is null. - /// is null. - /// The path represented by does not exist or is not valid. - public static IEnumerable EnumerateFiles(DirectoryInfo directoryInfo, string searchPattern) - { - if (directoryInfo == null) - throw new ArgumentNullException("directoryInfo"); - -#if FEATURE_DIRECTORYINFO_ENUMERATEFILES - return directoryInfo.EnumerateFiles(searchPattern); -#else - return directoryInfo.GetFiles(searchPattern); -#endif - } - } -} diff --git a/src/Renci.SshNet/Abstractions/ReflectionAbstraction.cs b/src/Renci.SshNet/Abstractions/ReflectionAbstraction.cs index 0c7abf5c..1140c9a6 100644 --- a/src/Renci.SshNet/Abstractions/ReflectionAbstraction.cs +++ b/src/Renci.SshNet/Abstractions/ReflectionAbstraction.cs @@ -1,10 +1,6 @@ using System; using System.Collections.Generic; -#if FEATURE_REFLECTION_TYPEINFO -using System.Reflection; -#else using System.Linq; -#endif // FEATURE_REFLECTION_TYPEINFO namespace Renci.SshNet.Abstractions { @@ -13,12 +9,8 @@ namespace Renci.SshNet.Abstractions public static IEnumerable GetCustomAttributes(this Type type, bool inherit) where T:Attribute { -#if FEATURE_REFLECTION_TYPEINFO - return type.GetTypeInfo().GetCustomAttributes(inherit); -#else var attributes = type.GetCustomAttributes(typeof(T), inherit); return attributes.Cast(); -#endif } } } diff --git a/src/Renci.SshNet/Abstractions/SocketAbstraction.cs b/src/Renci.SshNet/Abstractions/SocketAbstraction.cs index 287caea6..a1fe71d7 100644 --- a/src/Renci.SshNet/Abstractions/SocketAbstraction.cs +++ b/src/Renci.SshNet/Abstractions/SocketAbstraction.cs @@ -3,6 +3,8 @@ using System.Globalization; using System.Net; using System.Net.Sockets; using System.Threading; +using System.Threading.Tasks; + using Renci.SshNet.Common; using Renci.SshNet.Messages.Transport; @@ -14,15 +16,10 @@ namespace Renci.SshNet.Abstractions { if (socket.Connected) { -#if FEATURE_SOCKET_POLL return socket.Poll(-1, SelectMode.SelectRead) && socket.Available > 0; -#else - return true; -#endif // FEATURE_SOCKET_POLL } return false; - } /// @@ -37,11 +34,7 @@ namespace Renci.SshNet.Abstractions { if (socket != null && socket.Connected) { -#if FEATURE_SOCKET_POLL return socket.Poll(-1, SelectMode.SelectWrite); -#else - return true; -#endif // FEATURE_SOCKET_POLL } return false; @@ -50,19 +43,24 @@ namespace Renci.SshNet.Abstractions public static Socket Connect(IPEndPoint remoteEndpoint, TimeSpan connectTimeout) { var socket = new Socket(remoteEndpoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp) { NoDelay = true }; - ConnectCore(socket, remoteEndpoint, connectTimeout, true); + ConnectCore(socket, remoteEndpoint, connectTimeout, ownsSocket: true); return socket; } public static void Connect(Socket socket, IPEndPoint remoteEndpoint, TimeSpan connectTimeout) { - ConnectCore(socket, remoteEndpoint, connectTimeout, false); + ConnectCore(socket, remoteEndpoint, connectTimeout, ownsSocket: false); + } + + public static async Task ConnectAsync(Socket socket, IPEndPoint remoteEndpoint, CancellationToken cancellationToken) + { + await socket.ConnectAsync(remoteEndpoint, cancellationToken).ConfigureAwait(false); } private static void ConnectCore(Socket socket, IPEndPoint remoteEndpoint, TimeSpan connectTimeout, bool ownsSocket) { #if FEATURE_SOCKET_EAP - var connectCompleted = new ManualResetEvent(false); + var connectCompleted = new ManualResetEvent(initialState: false); var args = new SocketAsyncEventArgs { UserToken = connectCompleted, @@ -81,8 +79,10 @@ namespace Renci.SshNet.Abstractions // dispose Socket socket.Dispose(); } + // dispose ManualResetEvent connectCompleted.Dispose(); + // dispose SocketAsyncEventArgs args.Dispose(); @@ -143,7 +143,6 @@ namespace Renci.SshNet.Abstractions public static int ReadPartial(Socket socket, byte[] buffer, int offset, int size, TimeSpan timeout) { -#if FEATURE_SOCKET_SYNC socket.ReceiveTimeout = (int) timeout.TotalMilliseconds; try @@ -153,57 +152,18 @@ namespace Renci.SshNet.Abstractions catch (SocketException ex) { if (ex.SocketErrorCode == SocketError.TimedOut) + { throw new SshOperationTimeoutException(string.Format(CultureInfo.InvariantCulture, - "Socket read operation has timed out after {0:F0} milliseconds.", timeout.TotalMilliseconds)); + "Socket read operation has timed out after {0:F0} milliseconds.", + timeout.TotalMilliseconds)); + } + throw; } -#elif FEATURE_SOCKET_EAP - var receiveCompleted = new ManualResetEvent(false); - var sendReceiveToken = new PartialSendReceiveToken(socket, receiveCompleted); - var args = new SocketAsyncEventArgs - { - RemoteEndPoint = socket.RemoteEndPoint, - UserToken = sendReceiveToken - }; - args.Completed += ReceiveCompleted; - args.SetBuffer(buffer, offset, size); - - try - { - if (socket.ReceiveAsync(args)) - { - if (!receiveCompleted.WaitOne(timeout)) - throw new SshOperationTimeoutException( - string.Format( - CultureInfo.InvariantCulture, - "Socket read operation has timed out after {0:F0} milliseconds.", - timeout.TotalMilliseconds)); - } - else - { - sendReceiveToken.Process(args); - } - - if (args.SocketError != SocketError.Success) - throw new SocketException((int) args.SocketError); - - return args.BytesTransferred; - } - finally - { - // initialize token to avoid the waithandle getting used after it's disposed - args.UserToken = null; - args.Dispose(); - receiveCompleted.Dispose(); - } -#else - #error Receiving data from a Socket is not implemented. -#endif } public static void ReadContinuous(Socket socket, byte[] buffer, int offset, int size, Action processReceivedBytesAction) { -#if FEATURE_SOCKET_SYNC // do not time-out receive socket.ReceiveTimeout = 0; @@ -213,15 +173,20 @@ namespace Renci.SshNet.Abstractions { var bytesRead = socket.Receive(buffer, offset, size, SocketFlags.None); if (bytesRead == 0) + { break; + } processReceivedBytesAction(buffer, offset, bytesRead); } catch (SocketException ex) { if (IsErrorResumable(ex.SocketErrorCode)) + { continue; + } +#pragma warning disable IDE0010 // Add missing cases switch (ex.SocketErrorCode) { case SocketError.ConnectionAborted: @@ -235,32 +200,9 @@ namespace Renci.SshNet.Abstractions default: throw; // throw any other error } +#pragma warning restore IDE0010 // Add missing cases } } -#elif FEATURE_SOCKET_EAP - var completionWaitHandle = new ManualResetEvent(false); - var readToken = new ContinuousReceiveToken(socket, processReceivedBytesAction, completionWaitHandle); - var args = new SocketAsyncEventArgs - { - RemoteEndPoint = socket.RemoteEndPoint, - UserToken = readToken - }; - args.Completed += ReceiveCompleted; - args.SetBuffer(buffer, offset, size); - - if (!socket.ReceiveAsync(args)) - { - ReceiveCompleted(null, args); - } - - completionWaitHandle.WaitOne(); - completionWaitHandle.Dispose(); - - if (readToken.Exception != null) - throw readToken.Exception; -#else - #error Receiving data from a Socket is not implemented. -#endif } /// @@ -277,7 +219,9 @@ namespace Renci.SshNet.Abstractions { var buffer = new byte[1]; if (Read(socket, buffer, 0, 1, timeout) == 0) + { return -1; + } return buffer[0]; } @@ -290,14 +234,14 @@ namespace Renci.SshNet.Abstractions /// The write failed. public static void SendByte(Socket socket, byte value) { - var buffer = new[] {value}; + var buffer = new[] { value }; Send(socket, buffer, 0, 1); } /// /// Receives data from a bound . /// - /// + /// The to read from. /// The number of bytes to receive. /// Specifies the amount of time after which the call will time out. /// @@ -313,14 +257,19 @@ namespace Renci.SshNet.Abstractions public static byte[] Read(Socket socket, int size, TimeSpan timeout) { var buffer = new byte[size]; - Read(socket, buffer, 0, size, timeout); + _ = Read(socket, buffer, 0, size, timeout); return buffer; } + public static Task ReadAsync(Socket socket, byte[] buffer, int offset, int length, CancellationToken cancellationToken) + { + return socket.ReceiveAsync(buffer, offset, length, cancellationToken); + } + /// /// Receives data from a bound into a receive buffer. /// - /// + /// The to read from. /// An array of type that is the storage location for the received data. /// The position in parameter to store the received data. /// The number of bytes to receive. @@ -341,7 +290,6 @@ namespace Renci.SshNet.Abstractions /// public static int Read(Socket socket, byte[] buffer, int offset, int size, TimeSpan readTimeout) { -#if FEATURE_SOCKET_SYNC var totalBytesRead = 0; var totalBytesToRead = size; @@ -353,7 +301,9 @@ namespace Renci.SshNet.Abstractions { var bytesRead = socket.Receive(buffer, offset + totalBytesRead, totalBytesToRead - totalBytesRead, SocketFlags.None); if (bytesRead == 0) + { return 0; + } totalBytesRead += bytesRead; } @@ -366,55 +316,18 @@ namespace Renci.SshNet.Abstractions } if (ex.SocketErrorCode == SocketError.TimedOut) + { throw new SshOperationTimeoutException(string.Format(CultureInfo.InvariantCulture, - "Socket read operation has timed out after {0:F0} milliseconds.", readTimeout.TotalMilliseconds)); + "Socket read operation has timed out after {0:F0} milliseconds.", + readTimeout.TotalMilliseconds)); + } - throw; + throw; } } while (totalBytesRead < totalBytesToRead); return totalBytesRead; -#elif FEATURE_SOCKET_EAP - var receiveCompleted = new ManualResetEvent(false); - var sendReceiveToken = new BlockingSendReceiveToken(socket, buffer, offset, size, receiveCompleted); - - var args = new SocketAsyncEventArgs - { - UserToken = sendReceiveToken, - RemoteEndPoint = socket.RemoteEndPoint - }; - args.Completed += ReceiveCompleted; - args.SetBuffer(buffer, offset, size); - - try - { - if (socket.ReceiveAsync(args)) - { - if (!receiveCompleted.WaitOne(readTimeout)) - throw new SshOperationTimeoutException(string.Format(CultureInfo.InvariantCulture, - "Socket read operation has timed out after {0:F0} milliseconds.", readTimeout.TotalMilliseconds)); - } - else - { - sendReceiveToken.Process(args); - } - - if (args.SocketError != SocketError.Success) - throw new SocketException((int) args.SocketError); - - return sendReceiveToken.TotalBytesTransferred; - } - finally - { - // initialize token to avoid the waithandle getting used after it's disposed - args.UserToken = null; - args.Dispose(); - receiveCompleted.Dispose(); - } -#else -#error Receiving data from a Socket is not implemented. -#endif } public static void Send(Socket socket, byte[] data) @@ -424,7 +337,6 @@ namespace Renci.SshNet.Abstractions public static void Send(Socket socket, byte[] data, int offset, int size) { -#if FEATURE_SOCKET_SYNC var totalBytesSent = 0; // how many bytes are already sent var totalBytesToSend = size; @@ -434,8 +346,10 @@ namespace Renci.SshNet.Abstractions { var bytesSent = socket.Send(data, offset + totalBytesSent, totalBytesToSend - totalBytesSent, SocketFlags.None); if (bytesSent == 0) + { throw new SshConnectionException("An established connection was aborted by the server.", DisconnectReason.ConnectionLost); + } totalBytesSent += bytesSent; } @@ -447,53 +361,17 @@ namespace Renci.SshNet.Abstractions ThreadAbstraction.Sleep(30); } else - throw; // any serious error occurr + { + throw; // any serious error occurr + } } - } while (totalBytesSent < totalBytesToSend); -#elif FEATURE_SOCKET_EAP - var sendCompleted = new ManualResetEvent(false); - var sendReceiveToken = new BlockingSendReceiveToken(socket, data, offset, size, sendCompleted); - var socketAsyncSendArgs = new SocketAsyncEventArgs - { - RemoteEndPoint = socket.RemoteEndPoint, - UserToken = sendReceiveToken - }; - socketAsyncSendArgs.SetBuffer(data, offset, size); - socketAsyncSendArgs.Completed += SendCompleted; - - try - { - if (socket.SendAsync(socketAsyncSendArgs)) - { - if (!sendCompleted.WaitOne()) - throw new SocketException((int) SocketError.TimedOut); - } - else - { - sendReceiveToken.Process(socketAsyncSendArgs); - } - - if (socketAsyncSendArgs.SocketError != SocketError.Success) - throw new SocketException((int) socketAsyncSendArgs.SocketError); - - if (sendReceiveToken.TotalBytesTransferred == 0) - throw new SshConnectionException("An established connection was aborted by the server.", - DisconnectReason.ConnectionLost); } - finally - { - // initialize token to avoid the completion waithandle getting used after it's disposed - socketAsyncSendArgs.UserToken = null; - socketAsyncSendArgs.Dispose(); - sendCompleted.Dispose(); - } -#else - #error Sending data to a Socket is not implemented. -#endif + while (totalBytesSent < totalBytesToSend); } public static bool IsErrorResumable(SocketError socketError) { +#pragma warning disable IDE0010 // Add missing cases switch (socketError) { case SocketError.WouldBlock: @@ -503,210 +381,15 @@ namespace Renci.SshNet.Abstractions default: return false; } +#pragma warning restore IDE0010 // Add missing cases } #if FEATURE_SOCKET_EAP private static void ConnectCompleted(object sender, SocketAsyncEventArgs e) { var eventWaitHandle = (ManualResetEvent) e.UserToken; - if (eventWaitHandle != null) - eventWaitHandle.Set(); + _ = eventWaitHandle?.Set(); } #endif // FEATURE_SOCKET_EAP - -#if FEATURE_SOCKET_EAP && !FEATURE_SOCKET_SYNC - private static void ReceiveCompleted(object sender, SocketAsyncEventArgs e) - { - var sendReceiveToken = (Token) e.UserToken; - if (sendReceiveToken != null) - sendReceiveToken.Process(e); - } - - private static void SendCompleted(object sender, SocketAsyncEventArgs e) - { - var sendReceiveToken = (Token) e.UserToken; - if (sendReceiveToken != null) - sendReceiveToken.Process(e); - } - - private interface Token - { - void Process(SocketAsyncEventArgs args); - } - - private class BlockingSendReceiveToken : Token - { - public BlockingSendReceiveToken(Socket socket, byte[] buffer, int offset, int size, EventWaitHandle completionWaitHandle) - { - _socket = socket; - _buffer = buffer; - _offset = offset; - _bytesToTransfer = size; - _completionWaitHandle = completionWaitHandle; - } - - public void Process(SocketAsyncEventArgs args) - { - if (args.SocketError == SocketError.Success) - { - TotalBytesTransferred += args.BytesTransferred; - - if (TotalBytesTransferred == _bytesToTransfer) - { - // finished transferring specified bytes - _completionWaitHandle.Set(); - return; - } - - if (args.BytesTransferred == 0) - { - // remote server closed the connection - _completionWaitHandle.Set(); - return; - } - - _offset += args.BytesTransferred; - args.SetBuffer(_buffer, _offset, _bytesToTransfer - TotalBytesTransferred); - ResumeOperation(args); - return; - } - - if (IsErrorResumable(args.SocketError)) - { - ThreadAbstraction.Sleep(30); - ResumeOperation(args); - return; - } - - // we're dealing with a (fatal) error - _completionWaitHandle.Set(); - } - - private void ResumeOperation(SocketAsyncEventArgs args) - { - switch (args.LastOperation) - { - case SocketAsyncOperation.Receive: - _socket.ReceiveAsync(args); - break; - case SocketAsyncOperation.Send: - _socket.SendAsync(args); - break; - } - } - - private readonly int _bytesToTransfer; - public int TotalBytesTransferred { get; private set; } - private readonly EventWaitHandle _completionWaitHandle; - private readonly Socket _socket; - private readonly byte[] _buffer; - private int _offset; - } - - private class PartialSendReceiveToken : Token - { - public PartialSendReceiveToken(Socket socket, EventWaitHandle completionWaitHandle) - { - _socket = socket; - _completionWaitHandle = completionWaitHandle; - } - - public void Process(SocketAsyncEventArgs args) - { - if (args.SocketError == SocketError.Success) - { - _completionWaitHandle.Set(); - return; - } - - if (IsErrorResumable(args.SocketError)) - { - ThreadAbstraction.Sleep(30); - ResumeOperation(args); - return; - } - - // we're dealing with a (fatal) error - _completionWaitHandle.Set(); - } - - private void ResumeOperation(SocketAsyncEventArgs args) - { - switch (args.LastOperation) - { - case SocketAsyncOperation.Receive: - _socket.ReceiveAsync(args); - break; - case SocketAsyncOperation.Send: - _socket.SendAsync(args); - break; - } - } - - private readonly EventWaitHandle _completionWaitHandle; - private readonly Socket _socket; - } - - private class ContinuousReceiveToken : Token - { - public ContinuousReceiveToken(Socket socket, Action processReceivedBytesAction, EventWaitHandle completionWaitHandle) - { - _socket = socket; - _processReceivedBytesAction = processReceivedBytesAction; - _completionWaitHandle = completionWaitHandle; - } - - public Exception Exception { get; private set; } - - public void Process(SocketAsyncEventArgs args) - { - if (args.SocketError == SocketError.Success) - { - if (args.BytesTransferred == 0) - { - // remote socket was closed - _completionWaitHandle.Set(); - return; - } - - _processReceivedBytesAction(args.Buffer, args.Offset, args.BytesTransferred); - ResumeOperation(args); - return; - } - - if (IsErrorResumable(args.SocketError)) - { - ThreadAbstraction.Sleep(30); - ResumeOperation(args); - return; - } - - if (args.SocketError != SocketError.OperationAborted) - { - Exception = new SocketException((int) args.SocketError); - } - - // we're dealing with a (fatal) error - _completionWaitHandle.Set(); - } - - private void ResumeOperation(SocketAsyncEventArgs args) - { - switch (args.LastOperation) - { - case SocketAsyncOperation.Receive: - _socket.ReceiveAsync(args); - break; - case SocketAsyncOperation.Send: - _socket.SendAsync(args); - break; - } - } - - private readonly EventWaitHandle _completionWaitHandle; - private readonly Socket _socket; - private readonly Action _processReceivedBytesAction; - } -#endif // FEATURE_SOCKET_EAP && !FEATURE_SOCKET_SYNC } } diff --git a/src/Renci.SshNet/Abstractions/SocketExtensions.cs b/src/Renci.SshNet/Abstractions/SocketExtensions.cs new file mode 100644 index 00000000..a51d0cb8 --- /dev/null +++ b/src/Renci.SshNet/Abstractions/SocketExtensions.cs @@ -0,0 +1,119 @@ +using System; +using System.Net; +using System.Net.Sockets; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; + +namespace Renci.SshNet.Abstractions +{ + // Async helpers based on https://devblogs.microsoft.com/pfxteam/awaiting-socket-operations/ + internal static class SocketExtensions + { + private sealed class SocketAsyncEventArgsAwaitable : SocketAsyncEventArgs, INotifyCompletion + { + private static readonly Action SENTINEL = () => { }; + + private bool _isCancelled; + private Action _continuationAction; + + public SocketAsyncEventArgsAwaitable() + { + Completed += delegate { SetCompleted(); }; + } + + public SocketAsyncEventArgsAwaitable ExecuteAsync(Func func) + { + if (!func(this)) + { + SetCompleted(); + } + return this; + } + + public void SetCompleted() + { + IsCompleted = true; + + var continuation = _continuationAction ?? Interlocked.CompareExchange(ref _continuationAction, SENTINEL, comparand: null); + if (continuation is not null) + { + continuation(); + } + } + + public void SetCancelled() + { + _isCancelled = true; + SetCompleted(); + } + + public SocketAsyncEventArgsAwaitable GetAwaiter() + { + return this; + } + + public bool IsCompleted { get; private set; } + + void INotifyCompletion.OnCompleted(Action continuation) + { + if (_continuationAction == SENTINEL || Interlocked.CompareExchange(ref _continuationAction, continuation, comparand: null) == SENTINEL) + { + // We have already completed; run continuation asynchronously + _ = Task.Run(continuation); + } + } + + public void GetResult() + { + if (_isCancelled) + { + throw new TaskCanceledException(); + } + + if (!IsCompleted) + { + // We don't support sync/async + throw new InvalidOperationException("The asynchronous operation has not yet completed."); + } + + if (SocketError != SocketError.Success) + { + throw new SocketException((int)SocketError); + } + } + } + + public static async Task ConnectAsync(this Socket socket, IPEndPoint remoteEndpoint, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + using (var args = new SocketAsyncEventArgsAwaitable()) + { + args.RemoteEndPoint = remoteEndpoint; + + using (cancellationToken.Register(o => ((SocketAsyncEventArgsAwaitable)o).SetCancelled(), args, useSynchronizationContext: false)) + { + await args.ExecuteAsync(socket.ConnectAsync); + } + } + } + + public static async Task ReceiveAsync(this Socket socket, byte[] buffer, int offset, int length, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + using (var args = new SocketAsyncEventArgsAwaitable()) + { + args.SetBuffer(buffer, offset, length); + + using (cancellationToken.Register(o => ((SocketAsyncEventArgsAwaitable)o).SetCancelled(), args, useSynchronizationContext: false)) + { + await args.ExecuteAsync(socket.ReceiveAsync); + } + + return args.BytesTransferred; + } + } + } +} diff --git a/src/Renci.SshNet/Abstractions/ThreadAbstraction.cs b/src/Renci.SshNet/Abstractions/ThreadAbstraction.cs index ee21ec7e..0c9e3b64 100644 --- a/src/Renci.SshNet/Abstractions/ThreadAbstraction.cs +++ b/src/Renci.SshNet/Abstractions/ThreadAbstraction.cs @@ -10,24 +10,18 @@ namespace Renci.SshNet.Abstractions /// The number of milliseconds for which the thread is suspended. public static void Sleep(int millisecondsTimeout) { -#if FEATURE_THREAD_SLEEP System.Threading.Thread.Sleep(millisecondsTimeout); -#elif FEATURE_THREAD_TAP - System.Threading.Tasks.Task.Delay(millisecondsTimeout).GetAwaiter().GetResult(); -#else - #error Suspend of the current thread is not implemented. -#endif } public static void ExecuteThreadLongRunning(Action action) { -#if FEATURE_THREAD_TAP + if (action is null) + { + throw new ArgumentNullException(nameof(action)); + } + var taskCreationOptions = System.Threading.Tasks.TaskCreationOptions.LongRunning; - System.Threading.Tasks.Task.Factory.StartNew(action, taskCreationOptions); -#else - var thread = new System.Threading.Thread(() => action()); - thread.Start(); -#endif + _ = System.Threading.Tasks.Task.Factory.StartNew(action, taskCreationOptions); } /// @@ -36,16 +30,12 @@ namespace Renci.SshNet.Abstractions /// The action to execute. public static void ExecuteThread(Action action) { -#if FEATURE_THREAD_THREADPOOL - if (action == null) - throw new ArgumentNullException("action"); + if (action is null) + { + throw new ArgumentNullException(nameof(action)); + } - System.Threading.ThreadPool.QueueUserWorkItem(o => action()); -#elif FEATURE_THREAD_TAP - System.Threading.Tasks.Task.Run(action); -#else - #error Execution of action in a separate thread is not implemented. -#endif + _ = System.Threading.ThreadPool.QueueUserWorkItem(o => action()); } } } diff --git a/src/Renci.SshNet/AuthenticationMethod.cs b/src/Renci.SshNet/AuthenticationMethod.cs index 1a285d8c..99a5e102 100644 --- a/src/Renci.SshNet/AuthenticationMethod.cs +++ b/src/Renci.SshNet/AuthenticationMethod.cs @@ -1,10 +1,9 @@ -using Renci.SshNet.Common; -using System; +using System; namespace Renci.SshNet { /// - /// Base class for all supported authentication methods + /// Base class for all supported authentication methods. /// public abstract class AuthenticationMethod : IAuthenticationMethod { @@ -22,7 +21,7 @@ namespace Renci.SshNet public string Username { get; private set; } /// - /// Gets list of allowed authentications. + /// Gets or sets the list of allowed authentications. /// public string[] AllowedAuthentications { get; protected set; } @@ -33,8 +32,10 @@ namespace Renci.SshNet /// is whitespace or null. protected AuthenticationMethod(string username) { - if (username.IsNullOrWhiteSpace()) + if (string.IsNullOrWhiteSpace(username)) + { throw new ArgumentException("username"); + } Username = username; } diff --git a/src/Renci.SshNet/BaseClient.cs b/src/Renci.SshNet/BaseClient.cs index 5b0e01c9..a879c02f 100644 --- a/src/Renci.SshNet/BaseClient.cs +++ b/src/Renci.SshNet/BaseClient.cs @@ -1,6 +1,8 @@ using System; using System.Net.Sockets; using System.Threading; +using System.Threading.Tasks; + using Renci.SshNet.Abstractions; using Renci.SshNet.Common; using Renci.SshNet.Messages.Transport; @@ -10,7 +12,7 @@ namespace Renci.SshNet /// /// Serves as base class for client implementations, provides common client functionality. /// - public abstract class BaseClient : IDisposable + public abstract class BaseClient : IBaseClient, IDisposable { /// /// Holds value indicating whether the connection info is owned by this client. @@ -22,6 +24,7 @@ namespace Renci.SshNet private TimeSpan _keepAliveInterval; private Timer _keepAliveTimer; private ConnectionInfo _connectionInfo; + private bool _isDisposed; /// /// Gets the current session. @@ -99,7 +102,9 @@ namespace Renci.SshNet CheckDisposed(); if (value == _keepAliveInterval) + { return; + } if (value == SshNet.Session.InfiniteTimeSpan) { @@ -112,8 +117,7 @@ namespace Renci.SshNet { // change the due time and interval of the timer if has already // been created (which means the client is connected) - - _keepAliveTimer.Change(value, value); + _ = _keepAliveTimer.Change(value, value); } else if (IsSessionConnected()) { @@ -125,9 +129,10 @@ namespace Renci.SshNet _keepAliveTimer = CreateKeepAliveTimer(value, value); } - // note that if the client is not yet connected, then the timer will be created with the + // note that if the client is not yet connected, then the timer will be created with the // new interval when Connect() is invoked } + _keepAliveInterval = value; } } @@ -175,12 +180,17 @@ namespace Renci.SshNet /// If is true, then the /// connection info will be disposed when this instance is disposed. /// - internal BaseClient(ConnectionInfo connectionInfo, bool ownsConnectionInfo, IServiceFactory serviceFactory) + private protected BaseClient(ConnectionInfo connectionInfo, bool ownsConnectionInfo, IServiceFactory serviceFactory) { - if (connectionInfo == null) - throw new ArgumentNullException("connectionInfo"); - if (serviceFactory == null) - throw new ArgumentNullException("serviceFactory"); + if (connectionInfo is null) + { + throw new ArgumentNullException(nameof(connectionInfo)); + } + + if (serviceFactory is null) + { + throw new ArgumentNullException(nameof(serviceFactory)); + } ConnectionInfo = connectionInfo; _ownsConnectionInfo = ownsConnectionInfo; @@ -203,26 +213,29 @@ namespace Renci.SshNet // TODO (see issue #1758): // we're not stopping the keep-alive timer and disposing the session here - // + // // we could do this but there would still be side effects as concrete // implementations may still hang on to the original session - // + // // therefore it would be better to actually invoke the Disconnect method // (and then the Dispose on the session) but even that would have side effects // eg. it would remove all forwarded ports from SshClient - // + // // I think we should modify our concrete clients to better deal with a - // disconnect. In case of SshClient this would mean not removing the + // disconnect. In case of SshClient this would mean not removing the // forwarded ports on disconnect (but only on dispose ?) and link a // forwarded port with a client instead of with a session // // To be discussed with Oleg (or whoever is interested) if (IsSessionConnected()) + { throw new InvalidOperationException("The client is already connected."); + } OnConnecting(); Session = CreateAndConnectSession(); + try { // Even though the method we invoke makes you believe otherwise, at this point only @@ -236,6 +249,66 @@ namespace Renci.SshNet DisposeSession(); throw; } + + StartKeepAliveTimer(); + } + + /// + /// Asynchronously connects client to the server. + /// + /// The to observe. + /// A that represents the asynchronous connect operation. + /// + /// The client is already connected. + /// The method was called after the client was disposed. + /// Socket connection to the SSH server or proxy server could not be established, or an error occurred while resolving the hostname. + /// SSH session could not be established. + /// Authentication of SSH session failed. + /// Failed to establish proxy connection. + public async Task ConnectAsync(CancellationToken cancellationToken) + { + CheckDisposed(); + cancellationToken.ThrowIfCancellationRequested(); + + // TODO (see issue #1758): + // we're not stopping the keep-alive timer and disposing the session here + // + // we could do this but there would still be side effects as concrete + // implementations may still hang on to the original session + // + // therefore it would be better to actually invoke the Disconnect method + // (and then the Dispose on the session) but even that would have side effects + // eg. it would remove all forwarded ports from SshClient + // + // I think we should modify our concrete clients to better deal with a + // disconnect. In case of SshClient this would mean not removing the + // forwarded ports on disconnect (but only on dispose ?) and link a + // forwarded port with a client instead of with a session + // + // To be discussed with Oleg (or whoever is interested) + if (IsSessionConnected()) + { + throw new InvalidOperationException("The client is already connected."); + } + + OnConnecting(); + + Session = await CreateAndConnectSessionAsync(cancellationToken).ConfigureAwait(false); + + try + { + // Even though the method we invoke makes you believe otherwise, at this point only + // the SSH session itself is connected. + OnConnected(); + } + catch + { + // Only dispose the session as Disconnect() would have side-effects (such as remove forwarded + // ports in SshClient). + DisposeSession(); + throw; + } + StartKeepAliveTimer(); } @@ -295,11 +368,7 @@ namespace Renci.SshNet /// protected virtual void OnDisconnecting() { - var session = Session; - if (session != null) - { - session.OnDisconnecting(); - } + Session?.OnDisconnecting(); } /// @@ -311,26 +380,14 @@ namespace Renci.SshNet private void Session_ErrorOccured(object sender, ExceptionEventArgs e) { - var handler = ErrorOccurred; - if (handler != null) - { - handler(this, e); - } + ErrorOccurred?.Invoke(this, e); } private void Session_HostKeyReceived(object sender, HostKeyEventArgs e) { - var handler = HostKeyReceived; - if (handler != null) - { - handler(this, e); - } + HostKeyReceived?.Invoke(this, e); } -#region IDisposable Members - - private bool _isDisposed; - /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// @@ -338,18 +395,20 @@ namespace Renci.SshNet { DiagnosticAbstraction.Log("Disposing client."); - Dispose(true); + Dispose(disposing: true); GC.SuppressFinalize(this); } /// - /// Releases unmanaged and - optionally - managed resources + /// Releases unmanaged and - optionally - managed resources. /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected virtual void Dispose(bool disposing) { if (_isDisposed) + { return; + } if (disposing) { @@ -357,9 +416,10 @@ namespace Renci.SshNet if (_ownsConnectionInfo && _connectionInfo != null) { - var connectionInfoDisposable = _connectionInfo as IDisposable; - if (connectionInfoDisposable != null) + if (_connectionInfo is IDisposable connectionInfoDisposable) + { connectionInfoDisposable.Dispose(); + } _connectionInfo = null; } @@ -374,28 +434,29 @@ namespace Renci.SshNet protected void CheckDisposed() { if (_isDisposed) + { throw new ObjectDisposedException(GetType().FullName); + } } /// - /// Releases unmanaged resources and performs other cleanup operations before the - /// is reclaimed by garbage collection. + /// Finalizes an instance of the class. /// ~BaseClient() { - Dispose(false); + Dispose(disposing: false); } -#endregion - /// /// Stops the keep-alive timer, and waits until all timer callbacks have been /// executed. /// private void StopKeepAliveTimer() { - if (_keepAliveTimer == null) + if (_keepAliveTimer is null) + { return; + } _keepAliveTimer.Dispose(); _keepAliveTimer = null; @@ -406,15 +467,17 @@ namespace Renci.SshNet var session = Session; // do nothing if we have disposed or disconnected - if (session == null) + if (session is null) + { return; + } // do not send multiple keep-alive messages concurrently if (Monitor.TryEnter(_keepAliveLock)) { try { - session.TrySendMessage(new IgnoreMessage()); + _ = session.TrySendMessage(new IgnoreMessage()); } finally { @@ -433,11 +496,15 @@ namespace Renci.SshNet private void StartKeepAliveTimer() { if (_keepAliveInterval == SshNet.Session.InfiniteTimeSpan) + { return; + } if (_keepAliveTimer != null) + { // timer is already started return; + } _keepAliveTimer = CreateKeepAliveTimer(_keepAliveInterval, _keepAliveInterval); } @@ -473,6 +540,24 @@ namespace Renci.SshNet } } + private async Task CreateAndConnectSessionAsync(CancellationToken cancellationToken) + { + var session = _serviceFactory.CreateSession(ConnectionInfo, _serviceFactory.CreateSocketFactory()); + session.HostKeyReceived += Session_HostKeyReceived; + session.ErrorOccured += Session_ErrorOccured; + + try + { + await session.ConnectAsync(cancellationToken).ConfigureAwait(false); + return session; + } + catch + { + DisposeSession(session); + throw; + } + } + private void DisposeSession(ISession session) { session.ErrorOccured -= Session_ErrorOccured; diff --git a/src/Renci.SshNet/Channels/Channel.cs b/src/Renci.SshNet/Channels/Channel.cs index a6311a98..d3ec7ca6 100644 --- a/src/Renci.SshNet/Channels/Channel.cs +++ b/src/Renci.SshNet/Channels/Channel.cs @@ -1,11 +1,12 @@ using System; +using System.Globalization; using System.Net.Sockets; using System.Threading; + +using Renci.SshNet.Abstractions; using Renci.SshNet.Common; using Renci.SshNet.Messages; using Renci.SshNet.Messages.Connection; -using System.Globalization; -using Renci.SshNet.Abstractions; namespace Renci.SshNet.Channels { @@ -14,14 +15,15 @@ namespace Renci.SshNet.Channels /// internal abstract class Channel : IChannel { - private EventWaitHandle _channelClosedWaitHandle = new ManualResetEvent(false); - private EventWaitHandle _channelServerWindowAdjustWaitHandle = new ManualResetEvent(false); private readonly object _serverWindowSizeLock = new object(); private readonly uint _initialWindowSize; + private EventWaitHandle _channelClosedWaitHandle = new ManualResetEvent(initialState: false); + private EventWaitHandle _channelServerWindowAdjustWaitHandle = new ManualResetEvent(initialState: false); private uint? _remoteWindowSize; private uint? _remoteChannelNumber; private uint? _remotePacketSize; - private ISession _session; + private readonly ISession _session; + private bool _isDisposed; /// /// Holds a value indicating whether the SSH_MSG_CHANNEL_CLOSE has been sent to the remote party. @@ -66,7 +68,7 @@ namespace Renci.SshNet.Channels public event EventHandler Exception; /// - /// Initializes a new instance. + /// Initializes a new instance of the class. /// /// The session. /// The local channel number. @@ -155,7 +157,10 @@ namespace Renci.SshNet.Channels get { if (!_remoteChannelNumber.HasValue) + { throw CreateRemoteChannelInfoNotAvailableException(); + } + return _remoteChannelNumber.Value; } private set @@ -177,7 +182,10 @@ namespace Renci.SshNet.Channels get { if (!_remotePacketSize.HasValue) + { throw CreateRemoteChannelInfoNotAvailableException(); + } + return _remotePacketSize.Value; } private set @@ -197,7 +205,10 @@ namespace Renci.SshNet.Channels get { if (!_remoteWindowSize.HasValue) + { throw CreateRemoteChannelInfoNotAvailableException(); + } + return _remoteWindowSize.Value; } private set @@ -207,15 +218,13 @@ namespace Renci.SshNet.Channels } /// - /// Gets a value indicating whether this channel is open. + /// Gets or sets a value indicating whether this channel is open. /// /// /// true if this channel is open; otherwise, false. /// public bool IsOpen { get; protected set; } - #region Message events - /// /// Occurs when is received. /// @@ -251,8 +260,6 @@ namespace Renci.SshNet.Channels /// public event EventHandler RequestFailed; - #endregion - /// /// Gets a value indicating whether the session is connected. /// @@ -282,6 +289,12 @@ namespace Renci.SshNet.Channels get { return _session.SessionSemaphore; } } + /// + /// Initializes the information on the remote channel. + /// + /// The remote channel number. + /// The remote window size. + /// The remote packet size. protected void InitializeRemoteInfo(uint remoteChannelNumber, uint remoteWindowSize, uint remotePacketSize) { RemoteChannelNumber = remoteChannelNumber; @@ -320,7 +333,9 @@ namespace Renci.SshNet.Channels { // send channel messages only while channel is open if (!IsOpen) + { return; + } var totalBytesToSend = size; while (totalBytesToSend > 0) @@ -338,8 +353,6 @@ namespace Renci.SshNet.Channels } } - #region Channel virtual methods - /// /// Called when channel window need to be adjust. /// @@ -350,7 +363,8 @@ namespace Renci.SshNet.Channels { RemoteWindowSize += bytesToAdd; } - _channelServerWindowAdjustWaitHandle.Set(); + + _ = _channelServerWindowAdjustWaitHandle.Set(); } /// @@ -361,9 +375,7 @@ namespace Renci.SshNet.Channels { AdjustDataWindow(data); - var dataReceived = DataReceived; - if (dataReceived != null) - dataReceived(this, new ChannelDataEventArgs(LocalChannelNumber, data)); + DataReceived?.Invoke(this, new ChannelDataEventArgs(LocalChannelNumber, data)); } /// @@ -375,9 +387,7 @@ namespace Renci.SshNet.Channels { AdjustDataWindow(data); - var extendedDataReceived = ExtendedDataReceived; - if (extendedDataReceived != null) - extendedDataReceived(this, new ChannelExtendedDataEventArgs(LocalChannelNumber, data, dataTypeCode)); + ExtendedDataReceived?.Invoke(this, new ChannelExtendedDataEventArgs(LocalChannelNumber, data, dataTypeCode)); } /// @@ -387,9 +397,7 @@ namespace Renci.SshNet.Channels { _eofMessageReceived = true; - var endOfData = EndOfData; - if (endOfData != null) - endOfData(this, new ChannelEventArgs(LocalChannelNumber)); + EndOfData?.Invoke(this, new ChannelEventArgs(LocalChannelNumber)); } /// @@ -404,7 +412,9 @@ namespace Renci.SshNet.Channels // be blocked waiting for this signal. var channelClosedWaitHandle = _channelClosedWaitHandle; if (channelClosedWaitHandle != null) - channelClosedWaitHandle.Set(); + { + _ = channelClosedWaitHandle.Set(); + } // close the channel Close(); @@ -416,19 +426,15 @@ namespace Renci.SshNet.Channels /// Channel request information. protected virtual void OnRequest(RequestInfo info) { - var requestReceived = RequestReceived; - if (requestReceived != null) - requestReceived(this, new ChannelRequestEventArgs(info)); + RequestReceived?.Invoke(this, new ChannelRequestEventArgs(info)); } /// - /// Called when channel request was successful + /// Called when channel request was successful. /// protected virtual void OnSuccess() { - var requestSuccessed = RequestSucceeded; - if (requestSuccessed != null) - requestSuccessed(this, new ChannelEventArgs(LocalChannelNumber)); + RequestSucceeded?.Invoke(this, new ChannelEventArgs(LocalChannelNumber)); } /// @@ -436,24 +442,16 @@ namespace Renci.SshNet.Channels /// protected virtual void OnFailure() { - var requestFailed = RequestFailed; - if (requestFailed != null) - requestFailed(this, new ChannelEventArgs(LocalChannelNumber)); + RequestFailed?.Invoke(this, new ChannelEventArgs(LocalChannelNumber)); } - #endregion // Channel virtual methods - /// /// Raises event. /// /// The exception. private void RaiseExceptionEvent(Exception exception) { - var handlers = Exception; - if (handlers != null) - { - handlers(this, new ExceptionEventArgs(exception)); - } + Exception?.Invoke(this, new ExceptionEventArgs(exception)); } /// @@ -479,9 +477,11 @@ namespace Renci.SshNet.Channels /// The message. protected void SendMessage(Message message) { - // send channel messages only while channel is open + // Send channel messages only while channel is open if (!IsOpen) + { return; + } _session.SendMessage(message); } @@ -493,7 +493,9 @@ namespace Renci.SshNet.Channels public void SendEof() { if (!IsOpen) + { throw CreateChannelClosedException(); + } lock (this) { @@ -516,14 +518,16 @@ namespace Renci.SshNet.Channels /// protected virtual void Close() { - // synchronize sending SSH_MSG_CHANNEL_EOF and SSH_MSG_CHANNEL_CLOSE to ensure that these messages - // are sent in that other; when both the client and the server attempt to close the channel at the - // same time we would otherwise risk sending the SSH_MSG_CHANNEL_EOF after the SSH_MSG_CHANNEL_CLOSE - // message causing the server to disconnect the session. + /* + * Synchronize sending SSH_MSG_CHANNEL_EOF and SSH_MSG_CHANNEL_CLOSE to ensure that these messages + * are sent in that other; when both the client and the server attempt to close the channel at the + * same time we would otherwise risk sending the SSH_MSG_CHANNEL_EOF after the SSH_MSG_CHANNEL_CLOSE + * message causing the server to disconnect the session. + */ lock (this) { - // send EOF message first the following conditions are met: + // Send EOF message first the following conditions are met: // * we have not sent a SSH_MSG_CHANNEL_EOF message // * remote party has not already sent a SSH_MSG_CHANNEL_EOF message // * remote party has not already sent a SSH_MSG_CHANNEL_CLOSE message @@ -565,11 +569,7 @@ namespace Renci.SshNet.Channels if (_closeMessageReceived) { // raise event signaling that both ends of the channel have been closed - var closed = Closed; - if (closed != null) - { - closed(this, new ChannelEventArgs(LocalChannelNumber)); - } + Closed?.Invoke(this, new ChannelEventArgs(LocalChannelNumber)); } } } @@ -623,8 +623,6 @@ namespace Renci.SshNet.Channels } } - #region Channel message event handlers - private void OnChannelWindowAdjust(object sender, MessageEventArgs e) { if (e.Message.LocalChannelNumber == LocalChannelNumber) @@ -706,14 +704,13 @@ namespace Renci.SshNet.Channels { try { - RequestInfo requestInfo; - if (_session.ConnectionInfo.ChannelRequests.TryGetValue(e.Message.RequestName, out requestInfo)) + if (_session.ConnectionInfo.ChannelRequests.TryGetValue(e.Message.RequestName, out var requestInfo)) { - // Load request specific data + // Load request specific data requestInfo.Load(e.Message.RequestData); - // Raise request specific event + // Raise request specific event OnRequest(requestInfo); } else @@ -759,13 +756,11 @@ namespace Renci.SshNet.Channels } } - #endregion // Channel message event handlers - private void AdjustDataWindow(byte[] messageData) { - LocalWindowSize -= (uint)messageData.Length; + LocalWindowSize -= (uint) messageData.Length; - // Adjust window if window size is too low + // Adjust window if window size is too low if (LocalWindowSize < LocalPacketSize) { SendMessage(new ChannelWindowAdjustMessage(RemoteChannelNumber, _initialWindowSize - LocalWindowSize)); @@ -789,8 +784,8 @@ namespace Renci.SshNet.Channels var serverWindowSize = RemoteWindowSize; if (serverWindowSize == 0U) { - // allow us to be signal when remote window size is adjusted - _channelServerWindowAdjustWaitHandle.Reset(); + // Allow us to be signal when remote window size is adjusted + _ = _channelServerWindowAdjustWaitHandle.Reset(); } else { @@ -800,9 +795,11 @@ namespace Renci.SshNet.Channels return (int) bytesThatCanBeSent; } } - // wait for remote window size to change + + // Wait for remote window size to change WaitOnHandle(_channelServerWindowAdjustWaitHandle); - } while (true); + } + while (true); } private static InvalidOperationException CreateRemoteChannelInfoNotAvailableException() @@ -815,36 +812,28 @@ namespace Renci.SshNet.Channels throw new InvalidOperationException("The channel is closed."); } - #region IDisposable Members - - private bool _isDisposed; - /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// public void Dispose() { - Dispose(true); + Dispose(disposing: true); GC.SuppressFinalize(this); } /// - /// Releases unmanaged and - optionally - managed resources + /// Releases unmanaged and - optionally - managed resources. /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected virtual void Dispose(bool disposing) { - if (_isDisposed) - return; - - if (disposing) + if (!_isDisposed && disposing) { Close(); var session = _session; if (session != null) { - _session = null; session.ChannelWindowAdjustReceived -= OnChannelWindowAdjust; session.ChannelDataReceived -= OnChannelData; session.ChannelExtendedDataReceived -= OnChannelExtendedData; @@ -876,14 +865,11 @@ namespace Renci.SshNet.Channels } /// - /// Releases unmanaged resources and performs other cleanup operations before the - /// is reclaimed by garbage collection. + /// Finalizes an instance of the class. /// ~Channel() { - Dispose(false); + Dispose(disposing: false); } - - #endregion // IDisposable Members } } diff --git a/src/Renci.SshNet/Channels/ChannelDirectTcpip.cs b/src/Renci.SshNet/Channels/ChannelDirectTcpip.cs index b6ba7a0b..6c521bce 100644 --- a/src/Renci.SshNet/Channels/ChannelDirectTcpip.cs +++ b/src/Renci.SshNet/Channels/ChannelDirectTcpip.cs @@ -11,17 +11,17 @@ namespace Renci.SshNet.Channels /// /// Implements "direct-tcpip" SSH channel. /// - internal class ChannelDirectTcpip : ClientChannel, IChannelDirectTcpip + internal sealed class ChannelDirectTcpip : ClientChannel, IChannelDirectTcpip { private readonly object _socketLock = new object(); - private EventWaitHandle _channelOpen = new AutoResetEvent(false); - private EventWaitHandle _channelData = new AutoResetEvent(false); + private EventWaitHandle _channelOpen = new AutoResetEvent(initialState: false); + private EventWaitHandle _channelData = new AutoResetEvent(initialState: false); private IForwardedPort _forwardedPort; private Socket _socket; /// - /// Initializes a new instance. + /// Initializes a new instance of the class. /// /// The session. /// The local channel number. @@ -46,9 +46,14 @@ namespace Renci.SshNet.Channels public void Open(string remoteHost, uint port, IForwardedPort forwardedPort, Socket socket) { if (IsOpen) + { throw new SshException("Channel is already open."); + } + if (!IsConnected) + { throw new SshException("Session is not connected."); + } _socket = socket; _forwardedPort = forwardedPort; @@ -56,10 +61,13 @@ namespace Renci.SshNet.Channels var ep = (IPEndPoint) socket.RemoteEndPoint; - // open channel - SendMessage(new ChannelOpenMessage(LocalChannelNumber, LocalWindowSize, LocalPacketSize, - new DirectTcpipChannelInfo(remoteHost, port, ep.Address.ToString(), (uint) ep.Port))); - // Wait for channel to open + // Open channel + SendMessage(new ChannelOpenMessage(LocalChannelNumber, + LocalWindowSize, + LocalPacketSize, + new DirectTcpipChannelInfo(remoteHost, port, ep.Address.ToString(), (uint) ep.Port))); + + // Wait for channel to open WaitOnHandle(_channelOpen); } @@ -82,9 +90,11 @@ namespace Renci.SshNet.Channels /// public void Bind() { - // Cannot bind if channel is not open + // Cannot bind if channel is not open if (!IsOpen) + { return; + } var buffer = new byte[RemotePacketSize]; @@ -103,13 +113,17 @@ namespace Renci.SshNet.Channels /// private void CloseSocket() { - if (_socket == null) + if (_socket is null) + { return; + } lock (_socketLock) { - if (_socket == null) + if (_socket is null) + { return; + } // closing a socket actually disposes the socket, so we can safely dereference // the field to avoid entering the lock again later @@ -124,13 +138,17 @@ namespace Renci.SshNet.Channels /// One of the values that specifies the operation that will no longer be allowed. private void ShutdownSocket(SocketShutdown how) { - if (_socket == null) + if (_socket is null) + { return; + } lock (_socketLock) { if (!_socket.IsConnected()) + { return; + } try { @@ -199,14 +217,14 @@ namespace Renci.SshNet.Channels { base.OnOpenConfirmation(remoteChannelNumber, initialWindowSize, maximumPacketSize); - _channelOpen.Set(); + _ = _channelOpen.Set(); } protected override void OnOpenFailure(uint reasonCode, string description, string language) { base.OnOpenFailure(reasonCode, description, language); - _channelOpen.Set(); + _ = _channelOpen.Set(); } /// diff --git a/src/Renci.SshNet/Channels/ChannelForwardedTcpip.cs b/src/Renci.SshNet/Channels/ChannelForwardedTcpip.cs index 7d731b9e..0d67c7af 100644 --- a/src/Renci.SshNet/Channels/ChannelForwardedTcpip.cs +++ b/src/Renci.SshNet/Channels/ChannelForwardedTcpip.cs @@ -10,7 +10,7 @@ namespace Renci.SshNet.Channels /// /// Implements "forwarded-tcpip" SSH channel. /// - internal class ChannelForwardedTcpip : ServerChannel, IChannelForwardedTcpip + internal sealed class ChannelForwardedTcpip : ServerChannel, IChannelForwardedTcpip { private readonly object _socketShutdownAndCloseLock = new object(); private Socket _socket; @@ -119,14 +119,18 @@ namespace Renci.SshNet.Channels /// One of the values that specifies the operation that will no longer be allowed. private void ShutdownSocket(SocketShutdown how) { - if (_socket == null) + if (_socket is null) + { return; + } lock (_socketShutdownAndCloseLock) { var socket = _socket; if (!socket.IsConnected()) + { return; + } try { @@ -145,8 +149,10 @@ namespace Renci.SshNet.Channels /// private void CloseSocket() { - if (_socket == null) + if (_socket is null) + { return; + } lock (_socketShutdownAndCloseLock) { diff --git a/src/Renci.SshNet/Channels/ChannelSession.cs b/src/Renci.SshNet/Channels/ChannelSession.cs index 38e13096..3b685c42 100644 --- a/src/Renci.SshNet/Channels/ChannelSession.cs +++ b/src/Renci.SshNet/Channels/ChannelSession.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Globalization; using System.Threading; + using Renci.SshNet.Common; using Renci.SshNet.Messages.Connection; @@ -13,7 +14,7 @@ namespace Renci.SshNet.Channels internal sealed class ChannelSession : ClientChannel, IChannelSession { /// - /// Counts failed channel open attempts + /// Counts failed channel open attempts. /// private int _failedOpenAttempts; @@ -28,16 +29,16 @@ namespace Renci.SshNet.Channels private int _sessionSemaphoreObtained; /// - /// Wait handle to signal when response was received to open the channel + /// Wait handle to signal when response was received to open the channel. /// - private EventWaitHandle _channelOpenResponseWaitHandle = new AutoResetEvent(false); + private EventWaitHandle _channelOpenResponseWaitHandle = new AutoResetEvent(initialState: false); - private EventWaitHandle _channelRequestResponse = new ManualResetEvent(false); + private EventWaitHandle _channelRequestResponse = new ManualResetEvent(initialState: false); private bool _channelRequestSucces; /// - /// Initializes a new instance. + /// Initializes a new instance of the class. /// /// The session. /// The local channel number. @@ -64,7 +65,7 @@ namespace Renci.SshNet.Channels /// public void Open() { - // Try to open channel several times + // Try to open channel several times while (!IsOpen && _failedOpenAttempts < ConnectionInfo.RetryAttempts) { SendChannelOpenMessage(); @@ -81,7 +82,9 @@ namespace Renci.SshNet.Channels } if (!IsOpen) + { throw new SshException(string.Format(CultureInfo.CurrentCulture, "Failed to open a channel after {0} attempts.", _failedOpenAttempts)); + } } /// @@ -93,7 +96,8 @@ namespace Renci.SshNet.Channels protected override void OnOpenConfirmation(uint remoteChannelNumber, uint initialWindowSize, uint maximumPacketSize) { base.OnOpenConfirmation(remoteChannelNumber, initialWindowSize, maximumPacketSize); - _channelOpenResponseWaitHandle.Set(); + + _ = _channelOpenResponseWaitHandle.Set(); } /// @@ -106,7 +110,7 @@ namespace Renci.SshNet.Channels { _failedOpenAttempts++; ReleaseSemaphore(); - _channelOpenResponseWaitHandle.Set(); + _ = _channelOpenResponseWaitHandle.Set(); } protected override void Close() @@ -129,7 +133,7 @@ namespace Renci.SshNet.Channels /// public bool SendPseudoTerminalRequest(string environmentVariable, uint columns, uint rows, uint width, uint height, IDictionary terminalModeValues) { - _channelRequestResponse.Reset(); + _ = _channelRequestResponse.Reset(); SendMessage(new ChannelRequestMessage(RemoteChannelNumber, new PseudoTerminalRequestInfo(environmentVariable, columns, rows, width, height, terminalModeValues))); WaitOnHandle(_channelRequestResponse); return _channelRequestSucces; @@ -147,7 +151,7 @@ namespace Renci.SshNet.Channels /// public bool SendX11ForwardingRequest(bool isSingleConnection, string protocol, byte[] cookie, uint screenNumber) { - _channelRequestResponse.Reset(); + _ = _channelRequestResponse.Reset(); SendMessage(new ChannelRequestMessage(RemoteChannelNumber, new X11ForwardingRequestInfo(isSingleConnection, protocol, cookie, screenNumber))); WaitOnHandle(_channelRequestResponse); return _channelRequestSucces; @@ -163,7 +167,7 @@ namespace Renci.SshNet.Channels /// public bool SendEnvironmentVariableRequest(string variableName, string variableValue) { - _channelRequestResponse.Reset(); + _ = _channelRequestResponse.Reset(); SendMessage(new ChannelRequestMessage(RemoteChannelNumber, new EnvironmentVariableRequestInfo(variableName, variableValue))); WaitOnHandle(_channelRequestResponse); return _channelRequestSucces; @@ -177,7 +181,7 @@ namespace Renci.SshNet.Channels /// public bool SendShellRequest() { - _channelRequestResponse.Reset(); + _ = _channelRequestResponse.Reset(); SendMessage(new ChannelRequestMessage(RemoteChannelNumber, new ShellRequestInfo())); WaitOnHandle(_channelRequestResponse); return _channelRequestSucces; @@ -192,7 +196,7 @@ namespace Renci.SshNet.Channels /// public bool SendExecRequest(string command) { - _channelRequestResponse.Reset(); + _ = _channelRequestResponse.Reset(); SendMessage(new ChannelRequestMessage(RemoteChannelNumber, new ExecRequestInfo(command, ConnectionInfo.Encoding))); WaitOnHandle(_channelRequestResponse); return _channelRequestSucces; @@ -207,7 +211,7 @@ namespace Renci.SshNet.Channels /// public bool SendBreakRequest(uint breakLength) { - _channelRequestResponse.Reset(); + _ = _channelRequestResponse.Reset(); SendMessage(new ChannelRequestMessage(RemoteChannelNumber, new BreakRequestInfo(breakLength))); WaitOnHandle(_channelRequestResponse); return _channelRequestSucces; @@ -222,7 +226,7 @@ namespace Renci.SshNet.Channels /// public bool SendSubsystemRequest(string subsystem) { - _channelRequestResponse.Reset(); + _ = _channelRequestResponse.Reset(); SendMessage(new ChannelRequestMessage(RemoteChannelNumber, new SubsystemRequestInfo(subsystem))); WaitOnHandle(_channelRequestResponse); return _channelRequestSucces; @@ -307,7 +311,7 @@ namespace Renci.SshNet.Channels /// public bool SendEndOfWriteRequest() { - _channelRequestResponse.Reset(); + _ = _channelRequestResponse.Reset(); SendMessage(new ChannelRequestMessage(RemoteChannelNumber, new EndOfWriteRequestInfo())); WaitOnHandle(_channelRequestResponse); return _channelRequestSucces; @@ -321,23 +325,21 @@ namespace Renci.SshNet.Channels /// public bool SendKeepAliveRequest() { - _channelRequestResponse.Reset(); + _ = _channelRequestResponse.Reset(); SendMessage(new ChannelRequestMessage(RemoteChannelNumber, new KeepAliveRequestInfo())); WaitOnHandle(_channelRequestResponse); return _channelRequestSucces; } /// - /// Called when channel request was successful + /// Called when channel request was successful. /// protected override void OnSuccess() { base.OnSuccess(); - _channelRequestSucces = true; - var channelRequestResponse = _channelRequestResponse; - if (channelRequestResponse != null) - channelRequestResponse.Set(); + _channelRequestSucces = true; + _ = _channelRequestResponse?.Set(); } /// @@ -346,11 +348,9 @@ namespace Renci.SshNet.Channels protected override void OnFailure() { base.OnFailure(); - _channelRequestSucces = false; - var channelRequestResponse = _channelRequestResponse; - if (channelRequestResponse != null) - channelRequestResponse.Set(); + _channelRequestSucces = false; + _ = _channelRequestResponse?.Set(); } /// @@ -407,9 +407,9 @@ namespace Renci.SshNet.Channels } /// - /// Releases unmanaged and - optionally - managed resources + /// Releases unmanaged and - optionally - managed resources. /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + /// to release both managed and unmanaged resources; to release only unmanaged resources. protected override void Dispose(bool disposing) { base.Dispose(disposing); @@ -442,7 +442,9 @@ namespace Renci.SshNet.Channels private void ReleaseSemaphore() { if (Interlocked.CompareExchange(ref _sessionSemaphoreObtained, 0, 1) == 1) - SessionSemaphore.Release(); + { + _ = SessionSemaphore.Release(); + } } } } diff --git a/src/Renci.SshNet/Channels/ClientChannel.cs b/src/Renci.SshNet/Channels/ClientChannel.cs index 844c1866..f1b332af 100644 --- a/src/Renci.SshNet/Channels/ClientChannel.cs +++ b/src/Renci.SshNet/Channels/ClientChannel.cs @@ -7,7 +7,7 @@ namespace Renci.SshNet.Channels internal abstract class ClientChannel : Channel { /// - /// Initializes a new instance. + /// Initializes a new instance of the class. /// /// The session. /// The local channel number. @@ -43,9 +43,7 @@ namespace Renci.SshNet.Channels // Channel is consider to be open when confirmation message was received IsOpen = true; - var openConfirmed = OpenConfirmed; - if (openConfirmed != null) - openConfirmed(this, new ChannelOpenConfirmedEventArgs(remoteChannelNumber, initialWindowSize, maximumPacketSize)); + OpenConfirmed?.Invoke(this, new ChannelOpenConfirmedEventArgs(remoteChannelNumber, initialWindowSize, maximumPacketSize)); } /// @@ -68,9 +66,7 @@ namespace Renci.SshNet.Channels /// The language. protected virtual void OnOpenFailure(uint reasonCode, string description, string language) { - var openFailed = OpenFailed; - if (openFailed != null) - openFailed(this, new ChannelOpenFailedEventArgs(LocalChannelNumber, reasonCode, description, language)); + OpenFailed?.Invoke(this, new ChannelOpenFailedEventArgs(LocalChannelNumber, reasonCode, description, language)); } private void OnChannelOpenConfirmation(object sender, MessageEventArgs e) @@ -79,8 +75,9 @@ namespace Renci.SshNet.Channels { try { - OnOpenConfirmation(e.Message.RemoteChannelNumber, e.Message.InitialWindowSize, - e.Message.MaximumPacketSize); + OnOpenConfirmation(e.Message.RemoteChannelNumber, + e.Message.InitialWindowSize, + e.Message.MaximumPacketSize); } catch (Exception ex) { @@ -120,8 +117,10 @@ namespace Renci.SshNet.Channels /// private void UnsubscribeFromSessionEvents(ISession session) { - if (session == null) + if (session is null) + { return; + } session.ChannelOpenConfirmationReceived -= OnChannelOpenConfirmation; session.ChannelOpenFailureReceived -= OnChannelOpenFailure; diff --git a/src/Renci.SshNet/Channels/ServerChannel.cs b/src/Renci.SshNet/Channels/ServerChannel.cs index dc5378b3..1a70ee9f 100644 --- a/src/Renci.SshNet/Channels/ServerChannel.cs +++ b/src/Renci.SshNet/Channels/ServerChannel.cs @@ -5,7 +5,7 @@ namespace Renci.SshNet.Channels internal abstract class ServerChannel : Channel { /// - /// Initializes a new instance. + /// Initializes a new instance of the class. /// /// The session. /// The local channel number. @@ -14,7 +14,13 @@ namespace Renci.SshNet.Channels /// The remote channel number. /// The window size of the remote party. /// The maximum size of a data packet that we can send to the remote party. - protected ServerChannel(ISession session, uint localChannelNumber, uint localWindowSize, uint localPacketSize, uint remoteChannelNumber, uint remoteWindowSize, uint remotePacketSize) + protected ServerChannel(ISession session, + uint localChannelNumber, + uint localWindowSize, + uint localPacketSize, + uint remoteChannelNumber, + uint remoteWindowSize, + uint remotePacketSize) : base(session, localChannelNumber, localWindowSize, localPacketSize) { InitializeRemoteInfo(remoteChannelNumber, remoteWindowSize, remotePacketSize); @@ -22,10 +28,10 @@ namespace Renci.SshNet.Channels protected void SendMessage(ChannelOpenConfirmationMessage message) { - // No need to check whether channel is open when trying to open a channel + // No need to check whether channel is open when trying to open a channel Session.SendMessage(message); - // When we act as server, consider the channel open when we've sent the + // When we act as server, consider the channel open when we've sent the // confirmation message to the peer IsOpen = true; } diff --git a/src/Renci.SshNet/CipherInfo.cs b/src/Renci.SshNet/CipherInfo.cs index 2641437b..81e5e068 100644 --- a/src/Renci.SshNet/CipherInfo.cs +++ b/src/Renci.SshNet/CipherInfo.cs @@ -30,7 +30,7 @@ namespace Renci.SshNet public CipherInfo(int keySize, Func cipher) { KeySize = keySize; - Cipher = (key, iv) => (cipher(key.Take(KeySize / 8), iv)); + Cipher = (key, iv) => cipher(key.Take(KeySize / 8), iv); } } } diff --git a/src/Renci.SshNet/ClientAuthentication.cs b/src/Renci.SshNet/ClientAuthentication.cs index f0b31f87..1cdfbecb 100644 --- a/src/Renci.SshNet/ClientAuthentication.cs +++ b/src/Renci.SshNet/ClientAuthentication.cs @@ -4,19 +4,21 @@ using Renci.SshNet.Common; namespace Renci.SshNet { - internal class ClientAuthentication : IClientAuthentication + internal sealed class ClientAuthentication : IClientAuthentication { private readonly int _partialSuccessLimit; /// - /// Initializes a new instance. + /// Initializes a new instance of the class. /// /// The number of times an authentication attempt with any given can result in before it is disregarded. /// is less than one. public ClientAuthentication(int partialSuccessLimit) { if (partialSuccessLimit < 1) - throw new ArgumentOutOfRangeException("partialSuccessLimit", "Cannot be less than one."); + { + throw new ArgumentOutOfRangeException(nameof(partialSuccessLimit), "Cannot be less than one."); + } _partialSuccessLimit = partialSuccessLimit; } @@ -42,10 +44,15 @@ namespace Renci.SshNet /// The for which to perform authentication. public void Authenticate(IConnectionInfoInternal connectionInfo, ISession session) { - if (connectionInfo == null) - throw new ArgumentNullException("connectionInfo"); - if (session == null) - throw new ArgumentNullException("session"); + if (connectionInfo is null) + { + throw new ArgumentNullException(nameof(connectionInfo)); + } + + if (session is null) + { + throw new ArgumentNullException(nameof(session)); + } session.RegisterMessage("SSH_MSG_USERAUTH_FAILURE"); session.RegisterMessage("SSH_MSG_USERAUTH_SUCCESS"); @@ -122,6 +129,7 @@ namespace Renci.SshNet { authenticationResult = AuthenticationResult.Success; } + break; case AuthenticationResult.Failure: authenticationState.RecordFailure(authenticationMethod); @@ -130,16 +138,20 @@ namespace Renci.SshNet case AuthenticationResult.Success: authenticationException = null; break; + default: + break; } if (authenticationResult == AuthenticationResult.Success) + { return true; + } } return false; } - private class AuthenticationState + private sealed class AuthenticationState { private readonly IList _supportedAuthenticationMethods; @@ -181,8 +193,7 @@ namespace Renci.SshNet /// An for which to record the result of an authentication attempt. public void RecordPartialSuccess(IAuthenticationMethod authenticationMethod) { - int partialSuccessCount; - if (_authenticationMethodPartialSuccessRegister.TryGetValue(authenticationMethod, out partialSuccessCount)) + if (_authenticationMethodPartialSuccessRegister.TryGetValue(authenticationMethod, out var partialSuccessCount)) { _authenticationMethodPartialSuccessRegister[authenticationMethod] = ++partialSuccessCount; } @@ -203,11 +214,11 @@ namespace Renci.SshNet /// public int GetPartialSuccessCount(IAuthenticationMethod authenticationMethod) { - int partialSuccessCount; - if (_authenticationMethodPartialSuccessRegister.TryGetValue(authenticationMethod, out partialSuccessCount)) + if (_authenticationMethodPartialSuccessRegister.TryGetValue(authenticationMethod, out var partialSuccessCount)) { return partialSuccessCount; } + return 0; } @@ -270,7 +281,9 @@ namespace Renci.SshNet // skip authentication methods that have already failed if (_failedAuthenticationMethods.Contains(authenticationMethod)) + { continue; + } // delay use of authentication methods that had a PartialSuccess result if (_authenticationMethodPartialSuccessRegister.ContainsKey(authenticationMethod)) @@ -283,7 +296,9 @@ namespace Renci.SshNet } foreach (var authenticationMethod in skippedAuthenticationMethods) + { yield return authenticationMethod; + } } } } diff --git a/src/Renci.SshNet/CommandAsyncResult.cs b/src/Renci.SshNet/CommandAsyncResult.cs index a9730d6d..11130e58 100644 --- a/src/Renci.SshNet/CommandAsyncResult.cs +++ b/src/Renci.SshNet/CommandAsyncResult.cs @@ -4,7 +4,7 @@ using System.Threading; namespace Renci.SshNet { /// - /// Provides additional information for asynchronous command execution + /// Provides additional information for asynchronous command execution. /// public class CommandAsyncResult : IAsyncResult { @@ -27,8 +27,6 @@ namespace Renci.SshNet /// Total bytes sent. public int BytesSent { get; set; } - #region IAsyncResult Members - /// /// Gets a user-defined object that qualifies or contains information about an asynchronous operation. /// @@ -36,27 +34,31 @@ namespace Renci.SshNet public object AsyncState { get; internal set; } /// - /// Gets a that is used to wait for an asynchronous operation to complete. + /// Gets a that is used to wait for an asynchronous operation to complete. /// - /// A that is used to wait for an asynchronous operation to complete. + /// + /// A that is used to wait for an asynchronous operation to complete. + /// public WaitHandle AsyncWaitHandle { get; internal set; } /// - /// Gets a value that indicates whether the asynchronous operation completed synchronously. + /// Gets a value indicating whether the asynchronous operation completed synchronously. /// - /// true if the asynchronous operation completed synchronously; otherwise, false. + /// + /// true if the asynchronous operation completed synchronously; otherwise, false. + /// public bool CompletedSynchronously { get; internal set; } /// - /// Gets a value that indicates whether the asynchronous operation has completed. + /// Gets a value indicating whether the asynchronous operation has completed. /// - /// true if the operation is complete; otherwise, false. + /// + /// true if the operation is complete; otherwise, false. + /// public bool IsCompleted { get; internal set; } - #endregion - /// - /// Gets a value indicating whether was already called for this + /// Gets or sets a value indicating whether was already called for this /// . /// /// diff --git a/src/Renci.SshNet/Common/ASCIIEncoding.cs b/src/Renci.SshNet/Common/ASCIIEncoding.cs deleted file mode 100644 index f41c49de..00000000 --- a/src/Renci.SshNet/Common/ASCIIEncoding.cs +++ /dev/null @@ -1,167 +0,0 @@ -#if !FEATURE_ENCODING_ASCII - -using System; -using System.Text; - -namespace Renci.SshNet.Common -{ - /// - /// Implementation of ASCII Encoding - /// - public class ASCIIEncoding : Encoding - { - private readonly char _fallbackChar; - - private static readonly char[] ByteToChar; - - static ASCIIEncoding() - { - if (ByteToChar == null) - { - ByteToChar = new char[128]; - var ch = '\0'; - for (byte i = 0; i < 128; i++) - { - ByteToChar[i] = ch++; - } - } - } - - /// - /// Initializes a new instance of the class. - /// - public ASCIIEncoding() - { - _fallbackChar = '?'; - } - - /// - /// Calculates the number of bytes produced by encoding a set of characters from the specified character array. - /// - /// The character array containing the set of characters to encode. - /// The index of the first character to encode. - /// The number of characters to encode. - /// - /// The number of bytes produced by encoding the specified characters. - /// - /// is null. - /// or is less than zero.-or- and do not denote a valid range in . - public override int GetByteCount(char[] chars, int index, int count) - { - return count; - } - - /// - /// Encodes a set of characters from the specified character array into the specified byte array. - /// - /// The character array containing the set of characters to encode. - /// The index of the first character to encode. - /// The number of characters to encode. - /// The byte array to contain the resulting sequence of bytes. - /// The index at which to start writing the resulting sequence of bytes. - /// - /// The actual number of bytes written into . - /// - /// is null.-or- is null. - /// or or is less than zero.-or- and do not denote a valid range in .-or- is not a valid index in . - /// does not have enough capacity from to the end of the array to accommodate the resulting bytes. - public override int GetBytes(char[] chars, int charIndex, int charCount, byte[] bytes, int byteIndex) - { - for (var i = 0; i < charCount && i < chars.Length; i++) - { - var b = (byte)chars[i + charIndex]; - - if (b > 127) - b = (byte) _fallbackChar; - - bytes[i + byteIndex] = b; - } - return charCount; - } - - /// - /// Calculates the number of characters produced by decoding a sequence of bytes from the specified byte array. - /// - /// The byte array containing the sequence of bytes to decode. - /// The index of the first byte to decode. - /// The number of bytes to decode. - /// - /// The number of characters produced by decoding the specified sequence of bytes. - /// - /// is null. - /// or is less than zero.-or- and do not denote a valid range in . - public override int GetCharCount(byte[] bytes, int index, int count) - { - return count; - } - - /// - /// Decodes a sequence of bytes from the specified byte array into the specified character array. - /// - /// The byte array containing the sequence of bytes to decode. - /// The index of the first byte to decode. - /// The number of bytes to decode. - /// The character array to contain the resulting set of characters. - /// The index at which to start writing the resulting set of characters. - /// - /// The actual number of characters written into . - /// - /// is null.-or- is null. - /// or or is less than zero.-or- and do not denote a valid range in .-or- is not a valid index in . - /// does not have enough capacity from to the end of the array to accommodate the resulting characters. - public override int GetChars(byte[] bytes, int byteIndex, int byteCount, char[] chars, int charIndex) - { - for (var i = 0; i < byteCount; i++) - { - var b = bytes[i + byteIndex]; - char ch; - - if (b > 127) - { - ch = _fallbackChar; - } - else - { - ch = ByteToChar[b]; - } - - chars[i + charIndex] = ch; - } - return byteCount; - } - - /// - /// Calculates the maximum number of bytes produced by encoding the specified number of characters. - /// - /// The number of characters to encode. - /// - /// The maximum number of bytes produced by encoding the specified number of characters. - /// - /// is less than zero. - public override int GetMaxByteCount(int charCount) - { - if (charCount < 0) - throw new ArgumentOutOfRangeException("charCount", "Non-negative number required."); - - return charCount + 1; - } - - /// - /// Calculates the maximum number of characters produced by decoding the specified number of bytes. - /// - /// The number of bytes to decode. - /// - /// The maximum number of characters produced by decoding the specified number of bytes. - /// - /// is less than zero. - public override int GetMaxCharCount(int byteCount) - { - if (byteCount < 0) - throw new ArgumentOutOfRangeException("byteCount", "Non-negative number required."); - - return byteCount; - } - } -} - -#endif // !FEATURE_ENCODING_ASCII \ No newline at end of file diff --git a/src/Renci.SshNet/Common/Array.cs b/src/Renci.SshNet/Common/Array.cs deleted file mode 100644 index c11c3565..00000000 --- a/src/Renci.SshNet/Common/Array.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Renci.SshNet.Common -{ - internal static class Array - { - public static readonly T[] Empty = new T[0]; - } -} diff --git a/src/Renci.SshNet/Common/AsyncResult.cs b/src/Renci.SshNet/Common/AsyncResult.cs index 3466a030..acc1da11 100644 --- a/src/Renci.SshNet/Common/AsyncResult.cs +++ b/src/Renci.SshNet/Common/AsyncResult.cs @@ -8,36 +8,18 @@ namespace Renci.SshNet.Common /// public abstract class AsyncResult : IAsyncResult { - // Fields set at construction which never change while operation is pending - private readonly AsyncCallback _asyncCallback; - - private readonly object _asyncState; - - // Field set at construction which do change after operation completes private const int StatePending = 0; private const int StateCompletedSynchronously = 1; private const int StateCompletedAsynchronously = 2; + private readonly AsyncCallback _asyncCallback; + private readonly object _asyncState; private int _completedState = StatePending; - - // Field that may or may not get set depending on usage private ManualResetEvent _asyncWaitHandle; - - // Fields set when operation completes private Exception _exception; - /// - /// Gets or sets a value indicating whether has been called on the current - /// . - /// - /// - /// true if has been called on the current ; - /// otherwise, false. - /// - public bool EndInvokeCalled { get; private set; } - /// /// Initializes a new instance of the class. /// @@ -49,37 +31,43 @@ namespace Renci.SshNet.Common _asyncState = state; } + /// + /// Gets a value indicating whether has been called on the current . + /// + /// + /// true if has been called on the current ; + /// otherwise, false. + /// + public bool EndInvokeCalled { get; private set; } + /// /// Marks asynchronous operation as completed. /// /// The exception. - /// if set to true [completed synchronously]. + /// If set to , completed synchronously. public void SetAsCompleted(Exception exception, bool completedSynchronously) { // Passing null for exception means no error occurred; this is the common case _exception = exception; - // The m_CompletedState field MUST be set prior calling the callback + // The '_completedState' field MUST be set prior calling the callback var prevState = Interlocked.Exchange(ref _completedState, - completedSynchronously ? StateCompletedSynchronously : StateCompletedAsynchronously); + completedSynchronously ? StateCompletedSynchronously : StateCompletedAsynchronously); + if (prevState != StatePending) + { throw new InvalidOperationException("You can set a result only once"); + } // If the event exists, set it - if (_asyncWaitHandle != null) - { - _asyncWaitHandle.Set(); - } + _ = _asyncWaitHandle?.Set(); // If a callback method was set, call it - if (_asyncCallback != null) - { - _asyncCallback(this); - } + _asyncCallback?.Invoke(this); } /// - /// Waits until the asynchronous operation completes, and then returns. + /// Waits until the asynchronous operation completes, and then returns. /// internal void EndInvoke() { @@ -87,7 +75,7 @@ namespace Renci.SshNet.Common if (!IsCompleted) { // If the operation isn't done, wait for it - AsyncWaitHandle.WaitOne(); + _ = AsyncWaitHandle.WaitOne(); _asyncWaitHandle = null; // Allow early GC AsyncWaitHandle.Dispose(); } @@ -96,21 +84,28 @@ namespace Renci.SshNet.Common // Operation is done: if an exception occurred, throw it if (_exception != null) + { throw _exception; + } } - #region Implementation of IAsyncResult - /// /// Gets a user-defined object that qualifies or contains information about an asynchronous operation. /// - /// A user-defined object that qualifies or contains information about an asynchronous operation. - public object AsyncState { get { return _asyncState; } } + /// + /// A user-defined object that qualifies or contains information about an asynchronous operation. + /// + public object AsyncState + { + get { return _asyncState; } + } /// - /// Gets a value that indicates whether the asynchronous operation completed synchronously. + /// Gets a value indicating whether the asynchronous operation completed synchronously. /// - /// true if the asynchronous operation completed synchronously; otherwise, false. + /// + /// if the asynchronous operation completed synchronously; otherwise, . + /// public bool CompletedSynchronously { get { return _completedState == StateCompletedSynchronously; } @@ -119,16 +114,18 @@ namespace Renci.SshNet.Common /// /// Gets a that is used to wait for an asynchronous operation to complete. /// - /// A that is used to wait for an asynchronous operation to complete. + /// + /// A that is used to wait for an asynchronous operation to complete. + /// public WaitHandle AsyncWaitHandle { get { - if (_asyncWaitHandle == null) + if (_asyncWaitHandle is null) { var done = IsCompleted; var mre = new ManualResetEvent(done); - if (Interlocked.CompareExchange(ref _asyncWaitHandle, mre, null) != null) + if (Interlocked.CompareExchange(ref _asyncWaitHandle, mre, comparand: null) != null) { // Another thread created this object's event; dispose the event we just created mre.Dispose(); @@ -137,27 +134,26 @@ namespace Renci.SshNet.Common { if (!done && IsCompleted) { - // If the operation wasn't done when we created - // the event but now it is done, set the event - _asyncWaitHandle.Set(); + // If the operation wasn't done when we created the event but now it is done, set the event + _ = _asyncWaitHandle.Set(); } } } + return _asyncWaitHandle; } } /// - /// Gets a value that indicates whether the asynchronous operation has completed. + /// Gets a value indicating whether the asynchronous operation has completed. /// /// - /// true if the operation is complete; otherwise, false. + /// if the operation is complete; otherwise, . + /// public bool IsCompleted { get { return _completedState != StatePending; } } - - #endregion } /// @@ -190,7 +186,7 @@ namespace Renci.SshNet.Common _result = result; // Tell the base class that the operation completed successfully (no exception) - SetAsCompleted(null, completedSynchronously); + SetAsCompleted(exception: null, completedSynchronously); } /// @@ -201,8 +197,8 @@ namespace Renci.SshNet.Common /// public new TResult EndInvoke() { - base.EndInvoke(); // Wait until operation has completed + base.EndInvoke(); // Wait until operation has completed return _result; // Return the result (if above didn't throw) } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet/Common/AuthenticationBannerEventArgs.cs b/src/Renci.SshNet/Common/AuthenticationBannerEventArgs.cs index 308105d4..790ba034 100644 --- a/src/Renci.SshNet/Common/AuthenticationBannerEventArgs.cs +++ b/src/Renci.SshNet/Common/AuthenticationBannerEventArgs.cs @@ -1,7 +1,7 @@ namespace Renci.SshNet.Common { /// - /// Provides data for event. + /// Provides data for event. /// public class AuthenticationBannerEventArgs : AuthenticationEventArgs { diff --git a/src/Renci.SshNet/Common/AuthenticationEventArgs.cs b/src/Renci.SshNet/Common/AuthenticationEventArgs.cs index dfeadbcc..4546d318 100644 --- a/src/Renci.SshNet/Common/AuthenticationEventArgs.cs +++ b/src/Renci.SshNet/Common/AuthenticationEventArgs.cs @@ -7,11 +7,6 @@ namespace Renci.SshNet.Common /// public abstract class AuthenticationEventArgs : EventArgs { - /// - /// Gets the username. - /// - public string Username { get; private set; } - /// /// Initializes a new instance of the class. /// @@ -20,5 +15,10 @@ namespace Renci.SshNet.Common { Username = username; } + + /// + /// Gets the username. + /// + public string Username { get; } } } diff --git a/src/Renci.SshNet/Common/AuthenticationPasswordChangeEventArgs.cs b/src/Renci.SshNet/Common/AuthenticationPasswordChangeEventArgs.cs index f6d82912..f4b4a3d8 100644 --- a/src/Renci.SshNet/Common/AuthenticationPasswordChangeEventArgs.cs +++ b/src/Renci.SshNet/Common/AuthenticationPasswordChangeEventArgs.cs @@ -1,18 +1,10 @@ namespace Renci.SshNet.Common { /// - /// Provides data for event. + /// Provides data for event. /// public class AuthenticationPasswordChangeEventArgs : AuthenticationEventArgs { - /// - /// Gets or sets the new password. - /// - /// - /// The new password. - /// - public byte[] NewPassword { get; set; } - /// /// Initializes a new instance of the class. /// @@ -21,5 +13,13 @@ : base(username) { } + + /// + /// Gets or sets the new password. + /// + /// + /// The new password. + /// + public byte[] NewPassword { get; set; } } } diff --git a/src/Renci.SshNet/Common/AuthenticationPrompt.cs b/src/Renci.SshNet/Common/AuthenticationPrompt.cs index 15d7a982..5a7a9688 100644 --- a/src/Renci.SshNet/Common/AuthenticationPrompt.cs +++ b/src/Renci.SshNet/Common/AuthenticationPrompt.cs @@ -1,36 +1,10 @@ namespace Renci.SshNet.Common { /// - /// Provides prompt information when is raised + /// Provides prompt information when is raised. /// public class AuthenticationPrompt { - /// - /// Gets the prompt sequence id. - /// - public int Id { get; private set; } - - /// - /// Gets or sets a value indicating whether the user input should be echoed as characters are typed. - /// - /// - /// true if the user input should be echoed as characters are typed; otherwise, false. - /// - public bool IsEchoed { get; private set; } - - /// - /// Gets server information request. - /// - public string Request { get; private set; } - - /// - /// Gets or sets server information response. - /// - /// - /// The response. - /// - public string Response { get; set; } - /// /// Initializes a new instance of the class. /// @@ -43,5 +17,31 @@ IsEchoed = isEchoed; Request = request; } + + /// + /// Gets the prompt sequence id. + /// + public int Id { get; } + + /// + /// Gets a value indicating whether the user input should be echoed as characters are typed. + /// + /// + /// true if the user input should be echoed as characters are typed; otherwise, false. + /// + public bool IsEchoed { get; } + + /// + /// Gets server information request. + /// + public string Request { get; } + + /// + /// Gets or sets server information response. + /// + /// + /// The response. + /// + public string Response { get; set; } } } diff --git a/src/Renci.SshNet/Common/AuthenticationPromptEventArgs.cs b/src/Renci.SshNet/Common/AuthenticationPromptEventArgs.cs index 9e220728..9c22c340 100644 --- a/src/Renci.SshNet/Common/AuthenticationPromptEventArgs.cs +++ b/src/Renci.SshNet/Common/AuthenticationPromptEventArgs.cs @@ -3,25 +3,10 @@ namespace Renci.SshNet.Common { /// - /// Provides data for event. + /// Provides data for event. /// public class AuthenticationPromptEventArgs : AuthenticationEventArgs { - /// - /// Gets prompt language. - /// - public string Language { get; private set; } - - /// - /// Gets prompt instruction. - /// - public string Instruction { get; private set; } - - /// - /// Gets server information request prompts. - /// - public IEnumerable Prompts { get; private set; } - /// /// Initializes a new instance of the class. /// @@ -36,5 +21,20 @@ namespace Renci.SshNet.Common Language = language; Prompts = prompts; } + + /// + /// Gets prompt language. + /// + public string Language { get; } + + /// + /// Gets prompt instruction. + /// + public string Instruction { get; } + + /// + /// Gets server information request prompts. + /// + public IEnumerable Prompts { get; } } } diff --git a/src/Renci.SshNet/Common/BigInteger.cs b/src/Renci.SshNet/Common/BigInteger.cs index 611b74dd..dd691968 100644 --- a/src/Renci.SshNet/Common/BigInteger.cs +++ b/src/Renci.SshNet/Common/BigInteger.cs @@ -2,8 +2,8 @@ // System.Numerics.BigInteger // // Authors: -// Rodrigo Kumpera (rkumpera@novell.com) -// Marek Safar +// Rodrigo Kumpera (rkumpera@novell.com) +// Marek Safar // // Copyright (C) 2010 Novell, Inc (http://www.novell.com) // Copyright (C) 2014 Xamarin Inc (http://www.xamarin.com) @@ -44,46 +44,39 @@ * * * ***************************************************************************/ -// -// slashdocs based on MSDN using System; using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; using System.Globalization; + using Renci.SshNet.Abstractions; /* -Optimization - Have proper popcount function for IsPowerOfTwo - Use unsafe ops to avoid bounds check - CoreAdd could avoid some resizes by checking for equal sized array that top overflow - For bitwise operators, hoist the conditionals out of their main loop - Optimize BitScanBackward - Use a carry variable to make shift opts do half the number of array ops. - Schoolbook multiply is O(n^2), use Karatsuba /Toom-3 for large numbers -*/ + * Optimization: + * - Have proper popcount function for IsPowerOfTwo + * - Use unsafe ops to avoid bounds check + * - CoreAdd could avoid some resizes by checking for equal sized array that top overflow + * - For bitwise operators, hoist the conditionals out of their main loop + * - Optimize BitScanBackward + * - Use a carry variable to make shift opts do half the number of array ops. + * -Schoolbook multiply is O(n^2), use Karatsuba /Toom-3 for large numbers + */ namespace Renci.SshNet.Common { /// /// Represents an arbitrarily large signed integer. /// - [SuppressMessage("ReSharper", "EmptyEmbeddedStatement")] - [SuppressMessage("ReSharper", "RedundantCast")] - [SuppressMessage("ReSharper", "RedundantAssignment")] - [SuppressMessage("ReSharper", "SuggestBaseTypeForParameter")] - [SuppressMessage("ReSharper", "MergeConditionalExpression")] public struct BigInteger : IComparable, IFormattable, IComparable, IEquatable { + private const ulong Base = 0x100000000; + private const int Bias = 1075; + private const int DecimalSignMask = unchecked((int)0x80000000); + private static readonly BigInteger ZeroSingleton = new BigInteger(0); private static readonly BigInteger OneSingleton = new BigInteger(1); private static readonly BigInteger MinusOneSingleton = new BigInteger(-1); - private const ulong Base = 0x100000000; - private const int Bias = 1075; - private const int DecimalSignMask = unchecked((int) 0x80000000); - - //LSB on [0] + // LSB on [0] private readonly uint[] _data; private readonly short _sign; @@ -95,17 +88,21 @@ namespace Renci.SshNet.Common /// /// The number of the bit used. /// - public int BitLength + public readonly int BitLength { get { if (_sign == 0) + { return 0; + } var msbIndex = _data.Length - 1; while (_data[msbIndex] == 0) + { msbIndex--; + } var msbBitCount = BitScanBackward(_data[msbIndex]) + 1; @@ -129,16 +126,22 @@ namespace Renci.SshNet.Common while (!b.IsZero) { if (b.IsOne) + { return p1; + } p0 += (a / b) * p1; a %= b; if (a.IsZero) + { break; + } if (a.IsOne) + { return modulus - p0; + } p1 += (b / a) * p0; b %= a; @@ -158,8 +161,11 @@ namespace Renci.SshNet.Common public static BigInteger PositiveMod(BigInteger dividend, BigInteger divisor) { var result = dividend % divisor; + if (result < 0) + { result += divisor; + } return result; } @@ -171,9 +177,9 @@ namespace Renci.SshNet.Common /// A random number of the specified length. public static BigInteger Random(int bitLength) { - var bytesArray = new byte[bitLength / 8 + (((bitLength % 8) > 0) ? 1 : 0)]; + var bytesArray = new byte[(bitLength / 8) + (((bitLength % 8) > 0) ? 1 : 0)]; CryptoAbstraction.GenerateRandom(bytesArray); - bytesArray[bytesArray.Length - 1] = (byte) (bytesArray[bytesArray.Length - 1] & 0x7F); // Ensure not a negative value + bytesArray[bytesArray.Length - 1] = (byte) (bytesArray[bytesArray.Length - 1] & 0x7F); // Ensure not a negative value return new BigInteger(bytesArray); } @@ -199,12 +205,12 @@ namespace Renci.SshNet.Common else if (value > 0) { _sign = 1; - _data = new[] {(uint) value}; + _data = new[] { (uint) value }; } else { _sign = -1; - _data = new[] {(uint) -value}; + _data = new[] { (uint) -value }; } } @@ -247,7 +253,9 @@ namespace Renci.SshNet.Common _data = new uint[high != 0 ? 2 : 1]; _data[0] = low; if (high != 0) + { _data[1] = high; + } } else { @@ -259,7 +267,9 @@ namespace Renci.SshNet.Common _data = new uint[high != 0 ? 2 : 1]; _data[0] = low; if (high != 0) + { _data[1] = high; + } } } @@ -278,34 +288,18 @@ namespace Renci.SshNet.Common else { _sign = 1; - var low = (uint)value; - var high = (uint)(value >> 32); + var low = (uint) value; + var high = (uint) (value >> 32); _data = new uint[high != 0 ? 2 : 1]; _data[0] = low; if (high != 0) + { _data[1] = high; + } } } - private static bool Negative(byte[] v) - { - return ((v[7] & 0x80) != 0); - } - - private static ushort Exponent(byte[] v) - { - return (ushort)((((ushort)(v[7] & 0x7F)) << (ushort)4) | (((ushort)(v[6] & 0xF0)) >> 4)); - } - - private static ulong Mantissa(byte[] v) - { - var i1 = ((uint)v[0] | ((uint)v[1] << 8) | ((uint)v[2] << 16) | ((uint)v[3] << 24)); - var i2 = ((uint)v[4] | ((uint)v[5] << 8) | ((uint)(v[6] & 0xF) << 16)); - - return (ulong)((ulong)i1 | ((ulong)i2 << 32)); - } - /// /// Initializes a new instance of the structure using a double-precision floating-point value. /// @@ -313,7 +307,9 @@ namespace Renci.SshNet.Common public BigInteger(double value) { if (double.IsNaN(value) || double.IsInfinity(value)) + { throw new OverflowException(); + } var bytes = BitConverter.GetBytes(value); var mantissa = Mantissa(bytes); @@ -329,7 +325,7 @@ namespace Renci.SshNet.Common } var res = Negative(bytes) ? MinusOne : One; - res = res << (exponent - 0x3ff); + res <<= exponent - 0x3ff; _sign = res._sign; _data = res._data; } @@ -341,7 +337,7 @@ namespace Renci.SshNet.Common BigInteger res = mantissa; res = exponent > Bias ? res << (exponent - Bias) : res >> (Bias - exponent); - _sign = (short)(Negative(bytes) ? -1 : 1); + _sign = (short) (Negative(bytes) ? -1 : 1); _data = res._data; } } @@ -350,7 +346,8 @@ namespace Renci.SshNet.Common /// Initializes a new instance of the structure using a single-precision floating-point value. /// /// A single-precision floating-point value. - public BigInteger(float value) : this((double)value) + public BigInteger(float value) + : this((double) value) { } @@ -364,7 +361,10 @@ namespace Renci.SshNet.Common var bits = decimal.GetBits(decimal.Truncate(value)); var size = 3; - while (size > 0 && bits[size - 1] == 0) size--; + while (size > 0 && bits[size - 1] == 0) + { + size--; + } if (size == 0) { @@ -373,14 +373,19 @@ namespace Renci.SshNet.Common return; } - _sign = (short)((bits[3] & DecimalSignMask) != 0 ? -1 : 1); + _sign = (short) ((bits[3] & DecimalSignMask) != 0 ? -1 : 1); _data = new uint[size]; - _data[0] = (uint)bits[0]; + _data[0] = (uint) bits[0]; if (size > 1) - _data[1] = (uint)bits[1]; + { + _data[1] = (uint) bits[1]; + } + if (size > 2) - _data[2] = (uint)bits[2]; + { + _data[2] = (uint) bits[2]; + } } /// @@ -391,8 +396,10 @@ namespace Renci.SshNet.Common [CLSCompliant(false)] public BigInteger(byte[] value) { - if (value == null) - throw new ArgumentNullException("value"); + if (value is null) + { + throw new ArgumentNullException(nameof(value)); + } var len = value.Length; @@ -404,9 +411,13 @@ namespace Renci.SshNet.Common } if ((value[len - 1] & 0x80) != 0) + { _sign = -1; + } else + { _sign = 1; + } if (_sign == 1) { @@ -423,7 +434,9 @@ namespace Renci.SshNet.Common int size; var fullWords = size = len / 4; if ((len & 0x3) != 0) + { ++size; + } _data = new uint[size]; var j = 0; @@ -434,12 +447,15 @@ namespace Renci.SshNet.Common (uint) (value[j++] << 16) | (uint) (value[j++] << 24); } + size = len & 0x3; if (size > 0) { var idx = _data.Length - 1; for (var i = 0; i < size; ++i) - _data[idx] |= (uint)(value[j++] << (i * 8)); + { + _data[idx] |= (uint) (value[j++] << (i * 8)); + } } } else @@ -447,7 +463,9 @@ namespace Renci.SshNet.Common int size; var fullWords = size = len / 4; if ((len & 0x3) != 0) + { ++size; + } _data = new uint[size]; @@ -462,11 +480,12 @@ namespace Renci.SshNet.Common (uint) (value[j++] << 16) | (uint) (value[j++] << 24); - sub = (ulong)word - borrow; - word = (uint)sub; - borrow = (uint)(sub >> 32) & 0x1u; + sub = (ulong) word - borrow; + word = (uint) sub; + borrow = (uint) (sub >> 32) & 0x1u; _data[i] = ~word; } + size = len & 0x3; if (size > 0) @@ -475,63 +494,95 @@ namespace Renci.SshNet.Common uint storeMask = 0; for (var i = 0; i < size; ++i) { - word |= (uint)(value[j++] << (i * 8)); + word |= (uint) (value[j++] << (i * 8)); storeMask = (storeMask << 8) | 0xFF; } sub = word - borrow; - word = (uint)sub; - borrow = (uint)(sub >> 32) & 0x1u; + word = (uint) sub; + borrow = (uint) (sub >> 32) & 0x1u; if ((~word & storeMask) == 0) + { Array.Resize(ref _data, _data.Length - 1); + } else + { _data[_data.Length - 1] = ~word & storeMask; + } } - if (borrow != 0) //FIXME I believe this can't happen, can someone write a test for it? + + if (borrow != 0) + { + // FIXME I believe this can't happen, can someone write a test for it? throw new Exception("non zero final carry"); + } } } + private static bool Negative(byte[] v) + { + return (v[7] & 0x80) != 0; + } + + private static ushort Exponent(byte[] v) + { + return (ushort)((((ushort)(v[7] & 0x7F)) << (ushort)4) | (((ushort)(v[6] & 0xF0)) >> 4)); + } + + private static ulong Mantissa(byte[] v) + { + var i1 = (uint)v[0] | ((uint)v[1] << 8) | ((uint)v[2] << 16) | ((uint)v[3] << 24); + var i2 = (uint)v[4] | ((uint)v[5] << 8) | ((uint)(v[6] & 0xF) << 16); + + return ((ulong) i1 | ((ulong) i2 << 32)); + } + /// - /// Indicates whether the value of the current object is an even number. + /// Gets a value indicating whether the value of the current object is an even number. /// /// /// true if the value of the BigInteger object is an even number; otherwise, false. /// - public bool IsEven + public readonly bool IsEven { get { return _sign == 0 || (_data[0] & 0x1) == 0; } } /// - /// Indicates whether the value of the current object is . + /// Gets a value indicating whether the value of the current object is . /// /// /// true if the value of the object is ; /// otherwise, false. /// - public bool IsOne + public readonly bool IsOne { get { return _sign == 1 && _data.Length == 1 && _data[0] == 1; } } - - //Gem from Hacker's Delight - //Returns the number of bits set in @x - static int PopulationCount(uint x) + // Gem from Hacker's Delight + // Returns the number of bits set in @x + private static int PopulationCount(uint x) { - x = x - ((x >> 1) & 0x55555555); + x -= (x >> 1) & 0x55555555; x = (x & 0x33333333) + ((x >> 2) & 0x33333333); x = (x + (x >> 4)) & 0x0F0F0F0F; - x = x + (x >> 8); - x = x + (x >> 16); - return (int)(x & 0x0000003F); + x += x >> 8; + x += x >> 16; + return (int) (x & 0x0000003F); } - //Based on code by Zilong Tan on Ulib released under MIT license - //Returns the number of bits set in @x - static int PopulationCount(ulong x) + /// + /// Returns the number of bits set in . + /// + /// + /// The number of bits set in . + /// + /// + /// Based on code by Zilong Tan on Ulib released under MIT license. + /// + private static int PopulationCount(ulong x) { x -= (x >> 1) & 0x5555555555555555UL; x = (x & 0x3333333333333333UL) + ((x >> 2) & 0x3333333333333333UL); @@ -539,7 +590,7 @@ namespace Renci.SshNet.Common return (int)((x * 0x0101010101010101UL) >> 56); } - static int LeadingZeroCount(uint value) + private static int LeadingZeroCount(uint value) { value |= value >> 1; value |= value >> 2; @@ -549,7 +600,7 @@ namespace Renci.SshNet.Common return 32 - PopulationCount(value); // 32 = bits in uint } - static int LeadingZeroCount(ulong value) + private static int LeadingZeroCount(ulong value) { value |= value >> 1; value |= value >> 2; @@ -560,7 +611,7 @@ namespace Renci.SshNet.Common return 64 - PopulationCount(value); // 64 = bits in ulong } - static double BuildDouble(int sign, ulong mantissa, int exponent) + private static double BuildDouble(int sign, ulong mantissa, int exponent) { const int exponentBias = 1023; const int mantissaLength = 52; @@ -581,6 +632,7 @@ namespace Renci.SshNet.Common { return sign > 0 ? double.PositiveInfinity : double.NegativeInfinity; } + if (offset < 0) { mantissa >>= -offset; @@ -596,7 +648,9 @@ namespace Renci.SshNet.Common mantissa <<= offset; exponent -= offset; } - mantissa = mantissa & mantissaMask; + + mantissa &= mantissaMask; + if ((exponent & exponentMask) == exponent) { unchecked @@ -606,49 +660,59 @@ namespace Renci.SshNet.Common { bits |= negativeMark; } + return BitConverter.Int64BitsToDouble((long)bits); } } + return sign > 0 ? double.PositiveInfinity : double.NegativeInfinity; } /// - /// Indicates whether the value of the current object is a power of two. + /// Gets a value Indicating whether the value of the current object is a power of two. /// /// /// true if the value of the object is a power of two; /// otherwise, false. /// - public bool IsPowerOfTwo + public readonly bool IsPowerOfTwo { get { - var foundBit = false; if (_sign != 1) + { return false; - //This function is pop count == 1 for positive numbers + } + + var foundBit = false; + + // This function is pop count == 1 for positive numbers foreach (var bit in _data) { var p = PopulationCount(bit); if (p > 0) { if (p > 1 || foundBit) + { return false; + } + foundBit = true; } } + return foundBit; } } /// - /// Indicates whether the value of the current object is . + /// Gets a value indicating whether the value of the current object is . /// /// /// true if the value of the object is ; /// otherwise, false. /// - public bool IsZero + public readonly bool IsZero { get { return _sign == 0; } } @@ -659,7 +723,7 @@ namespace Renci.SshNet.Common /// /// A number that indicates the sign of the object. /// - public int Sign + public readonly int Sign { get { return _sign; } } @@ -706,22 +770,35 @@ namespace Renci.SshNet.Common /// public static explicit operator int(BigInteger value) { - if (value._data == null) + if (value._data is null) + { return 0; + } + if (value._data.Length > 1) + { throw new OverflowException(); + } + var data = value._data[0]; if (value._sign == 1) { - if (data > (uint)int.MaxValue) + if (data > (uint) int.MaxValue) + { throw new OverflowException(); + } + return (int)data; } + if (value._sign == -1) { if (data > 0x80000000u) + { throw new OverflowException(); + } + return -(int)data; } @@ -738,10 +815,16 @@ namespace Renci.SshNet.Common [CLSCompliant(false)] public static explicit operator uint(BigInteger value) { - if (value._data == null) + if (value._data is null) + { return 0; + } + if (value._data.Length > 1 || value._sign == -1) + { throw new OverflowException(); + } + return value._data[0]; } @@ -754,26 +837,32 @@ namespace Renci.SshNet.Common /// public static explicit operator short(BigInteger value) { - var val = (int)value; - if (val < short.MinValue || val > short.MaxValue) + var val = (int) value; + if (val is < short.MinValue or > short.MaxValue) + { throw new OverflowException(); - return (short)val; + } + + return (short) val; } /// - /// + /// Defines an explicit conversion of a object to a 16-bit unsigned integer value. /// - /// + /// The value to convert to a 16-bit unsigned integer. /// /// An object that contains the value of the parameter. /// - [CLSCompliantAttribute(false)] + [CLSCompliant(false)] public static explicit operator ushort(BigInteger value) { - var val = (uint)value; + var val = (uint) value; if (val > ushort.MaxValue) + { throw new OverflowException(); - return (ushort)val; + } + + return (ushort) val; } /// @@ -785,10 +874,13 @@ namespace Renci.SshNet.Common /// public static explicit operator byte(BigInteger value) { - var val = (uint)value; + var val = (uint) value; if (val > byte.MaxValue) + { throw new OverflowException(); - return (byte)val; + } + + return (byte) val; } /// @@ -801,10 +893,13 @@ namespace Renci.SshNet.Common [CLSCompliant(false)] public static explicit operator sbyte(BigInteger value) { - var val = (int)value; - if (val < sbyte.MinValue || val > sbyte.MaxValue) + var val = (int) value; + if (val is < sbyte.MinValue or > sbyte.MaxValue) + { throw new OverflowException(); - return (sbyte)val; + } + + return (sbyte) val; } /// @@ -816,18 +911,25 @@ namespace Renci.SshNet.Common /// public static explicit operator long(BigInteger value) { - if (value._data == null) + if (value._data is null) + { return 0; + } if (value._data.Length > 2) + { throw new OverflowException(); + } var low = value._data[0]; if (value._data.Length == 1) { if (value._sign == 1) - return (long)low; + { + return (long) low; + } + var res = (long)low; return -res; } @@ -837,7 +939,10 @@ namespace Renci.SshNet.Common if (value._sign == 1) { if (high >= 0x80000000u) + { throw new OverflowException(); + } + return (((long)high) << 32) | low; } @@ -853,7 +958,10 @@ namespace Renci.SshNet.Common var result = -((((long)high) << 32) | (long)low); if (result > 0) + { throw new OverflowException(); + } + return result; } @@ -867,14 +975,21 @@ namespace Renci.SshNet.Common [CLSCompliant(false)] public static explicit operator ulong(BigInteger value) { - if (value._data == null) + if (value._data is null) + { return 0; + } + if (value._data.Length > 2 || value._sign == -1) + { throw new OverflowException(); + } var low = value._data[0]; if (value._data.Length == 1) + { return low; + } var high = value._data[1]; return (((ulong)high) << 32) | low; @@ -889,15 +1004,17 @@ namespace Renci.SshNet.Common /// public static explicit operator double(BigInteger value) { - if (value._data == null) + if (value._data is null) + { return 0.0; + } switch (value._data.Length) { case 1: return BuildDouble(value._sign, value._data[0], 0); case 2: - return BuildDouble(value._sign, (ulong)value._data[1] << 32 | (ulong)value._data[0], 0); + return BuildDouble(value._sign, (ulong) value._data[1] << 32 | (ulong) value._data[0], 0); default: var index = value._data.Length - 1; var word = value._data[index]; @@ -912,6 +1029,7 @@ namespace Renci.SshNet.Common { mantissa >>= -missing; } + return BuildDouble(value._sign, mantissa, ((value._data.Length - 2) * 32) - missing); } } @@ -925,7 +1043,7 @@ namespace Renci.SshNet.Common /// public static explicit operator float(BigInteger value) { - return (float)(double)value; + return (float) (double) value; } /// @@ -937,20 +1055,32 @@ namespace Renci.SshNet.Common /// public static explicit operator decimal(BigInteger value) { - if (value._data == null) + if (value._data is null) + { return decimal.Zero; + } var data = value._data; if (data.Length > 3) + { throw new OverflowException(); + } int lo = 0, mi = 0, hi = 0; if (data.Length > 2) - hi = (int)data[2]; + { + hi = (int) data[2]; + } + if (data.Length > 1) - mi = (int)data[1]; + { + mi = (int) data[1]; + } + if (data.Length > 0) - lo = (int)data[0]; + { + lo = (int) data[0]; + } return new decimal(lo, mi, hi, value._sign < 0, 0); } @@ -999,7 +1129,7 @@ namespace Renci.SshNet.Common /// /// An object that contains the value of the parameter. /// - [CLSCompliantAttribute(false)] + [CLSCompliant(false)] public static implicit operator BigInteger(ushort value) { return new BigInteger(value); @@ -1102,20 +1232,32 @@ namespace Renci.SshNet.Common public static BigInteger operator +(BigInteger left, BigInteger right) { if (left._sign == 0) + { return right; + } + if (right._sign == 0) + { return left; + } if (left._sign == right._sign) + { return new BigInteger(left._sign, CoreAdd(left._data, right._data)); + } var r = CoreCompare(left._data, right._data); if (r == 0) + { return Zero; + } - if (r > 0) //left > right + if (r > 0) + { + //left > right return new BigInteger(left._sign, CoreSub(left._data, right._data)); + } return new BigInteger(right._sign, CoreSub(right._data, left._data)); } @@ -1131,19 +1273,29 @@ namespace Renci.SshNet.Common public static BigInteger operator -(BigInteger left, BigInteger right) { if (right._sign == 0) + { return left; + } + if (left._sign == 0) - return new BigInteger((short)-right._sign, right._data); + { + return new BigInteger((short) -right._sign, right._data); + } if (left._sign == right._sign) { var r = CoreCompare(left._data, right._data); if (r == 0) + { return Zero; + } - if (r > 0) //left > right + if (r > 0) + { + // left > right return new BigInteger(left._sign, CoreSub(left._data, right._data)); + } return new BigInteger((short)-right._sign, CoreSub(right._data, left._data)); } @@ -1162,19 +1314,27 @@ namespace Renci.SshNet.Common public static BigInteger operator *(BigInteger left, BigInteger right) { if (left._sign == 0 || right._sign == 0) + { return Zero; + } if (left._data[0] == 1 && left._data.Length == 1) { if (left._sign == 1) + { return right; + } + return new BigInteger((short)-right._sign, right._data); } if (right._data[0] == 1 && right._data.Length == 1) { if (right._sign == 1) + { return left; + } + return new BigInteger((short)-left._sign, left._data); } @@ -1191,7 +1351,7 @@ namespace Renci.SshNet.Common ulong carry = 0; for (var j = 0; j < b.Length; ++j) { - carry = carry + ((ulong)ai) * b[j] + res[k]; + carry = carry + ((ulong) ai) * b[j] + res[k]; res[k++] = (uint)carry; carry >>= 32; } @@ -1205,9 +1365,15 @@ namespace Renci.SshNet.Common } int m; - for (m = res.Length - 1; m >= 0 && res[m] == 0; --m) ; + for (m = res.Length - 1; m >= 0 && res[m] == 0; --m) + { + // Intentionally empty block + } + if (m < res.Length - 1) + { Array.Resize(ref res, m + 1); + } return new BigInteger((short) (left._sign*right._sign), res); } @@ -1224,22 +1390,32 @@ namespace Renci.SshNet.Common public static BigInteger operator /(BigInteger dividend, BigInteger divisor) { if (divisor._sign == 0) + { throw new DivideByZeroException(); + } if (dividend._sign == 0) + { return dividend; + } - uint[] quotient; - uint[] remainderValue; - - DivModUnsigned(dividend._data, divisor._data, out quotient, out remainderValue); + DivModUnsigned(dividend._data, divisor._data, out var quotient, out _); int i; - for (i = quotient.Length - 1; i >= 0 && quotient[i] == 0; --i) ; + for (i = quotient.Length - 1; i >= 0 && quotient[i] == 0; --i) + { + // Intentionally empty block + } + if (i == -1) + { return Zero; + } + if (i < quotient.Length - 1) + { Array.Resize(ref quotient, i + 1); + } return new BigInteger((short)(dividend._sign * divisor._sign), quotient); } @@ -1255,23 +1431,33 @@ namespace Renci.SshNet.Common public static BigInteger operator %(BigInteger dividend, BigInteger divisor) { if (divisor._sign == 0) + { throw new DivideByZeroException(); + } if (dividend._sign == 0) + { return dividend; + } - uint[] quotient; - uint[] remainderValue; - - DivModUnsigned(dividend._data, divisor._data, out quotient, out remainderValue); + DivModUnsigned(dividend._data, divisor._data, out _, out var remainderValue); int i; - for (i = remainderValue.Length - 1; i >= 0 && remainderValue[i] == 0; --i) ; + for (i = remainderValue.Length - 1; i >= 0 && remainderValue[i] == 0; --i) + { + // Intentionally empty block + } + if (i == -1) + { return Zero; + } if (i < remainderValue.Length - 1) + { Array.Resize(ref remainderValue, i + 1); + } + return new BigInteger(dividend._sign, remainderValue); } @@ -1279,14 +1465,17 @@ namespace Renci.SshNet.Common /// Negates a specified value. /// /// The value to negate. - /// + /// /// The result of the parameter multiplied by negative one (-1). /// public static BigInteger operator -(BigInteger value) { - if (value._data == null) + if (value._data is null) + { return value; - return new BigInteger((short)-value._sign, value._data); + } + + return new BigInteger((short) -value._sign, value._data); } /// @@ -1313,17 +1502,24 @@ namespace Renci.SshNet.Common /// public static BigInteger operator ++(BigInteger value) { - if (value._data == null) + if (value._data is null) + { return One; + } var sign = value._sign; var data = value._data; if (data.Length == 1) { if (sign == -1 && data[0] == 1) + { return Zero; + } + if (sign == 0) + { return One; + } } data = sign == -1 ? CoreSub(data, 1) : CoreAdd(data, 1); @@ -1340,17 +1536,24 @@ namespace Renci.SshNet.Common /// public static BigInteger operator --(BigInteger value) { - if (value._data == null) + if (value._data is null) + { return MinusOne; + } var sign = value._sign; var data = value._data; if (data.Length == 1) { if (sign == 1 && data[0] == 1) + { return Zero; + } + if (sign == 0) + { return MinusOne; + } } data = sign == -1 ? CoreAdd(data, 1) : CoreSub(data, 1); @@ -1369,10 +1572,14 @@ namespace Renci.SshNet.Common public static BigInteger operator &(BigInteger left, BigInteger right) { if (left._sign == 0) + { return left; + } if (right._sign == 0) + { return right; + } var a = left._data; var b = right._data; @@ -1390,7 +1597,10 @@ namespace Renci.SshNet.Common { uint va = 0; if (i < a.Length) + { va = a[i]; + } + if (ls == -1) { ac = ~va + ac; @@ -1400,7 +1610,10 @@ namespace Renci.SshNet.Common uint vb = 0; if (i < b.Length) + { vb = b[i]; + } + if (rs == -1) { bc = ~vb + bc; @@ -1420,12 +1633,20 @@ namespace Renci.SshNet.Common result[i] = word; } - for (i = result.Length - 1; i >= 0 && result[i] == 0; --i) ; + for (i = result.Length - 1; i >= 0 && result[i] == 0; --i) + { + // Intentionally empty block + } + if (i == -1) + { return Zero; + } if (i < result.Length - 1) + { Array.Resize(ref result, i + 1); + } return new BigInteger(negRes ? (short)-1 : (short)1, result); } @@ -1441,10 +1662,14 @@ namespace Renci.SshNet.Common public static BigInteger operator |(BigInteger left, BigInteger right) { if (left._sign == 0) + { return right; + } if (right._sign == 0) + { return left; + } var a = left._data; var b = right._data; @@ -1462,7 +1687,10 @@ namespace Renci.SshNet.Common { uint va = 0; if (i < a.Length) + { va = a[i]; + } + if (ls == -1) { ac = ~va + ac; @@ -1472,7 +1700,10 @@ namespace Renci.SshNet.Common uint vb = 0; if (i < b.Length) + { vb = b[i]; + } + if (rs == -1) { bc = ~vb + bc; @@ -1492,12 +1723,20 @@ namespace Renci.SshNet.Common result[i] = word; } - for (i = result.Length - 1; i >= 0 && result[i] == 0; --i) ; + for (i = result.Length - 1; i >= 0 && result[i] == 0; --i) + { + // Intentionally empty block + } + if (i == -1) + { return Zero; + } if (i < result.Length - 1) + { Array.Resize(ref result, i + 1); + } return new BigInteger(negRes ? (short)-1 : (short)1, result); } @@ -1513,10 +1752,14 @@ namespace Renci.SshNet.Common public static BigInteger operator ^(BigInteger left, BigInteger right) { if (left._sign == 0) + { return right; + } if (right._sign == 0) + { return left; + } var a = left._data; var b = right._data; @@ -1534,7 +1777,10 @@ namespace Renci.SshNet.Common { uint va = 0; if (i < a.Length) + { va = a[i]; + } + if (ls == -1) { ac = ~va + ac; @@ -1544,7 +1790,10 @@ namespace Renci.SshNet.Common uint vb = 0; if (i < b.Length) + { vb = b[i]; + } + if (rs == -1) { bc = ~vb + bc; @@ -1564,12 +1813,20 @@ namespace Renci.SshNet.Common result[i] = word; } - for (i = result.Length - 1; i >= 0 && result[i] == 0; --i) ; + for (i = result.Length - 1; i >= 0 && result[i] == 0; --i) + { + // Intentionally empty block + } + if (i == -1) + { return Zero; + } if (i < result.Length - 1) + { Array.Resize(ref result, i + 1); + } return new BigInteger(negRes ? (short)-1 : (short)1, result); } @@ -1583,8 +1840,10 @@ namespace Renci.SshNet.Common /// public static BigInteger operator ~(BigInteger value) { - if (value._data == null) + if (value._data is null) + { return MinusOne; + } var data = value._data; int sign = value._sign; @@ -1618,12 +1877,20 @@ namespace Renci.SshNet.Common result[i] = word; } - for (i = result.Length - 1; i >= 0 && result[i] == 0; --i) ; + for (i = result.Length - 1; i >= 0 && result[i] == 0; --i) + { + // Intentionally empty block + } + if (i == -1) + { return Zero; + } if (i < result.Length - 1) + { Array.Resize(ref result, i + 1); + } return new BigInteger(negRes ? (short)-1 : (short)1, result); } @@ -1636,8 +1903,11 @@ namespace Renci.SshNet.Common { var mask = 1u << i; if ((word & mask) == mask) + { return i; + } } + return 0; } @@ -1651,10 +1921,15 @@ namespace Renci.SshNet.Common /// public static BigInteger operator <<(BigInteger value, int shift) { - if (shift == 0 || value._data == null) + if (shift == 0 || value._data is null) + { return value; + } + if (shift < 0) + { return value >> -shift; + } var data = value._data; int sign = value._sign; @@ -1684,7 +1959,9 @@ namespace Renci.SshNet.Common var word = data[i]; res[i + idxShift] |= word << bitShift; if (i + idxShift + 1 < res.Length) + { res[i + idxShift + 1] = word >> carryShift; + } } } @@ -1702,9 +1979,14 @@ namespace Renci.SshNet.Common public static BigInteger operator >>(BigInteger value, int shift) { if (shift == 0 || value._sign == 0) + { return value; + } + if (shift < 0) + { return value << -shift; + } var data = value._data; int sign = value._sign; @@ -1715,14 +1997,15 @@ namespace Renci.SshNet.Common var extraWords = idxShift; if (bitShift > topMostIdx) + { ++extraWords; + } + var size = data.Length - extraWords; if (size <= 0) { - if (sign == 1) - return Zero; - return MinusOne; + return sign == 1 ? Zero : MinusOne; } var res = new uint[size]; @@ -1735,7 +2018,9 @@ namespace Renci.SshNet.Common var word = data[i]; if (i - idxShift < res.Length) + { res[i - idxShift] |= word >> bitShift; + } } } else @@ -1745,14 +2030,18 @@ namespace Renci.SshNet.Common var word = data[i]; if (i - idxShift < res.Length) + { res[i - idxShift] |= word >> bitShift; - if (i - idxShift - 1 >= 0) - res[i - idxShift - 1] = word << carryShift; - } + } + if (i - idxShift - 1 >= 0) + { + res[i - idxShift - 1] = word << carryShift; + } + } } - //Round down instead of toward zero + // Round down instead of toward zero if (sign == -1) { for (var i = 0; i < idxShift; i++) @@ -1764,6 +2053,7 @@ namespace Renci.SshNet.Common return tmp; } } + if (bitShift > 0 && (data[idxShift] << carryShift) != 0u) { var tmp = new BigInteger((short)sign, res); @@ -1771,6 +2061,7 @@ namespace Renci.SshNet.Common return tmp; } } + return new BigInteger((short)sign, res); } @@ -1816,7 +2107,6 @@ namespace Renci.SshNet.Common return right.CompareTo(left) > 0; } - /// /// Returns a value that indicates whether a 64-bit signed integer is less than a value. /// @@ -2224,11 +2514,14 @@ namespace Renci.SshNet.Common /// of implicit conversion to a value, and its value is equal to the value of the /// current object; otherwise, false. /// - public override bool Equals(object obj) + public override readonly bool Equals(object obj) { - if (!(obj is BigInteger)) + if (obj is not BigInteger other) + { return false; - return Equals((BigInteger)obj); + } + + return Equals(other); } /// @@ -2240,21 +2533,29 @@ namespace Renci.SshNet.Common /// true if this object and have the same value; /// otherwise, false. /// - public bool Equals(BigInteger other) + public readonly bool Equals(BigInteger other) { if (_sign != other._sign) + { return false; + } var alen = _data != null ? _data.Length : 0; var blen = other._data != null ? other._data.Length : 0; if (alen != blen) + { return false; + } + for (var i = 0; i < alen; ++i) { if (_data[i] != other._data[i]) + { return false; + } } + return true; } @@ -2265,7 +2566,20 @@ namespace Renci.SshNet.Common /// /// true if the signed 64-bit integer and the current instance have the same value; otherwise, false. /// - public bool Equals(long other) + public readonly bool Equals(long other) + { + return CompareTo(other) == 0; + } + + /// + /// Returns a value that indicates whether the current instance and an unsigned 64-bit integer have the same value. + /// + /// The unsigned 64-bit integer to compare. + /// + /// true if the current instance and the unsigned 64-bit integer have the same value; otherwise, false. + /// + [CLSCompliant(false)] + public readonly bool Equals(ulong other) { return CompareTo(other) == 0; } @@ -2276,29 +2590,9 @@ namespace Renci.SshNet.Common /// /// The string representation of the current value. /// - public override string ToString() + public override readonly string ToString() { - return ToString(10, null); - } - - private string ToStringWithPadding(string format, uint radix, IFormatProvider provider) - { - if (format.Length > 1) - { - var precision = Convert.ToInt32(format.Substring(1), CultureInfo.InvariantCulture.NumberFormat); - var baseStr = ToString(radix, provider); - if (baseStr.Length < precision) - { - var additional = new string('0', precision - baseStr.Length); - if (baseStr[0] != '-') - { - return additional + baseStr; - } - return "-" + additional + baseStr.Substring(1); - } - return baseStr; - } - return ToString(radix, provider); + return ToString(10, provider: null); } /// @@ -2311,23 +2605,23 @@ namespace Renci.SshNet.Common /// parameter. /// /// is not a valid format string. - public string ToString(string format) + public readonly string ToString(string format) { - return ToString(format, null); + return ToString(format, formatProvider: null); } /// /// Converts the numeric value of the current object to its equivalent string representation - /// by using the specified culture-specific formatting information. + /// by using the specified culture-specific formatting information. /// /// An object that supplies culture-specific formatting information. /// /// The string representation of the current value in the format specified by the /// parameter. /// - public string ToString(IFormatProvider provider) + public readonly string ToString(IFormatProvider provider) { - return ToString(null, provider); + return ToString(format: null, provider); } /// @@ -2335,15 +2629,17 @@ namespace Renci.SshNet.Common /// by using the specified format and culture-specific format information. /// /// A standard or custom numeric format string. - /// An object that supplies culture-specific formatting information. + /// An object that supplies culture-specific formatting information. /// /// The string representation of the current value as specified by the - /// and parameters. + /// and parameters. /// - public string ToString(string format, IFormatProvider provider) + public readonly string ToString(string format, IFormatProvider formatProvider) { if (string.IsNullOrEmpty(format)) - return ToString(10, provider); + { + return ToString(10, formatProvider); + } switch (format[0]) { @@ -2353,15 +2649,38 @@ namespace Renci.SshNet.Common case 'G': case 'r': case 'R': - return ToStringWithPadding(format, 10, provider); + return ToStringWithPadding(format, 10, formatProvider); case 'x': case 'X': - return ToStringWithPadding(format, 16, null); + return ToStringWithPadding(format, 16, provider: null); default: throw new FormatException(string.Format("format '{0}' not implemented", format)); } } + private readonly string ToStringWithPadding(string format, uint radix, IFormatProvider provider) + { + if (format.Length > 1) + { + var precision = Convert.ToInt32(format.Substring(1), CultureInfo.InvariantCulture.NumberFormat); + var baseStr = ToString(radix, provider); + if (baseStr.Length < precision) + { + var additional = new string('0', precision - baseStr.Length); + if (baseStr[0] != '-') + { + return additional + baseStr; + } + + return "-" + additional + baseStr.Substring(1); + } + + return baseStr; + } + + return ToString(radix, provider); + } + private static uint[] MakeTwoComplement(uint[] v) { var res = new uint[v.Length]; @@ -2380,56 +2699,77 @@ namespace Renci.SshNet.Common var idx = FirstNonFfByte(last); uint mask = 0xFF; for (var i = 1; i < idx; ++i) + { mask = (mask << 8) | 0xFF; + } res[res.Length - 1] = last & mask; return res; } - private string ToString(uint radix, IFormatProvider provider) + private readonly string ToString(uint radix, IFormatProvider provider) { const string characterSet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; if (characterSet.Length < radix) + { throw new ArgumentException("charSet length less than radix", "characterSet"); + } + if (radix == 1) - throw new ArgumentException("There is no such thing as radix one notation", "radix"); + { + throw new ArgumentException("There is no such thing as radix one notation", nameof(radix)); + } if (_sign == 0) + { return "0"; + } + if (_data.Length == 1 && _data[0] == 1) + { return _sign == 1 ? "1" : "-1"; + } var digits = new List(1 + _data.Length * 3 / 10); BigInteger a; if (_sign == 1) + { a = this; + } else { var dt = _data; if (radix > 10) + { dt = MakeTwoComplement(dt); + } + a = new BigInteger(1, dt); } while (a != 0) { - BigInteger rem; - a = DivRem(a, radix, out rem); - digits.Add(characterSet[(int)rem]); + a = DivRem(a, radix, out var rem); + digits.Add(characterSet[(int) rem]); } if (_sign == -1 && radix == 10) { NumberFormatInfo info = null; if (provider != null) + { info = provider.GetFormat(typeof(NumberFormatInfo)) as NumberFormatInfo; + } + if (info != null) { var str = info.NegativeSign; for (var i = str.Length - 1; i >= 0; --i) + { digits.Add(str[i]); + } } else { @@ -2439,7 +2779,9 @@ namespace Renci.SshNet.Common var last = digits[digits.Count - 1]; if (_sign == 1 && radix > 10 && (last < '0' || last > '9')) + { digits.Add('0'); + } digits.Reverse(); @@ -2457,11 +2799,11 @@ namespace Renci.SshNet.Common /// is not in the correct format. public static BigInteger Parse(string value) { - Exception ex; - BigInteger result; - - if (!Parse(value, false, out result, out ex)) + if (!Parse(value, tryParse: false, out var result, out var ex)) + { throw ex; + } + return result; } @@ -2482,7 +2824,7 @@ namespace Renci.SshNet.Common /// does not comply with the input pattern specified by . public static BigInteger Parse(string value, NumberStyles style) { - return Parse(value, style, null); + return Parse(value, style, provider: null); } /// @@ -2518,11 +2860,10 @@ namespace Renci.SshNet.Common /// does not comply with the input pattern specified by . public static BigInteger Parse(string value, NumberStyles style, IFormatProvider provider) { - Exception exc; - BigInteger res; - - if (!Parse(value, style, provider, false, out res, out exc)) + if (!Parse(value, style, provider, tryParse: false, out var res, out var exc)) + { throw exc; + } return res; } @@ -2539,8 +2880,7 @@ namespace Renci.SshNet.Common /// is null. public static bool TryParse(string value, out BigInteger result) { - Exception ex; - return Parse(value, true, out result, out ex); + return Parse(value, tryParse: true, out result, out _); } /// @@ -2561,8 +2901,7 @@ namespace Renci.SshNet.Common /// public static bool TryParse(string value, NumberStyles style, IFormatProvider provider, out BigInteger result) { - Exception exc; - if (!Parse(value, style, provider, true, out result, out exc)) + if (!Parse(value, style, provider, tryParse: true, out result, out _)) { result = Zero; return false; @@ -2576,17 +2915,23 @@ namespace Renci.SshNet.Common result = Zero; exc = null; - if (value == null) + if (value is null) { if (!tryParse) - exc = new ArgumentNullException("value"); + { + exc = new ArgumentNullException(nameof(value)); + } + return false; } if (value.Length == 0) { if (!tryParse) + { exc = GetFormatException(); + } + return false; } @@ -2596,11 +2941,13 @@ namespace Renci.SshNet.Common var typeNfi = typeof(NumberFormatInfo); nfi = (NumberFormatInfo) fp.GetFormat(typeNfi); } - if (nfi == null) - nfi = NumberFormatInfo.CurrentInfo; + + nfi ??= NumberFormatInfo.CurrentInfo; if (!CheckStyle(style, tryParse, ref exc)) + { return false; + } var allowCurrencySymbol = (style & NumberStyles.AllowCurrencySymbol) != 0; var allowHexSpecifier = (style & NumberStyles.AllowHexSpecifier) != 0; @@ -2615,8 +2962,10 @@ namespace Renci.SshNet.Common var pos = 0; - if (allowLeadingWhite && !JumpOverWhitespace(ref pos, value, true, tryParse, ref exc)) + if (allowLeadingWhite && !JumpOverWhitespace(ref pos, value, reportError: true, tryParse, ref exc)) + { return false; + } var foundOpenParentheses = false; var negative = false; @@ -2628,23 +2977,31 @@ namespace Renci.SshNet.Common { foundOpenParentheses = true; foundSign = true; - negative = true; // MS always make the number negative when there parentheses - // even when NumberFormatInfo.NumberNegativePattern != 0!!! + negative = true; // MS always make the number negative when there parentheses, even when NumberFormatInfo.NumberNegativePattern != 0 pos++; - if (allowLeadingWhite && !JumpOverWhitespace(ref pos, value, true, tryParse, ref exc)) + + if (allowLeadingWhite && !JumpOverWhitespace(ref pos, value, reportError: true, tryParse, ref exc)) + { return false; + } if (value.Substring(pos, nfi.NegativeSign.Length) == nfi.NegativeSign) { if (!tryParse) + { exc = GetFormatException(); + } + return false; } if (value.Substring(pos, nfi.PositiveSign.Length) == nfi.PositiveSign) { if (!tryParse) + { exc = GetFormatException(); + } + return false; } } @@ -2655,15 +3012,18 @@ namespace Renci.SshNet.Common FindSign(ref pos, value, nfi, ref foundSign, ref negative); if (foundSign) { - if (allowLeadingWhite && !JumpOverWhitespace(ref pos, value, true, tryParse, ref exc)) + if (allowLeadingWhite && !JumpOverWhitespace(ref pos, value, reportError: true, tryParse, ref exc)) + { return false; + } + if (allowCurrencySymbol) { - FindCurrency(ref pos, value, nfi, - ref foundCurrency); - if (foundCurrency && allowLeadingWhite && - !JumpOverWhitespace(ref pos, value, true, tryParse, ref exc)) + FindCurrency(ref pos, value, nfi, ref foundCurrency); + if (foundCurrency && allowLeadingWhite && !JumpOverWhitespace(ref pos, value, reportError: true, tryParse, ref exc)) + { return false; + } } } } @@ -2674,17 +3034,20 @@ namespace Renci.SshNet.Common FindCurrency(ref pos, value, nfi, ref foundCurrency); if (foundCurrency) { - if (allowLeadingWhite && !JumpOverWhitespace(ref pos, value, true, tryParse, ref exc)) + if (allowLeadingWhite && !JumpOverWhitespace(ref pos, value, reportError: true, tryParse, ref exc)) + { return false; + } + if (foundCurrency) { if (!foundSign && allowLeadingSign) { - FindSign(ref pos, value, nfi, ref foundSign, - ref negative); - if (foundSign && allowLeadingWhite && - !JumpOverWhitespace(ref pos, value, true, tryParse, ref exc)) + FindSign(ref pos, value, nfi, ref foundSign, ref negative); + if (foundSign && allowLeadingWhite && !JumpOverWhitespace(ref pos, value, reportError: true, tryParse, ref exc)) + { return false; + } } } } @@ -2698,13 +3061,14 @@ namespace Renci.SshNet.Common // Number stuff while (pos < value.Length) { - if (!ValidDigit(value[pos], allowHexSpecifier)) { if (allowThousands && (FindOther(ref pos, value, nfi.NumberGroupSeparator) || FindOther(ref pos, value, nfi.CurrencyGroupSeparator))) + { continue; + } if (allowDecimalPoint && decimalPointPos < 0 && (FindOther(ref pos, value, nfi.NumberDecimalSeparator) @@ -2724,32 +3088,43 @@ namespace Renci.SshNet.Common var hexDigit = value[pos++]; byte digitValue; if (char.IsDigit(hexDigit)) - digitValue = (byte)(hexDigit - '0'); + { + digitValue = (byte) (hexDigit - '0'); + } else if (char.IsLower(hexDigit)) - digitValue = (byte)(hexDigit - 'a' + 10); + { + digitValue = (byte) (hexDigit - 'a' + 10); + } else - digitValue = (byte)(hexDigit - 'A' + 10); + { + digitValue = (byte) (hexDigit - 'A' + 10); + } if (firstHexDigit && digitValue >= 8) + { negative = true; + } - number = number * 16 + digitValue; + number = (number * 16) + digitValue; firstHexDigit = false; continue; } - number = number * 10 + (byte)(value[pos++] - '0'); + number = (number * 10) + (byte)(value[pos++] - '0'); } // Post number stuff if (nDigits == 0) { if (!tryParse) + { exc = GetFormatException(); + } + return false; } - //Signed hex value (Two's Complement) + // Signed hex value (Two's Complement) if (allowHexSpecifier && negative) { var mask = Pow(16, nDigits) - 1; @@ -2758,8 +3133,12 @@ namespace Renci.SshNet.Common var exponent = 0; if (allowExponent) + { if (FindExponent(ref pos, value, ref exponent, tryParse, ref exc) && exc != null) + { return false; + } + } if (allowTrailingSign && !foundSign) { @@ -2767,65 +3146,86 @@ namespace Renci.SshNet.Common FindSign(ref pos, value, nfi, ref foundSign, ref negative); if (foundSign && pos < value.Length) { - if (allowTrailingWhite && !JumpOverWhitespace(ref pos, value, true, tryParse, ref exc)) + if (allowTrailingWhite && !JumpOverWhitespace(ref pos, value, reportError: true, tryParse, ref exc)) + { return false; + } } } if (allowCurrencySymbol && !foundCurrency) { - if (allowTrailingWhite && pos < value.Length && !JumpOverWhitespace(ref pos, value, false, tryParse, ref exc)) + if (allowTrailingWhite && pos < value.Length && !JumpOverWhitespace(ref pos, value, reportError: false, tryParse, ref exc)) + { return false; + } // Currency + sign FindCurrency(ref pos, value, nfi, ref foundCurrency); if (foundCurrency && pos < value.Length) { - if (allowTrailingWhite && !JumpOverWhitespace(ref pos, value, true, tryParse, ref exc)) + if (allowTrailingWhite && !JumpOverWhitespace(ref pos, value, reportError: true, tryParse, ref exc)) + { return false; + } + if (!foundSign && allowTrailingSign) - FindSign(ref pos, value, nfi, ref foundSign, - ref negative); + { + FindSign(ref pos, value, nfi, ref foundSign, ref negative); + } } } - if (allowTrailingWhite && pos < value.Length && !JumpOverWhitespace(ref pos, value, false, tryParse, ref exc)) + if (allowTrailingWhite && pos < value.Length && !JumpOverWhitespace(ref pos, value, reportError: false, tryParse, ref exc)) + { return false; + } if (foundOpenParentheses) { if (pos >= value.Length || value[pos++] != ')') { if (!tryParse) + { exc = GetFormatException(); + } + return false; } - if (allowTrailingWhite && pos < value.Length && !JumpOverWhitespace(ref pos, value, false, tryParse, ref exc)) + + if (allowTrailingWhite && pos < value.Length && !JumpOverWhitespace(ref pos, value, reportError: false, tryParse, ref exc)) + { return false; + } } if (pos < value.Length && value[pos] != '\u0000') { if (!tryParse) + { exc = GetFormatException(); + } + return false; } if (decimalPointPos >= 0) + { exponent = exponent - nDigits + decimalPointPos; + } if (exponent < 0) { - // // Any non-zero values after decimal point are not allowed - // - BigInteger remainder; - number = DivRem(number, Pow(10, -exponent), out remainder); + number = DivRem(number, Pow(10, -exponent), out var remainder); if (!remainder.IsZero) { if (!tryParse) + { exc = new OverflowException("Value too large or too small. exp=" + exponent + " rem = " + remainder + " pow = " + Pow(10, -exponent)); + } + return false; } } @@ -2835,11 +3235,17 @@ namespace Renci.SshNet.Common } if (number._sign == 0) + { result = number; + } else if (negative) + { result = new BigInteger(-1, number._data); + } else + { result = new BigInteger(1, number._data); + } return true; } @@ -2850,23 +3256,34 @@ namespace Renci.SshNet.Common { var ne = style ^ NumberStyles.AllowHexSpecifier; if ((ne & NumberStyles.AllowLeadingWhite) != 0) + { ne ^= NumberStyles.AllowLeadingWhite; + } + if ((ne & NumberStyles.AllowTrailingWhite) != 0) + { ne ^= NumberStyles.AllowTrailingWhite; + } + if (ne != 0) { if (!tryParse) - exc = new ArgumentException( - "With AllowHexSpecifier only " + - "AllowLeadingWhite and AllowTrailingWhite " + - "are permitted."); + { + exc = new ArgumentException("With AllowHexSpecifier only " + + "AllowLeadingWhite and AllowTrailingWhite " + + "are permitted."); + } + return false; } } else if ((uint)style > (uint)NumberStyles.Any) { if (!tryParse) + { exc = new ArgumentException("Not a valid number style"); + } + return false; } @@ -2876,12 +3293,17 @@ namespace Renci.SshNet.Common private static bool JumpOverWhitespace(ref int pos, string s, bool reportError, bool tryParse, ref Exception exc) { while (pos < s.Length && char.IsWhiteSpace(s[pos])) + { pos++; + } if (reportError && pos >= s.Length) { if (!tryParse) + { exc = GetFormatException(); + } + return false; } @@ -2960,8 +3382,8 @@ namespace Renci.SshNet.Common } // Reduce the risk of throwing an overflow exc - exp = checked(exp * 10 - (int)(s[i] - '0')); - if (exp < int.MinValue || exp > int.MaxValue) + exp = checked((exp * 10) - (s[i] - '0')); + if (exp is < int.MinValue or > int.MaxValue) { exc = tryParse ? null : new OverflowException("Value too large or too small."); return true; @@ -2970,7 +3392,9 @@ namespace Renci.SshNet.Common // exp value saved as negative if (!negative) + { exp = -exp; + } exc = null; exponent = (int) exp; @@ -2993,7 +3417,9 @@ namespace Renci.SshNet.Common private static bool ValidDigit(char e, bool allowHex) { if (allowHex) + { return char.IsDigit(e) || (e >= 'A' && e <= 'F') || (e >= 'a' && e <= 'f'); + } return char.IsDigit(e); } @@ -3014,10 +3440,14 @@ namespace Renci.SshNet.Common if (c != 0 && !char.IsWhiteSpace(c)) { if (!tryParse) + { exc = GetFormatException(); + } + return false; } } + return true; } @@ -3029,10 +3459,13 @@ namespace Renci.SshNet.Common result = Zero; exc = null; - if (value == null) + if (value is null) { if (!tryParse) - exc = new ArgumentNullException("value"); + { + exc = new ArgumentNullException(nameof(value)); + } + return false; } @@ -3043,13 +3476,18 @@ namespace Renci.SshNet.Common { c = value[i]; if (!char.IsWhiteSpace(c)) + { break; + } } if (i == len) { if (!tryParse) + { exc = GetFormatException(); + } + return false; } @@ -3059,7 +3497,9 @@ namespace Renci.SshNet.Common var positive = info.PositiveSign; if (string.CompareOrdinal(value, i, positive, 0, positive.Length) == 0) + { i += positive.Length; + } else if (string.CompareOrdinal(value, i, negative, 0, negative.Length) == 0) { sign = -1; @@ -3077,31 +3517,42 @@ namespace Renci.SshNet.Common continue; } - if (c >= '0' && c <= '9') + if (c is >= '0' and <= '9') { - var d = (byte)(c - '0'); + var d = (byte) (c - '0'); - val = val * 10 + d; + val = (val * 10) + d; digitsSeen = true; } else if (!ProcessTrailingWhitespace(tryParse, value, i, ref exc)) + { return false; + } } if (!digitsSeen) { if (!tryParse) + { exc = GetFormatException(); + } + return false; } if (val._sign == 0) + { result = val; + } else if (sign == -1) + { result = new BigInteger(-1, val._data); + } else + { result = new BigInteger(1, val._data); + } return true; } @@ -3120,16 +3571,26 @@ namespace Renci.SshNet.Common int rs = right._sign; if (ls < rs) + { return left; + } + if (rs < ls) + { return right; + } var r = CoreCompare(left._data, right._data); if (ls == -1) + { r = -r; + } if (r <= 0) + { return left; + } + return right; } @@ -3147,16 +3608,26 @@ namespace Renci.SshNet.Common int rs = right._sign; if (ls > rs) + { return left; + } + if (rs > ls) + { return right; + } var r = CoreCompare(left._data, right._data); if (ls == -1) + { r = -r; + } if (r >= 0) + { return left; + } + return right; } @@ -3169,7 +3640,7 @@ namespace Renci.SshNet.Common /// public static BigInteger Abs(BigInteger value) { - return new BigInteger((short)Math.Abs(value._sign), value._data); + return new BigInteger(Math.Abs(value._sign), value._data); } /// @@ -3185,7 +3656,9 @@ namespace Renci.SshNet.Common public static BigInteger DivRem(BigInteger dividend, BigInteger divisor, out BigInteger remainder) { if (divisor._sign == 0) + { throw new DivideByZeroException(); + } if (dividend._sign == 0) { @@ -3193,13 +3666,14 @@ namespace Renci.SshNet.Common return dividend; } - uint[] quotient; - uint[] remainderValue; - - DivModUnsigned(dividend._data, divisor._data, out quotient, out remainderValue); + DivModUnsigned(dividend._data, divisor._data, out var quotient, out var remainderValue); int i; - for (i = remainderValue.Length - 1; i >= 0 && remainderValue[i] == 0; --i) ; + for (i = remainderValue.Length - 1; i >= 0 && remainderValue[i] == 0; --i) + { + // Intentionally empty block + } + if (i == -1) { remainder = Zero; @@ -3207,15 +3681,27 @@ namespace Renci.SshNet.Common else { if (i < remainderValue.Length - 1) + { Array.Resize(ref remainderValue, i + 1); + } + remainder = new BigInteger(dividend._sign, remainderValue); } - for (i = quotient.Length - 1; i >= 0 && quotient[i] == 0; --i) ; + for (i = quotient.Length - 1; i >= 0 && quotient[i] == 0; --i) + { + // Intentionally empty block + } + if (i == -1) + { return Zero; + } + if (i < quotient.Length - 1) + { Array.Resize(ref quotient, i + 1); + } return new BigInteger((short)(dividend._sign * divisor._sign), quotient); } @@ -3231,23 +3717,37 @@ namespace Renci.SshNet.Common public static BigInteger Pow(BigInteger value, int exponent) { if (exponent < 0) - throw new ArgumentOutOfRangeException("exponent", "exp must be >= 0"); + { + throw new ArgumentOutOfRangeException(nameof(exponent), "exp must be >= 0"); + } + if (exponent == 0) + { return One; + } + if (exponent == 1) + { return value; + } var result = One; while (exponent != 0) { if ((exponent & 1) != 0) - result = result * value; - if (exponent == 1) - break; + { + result *= value; + } - value = value * value; + if (exponent == 1) + { + break; + } + + value *= value; exponent >>= 1; } + return result; } @@ -3265,24 +3765,34 @@ namespace Renci.SshNet.Common public static BigInteger ModPow(BigInteger value, BigInteger exponent, BigInteger modulus) { if (exponent._sign == -1) - throw new ArgumentOutOfRangeException("exponent", "power must be >= 0"); + { + throw new ArgumentOutOfRangeException(nameof(exponent), "power must be >= 0"); + } + if (modulus._sign == 0) + { throw new DivideByZeroException(); + } var result = One % modulus; while (exponent._sign != 0) { if (!exponent.IsEven) { - result = result * value; - result = result % modulus; + result *= value; + result %= modulus; } + if (exponent.IsOne) + { break; - value = value * value; - value = value % modulus; + } + + value *= value; + value %= modulus; exponent >>= 1; } + return result; } @@ -3297,13 +3807,24 @@ namespace Renci.SshNet.Common public static BigInteger GreatestCommonDivisor(BigInteger left, BigInteger right) { if (left._sign != 0 && left._data.Length == 1 && left._data[0] == 1) + { return One; + } + if (right._sign != 0 && right._data.Length == 1 && right._data[0] == 1) + { return One; + } + if (left.IsZero) + { return Abs(right); + } + if (right.IsZero) + { return Abs(left); + } var x = new BigInteger(1, left._data); var y = new BigInteger(1, right._data); @@ -3317,14 +3838,18 @@ namespace Renci.SshNet.Common y = g; } - if (x.IsZero) return g; + + if (x.IsZero) + { + return g; + } // TODO: should we have something here if we can convert to long? - // - // Now we can just do it with single precision. I am using the binary gcd method, - // as it should be faster. - // + /* + * Now we can just do it with single precision. I am using the binary gcd method, + * as it should be faster. + */ var yy = x._data[0]; var xx = (uint)(y % yy); @@ -3333,24 +3858,40 @@ namespace Renci.SshNet.Common while (((xx | yy) & 1) == 0) { - xx >>= 1; yy >>= 1; t++; + xx >>= 1; + yy >>= 1; + t++; } + while (xx != 0) { - while ((xx & 1) == 0) xx >>= 1; - while ((yy & 1) == 0) yy >>= 1; + while ((xx & 1) == 0) + { + xx >>= 1; + } + + while ((yy & 1) == 0) + { + yy >>= 1; + } + if (xx >= yy) + { xx = (xx - yy) >> 1; + } else + { yy = (yy - xx) >> 1; + } } return yy << t; } - /*LAMESPEC Log doesn't specify to how many ulp is has to be precise - We are equilavent to MS with about 2 ULP - */ + /* + * LAMESPEC Log doesn't specify to how many ulp is has to be precise + * We are equilavent to MS with about 2 ULP + */ /// /// Returns the logarithm of a specified number in a specified base. @@ -3358,20 +3899,26 @@ namespace Renci.SshNet.Common /// A number whose logarithm is to be found. /// The base of the logarithm. /// - /// The base logarithm of value, + /// The base logarithm of value. /// /// The log of is out of range of the data type. public static double Log(BigInteger value, double baseValue) { if (value._sign == -1 || baseValue == 1.0d || baseValue == -1.0d || baseValue == double.NegativeInfinity || double.IsNaN(baseValue)) + { return double.NaN; + } - if (baseValue == 0.0d || baseValue == double.PositiveInfinity) + if (baseValue is 0.0d or double.PositiveInfinity) + { return value.IsOne ? 0 : double.NaN; + } - if (value._data == null) + if (value._data is null) + { return double.NegativeInfinity; + } var length = value._data.Length - 1; var bitCount = -1; @@ -3391,19 +3938,24 @@ namespace Renci.SshNet.Common var tempBitlen = bitlen; while (tempBitlen > int.MaxValue) { - testBit = testBit << int.MaxValue; + testBit <<= int.MaxValue; tempBitlen -= int.MaxValue; } - testBit = testBit << (int)tempBitlen; + + testBit <<= (int)tempBitlen; for (var curbit = bitlen; curbit >= 0; --curbit) { if ((value & testBit)._sign != 0) + { c += d; + } + d *= 0.5; - testBit = testBit >> 1; + testBit >>= 1; } - return (Math.Log(c) + Math.Log(2) * bitlen) / Math.Log(baseValue); + + return (Math.Log(c) + (Math.Log(2) * bitlen)) / Math.Log(baseValue); } /// @@ -3432,35 +3984,24 @@ namespace Renci.SshNet.Common return Log(value, 10); } - /// - /// Returns a value that indicates whether the current instance and an unsigned 64-bit integer have the same value. - /// - /// The unsigned 64-bit integer to compare. - /// - /// true if the current instance and the unsigned 64-bit integer have the same value; otherwise, false. - /// - [CLSCompliant(false)] - public bool Equals(ulong other) - { - return CompareTo(other) == 0; - } - /// /// Returns the hash code for the current object. /// /// /// A 32-bit signed integer hash code. /// - public override int GetHashCode() + public override readonly int GetHashCode() { - var hash = (uint)(_sign * 0x01010101u); + var hash = (uint) (_sign * 0x01010101u); if (_data != null) { foreach (var bit in _data) + { hash ^= bit; + } } - return (int)hash; + return (int) hash; } /// @@ -3568,15 +4109,19 @@ namespace Renci.SshNet.Common /// /// /// is not a . - public int CompareTo(object obj) + public readonly int CompareTo(object obj) { - if (obj == null) + if (obj is null) + { return 1; + } - if (!(obj is BigInteger)) + if (obj is not BigInteger other) + { return -1; + } - return Compare(this, (BigInteger)obj); + return Compare(this, other); } /// @@ -3606,7 +4151,7 @@ namespace Renci.SshNet.Common /// /// /// - public int CompareTo(BigInteger other) + public readonly int CompareTo(BigInteger other) { return Compare(this, other); } @@ -3639,15 +4184,22 @@ namespace Renci.SshNet.Common /// /// [CLSCompliant(false)] - public int CompareTo(ulong other) + public readonly int CompareTo(ulong other) { if (_sign < 0) + { return -1; + } + if (_sign == 0) + { return other == 0 ? 0 : -1; + } if (_data.Length > 2) + { return 1; + } var high = (uint)(other >> 32); var low = (uint)other; @@ -3655,27 +4207,6 @@ namespace Renci.SshNet.Common return LongCompare(low, high); } - private int LongCompare(uint low, uint high) - { - uint h = 0; - if (_data.Length > 1) - h = _data[1]; - - if (h > high) - return 1; - if (h < high) - return -1; - - var l = _data[0]; - - if (l > low) - return 1; - if (l < low) - return -1; - - return 0; - } - /// /// Compares this instance to a signed 64-bit integer and returns an integer that indicates whether the value of this /// instance is less than, equal to, or greater than the value of the signed 64-bit integer. @@ -3703,32 +4234,77 @@ namespace Renci.SshNet.Common /// /// /// - public int CompareTo(long other) + public readonly int CompareTo(long other) { int ls = _sign; var rs = Math.Sign(other); if (ls != rs) + { return ls > rs ? 1 : -1; + } if (ls == 0) + { return 0; + } if (_data.Length > 2) + { return _sign; + } if (other < 0) + { other = -other; - var low = (uint)other; - var high = (uint)((ulong)other >> 32); + } + + var low = (uint) other; + var high = (uint) ((ulong) other >> 32); var r = LongCompare(low, high); if (ls == -1) + { r = -r; + } return r; } + private readonly int LongCompare(uint low, uint high) + { + uint h = 0; + + if (_data.Length > 1) + { + h = _data[1]; + } + + if (h > high) + { + return 1; + } + + if (h < high) + { + return -1; + } + + var l = _data[0]; + + if (l > low) + { + return 1; + } + + if (l < low) + { + return -1; + } + + return 0; + } + /// /// Compares two values and returns an integer that indicates whether the first value is less than, equal to, or greater than the second value. /// @@ -3761,11 +4337,16 @@ namespace Renci.SshNet.Common int rs = right._sign; if (ls != rs) + { return ls > rs ? 1 : -1; + } var r = CoreCompare(left._data, right._data); if (ls < 0) + { r = -r; + } + return r; } @@ -3774,22 +4355,38 @@ namespace Renci.SshNet.Common if ((x & 0xFFFF0000u) != 0) { if ((x & 0xFF000000u) != 0) + { return 4; + } + return 3; } + if ((x & 0xFF00u) != 0) + { return 2; + } + return 1; } private static int FirstNonFfByte(uint word) { if ((word & 0xFF000000u) != 0xFF000000u) + { return 4; + } + if ((word & 0xFF0000u) != 0xFF0000u) + { return 3; + } + if ((word & 0xFF00u) != 0xFF00u) + { return 2; + } + return 1; } @@ -3799,19 +4396,21 @@ namespace Renci.SshNet.Common /// /// The value of the current object converted to an array of bytes. /// - public byte[] ToByteArray() + public readonly byte[] ToByteArray() { if (_sign == 0) + { return new byte[1]; + } - //number of bytes not counting upper word + // number of bytes not counting upper word var bytes = (_data.Length - 1) * 4; var needExtraZero = false; var topWord = _data[_data.Length - 1]; int extra; - //if the topmost bit is set we need an extra + // if the topmost bit is set we need an extra if (_sign == 1) { extra = TopByte(topWord); @@ -3840,6 +4439,7 @@ namespace Renci.SshNet.Common res[j++] = (byte)(word >> 16); res[j++] = (byte)(word >> 24); } + while (extra-- > 0) { res[j++] = (byte)topWord; @@ -3866,7 +4466,7 @@ namespace Renci.SshNet.Common res[j++] = (byte)(word >> 24); } - add = (ulong)~topWord + (carry); + add = (ulong)~topWord + carry; word = (uint)add; carry = (uint)(add >> 32); if (carry == 0) @@ -3876,15 +4476,20 @@ namespace Renci.SshNet.Common var to = ex + (needExtra ? 1 : 0); if (to != extra) + { Array.Resize(ref res, bytes + to); + } while (ex-- > 0) { res[j++] = (byte)word; word >>= 8; } + if (needExtra) + { res[j++] = 0xFF; + } } else { @@ -3926,7 +4531,7 @@ namespace Renci.SshNet.Common for (; i < bl; i++) { - sum = sum + a[i]; + sum += a[i]; res[i] = (uint)sum; sum >>= 32; } @@ -3940,6 +4545,29 @@ namespace Renci.SshNet.Common return res; } + private static uint[] CoreAdd(uint[] a, uint b) + { + var len = a.Length; + var res = new uint[len]; + + ulong sum = b; + int i; + for (i = 0; i < len; i++) + { + sum += a[i]; + res[i] = (uint) sum; + sum >>= 32; + } + + if (sum != 0) + { + Array.Resize(ref res, len + 1); + res[i] = (uint) sum; + } + + return res; + } + /*invariant a > b*/ private static uint[] CoreSub(uint[] a, uint[] b) { @@ -3965,32 +4593,15 @@ namespace Renci.SshNet.Common borrow = (borrow >> 32) & 0x1; } - //remove extra zeroes - for (i = bl - 1; i >= 0 && res[i] == 0; --i) ; - if (i < bl - 1) - Array.Resize(ref res, i + 1); - - return res; - } - - private static uint[] CoreAdd(uint[] a, uint b) - { - var len = a.Length; - var res = new uint[len]; - - ulong sum = b; - int i; - for (i = 0; i < len; i++) + // remove extra zeroes + for (i = bl - 1; i >= 0 && res[i] == 0; --i) { - sum = sum + a[i]; - res[i] = (uint)sum; - sum >>= 32; + // Intentionally empty block } - if (sum != 0) + if (i < bl - 1) { - Array.Resize(ref res, len + 1); - res[i] = (uint)sum; + Array.Resize(ref res, i + 1); } return res; @@ -4010,10 +4621,16 @@ namespace Renci.SshNet.Common borrow = (borrow >> 32) & 0x1; } - //remove extra zeroes - for (i = len - 1; i >= 0 && res[i] == 0; --i) ; + // Remove extra zeroes + for (i = len - 1; i >= 0 && res[i] == 0; --i) + { + // Intentionally empty block + } + if (i < len - 1) + { Array.Resize(ref res, i + 1); + } return res; } @@ -4024,19 +4641,30 @@ namespace Renci.SshNet.Common var bl = b != null ? b.Length : 0; if (al > bl) + { return 1; + } + if (bl > al) + { return -1; + } for (var i = al - 1; i >= 0; --i) { var ai = a[i]; var bi = b[i]; if (ai > bi) + { return 1; + } + if (ai < bi) + { return -1; + } } + return 0; } @@ -4044,11 +4672,37 @@ namespace Renci.SshNet.Common { var shift = 0; - if ((value & 0xFFFF0000) == 0) { value <<= 16; shift += 16; } - if ((value & 0xFF000000) == 0) { value <<= 8; shift += 8; } - if ((value & 0xF0000000) == 0) { value <<= 4; shift += 4; } - if ((value & 0xC0000000) == 0) { value <<= 2; shift += 2; } - if ((value & 0x80000000) == 0) { value <<= 1; shift += 1; } + if ((value & 0xFFFF0000) == 0) + { + value <<= 16; + shift += 16; + } + + if ((value & 0xFF000000) == 0) + { + value <<= 8; + shift += 8; + } + + if ((value & 0xF0000000) == 0) + { + value <<= 4; + shift += 4; + } + + if ((value & 0xC0000000) == 0) + { + value <<= 2; + shift += 2; + } + + if ((value & 0x80000000) == 0) + { +#pragma warning disable IDE0059 // Unnecessary assignment of a value + value <<= 1; +#pragma warning restore IDE0059 // Unnecessary assignment of a value + shift += 1; + } return shift; } @@ -4099,7 +4753,7 @@ namespace Renci.SshNet.Common { var uni = un[i]; r[i] = (uni >> shift) | carry; - carry = (uni << lshift); + carry = uni << lshift; } } else @@ -4118,8 +4772,7 @@ namespace Renci.SshNet.Common if (n <= 1) { - // Divide by single digit - // + // Divide by single digit ulong rem = 0; var v0 = v[0]; q = new uint[m]; @@ -4134,7 +4787,8 @@ namespace Renci.SshNet.Common rem -= div * v0; q[j] = (uint)div; } - r[0] = (uint)rem; + + r[0] = (uint) rem; } else if (m >= n) { @@ -4147,10 +4801,8 @@ namespace Renci.SshNet.Common Normalize(v, n, vn, shift); q = new uint[m - n + 1]; - r = null; - // Main division loop - // + // Main division loop for (var j = m - n; j >= 0; j--) { int i; @@ -4162,22 +4814,23 @@ namespace Renci.SshNet.Common for (;;) { // Estimate too big ? - // if ((qq >= Base) || (qq * vn[n - 2] > (rr * Base + un[j + n - 2]))) { qq--; rr += (ulong)vn[n - 1]; if (rr < Base) + { continue; + } } + break; } - // Multiply and subtract - // + // Multiply and subtract long b = 0; - long t = 0; + long t; for (i = 0; i < n; i++) { var p = vn[i] * qq; @@ -4187,15 +4840,14 @@ namespace Renci.SshNet.Common t >>= 32; b = (long)p - t; } + t = (long)un[j + n] - b; un[j + n] = (uint)t; - // Store the calculated value - // + // Store the calculated value q[j] = (uint)qq; - // Add back vn[0..n] to un[j..j+n] - // + // Add back vn[0..n] to un[j..j+n] if (t < 0) { q[j]--; @@ -4206,6 +4858,7 @@ namespace Renci.SshNet.Common un[j + i] = (uint)c; c >>= 32; } + c += (ulong)un[j + n]; un[j + n] = (uint)c; } diff --git a/src/Renci.SshNet/Common/ChannelDataEventArgs.cs b/src/Renci.SshNet/Common/ChannelDataEventArgs.cs index 6b47eb6e..4891fe24 100644 --- a/src/Renci.SshNet/Common/ChannelDataEventArgs.cs +++ b/src/Renci.SshNet/Common/ChannelDataEventArgs.cs @@ -1,15 +1,10 @@ namespace Renci.SshNet.Common { /// - /// Provides data for event. + /// Provides data for event. /// internal class ChannelDataEventArgs : ChannelEventArgs { - /// - /// Gets channel data. - /// - public byte[] Data { get; private set; } - /// /// Initializes a new instance of the class. /// @@ -20,5 +15,10 @@ { Data = data; } + + /// + /// Gets channel data. + /// + public byte[] Data { get; } } } diff --git a/src/Renci.SshNet/Common/ChannelEventArgs.cs b/src/Renci.SshNet/Common/ChannelEventArgs.cs index a8e4549c..ef514fd2 100644 --- a/src/Renci.SshNet/Common/ChannelEventArgs.cs +++ b/src/Renci.SshNet/Common/ChannelEventArgs.cs @@ -7,11 +7,6 @@ namespace Renci.SshNet.Common /// internal class ChannelEventArgs : EventArgs { - /// - /// Gets the channel number. - /// - public uint ChannelNumber { get; private set; } - /// /// Initializes a new instance of the class. /// @@ -20,5 +15,13 @@ namespace Renci.SshNet.Common { ChannelNumber = channelNumber; } + + /// + /// Gets the channel number. + /// + /// + /// The channel number. + /// + public uint ChannelNumber { get; } } } diff --git a/src/Renci.SshNet/Common/ChannelExtendedDataEventArgs.cs b/src/Renci.SshNet/Common/ChannelExtendedDataEventArgs.cs index 9098d6b1..e67ccb90 100644 --- a/src/Renci.SshNet/Common/ChannelExtendedDataEventArgs.cs +++ b/src/Renci.SshNet/Common/ChannelExtendedDataEventArgs.cs @@ -1,9 +1,9 @@ namespace Renci.SshNet.Common { /// - /// Provides data for events. + /// Provides data for events. /// - internal class ChannelExtendedDataEventArgs : ChannelDataEventArgs + internal sealed class ChannelExtendedDataEventArgs : ChannelDataEventArgs { /// /// Initializes a new instance of the class. @@ -11,7 +11,8 @@ /// Channel number. /// Channel data. /// Channel data type code. - public ChannelExtendedDataEventArgs(uint channelNumber, byte[] data, uint dataTypeCode) : base(channelNumber, data) + public ChannelExtendedDataEventArgs(uint channelNumber, byte[] data, uint dataTypeCode) + : base(channelNumber, data) { DataTypeCode = dataTypeCode; } @@ -19,6 +20,9 @@ /// /// Gets the data type code. /// - public uint DataTypeCode { get; private set; } + /// + /// The data type code. + /// + public uint DataTypeCode { get; } } } diff --git a/src/Renci.SshNet/Common/ChannelOpenConfirmedEventArgs.cs b/src/Renci.SshNet/Common/ChannelOpenConfirmedEventArgs.cs index bd10d825..78cd9cd6 100644 --- a/src/Renci.SshNet/Common/ChannelOpenConfirmedEventArgs.cs +++ b/src/Renci.SshNet/Common/ChannelOpenConfirmedEventArgs.cs @@ -1,9 +1,9 @@ namespace Renci.SshNet.Common { /// - /// Provides data for event. + /// Provides data for event. /// - internal class ChannelOpenConfirmedEventArgs : ChannelEventArgs + internal sealed class ChannelOpenConfirmedEventArgs : ChannelEventArgs { /// /// Initializes a new instance of the class. @@ -24,7 +24,7 @@ /// /// The initial size of the window. /// - public uint InitialWindowSize { get; private set; } + public uint InitialWindowSize { get; } /// /// Gets the maximum size of the packet. @@ -32,6 +32,6 @@ /// /// The maximum size of the packet. /// - public uint MaximumPacketSize { get; private set; } + public uint MaximumPacketSize { get; } } } diff --git a/src/Renci.SshNet/Common/ChannelOpenFailedEventArgs.cs b/src/Renci.SshNet/Common/ChannelOpenFailedEventArgs.cs index f130a59e..291e9d7a 100644 --- a/src/Renci.SshNet/Common/ChannelOpenFailedEventArgs.cs +++ b/src/Renci.SshNet/Common/ChannelOpenFailedEventArgs.cs @@ -1,25 +1,10 @@ namespace Renci.SshNet.Common { /// - /// Provides data for event. + /// Provides data for event. /// - internal class ChannelOpenFailedEventArgs : ChannelEventArgs + internal sealed class ChannelOpenFailedEventArgs : ChannelEventArgs { - /// - /// Gets failure reason code. - /// - public uint ReasonCode { get; private set; } - - /// - /// Gets failure description. - /// - public string Description { get; private set; } - - /// - /// Gets failure language. - /// - public string Language { get; private set; } - /// /// Initializes a new instance of the class. /// @@ -34,5 +19,20 @@ Description = description; Language = language; } + + /// + /// Gets failure reason code. + /// + public uint ReasonCode { get; } + + /// + /// Gets failure description. + /// + public string Description { get; } + + /// + /// Gets failure language. + /// + public string Language { get; } } } diff --git a/src/Renci.SshNet/Common/ChannelRequestEventArgs.cs b/src/Renci.SshNet/Common/ChannelRequestEventArgs.cs index 0de905a8..7747b762 100644 --- a/src/Renci.SshNet/Common/ChannelRequestEventArgs.cs +++ b/src/Renci.SshNet/Common/ChannelRequestEventArgs.cs @@ -1,18 +1,14 @@ using System; + using Renci.SshNet.Messages.Connection; namespace Renci.SshNet.Common { /// - /// Provides data for event. + /// Provides data for event. /// - internal class ChannelRequestEventArgs : EventArgs + internal sealed class ChannelRequestEventArgs : EventArgs { - /// - /// Gets request information. - /// - public RequestInfo Info { get; private set; } - /// /// Initializes a new instance of the class. /// @@ -21,5 +17,13 @@ namespace Renci.SshNet.Common { Info = info; } + + /// + /// Gets the request information. + /// + /// + /// The request information. + /// + public RequestInfo Info { get; } } } diff --git a/src/Renci.SshNet/Common/CountdownEvent.cs b/src/Renci.SshNet/Common/CountdownEvent.cs deleted file mode 100644 index 7387a786..00000000 --- a/src/Renci.SshNet/Common/CountdownEvent.cs +++ /dev/null @@ -1,171 +0,0 @@ -#if !FEATURE_THREAD_COUNTDOWNEVENT - -using System; -using System.Threading; - -namespace Renci.SshNet.Common -{ - /// - /// Represents a synchronization primitive that is signaled when its count reaches zero. - /// - internal class CountdownEvent : IDisposable - { - private int _count; - private ManualResetEvent _event; - private bool _disposed; - - /// - /// Initializes a new instance of class with the specified count. - /// - /// The number of signals initially required to set the . - /// is less than zero. - /// - /// If is zero, the event is created in a signaled state. - /// - public CountdownEvent(int initialCount) - { - if (initialCount < 0) - { - throw new ArgumentOutOfRangeException("initialCount"); - } - - _count = initialCount; - - var initialState = _count == 0; - _event = new ManualResetEvent(initialState); - } - - /// - /// Gets the number of remaining signals required to set the event. - /// - /// - /// The number of remaining signals required to set the event. - /// - public int CurrentCount - { - get { return _count; } - } - - /// - /// Indicates whether the 's current count has reached zero. - /// - /// - /// true if the current count is zero; otherwise, false. - /// - public bool IsSet - { - get { return _count == 0; } - } - - /// - /// Gets a that is used to wait for the event to be set. - /// - /// - /// A that is used to wait for the event to be set. - /// - /// The current instance has already been disposed. - public WaitHandle WaitHandle - { - get - { - EnsureNotDisposed(); - - return _event; - } - } - - - /// - /// Registers a signal with the , decrementing the value of . - /// - /// - /// true if the signal caused the count to reach zero and the event was set; otherwise, false. - /// - /// The current instance has already been disposed. - /// The current instance is already set. - public bool Signal() - { - EnsureNotDisposed(); - - if (_count <= 0) - throw new InvalidOperationException("Invalid attempt made to decrement the event's count below zero."); - - var newCount = Interlocked.Decrement(ref _count); - if (newCount == 0) - { - _event.Set(); - return true; - } - - return false; - } - - /// - /// Increments the 's current count by one. - /// - /// The current instance has already been disposed. - /// The current instance is already set. - /// is equal to or greather than . - public void AddCount() - { - EnsureNotDisposed(); - - if (_count == int.MaxValue) - throw new InvalidOperationException("TODO"); - - Interlocked.Increment(ref _count); - } - - /// - /// Blocks the current thread until the is set, using a - /// to measure the timeout. - /// - /// A that represents the number of milliseconds to wait, or a that represents -1 milliseconds to wait indefinitely. - /// - /// true if the was set; otherwise, false. - /// - /// The current instance has already been disposed. - public bool Wait(TimeSpan timeout) - { - EnsureNotDisposed(); - - return _event.WaitOne(timeout); - } - - /// - /// Releases all resources used by the current instance of the class. - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Releases the unmanaged resources used by the , and optionally releases the managed resources. - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. - protected virtual void Dispose(bool disposing) - { - if (disposing) - { - var theEvent = _event; - if (theEvent != null) - { - _event = null; - theEvent.Dispose(); - } - - _disposed = true; - } - } - - private void EnsureNotDisposed() - { - if (_disposed) - throw new ObjectDisposedException(GetType().Name); - } - } -} - -#endif // FEATURE_THREAD_COUNTDOWNEVENT \ No newline at end of file diff --git a/src/Renci.SshNet/Common/DerData.cs b/src/Renci.SshNet/Common/DerData.cs index 35178798..5928d308 100644 --- a/src/Renci.SshNet/Common/DerData.cs +++ b/src/Renci.SshNet/Common/DerData.cs @@ -16,39 +16,17 @@ namespace Renci.SshNet.Common private const byte Octetstring = 0x04; private const byte Null = 0x05; private const byte Objectidentifier = 0x06; - //private const byte EXTERNAL = 0x08; - //private const byte ENUMERATED = 0x0a; private const byte Sequence = 0x10; - //private const byte SEQUENCEOF = 0x10; // for completeness - //private const byte SET = 0x11; - //private const byte SETOF = 0x11; // for completeness - - //private const byte NUMERICSTRING = 0x12; - //private const byte PRINTABLESTRING = 0x13; - //private const byte T61STRING = 0x14; - //private const byte VIDEOTEXSTRING = 0x15; - //private const byte IA5STRING = 0x16; - //private const byte UTCTIME = 0x17; - //private const byte GENERALIZEDTIME = 0x18; - //private const byte GRAPHICSTRING = 0x19; - //private const byte VISIBLESTRING = 0x1a; - //private const byte GENERALSTRING = 0x1b; - //private const byte UNIVERSALSTRING = 0x1c; - //private const byte BMPSTRING = 0x1e; - //private const byte UTF8STRING = 0x0c; - //private const byte APPLICATION = 0x40; - //private const byte TAGGED = 0x80; private readonly List _data; - - private int _readerIndex; private readonly int _lastIndex; + private int _readerIndex; /// /// Gets a value indicating whether end of data is reached. /// /// - /// true if end of data is reached; otherwise, false. + /// true if end of data is reached; otherwise, false. /// public bool IsEndOfData { @@ -80,7 +58,7 @@ namespace Renci.SshNet.Common } else { - ReadByte(); // skip dataType + _ = ReadByte(); // skip dataType var length = ReadLength(); _lastIndex = _readerIndex + length; } @@ -109,7 +87,9 @@ namespace Renci.SshNet.Common { var type = ReadByte(); if (type != Integer) + { throw new InvalidOperationException(string.Format("Invalid data type, INTEGER(02) is expected, but was {0}", type.ToString("X2"))); + } var length = ReadLength(); @@ -126,14 +106,18 @@ namespace Renci.SshNet.Common { var type = ReadByte(); if (type != Integer) + { throw new InvalidOperationException(string.Format("Invalid data type, INTEGER(02) is expected, but was {0}", type.ToString("X2"))); + } var length = ReadLength(); var data = ReadBytes(length); if (length > 4) + { throw new InvalidOperationException("Integer type cannot occupy more then 4 bytes"); + } var result = 0; var shift = (length - 1) * 8; @@ -143,8 +127,6 @@ namespace Renci.SshNet.Common shift -= 8; } - //return (int)(data[0] << 56 | data[1] << 48 | data[2] << 40 | data[3] << 32 | data[4] << 24 | data[5] << 16 | data[6] << 8 | data[7]); - return result; } @@ -156,7 +138,9 @@ namespace Renci.SshNet.Common { var type = ReadByte(); if (type != Octetstring) + { throw new InvalidOperationException(string.Format("Invalid data type, OCTETSTRING(04) is expected, but was {0}", type.ToString("X2"))); + } var length = ReadLength(); var data = ReadBytes(length); @@ -171,7 +155,9 @@ namespace Renci.SshNet.Common { var type = ReadByte(); if (type != BITSTRING) + { throw new InvalidOperationException(string.Format("Invalid data type, BITSTRING(03) is expected, but was {0}", type.ToString("X2"))); + } var length = ReadLength(); var data = ReadBytes(length); @@ -186,7 +172,9 @@ namespace Renci.SshNet.Common { var type = ReadByte(); if (type != Objectidentifier) + { throw new InvalidOperationException(string.Format("Invalid data type, OBJECT(06) is expected, but was {0}", type.ToString("X2"))); + } var length = ReadLength(); var data = ReadBytes(length); @@ -242,18 +230,6 @@ namespace Renci.SshNet.Common WriteBytes(data); } - /// - /// Writes BITSTRING data into internal buffer. - /// - /// The data. - public void WriteBitstring(byte[] data) - { - _data.Add(BITSTRING); - var length = GetLength(data.Length); - WriteBytes(length); - WriteBytes(data); - } - /// /// Writes OBJECTIDENTIFIER data into internal buffer. /// @@ -261,7 +237,7 @@ namespace Renci.SshNet.Common public void Write(ObjectIdentifier identifier) { var temp = new ulong[identifier.Identifiers.Length - 1]; - temp[0] = identifier.Identifiers[0] * 40 + identifier.Identifiers[1]; + temp[0] = (identifier.Identifiers[0] * 40) + identifier.Identifiers[1]; Buffer.BlockCopy(identifier.Identifiers, 2 * sizeof(ulong), temp, 1 * sizeof(ulong), (identifier.Identifiers.Length - 2) * sizeof(ulong)); var bytes = new List(); foreach (var subidentifier in temp) @@ -275,7 +251,10 @@ namespace Renci.SshNet.Common { buffer[bufferIndex] = current; if (bufferIndex < buffer.Length - 1) + { buffer[bufferIndex] |= 0x80; + } + item >>= 7; current = (byte)(item & 0x7F); bufferIndex--; @@ -294,6 +273,28 @@ namespace Renci.SshNet.Common WriteBytes(bytes); } + /// + /// Writes DerData data into internal buffer. + /// + /// DerData data to write. + public void Write(DerData data) + { + var bytes = data.Encode(); + _data.AddRange(bytes); + } + + /// + /// Writes BITSTRING data into internal buffer. + /// + /// The data. + public void WriteBitstring(byte[] data) + { + _data.Add(BITSTRING); + var length = GetLength(data.Length); + WriteBytes(length); + WriteBytes(data); + } + /// /// Writes OBJECTIDENTIFIER data into internal buffer. /// @@ -315,17 +316,7 @@ namespace Renci.SshNet.Common _data.Add(0); } - /// - /// Writes DerData data into internal buffer. - /// - /// DerData data to write. - public void Write(DerData data) - { - var bytes = data.Encode(); - _data.AddRange(bytes); - } - - private static IEnumerable GetLength(int length) + private static byte[] GetLength(int length) { if (length > 127) { @@ -333,7 +324,9 @@ namespace Renci.SshNet.Common var val = length; while ((val >>= 8) != 0) + { size++; + } var data = new byte[size]; data[0] = (byte)(size | 0x80); @@ -345,12 +338,16 @@ namespace Renci.SshNet.Common return data; } - return new[] { (byte)length }; + + return new[] { (byte) length }; } + /// - /// Gets Data Length + /// Gets Data Length. /// - /// length + /// + /// The length. + /// public int ReadLength() { int length = ReadByte(); @@ -366,7 +363,9 @@ namespace Renci.SshNet.Common // Note: The invalid long form "0xff" (see X.690 8.1.3.5c) will be caught here if (size > 4) + { throw new InvalidOperationException(string.Format("DER length is '{0}' and cannot be more than 4 bytes.", size)); + } length = 0; for (var i = 0; i < size; i++) @@ -377,10 +376,9 @@ namespace Renci.SshNet.Common } if (length < 0) + { throw new InvalidOperationException("Corrupted data - negative length found"); - - //if (length >= limit) // after all we must have read at least 1 byte - // throw new IOException("Corrupted stream - out of bounds length found"); + } } return length; @@ -389,6 +387,7 @@ namespace Renci.SshNet.Common /// /// Write Byte data into internal buffer. /// + /// The data to write. public void WriteBytes(IEnumerable data) { _data.AddRange(data); @@ -397,11 +396,15 @@ namespace Renci.SshNet.Common /// /// Reads Byte data into internal buffer. /// - /// data read + /// + /// The data read. + /// public byte ReadByte() { if (_readerIndex > _data.Count) + { throw new InvalidOperationException("Read out of boundaries."); + } return _data[_readerIndex++]; } @@ -409,12 +412,16 @@ namespace Renci.SshNet.Common /// /// Reads lengths Bytes data into internal buffer. /// - /// data read - /// amount of data to read. + /// + /// The data read. + /// + /// amount of data to read. public byte[] ReadBytes(int length) { if (_readerIndex + length > _data.Count) + { throw new InvalidOperationException("Read out of boundaries."); + } var result = new byte[length]; _data.CopyTo(_readerIndex, result, 0, length); @@ -422,4 +429,4 @@ namespace Renci.SshNet.Common return result; } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet/Common/ExceptionEventArgs.cs b/src/Renci.SshNet/Common/ExceptionEventArgs.cs index 0211be94..7e99def5 100644 --- a/src/Renci.SshNet/Common/ExceptionEventArgs.cs +++ b/src/Renci.SshNet/Common/ExceptionEventArgs.cs @@ -7,11 +7,6 @@ namespace Renci.SshNet.Common /// public class ExceptionEventArgs : EventArgs { - /// - /// Gets the System.Exception that represents the error that occurred. - /// - public Exception Exception { get; private set; } - /// /// Initializes a new instance of the class. /// @@ -20,5 +15,13 @@ namespace Renci.SshNet.Common { Exception = exception; } + + /// + /// Gets the that represents the error that occurred. + /// + /// + /// The that represents the error that occurred. + /// + public Exception Exception { get; } } } diff --git a/src/Renci.SshNet/Common/Extensions.cs b/src/Renci.SshNet/Common/Extensions.cs index 17d72d7f..d734ffd8 100644 --- a/src/Renci.SshNet/Common/Extensions.cs +++ b/src/Renci.SshNet/Common/Extensions.cs @@ -5,39 +5,16 @@ using System.Globalization; using System.Net; using System.Net.Sockets; using System.Text; -#if !FEATURE_WAITHANDLE_DISPOSE -using System.Threading; -#endif // !FEATURE_WAITHANDLE_DISPOSE using Renci.SshNet.Abstractions; using Renci.SshNet.Messages; namespace Renci.SshNet.Common { /// - /// Collection of different extension method + /// Collection of different extension methods. /// internal static partial class Extensions { - /// - /// Determines whether the specified value is null or white space. - /// - /// The value. - /// - /// true if is null or white space; otherwise, false. - /// - public static bool IsNullOrWhiteSpace(this string value) - { - if (string.IsNullOrEmpty(value)) return true; - - for (var i = 0; i < value.Length; i++) - { - if (!char.IsWhiteSpace(value[i])) - return false; - } - - return true; - } - internal static byte[] ToArray(this ServiceName serviceName) { switch (serviceName) @@ -100,7 +77,7 @@ namespace Renci.SshNet.Common } /// - /// Prints out + /// Prints out the specified bytes. /// /// The bytes. internal static void DebugPrint(this IEnumerable bytes) @@ -109,8 +86,9 @@ namespace Renci.SshNet.Common foreach (var b in bytes) { - sb.AppendFormat(CultureInfo.CurrentCulture, "0x{0:x2}, ", b); + _ = sb.AppendFormat(CultureInfo.CurrentCulture, "0x{0:x2}, ", b); } + Debug.WriteLine(sb.ToString()); } @@ -120,32 +98,37 @@ namespace Renci.SshNet.Common /// The type to create. /// Type of the instance to create. /// A reference to the newly created object. - internal static T CreateInstance(this Type type) where T : class + internal static T CreateInstance(this Type type) + where T : class { - if (type == null) + if (type is null) + { return null; + } + return Activator.CreateInstance(type) as T; } internal static void ValidatePort(this uint value, string argument) { if (value > IPEndPoint.MaxPort) + { throw new ArgumentOutOfRangeException(argument, - string.Format(CultureInfo.InvariantCulture, "Specified value cannot be greater than {0}.", - IPEndPoint.MaxPort)); + string.Format(CultureInfo.InvariantCulture, "Specified value cannot be greater than {0}.", IPEndPoint.MaxPort)); + } } internal static void ValidatePort(this int value, string argument) { if (value < IPEndPoint.MinPort) - throw new ArgumentOutOfRangeException(argument, - string.Format(CultureInfo.InvariantCulture, "Specified value cannot be less than {0}.", - IPEndPoint.MinPort)); + { + throw new ArgumentOutOfRangeException(argument, string.Format(CultureInfo.InvariantCulture, "Specified value cannot be less than {0}.", IPEndPoint.MinPort)); + } if (value > IPEndPoint.MaxPort) - throw new ArgumentOutOfRangeException(argument, - string.Format(CultureInfo.InvariantCulture, "Specified value cannot be greater than {0}.", - IPEndPoint.MaxPort)); + { + throw new ArgumentOutOfRangeException(argument, string.Format(CultureInfo.InvariantCulture, "Specified value cannot be greater than {0}.", IPEndPoint.MaxPort)); + } } /// @@ -165,14 +148,20 @@ namespace Renci.SshNet.Common /// public static byte[] Take(this byte[] value, int offset, int count) { - if (value == null) - throw new ArgumentNullException("value"); + if (value is null) + { + throw new ArgumentNullException(nameof(value)); + } if (count == 0) - return Array.Empty; + { + return Array.Empty(); + } if (offset == 0 && value.Length == count) + { return value; + } var taken = new byte[count]; Buffer.BlockCopy(value, offset, taken, 0, count); @@ -194,14 +183,20 @@ namespace Renci.SshNet.Common /// public static byte[] Take(this byte[] value, int count) { - if (value == null) - throw new ArgumentNullException("value"); + if (value is null) + { + throw new ArgumentNullException(nameof(value)); + } if (count == 0) - return Array.Empty; + { + return Array.Empty(); + } if (value.Length == count) + { return value; + } var taken = new byte[count]; Buffer.BlockCopy(value, 0, taken, 0, count); @@ -210,21 +205,32 @@ namespace Renci.SshNet.Common public static bool IsEqualTo(this byte[] left, byte[] right) { - if (left == null) - throw new ArgumentNullException("left"); - if (right == null) - throw new ArgumentNullException("right"); + if (left is null) + { + throw new ArgumentNullException(nameof(left)); + } + + if (right is null) + { + throw new ArgumentNullException(nameof(right)); + } if (left == right) + { return true; + } if (left.Length != right.Length) + { return false; + } for (var i = 0; i < left.Length; i++) { if (left[i] != right[i]) + { return false; + } } return true; @@ -239,17 +245,23 @@ namespace Renci.SshNet.Common /// public static byte[] TrimLeadingZeros(this byte[] value) { - if (value == null) - throw new ArgumentNullException("value"); + if (value is null) + { + throw new ArgumentNullException(nameof(value)); + } for (var i = 0; i < value.Length; i++) { if (value[i] == 0) + { continue; + } // if the first byte is non-zero, then we return the byte array as is if (i == 0) + { return value; + } var remainingBytes = value.Length - i; @@ -269,7 +281,10 @@ namespace Renci.SshNet.Common public static byte[] Pad(this byte[] data, int length) { if (length <= data.Length) + { return data; + } + var newData = new byte[length]; Buffer.BlockCopy(data, 0, newData, newData.Length - data.Length, data.Length); return newData; @@ -277,11 +292,15 @@ namespace Renci.SshNet.Common public static byte[] Concat(this byte[] first, byte[] second) { - if (first == null || first.Length == 0) + if (first is null || first.Length == 0) + { return second; + } - if (second == null || second.Length == 0) + if (second is null || second.Length == 0) + { return first; + } var concat = new byte[first.Length + second.Length]; Buffer.BlockCopy(first, 0, concat, 0, first.Length); @@ -301,66 +320,12 @@ namespace Renci.SshNet.Common internal static bool IsConnected(this Socket socket) { - if (socket == null) + if (socket is null) + { return false; + } + return socket.Connected; } - -#if !FEATURE_SOCKET_DISPOSE - /// - /// Disposes the specified socket. - /// - /// The socket. - [DebuggerNonUserCode] - internal static void Dispose(this Socket socket) - { - if (socket == null) - throw new NullReferenceException(); - - socket.Close(); - } -#endif // !FEATURE_SOCKET_DISPOSE - -#if !FEATURE_WAITHANDLE_DISPOSE - /// - /// Disposes the specified handle. - /// - /// The handle. - [DebuggerNonUserCode] - internal static void Dispose(this WaitHandle handle) - { - if (handle == null) - throw new NullReferenceException(); - - handle.Close(); - } -#endif // !FEATURE_WAITHANDLE_DISPOSE - -#if !FEATURE_HASHALGORITHM_DISPOSE - /// - /// Disposes the specified algorithm. - /// - /// The algorithm. - [DebuggerNonUserCode] - internal static void Dispose(this System.Security.Cryptography.HashAlgorithm algorithm) - { - if (algorithm == null) - throw new NullReferenceException(); - - algorithm.Clear(); - } -#endif // FEATURE_HASHALGORITHM_DISPOSE - -#if !FEATURE_STRINGBUILDER_CLEAR - /// - /// Clears the contents of the string builder. - /// - /// The to clear. - public static void Clear(this StringBuilder value) - { - value.Length = 0; - value.Capacity = 16; - } -#endif // !FEATURE_STRINGBUILDER_CLEAR } } diff --git a/src/Renci.SshNet/Common/HostKeyEventArgs.cs b/src/Renci.SshNet/Common/HostKeyEventArgs.cs index 7f0d6bef..993b6d64 100644 --- a/src/Renci.SshNet/Common/HostKeyEventArgs.cs +++ b/src/Renci.SshNet/Common/HostKeyEventArgs.cs @@ -1,4 +1,5 @@ using System; + using Renci.SshNet.Abstractions; using Renci.SshNet.Security; @@ -9,6 +10,10 @@ namespace Renci.SshNet.Common /// public class HostKeyEventArgs : EventArgs { + private readonly Lazy _lazyFingerPrint; + private readonly Lazy _lazyFingerPrintSHA256; + private readonly Lazy _lazyFingerPrintMD5; + /// /// Gets or sets a value indicating whether host key can be trusted. /// @@ -28,9 +33,47 @@ namespace Renci.SshNet.Common public string HostKeyName{ get; private set; } /// - /// Gets the finger print. + /// Gets the MD5 fingerprint. /// - public byte[] FingerPrint { get; private set; } + /// + /// MD5 fingerprint as byte array. + /// + public byte[] FingerPrint + { + get + { + return _lazyFingerPrint.Value; + } + } + + /// + /// Gets the SHA256 fingerprint of the host key in the same format as the ssh command, + /// i.e. non-padded base64, but without the SHA256: prefix. + /// + /// ohD8VZEXGWo6Ez8GSEJQ9WpafgLFsOfLOtGGQCQo6Og + /// + /// Base64 encoded SHA256 fingerprint with padding (equals sign) removed. + /// + public string FingerPrintSHA256 + { + get + { + return _lazyFingerPrintSHA256.Value; + } + } + + /// + /// Gets the MD5 fingerprint of the host key in the same format as the ssh command, + /// i.e. hexadecimal bytes separated by colons, but without the MD5: prefix. + /// + /// 97:70:33:82:fd:29:3a:73:39:af:6a:07:ad:f8:80:49 + public string FingerPrintMD5 + { + get + { + return _lazyFingerPrintMD5.Value; + } + } /// /// Gets the length of the key in bits. @@ -46,18 +89,25 @@ namespace Renci.SshNet.Common /// The host. public HostKeyEventArgs(KeyHostAlgorithm host) { - CanTrust = true; // Set default value - + CanTrust = true; HostKey = host.Data; - HostKeyName = host.Name; - KeyLength = host.Key.KeyLength; - - using (var md5 = CryptoAbstraction.CreateMD5()) + + _lazyFingerPrint = new Lazy(() => { - FingerPrint = md5.ComputeHash(host.Data); - } + using var md5 = CryptoAbstraction.CreateMD5(); + return md5.ComputeHash(HostKey); + }); + + _lazyFingerPrintSHA256 = new Lazy(() => + { + using var sha256 = CryptoAbstraction.CreateSHA256(); + return Convert.ToBase64String(sha256.ComputeHash(HostKey)).Replace("=", ""); + }); + + _lazyFingerPrintMD5 = new Lazy(() => + BitConverter.ToString(FingerPrint).Replace("-", ":").ToLowerInvariant()); } } } diff --git a/src/Renci.SshNet/Common/NetConfServerException.cs b/src/Renci.SshNet/Common/NetConfServerException.cs index 86fbeabe..39f3f8eb 100644 --- a/src/Renci.SshNet/Common/NetConfServerException.cs +++ b/src/Renci.SshNet/Common/NetConfServerException.cs @@ -34,8 +34,8 @@ namespace Renci.SshNet.Common /// /// The message. /// The inner exception. - public NetConfServerException(string message, Exception innerException) : - base(message, innerException) + public NetConfServerException(string message, Exception innerException) + : base(message, innerException) { } diff --git a/src/Renci.SshNet/Common/ObjectIdentifier.cs b/src/Renci.SshNet/Common/ObjectIdentifier.cs index 3ff128bc..a02a4090 100644 --- a/src/Renci.SshNet/Common/ObjectIdentifier.cs +++ b/src/Renci.SshNet/Common/ObjectIdentifier.cs @@ -1,9 +1,11 @@ using System; +using System.Linq; +using System.Security.Cryptography; namespace Renci.SshNet.Common { /// - /// Describes object identifier for DER encoding + /// Describes object identifier for DER encoding. /// public struct ObjectIdentifier { @@ -13,16 +15,38 @@ namespace Renci.SshNet.Common public ulong[] Identifiers { get; private set; } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the struct. /// /// The identifiers. + /// is . + /// has less than two elements. public ObjectIdentifier(params ulong[] identifiers) - : this() { + if (identifiers is null) + { + throw new ArgumentNullException(nameof(identifiers)); + } + if (identifiers.Length < 2) - throw new ArgumentException("identifiers"); + { + throw new ArgumentException("Must contain at least two elements.", nameof(identifiers)); + } Identifiers = identifiers; } + + internal static ObjectIdentifier FromHashAlgorithmName(HashAlgorithmName hashAlgorithmName) + { + var oid = CryptoConfig.MapNameToOID(hashAlgorithmName.Name); + + if (oid is null) + { + throw new ArgumentException($"Could not map `{hashAlgorithmName}` to OID.", nameof(hashAlgorithmName)); + } + + var identifiers = oid.Split('.').Select(ulong.Parse).ToArray(); + + return new ObjectIdentifier(identifiers); + } } } diff --git a/src/Renci.SshNet/Common/PacketDump.cs b/src/Renci.SshNet/Common/PacketDump.cs index 7b50582c..47329f6d 100644 --- a/src/Renci.SshNet/Common/PacketDump.cs +++ b/src/Renci.SshNet/Common/PacketDump.cs @@ -5,7 +5,7 @@ using System.Text; namespace Renci.SshNet.Common { - internal class PacketDump + internal static class PacketDump { public static string Create(List data, int indentLevel) { @@ -14,10 +14,15 @@ namespace Renci.SshNet.Common public static string Create(byte[] data, int indentLevel) { - if (data == null) - throw new ArgumentNullException("data"); + if (data is null) + { + throw new ArgumentNullException(nameof(data)); + } + if (indentLevel < 0) - throw new ArgumentOutOfRangeException("indentLevel", "Cannot be less than zero."); + { + throw new ArgumentOutOfRangeException(nameof(indentLevel), "Cannot be less than zero."); + } const int lineWidth = 16; @@ -31,12 +36,12 @@ namespace Renci.SshNet.Common if (result.Length > 0) { - result.Append(Environment.NewLine); + _ = result.Append(Environment.NewLine); } - result.Append(indentChars); - result.Append(pos.ToString("X8")); - result.Append(" "); + _ = result.Append(indentChars); + _ = result.Append(pos.ToString("X8")); + _ = result.Append(" "); while (true) { @@ -48,10 +53,11 @@ namespace Renci.SshNet.Common } } - result.Append(AsHex(line, linePos)); - result.Append(" "); - result.Append(AsAscii(line, linePos)); + _ = result.Append(AsHex(line, linePos)); + _ = result.Append(" "); + _ = result.Append(AsAscii(line, linePos)); } + return result.ToString(); } @@ -63,15 +69,15 @@ namespace Renci.SshNet.Common { if (i > 0) { - hex.Append(' '); + _ = hex.Append(' '); } - hex.Append(data[i].ToString("X2", CultureInfo.InvariantCulture)); + _ = hex.Append(data[i].ToString("X2", CultureInfo.InvariantCulture)); } if (length < data.Length) { - hex.Append(new string(' ', (data.Length - length) * 3)); + _ = hex.Append(new string(' ', (data.Length - length) * 3)); } return hex.ToString(); @@ -79,30 +85,26 @@ namespace Renci.SshNet.Common private static string AsAscii(byte[] data, int length) { -#if FEATURE_ENCODING_ASCII - var encoding = Encoding.ASCII; -#else - var encoding = new ASCIIEncoding(); -#endif + var encoding = Encoding.ASCII; - var ascii = new StringBuilder(); + var ascii = new StringBuilder(); const char dot = '.'; for (var i = 0; i < length; i++) { var b = data[i]; - if (b < 32 || b >= 127) + if (b is < 32 or >= 127) { - ascii.Append(dot); + _ = ascii.Append(dot); } else { - ascii.Append(encoding.GetString(data, i, 1)); + _ = ascii.Append(encoding.GetString(data, i, 1)); } } return ascii.ToString(); } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet/Common/PipeStream.cs b/src/Renci.SshNet/Common/PipeStream.cs index fac54fb6..d2fe8d36 100644 --- a/src/Renci.SshNet/Common/PipeStream.cs +++ b/src/Renci.SshNet/Common/PipeStream.cs @@ -1,40 +1,38 @@ -namespace Renci.SshNet.Common -{ - using System; - using System.Collections.Generic; - using System.IO; - using System.Threading; - using System.Globalization; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Threading; +namespace Renci.SshNet.Common +{ /// - /// PipeStream is a thread-safe read/write data stream for use between two threads in a + /// PipeStream is a thread-safe read/write data stream for use between two threads in a /// single-producer/single-consumer type problem. /// /// 2006/10/13 1.0 /// Update on 2008/10/9 1.1 - uses Monitor instead of Manual Reset events for more elegant synchronicity. /// - /// Copyright (c) 2006 James Kolpack (james dot kolpack at google mail) - /// - /// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and - /// associated documentation files (the "Software"), to deal in the Software without restriction, - /// including without limitation the rights to use, copy, modify, merge, publish, distribute, - /// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is - /// furnished to do so, subject to the following conditions: - /// - /// The above copyright notice and this permission notice shall be included in all copies or - /// substantial portions of the Software. - /// - /// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, - /// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR - /// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - /// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT - /// OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - /// OTHER DEALINGS IN THE SOFTWARE. + /// Copyright (c) 2006 James Kolpack (james dot kolpack at google mail) + /// + /// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + /// associated documentation files (the "Software"), to deal in the Software without restriction, + /// including without limitation the rights to use, copy, modify, merge, publish, distribute, + /// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + /// furnished to do so, subject to the following conditions: + /// + /// The above copyright notice and this permission notice shall be included in all copies or + /// substantial portions of the Software. + /// + /// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + /// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + /// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + /// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT + /// OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + /// OTHER DEALINGS IN THE SOFTWARE. /// public class PipeStream : Stream { - #region Private members - /// /// Queue of bytes provides the datastructure for transmitting from an /// input stream to an output stream. @@ -64,10 +62,6 @@ /// private bool _isDisposed; - #endregion - - #region Public properties - /// /// Gets or sets the maximum number of bytes to store in the buffer. /// @@ -87,7 +81,7 @@ /// Setting to true will remove the possibility of ending a stream reader prematurely. /// /// - /// true if block last read method before the buffer is empty; otherwise, false. + /// true if block last read method before the buffer is empty; otherwise, false. /// /// Methods were called after the stream was closed. public bool BlockLastReadBuffer @@ -95,28 +89,32 @@ get { if (_isDisposed) + { throw CreateObjectDisposedException(); + } return _canBlockLastRead; } set { if (_isDisposed) + { throw CreateObjectDisposedException(); + } _canBlockLastRead = value; // when turning off the block last read, signal Read() that it may now read the rest of the buffer. if (!_canBlockLastRead) + { lock (_buffer) + { Monitor.Pulse(_buffer); + } + } } } - #endregion - - #region Stream overide methods - /// /// When overridden in a derived class, clears all buffers for this stream and causes any buffered data to be written to the underlying device. /// @@ -129,7 +127,9 @@ public override void Flush() { if (_isDisposed) + { throw CreateObjectDisposedException(); + } _isFlushed = true; lock (_buffer) @@ -163,37 +163,57 @@ throw new NotSupportedException(); } - /// - ///When overridden in a derived class, reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read. - /// - /// - ///The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available, or zero if the stream is closed or end of the stream has been reached. - /// - ///The zero-based byte offset in buffer at which to begin storing the data read from the current stream. - ///The maximum number of bytes to be read from the current stream. - ///An array of bytes. When this method returns, the buffer contains the specified byte array with the values between offset and (offset + count - 1) replaced by the bytes read from the current source. - ///The sum of offset and count is larger than the buffer length. - ///Methods were called after the stream was closed. - ///The stream does not support reading. - /// is null. - ///An I/O error occurs. - ///offset or count is negative. + /// + /// When overridden in a derived class, reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read. + /// + /// + /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available, or zero if the stream is closed or end of the stream has been reached. + /// + /// An array of bytes. When this method returns, the buffer contains the specified byte array with the values between offset and (offset + count - 1) replaced by the bytes read from the current source. + /// The zero-based byte offset in buffer at which to begin storing the data read from the current stream. + /// The maximum number of bytes to be read from the current stream. + /// The sum of offset and count is larger than the buffer length. + /// Methods were called after the stream was closed. + /// The stream does not support reading. + /// is null. + /// An I/O error occurs. + /// offset or count is negative. public override int Read(byte[] buffer, int offset, int count) { if (offset != 0) + { throw new NotSupportedException("Offsets with value of non-zero are not supported"); - if (buffer == null) - throw new ArgumentNullException("buffer"); + } + + if (buffer is null) + { + throw new ArgumentNullException(nameof(buffer)); + } + if (offset + count > buffer.Length) + { throw new ArgumentException("The sum of offset and count is greater than the buffer length."); + } + if (offset < 0 || count < 0) - throw new ArgumentOutOfRangeException("offset", "offset or count is negative."); + { + throw new ArgumentOutOfRangeException(nameof(offset), "offset or count is negative."); + } + if (BlockLastReadBuffer && count >= _maxBufferLength) + { throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "count({0}) > mMaxBufferLength({1})", count, _maxBufferLength)); + } + if (_isDisposed) + { throw CreateObjectDisposedException(); + } + if (count == 0) + { return 0; + } var readLength = 0; @@ -201,7 +221,7 @@ { while (!_isDisposed && !ReadAvailable(count)) { - Monitor.Wait(_buffer); + _ = Monitor.Wait(_buffer); } // return zero when the read is interrupted by a close/dispose of the stream @@ -223,46 +243,64 @@ } /// - /// Returns true if there are + /// Returns a value indicating whether data is available. /// /// The count. - /// True if data available; otherwisefalse. + /// + /// if data is available; otherwise, . + /// private bool ReadAvailable(int count) { var length = Length; return (_isFlushed || length >= count) && (length >= (count + 1) || !BlockLastReadBuffer); } - /// - ///When overridden in a derived class, writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written. - /// - ///The zero-based byte offset in buffer at which to begin copying bytes to the current stream. - ///The number of bytes to be written to the current stream. - ///An array of bytes. This method copies count bytes from buffer to the current stream. - ///An I/O error occurs. - ///The stream does not support writing. - ///Methods were called after the stream was closed. - /// is null. - ///The sum of offset and count is greater than the buffer length. - ///offset or count is negative. + /// + /// Writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written. + /// + /// An array of bytes. This method copies count bytes from buffer to the current stream. + /// The zero-based byte offset in buffer at which to begin copying bytes to the current stream. + /// The number of bytes to be written to the current stream. + /// An I/O error occurs. + /// The stream does not support writing. + /// Methods were called after the stream was closed. + /// is null. + /// The sum of offset and count is greater than the buffer length. + /// offset or count is negative. public override void Write(byte[] buffer, int offset, int count) { - if (buffer == null) - throw new ArgumentNullException("buffer"); + if (buffer is null) + { + throw new ArgumentNullException(nameof(buffer)); + } + if (offset + count > buffer.Length) + { throw new ArgumentException("The sum of offset and count is greater than the buffer length."); + } + if (offset < 0 || count < 0) - throw new ArgumentOutOfRangeException("offset", "offset or count is negative."); + { + throw new ArgumentOutOfRangeException(nameof(offset), "offset or count is negative."); + } + if (_isDisposed) + { throw CreateObjectDisposedException(); + } + if (count == 0) + { return; + } lock (_buffer) { // wait until the buffer isn't full while (Length >= _maxBufferLength) - Monitor.Wait(_buffer); + { + _ = Monitor.Wait(_buffer); + } _isFlushed = false; // if it were flushed before, it soon will not be. @@ -297,30 +335,30 @@ } } - /// - ///When overridden in a derived class, gets a value indicating whether the current stream supports reading. - /// - /// - ///true if the stream supports reading; otherwise, false. - /// + /// + /// Gets a value indicating whether the current stream supports reading. + /// + /// + /// true if the stream supports reading; otherwise, false. + /// public override bool CanRead { get { return !_isDisposed; } } /// - /// When overridden in a derived class, gets a value indicating whether the current stream supports seeking. + /// Gets a value indicating whether the current stream supports seeking. /// /// /// true if the stream supports seeking; otherwise, false. - /// + /// public override bool CanSeek { get { return false; } } /// - /// When overridden in a derived class, gets a value indicating whether the current stream supports writing. + /// Gets a value indicating whether the current stream supports writing. /// /// /// true if the stream supports writing; otherwise, false. @@ -331,7 +369,7 @@ } /// - /// When overridden in a derived class, gets the length in bytes of the stream. + /// Gets the length in bytes of the stream. /// /// /// A long value representing the length of the stream in bytes. @@ -343,14 +381,16 @@ get { if (_isDisposed) + { throw CreateObjectDisposedException(); + } return _buffer.Count; } } /// - /// When overridden in a derived class, gets or sets the position within the current stream. + /// Gets or sets the position within the current stream. /// /// /// The current position within the stream. @@ -362,8 +402,6 @@ set { throw new NotSupportedException(); } } - #endregion - private ObjectDisposedException CreateObjectDisposedException() { return new ObjectDisposedException(GetType().FullName); diff --git a/src/Renci.SshNet/Common/PortForwardEventArgs.cs b/src/Renci.SshNet/Common/PortForwardEventArgs.cs index 96a7f825..a94748b6 100644 --- a/src/Renci.SshNet/Common/PortForwardEventArgs.cs +++ b/src/Renci.SshNet/Common/PortForwardEventArgs.cs @@ -1,37 +1,41 @@ using System; +using System.Net; namespace Renci.SshNet.Common { /// - /// Provides data for event. + /// Provides data for event. /// public class PortForwardEventArgs : EventArgs { - /// - /// Gets request originator host. - /// - public string OriginatorHost { get; private set; } - - /// - /// Gets request originator port. - /// - public uint OriginatorPort { get; private set; } - /// /// Initializes a new instance of the class. /// /// The host. /// The port. /// is null. - /// is not within and . + /// is not within and . internal PortForwardEventArgs(string host, uint port) { - if (host == null) - throw new ArgumentNullException("host"); + if (host is null) + { + throw new ArgumentNullException(nameof(host)); + } + port.ValidatePort("port"); OriginatorHost = host; OriginatorPort = port; } + + /// + /// Gets request originator host. + /// + public string OriginatorHost { get; } + + /// + /// Gets request originator port. + /// + public uint OriginatorPort { get; } } } diff --git a/src/Renci.SshNet/Common/PosixPath.cs b/src/Renci.SshNet/Common/PosixPath.cs index 465916b3..1eddc571 100644 --- a/src/Renci.SshNet/Common/PosixPath.cs +++ b/src/Renci.SshNet/Common/PosixPath.cs @@ -2,16 +2,45 @@ namespace Renci.SshNet.Common { - internal class PosixPath + /// + /// Represents a POSIX path. + /// + internal sealed class PosixPath { + private PosixPath() + { + } + + /// + /// Gets the directory of the path. + /// + /// + /// The directory of the path. + /// public string Directory { get; private set; } + + /// + /// Gets the file part of the path. + /// + /// + /// The file part of the path, or if the path represents a directory. + /// public string File { get; private set; } + /// + /// Create a from the specified path. + /// + /// The path. + /// + /// A created from the specified path. + /// + /// is . + /// is empty (""). public static PosixPath CreateAbsoluteOrRelativeFilePath(string path) { - if (path == null) + if (path is null) { - throw new ArgumentNullException("path"); + throw new ArgumentNullException(nameof(path)); } var posixPath = new PosixPath(); @@ -21,7 +50,7 @@ namespace Renci.SshNet.Common { if (path.Length == 0) { - throw new ArgumentException("The path is a zero-length string.", "path"); + throw new ArgumentException("The path is a zero-length string.", nameof(path)); } posixPath.Directory = "."; @@ -54,7 +83,7 @@ namespace Renci.SshNet.Common /// /// The file name part of . /// - /// is null. + /// is null. /// /// /// If contains no forward slash, then @@ -66,14 +95,22 @@ namespace Renci.SshNet.Common /// public static string GetFileName(string path) { - if (path == null) - throw new ArgumentNullException("path"); + if (path is null) + { + throw new ArgumentNullException(nameof(path)); + } var pathEnd = path.LastIndexOf('/'); if (pathEnd == -1) + { return path; + } + if (pathEnd == path.Length - 1) + { return string.Empty; + } + return path.Substring(pathEnd + 1); } @@ -88,16 +125,27 @@ namespace Renci.SshNet.Common /// is null. public static string GetDirectoryName(string path) { - if (path == null) - throw new ArgumentNullException("path"); + if (path is null) + { + throw new ArgumentNullException(nameof(path)); + } var pathEnd = path.LastIndexOf('/'); if (pathEnd == -1) + { return "."; + } + if (pathEnd == 0) + { return "/"; + } + if (pathEnd == path.Length - 1) + { return path.Substring(0, pathEnd); + } + return path.Substring(0, pathEnd); } } diff --git a/src/Renci.SshNet/Common/ProxyException.cs b/src/Renci.SshNet/Common/ProxyException.cs index efb387b4..371f4f7a 100644 --- a/src/Renci.SshNet/Common/ProxyException.cs +++ b/src/Renci.SshNet/Common/ProxyException.cs @@ -14,14 +14,14 @@ namespace Renci.SshNet.Common public class ProxyException : SshException { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// public ProxyException() { } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The message. public ProxyException(string message) @@ -30,12 +30,12 @@ namespace Renci.SshNet.Common } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The message. /// The inner exception. - public ProxyException(string message, Exception innerException) : - base(message, innerException) + public ProxyException(string message, Exception innerException) + : base(message, innerException) { } diff --git a/src/Renci.SshNet/Common/ScpDownloadEventArgs.cs b/src/Renci.SshNet/Common/ScpDownloadEventArgs.cs index 95ddcd63..59c6312a 100644 --- a/src/Renci.SshNet/Common/ScpDownloadEventArgs.cs +++ b/src/Renci.SshNet/Common/ScpDownloadEventArgs.cs @@ -7,21 +7,6 @@ namespace Renci.SshNet.Common /// public class ScpDownloadEventArgs : EventArgs { - /// - /// Gets the downloaded filename. - /// - public string Filename { get; private set; } - - /// - /// Gets the downloaded file size. - /// - public long Size { get; private set; } - - /// - /// Gets number of downloaded bytes so far. - /// - public long Downloaded { get; private set; } - /// /// Initializes a new instance of the class. /// @@ -34,5 +19,20 @@ namespace Renci.SshNet.Common Size = size; Downloaded = downloaded; } + + /// + /// Gets the downloaded filename. + /// + public string Filename { get; } + + /// + /// Gets the downloaded file size. + /// + public long Size { get; } + + /// + /// Gets number of downloaded bytes so far. + /// + public long Downloaded { get; } } } diff --git a/src/Renci.SshNet/Common/ScpException.cs b/src/Renci.SshNet/Common/ScpException.cs index e98bc688..42d7e8d6 100644 --- a/src/Renci.SshNet/Common/ScpException.cs +++ b/src/Renci.SshNet/Common/ScpException.cs @@ -34,8 +34,8 @@ namespace Renci.SshNet.Common /// /// The message. /// The inner exception. - public ScpException(string message, Exception innerException) : - base(message, innerException) + public ScpException(string message, Exception innerException) + : base(message, innerException) { } diff --git a/src/Renci.SshNet/Common/ScpUploadEventArgs.cs b/src/Renci.SshNet/Common/ScpUploadEventArgs.cs index 555bdc07..ca8514e9 100644 --- a/src/Renci.SshNet/Common/ScpUploadEventArgs.cs +++ b/src/Renci.SshNet/Common/ScpUploadEventArgs.cs @@ -7,21 +7,6 @@ namespace Renci.SshNet.Common /// public class ScpUploadEventArgs : EventArgs { - /// - /// Gets the uploaded filename. - /// - public string Filename { get; private set; } - - /// - /// Gets the uploaded file size. - /// - public long Size { get; private set; } - - /// - /// Gets number of uploaded bytes so far. - /// - public long Uploaded { get; private set; } - /// /// Initializes a new instance of the class. /// @@ -34,5 +19,20 @@ namespace Renci.SshNet.Common Size = size; Uploaded = uploaded; } + + /// + /// Gets the uploaded filename. + /// + public string Filename { get; } + + /// + /// Gets the uploaded file size. + /// + public long Size { get; } + + /// + /// Gets number of uploaded bytes so far. + /// + public long Uploaded { get; } } } diff --git a/src/Renci.SshNet/Common/SemaphoreLight.cs b/src/Renci.SshNet/Common/SemaphoreLight.cs index 55e4a840..5c20a9e9 100644 --- a/src/Renci.SshNet/Common/SemaphoreLight.cs +++ b/src/Renci.SshNet/Common/SemaphoreLight.cs @@ -14,15 +14,17 @@ namespace Renci.SshNet.Common private int _currentCount; /// - /// Initializes a new instance of the class, specifying - /// the initial number of requests that can be granted concurrently. + /// Initializes a new instance of the class, specifying the initial number of requests that can + /// be granted concurrently. /// /// The initial number of requests for the semaphore that can be granted concurrently. /// is a negative number. public SemaphoreLight(int initialCount) { - if (initialCount < 0 ) - throw new ArgumentOutOfRangeException("initialCount", "The value cannot be negative."); + if (initialCount < 0) + { + throw new ArgumentOutOfRangeException(nameof(initialCount), "The value cannot be negative."); + } _currentCount = initialCount; } @@ -30,10 +32,13 @@ namespace Renci.SshNet.Common /// /// Gets the current count of the . /// - public int CurrentCount { get { return _currentCount; } } + public int CurrentCount + { + get { return _currentCount; } + } /// - /// Returns a that can be used to wait on the semaphore. + /// Gets a that can be used to wait on the semaphore. /// /// /// A that can be used to wait on the semaphore. @@ -47,14 +52,11 @@ namespace Renci.SshNet.Common { get { - if (_waitHandle == null) + if (_waitHandle is null) { lock (_lock) { - if (_waitHandle == null) - { - _waitHandle = new ManualResetEvent(_currentCount > 0); - } + _waitHandle ??= new ManualResetEvent(_currentCount > 0); } } @@ -89,7 +91,7 @@ namespace Renci.SshNet.Common // signal waithandle when the original semaphore count was zero if (_waitHandle != null && oldCount == 0) { - _waitHandle.Set(); + _ = _waitHandle.Set(); } Monitor.PulseAll(_lock); @@ -107,7 +109,7 @@ namespace Renci.SshNet.Common { while (_currentCount < 1) { - Monitor.Wait(_lock); + _ = Monitor.Wait(_lock); } _currentCount--; @@ -115,7 +117,7 @@ namespace Renci.SshNet.Common // unsignal waithandle when the semaphore count reaches zero if (_waitHandle != null && _currentCount == 0) { - _waitHandle.Reset(); + _ = _waitHandle.Reset(); } Monitor.PulseAll(_lock); @@ -133,7 +135,9 @@ namespace Renci.SshNet.Common public bool Wait(int millisecondsTimeout) { if (millisecondsTimeout < -1) - throw new ArgumentOutOfRangeException("millisecondsTimeout", "The timeout must represent a value between -1 and Int32.MaxValue, inclusive."); + { + throw new ArgumentOutOfRangeException(nameof(millisecondsTimeout), "The timeout must represent a value between -1 and Int32.MaxValue, inclusive."); + } return WaitWithTimeout(millisecondsTimeout); } @@ -149,8 +153,10 @@ namespace Renci.SshNet.Common public bool Wait(TimeSpan timeout) { var timeoutInMilliseconds = timeout.TotalMilliseconds; - if (timeoutInMilliseconds < -1d || timeoutInMilliseconds > int.MaxValue) - throw new ArgumentOutOfRangeException("timeout", "The timeout must represent a value between -1 and Int32.MaxValue, inclusive."); + if (timeoutInMilliseconds is < -1d or > int.MaxValue) + { + throw new ArgumentOutOfRangeException(nameof(timeout), "The timeout must represent a value between -1 and Int32.MaxValue, inclusive."); + } return WaitWithTimeout((int) timeoutInMilliseconds); } @@ -162,14 +168,18 @@ namespace Renci.SshNet.Common if (timeoutInMilliseconds == Session.Infinite) { while (_currentCount < 1) - Monitor.Wait(_lock); + { + _ = Monitor.Wait(_lock); + } } else { if (_currentCount < 1) { if (timeoutInMilliseconds > 0) + { return false; + } var remainingTimeInMilliseconds = timeoutInMilliseconds; var startTicks = Environment.TickCount; @@ -184,7 +194,9 @@ namespace Renci.SshNet.Common var elapsed = Environment.TickCount - startTicks; remainingTimeInMilliseconds -= elapsed; if (remainingTimeInMilliseconds < 0) + { return false; + } } } } @@ -194,7 +206,7 @@ namespace Renci.SshNet.Common // unsignal waithandle when the semaphore count is zero if (_waitHandle != null && _currentCount == 0) { - _waitHandle.Reset(); + _ = _waitHandle.Reset(); } Monitor.PulseAll(_lock); @@ -203,25 +215,17 @@ namespace Renci.SshNet.Common } } - /// - /// Finalizes the current . - /// - ~SemaphoreLight() - { - Dispose(false); - } - /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// public void Dispose() { - Dispose(true); + Dispose(disposing: true); GC.SuppressFinalize(this); } /// - /// Releases unmanaged and - optionally - managed resources + /// Releases unmanaged and - optionally - managed resources. /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected void Dispose(bool disposing) @@ -229,7 +233,7 @@ namespace Renci.SshNet.Common if (disposing) { var waitHandle = _waitHandle; - if (waitHandle != null) + if (waitHandle is not null) { waitHandle.Dispose(); _waitHandle = null; diff --git a/src/Renci.SshNet/Common/SftpPathNotFoundException.cs b/src/Renci.SshNet/Common/SftpPathNotFoundException.cs index 681a4fe5..f56f2ea3 100644 --- a/src/Renci.SshNet/Common/SftpPathNotFoundException.cs +++ b/src/Renci.SshNet/Common/SftpPathNotFoundException.cs @@ -34,8 +34,8 @@ namespace Renci.SshNet.Common /// /// The message. /// The inner exception. - public SftpPathNotFoundException(string message, Exception innerException) : - base(message, innerException) + public SftpPathNotFoundException(string message, Exception innerException) + : base(message, innerException) { } diff --git a/src/Renci.SshNet/Common/SftpPermissionDeniedException.cs b/src/Renci.SshNet/Common/SftpPermissionDeniedException.cs index 559c98cf..a734bdb1 100644 --- a/src/Renci.SshNet/Common/SftpPermissionDeniedException.cs +++ b/src/Renci.SshNet/Common/SftpPermissionDeniedException.cs @@ -34,8 +34,8 @@ namespace Renci.SshNet.Common /// /// The message. /// The inner exception. - public SftpPermissionDeniedException(string message, Exception innerException) : - base(message, innerException) + public SftpPermissionDeniedException(string message, Exception innerException) + : base(message, innerException) { } diff --git a/src/Renci.SshNet/Common/ShellDataEventArgs.cs b/src/Renci.SshNet/Common/ShellDataEventArgs.cs index 34fea8b4..e99913d2 100644 --- a/src/Renci.SshNet/Common/ShellDataEventArgs.cs +++ b/src/Renci.SshNet/Common/ShellDataEventArgs.cs @@ -3,20 +3,10 @@ namespace Renci.SshNet.Common { /// - /// Provides data for Shell DataReceived event + /// Provides data for Shell DataReceived event. /// public class ShellDataEventArgs : EventArgs { - /// - /// Gets the data. - /// - public byte[] Data { get; private set; } - - /// - /// Gets the line data. - /// - public string Line { get; private set; } - /// /// Initializes a new instance of the class. /// @@ -34,5 +24,15 @@ namespace Renci.SshNet.Common { Line = line; } + + /// + /// Gets the data. + /// + public byte[] Data { get; } + + /// + /// Gets the line data. + /// + public string Line { get; } } } diff --git a/src/Renci.SshNet/Common/SshAuthenticationException.cs b/src/Renci.SshNet/Common/SshAuthenticationException.cs index 4c6f7254..260fe552 100644 --- a/src/Renci.SshNet/Common/SshAuthenticationException.cs +++ b/src/Renci.SshNet/Common/SshAuthenticationException.cs @@ -18,7 +18,6 @@ namespace Renci.SshNet.Common /// public SshAuthenticationException() { - } /// @@ -28,7 +27,6 @@ namespace Renci.SshNet.Common public SshAuthenticationException(string message) : base(message) { - } /// @@ -36,8 +34,8 @@ namespace Renci.SshNet.Common /// /// The message. /// The inner exception. - public SshAuthenticationException(string message, Exception innerException) : - base(message, innerException) + public SshAuthenticationException(string message, Exception innerException) + : base(message, innerException) { } diff --git a/src/Renci.SshNet/Common/SshData.cs b/src/Renci.SshNet/Common/SshData.cs index 8e4eca40..ac063b7a 100644 --- a/src/Renci.SshNet/Common/SshData.cs +++ b/src/Renci.SshNet/Common/SshData.cs @@ -5,17 +5,14 @@ using System.Text; namespace Renci.SshNet.Common { /// - /// Base ssh data serialization type + /// Base ssh data serialization type. /// public abstract class SshData { internal const int DefaultCapacity = 64; -#if FEATURE_ENCODING_ASCII internal static readonly Encoding Ascii = Encoding.ASCII; -#else - internal static readonly Encoding Ascii = new ASCIIEncoding(); -#endif + internal static readonly Encoding Utf8 = Encoding.UTF8; private SshDataStream _stream; @@ -60,7 +57,7 @@ namespace Renci.SshNet.Common /// Gets data bytes array. /// /// - /// A array representation of data structure. + /// A array representation of data structure. /// public byte[] GetBytes() { @@ -88,8 +85,10 @@ namespace Renci.SshNet.Common /// is null. public void Load(byte[] data) { - if (data == null) - throw new ArgumentNullException("data"); + if (data is null) + { + throw new ArgumentNullException(nameof(data)); + } LoadInternal(data, 0, data.Length); } @@ -103,8 +102,10 @@ namespace Renci.SshNet.Common /// is null. public void Load(byte[] data, int offset, int count) { - if (data == null) - throw new ArgumentNullException("data"); + if (data is null) + { + throw new ArgumentNullException(nameof(data)); + } LoadInternal(data, offset, count); } @@ -133,7 +134,7 @@ namespace Renci.SshNet.Common { var bytesLength = (int) (_stream.Length - _stream.Position); var data = new byte[bytesLength]; - _stream.Read(data, 0, bytesLength); + _ = _stream.Read(data, 0, bytesLength); return data; } @@ -145,15 +146,19 @@ namespace Renci.SshNet.Common /// is greater than the internal buffer size. protected byte[] ReadBytes(int length) { - // Note that this also prevents allocating non-relevant lengths, such as if length is greater than _data.Count but less than int.MaxValue. - // For the nerds, the condition translates to: if (length > data.Count && length < int.MaxValue) - // Which probably would cause all sorts of exception, most notably OutOfMemoryException. + /* + * Note that this also prevents allocating non-relevant lengths, such as if length is greater than _data.Count but less than int.MaxValue. + * For the nerds, the condition translates to: if (length > data.Count && length < int.MaxValue) + * Which probably would cause all sorts of exception, most notably OutOfMemoryException. + */ var data = new byte[length]; var bytesRead = _stream.Read(data, 0, length); if (bytesRead < length) - throw new ArgumentOutOfRangeException("length"); + { + throw new ArgumentOutOfRangeException(nameof(length)); + } return data; } @@ -166,51 +171,63 @@ namespace Renci.SshNet.Common { var byteRead = _stream.ReadByte(); if (byteRead == -1) + { throw new InvalidOperationException("Attempt to read past the end of the SSH data stream."); + } + return (byte) byteRead; } /// - /// Reads next boolean data type from internal buffer. + /// Reads the next from the internal buffer. /// - /// Boolean read. + /// + /// The that was read. + /// protected bool ReadBoolean() { return ReadByte() != 0; } /// - /// Reads next uint16 data type from internal buffer. + /// Reads the next from the internal buffer. /// - /// uint16 read + /// + /// The that was read. + /// protected ushort ReadUInt16() { return Pack.BigEndianToUInt16(ReadBytes(2)); } /// - /// Reads next uint32 data type from internal buffer. + /// Reads the next from the internal buffer. /// - /// uint32 read + /// + /// The that was read. + /// protected uint ReadUInt32() { return Pack.BigEndianToUInt32(ReadBytes(4)); } /// - /// Reads next uint64 data type from internal buffer. + /// Reads the next from the internal buffer. /// - /// uint64 read + /// + /// The that was read. + /// protected ulong ReadUInt64() { return Pack.BigEndianToUInt64(ReadBytes(8)); } /// - /// Reads next string data type from internal buffer using the specific encoding. + /// Reads the next from the internal buffer using the specified encoding. /// + /// The character encoding to use. /// - /// The read. + /// The that was read. /// protected string ReadString(Encoding encoding) { @@ -247,12 +264,14 @@ namespace Renci.SshNet.Common protected IDictionary ReadExtensionPair() { var result = new Dictionary(); + while (!IsEndOfData) { var extensionName = ReadString(Ascii); var extensionData = ReadString(Ascii); result.Add(extensionName, extensionData); } + return result; } @@ -339,30 +358,6 @@ namespace Renci.SshNet.Common _stream.Write(data, encoding); } - /// - /// Writes data into internal buffer. - /// - /// The data to write. - /// is null. - protected void WriteBinaryString(byte[] buffer) - { - _stream.WriteBinary(buffer); - } - - /// - /// Writes data into internal buffer. - /// - /// An array of bytes. This method write bytes from buffer to the current SSH data stream. - /// The zero-based byte offset in at which to begin writing bytes to the SSH data stream. - /// The number of bytes to be written to the current SSH data stream. - /// is null. - /// The sum of and is greater than the buffer length. - /// or is negative. - protected void WriteBinary(byte[] buffer, int offset, int count) - { - _stream.WriteBinary(buffer, offset, count); - } - /// /// Writes mpint data into internal buffer. /// @@ -393,5 +388,29 @@ namespace Renci.SshNet.Common Write(item.Value, Ascii); } } + + /// + /// Writes data into internal buffer. + /// + /// The data to write. + /// is null. + protected void WriteBinaryString(byte[] buffer) + { + _stream.WriteBinary(buffer); + } + + /// + /// Writes data into internal buffer. + /// + /// An array of bytes. This method write bytes from buffer to the current SSH data stream. + /// The zero-based byte offset in at which to begin writing bytes to the SSH data stream. + /// The number of bytes to be written to the current SSH data stream. + /// is null. + /// The sum of and is greater than the buffer length. + /// or is negative. + protected void WriteBinary(byte[] buffer, int offset, int count) + { + _stream.WriteBinary(buffer, offset, count); + } } } diff --git a/src/Renci.SshNet/Common/SshDataStream.cs b/src/Renci.SshNet/Common/SshDataStream.cs index 010800c3..1eedd3ff 100644 --- a/src/Renci.SshNet/Common/SshDataStream.cs +++ b/src/Renci.SshNet/Common/SshDataStream.cs @@ -21,7 +21,7 @@ namespace Renci.SshNet.Common } /// - /// Initializes a new non-resizable instance of the class based on the specified byte array. + /// Initializes a new instance of the class for the specified byte array. /// /// The array of unsigned bytes from which to create the current stream. /// is null. @@ -31,7 +31,7 @@ namespace Renci.SshNet.Common } /// - /// Initializes a new non-resizable instance of the class based on the specified byte array. + /// Initializes a new instance of the class for the specified byte array. /// /// The array of unsigned bytes from which to create the current stream. /// The zero-based offset in at which to begin reading SSH data. @@ -93,8 +93,10 @@ namespace Renci.SshNet.Common /// is null. public void Write(byte[] data) { - if (data == null) - throw new ArgumentNullException("data"); + if (data is null) + { + throw new ArgumentNullException(nameof(data)); + } Write(data, 0, data.Length); } @@ -124,8 +126,10 @@ namespace Renci.SshNet.Common /// is null. public void WriteBinary(byte[] buffer) { - if (buffer == null) - throw new ArgumentNullException("buffer"); + if (buffer is null) + { + throw new ArgumentNullException(nameof(buffer)); + } WriteBinary(buffer, 0, buffer.Length); } @@ -154,8 +158,10 @@ namespace Renci.SshNet.Common /// is null. public void Write(string s, Encoding encoding) { - if (encoding == null) - throw new ArgumentNullException("encoding"); + if (encoding is null) + { + throw new ArgumentNullException(nameof(encoding)); + } var bytes = encoding.GetBytes(s); WriteBinary(bytes, 0, bytes.Length); @@ -201,6 +207,7 @@ namespace Renci.SshNet.Common /// /// Reads the next data type from the SSH data stream. /// + /// The character encoding to use. /// /// The read from the SSH data stream. /// @@ -217,27 +224,6 @@ namespace Renci.SshNet.Common return encoding.GetString(bytes, 0, bytes.Length); } - /// - /// Reads next specified number of bytes data type from internal buffer. - /// - /// Number of bytes to read. - /// - /// An array of bytes that was read from the internal buffer. - /// - /// is greater than the internal buffer size. - private byte[] ReadBytes(int length) - { - var data = new byte[length]; - var bytesRead = Read(data, 0, length); - - if (bytesRead < length) - throw new ArgumentOutOfRangeException("length", - string.Format(CultureInfo.InvariantCulture, - "The requested length ({0}) is greater than the actual number of bytes read ({1}).", length, bytesRead)); - - return data; - } - /// /// Writes the stream contents to a byte array, regardless of the . /// @@ -252,15 +238,31 @@ namespace Renci.SshNet.Common { if (Capacity == Length) { -#if FEATURE_MEMORYSTREAM_GETBUFFER return GetBuffer(); -#elif FEATURE_MEMORYSTREAM_TRYGETBUFFER - ArraySegment buffer; - if (TryGetBuffer(out buffer)) - return buffer.Array; -#endif } + return base.ToArray(); } + + /// + /// Reads next specified number of bytes data type from internal buffer. + /// + /// Number of bytes to read. + /// + /// An array of bytes that was read from the internal buffer. + /// + /// is greater than the internal buffer size. + private byte[] ReadBytes(int length) + { + var data = new byte[length]; + var bytesRead = Read(data, 0, length); + + if (bytesRead < length) + { + throw new ArgumentOutOfRangeException(nameof(length), string.Format(CultureInfo.InvariantCulture, "The requested length ({0}) is greater than the actual number of bytes read ({1}).", length, bytesRead)); + } + + return data; + } } } diff --git a/src/Renci.SshNet/Common/SshOperationTimeoutException.cs b/src/Renci.SshNet/Common/SshOperationTimeoutException.cs index 7dff901f..9fcce26a 100644 --- a/src/Renci.SshNet/Common/SshOperationTimeoutException.cs +++ b/src/Renci.SshNet/Common/SshOperationTimeoutException.cs @@ -34,8 +34,8 @@ namespace Renci.SshNet.Common /// /// The message. /// The inner exception. - public SshOperationTimeoutException(string message, Exception innerException) : - base(message, innerException) + public SshOperationTimeoutException(string message, Exception innerException) + : base(message, innerException) { } diff --git a/src/Renci.SshNet/Common/SshPassPhraseNullOrEmptyException.cs b/src/Renci.SshNet/Common/SshPassPhraseNullOrEmptyException.cs index 1aa0f0d9..102b585d 100644 --- a/src/Renci.SshNet/Common/SshPassPhraseNullOrEmptyException.cs +++ b/src/Renci.SshNet/Common/SshPassPhraseNullOrEmptyException.cs @@ -6,7 +6,7 @@ using System.Runtime.Serialization; namespace Renci.SshNet.Common { /// - /// The exception that is thrown when pass phrase for key file is empty or null + /// The exception that is thrown when pass phrase for key file is empty or . /// #if FEATURE_BINARY_SERIALIZATION [Serializable] @@ -18,7 +18,6 @@ namespace Renci.SshNet.Common /// public SshPassPhraseNullOrEmptyException() { - } /// @@ -28,7 +27,6 @@ namespace Renci.SshNet.Common public SshPassPhraseNullOrEmptyException(string message) : base(message) { - } /// @@ -36,8 +34,8 @@ namespace Renci.SshNet.Common /// /// The message. /// The inner exception. - public SshPassPhraseNullOrEmptyException(string message, Exception innerException) : - base(message, innerException) + public SshPassPhraseNullOrEmptyException(string message, Exception innerException) + : base(message, innerException) { } diff --git a/src/Renci.SshNet/Common/TerminalModes.cs b/src/Renci.SshNet/Common/TerminalModes.cs index 98c7b8fe..8737872b 100644 --- a/src/Renci.SshNet/Common/TerminalModes.cs +++ b/src/Renci.SshNet/Common/TerminalModes.cs @@ -7,21 +7,21 @@ { /// /// Indicates end of options. - /// + /// TTY_OP_END = 0, - + /// /// Interrupt character; 255 if none. Similarly for the other characters. Not all of these characters are supported on all systems. - /// + /// VINTR = 1, /// /// The quit character (sends SIGQUIT signal on POSIX systems). - /// + /// VQUIT = 2, - + /// - /// Erase the character to left of the cursor. + /// Erase the character to left of the cursor. /// VERASE = 3, @@ -34,32 +34,32 @@ /// End-of-file character (sends EOF from the terminal). /// VEOF = 5, - + /// /// End-of-line character in addition to carriage return and/or linefeed. /// VEOL = 6, - + /// /// Additional end-of-line character. /// VEOL2 = 7, - + /// /// Continues paused output (normally control-Q). /// VSTART = 8, - + /// /// Pauses output (normally control-S). /// VSTOP = 9, - + /// /// Suspends the current program. /// VSUSP = 10, - + /// /// Another suspend character. /// @@ -76,7 +76,7 @@ VWERASE = 13, /// - /// Enter the next character typed literally, even if it is a special character + /// Enter the next character typed literally, even if it is a special character. /// VLNEXT = 14, diff --git a/src/Renci.SshNet/Compression/Compressor.cs b/src/Renci.SshNet/Compression/Compressor.cs index 5d06c781..df887aa1 100644 --- a/src/Renci.SshNet/Compression/Compressor.cs +++ b/src/Renci.SshNet/Compression/Compressor.cs @@ -1,6 +1,7 @@ -using Renci.SshNet.Security; +using System; using System.IO; -using System; + +using Renci.SshNet.Security; namespace Renci.SshNet.Compression { @@ -11,9 +12,9 @@ namespace Renci.SshNet.Compression { private readonly ZlibStream _compressor; private readonly ZlibStream _decompressor; - private MemoryStream _compressorStream; private MemoryStream _decompressorStream; + private bool _isDisposed; /// /// Gets or sets a value indicating whether compression is active. @@ -73,7 +74,9 @@ namespace Renci.SshNet.Compression if (!IsActive) { if (offset == 0 && length == data.Length) + { return data; + } var buffer = new byte[length]; Buffer.BlockCopy(data, offset, buffer, 0, length); @@ -113,7 +116,9 @@ namespace Renci.SshNet.Compression if (!IsActive) { if (offset == 0 && length == data.Length) + { return data; + } var buffer = new byte[length]; Buffer.BlockCopy(data, offset, buffer, 0, length); @@ -127,16 +132,12 @@ namespace Renci.SshNet.Compression return _decompressorStream.ToArray(); } - #region IDisposable Members - - private bool _isDisposed; - /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// public void Dispose() { - Dispose(true); + Dispose(disposing: true); GC.SuppressFinalize(this); } @@ -147,7 +148,9 @@ namespace Renci.SshNet.Compression protected virtual void Dispose(bool disposing) { if (_isDisposed) + { return; + } if (disposing) { @@ -175,9 +178,7 @@ namespace Renci.SshNet.Compression /// ~Compressor() { - Dispose(false); + Dispose(disposing: false); } - - #endregion } } diff --git a/src/Renci.SshNet/Compression/Zlib.cs b/src/Renci.SshNet/Compression/Zlib.cs index b518717a..0dc5918c 100644 --- a/src/Renci.SshNet/Compression/Zlib.cs +++ b/src/Renci.SshNet/Compression/Zlib.cs @@ -3,7 +3,7 @@ /// /// Represents "zlib" compression implementation /// - internal class Zlib : Compressor + internal sealed class Zlib : Compressor { /// /// Gets algorithm name. @@ -20,7 +20,8 @@ public override void Init(Session session) { base.Init(session); + IsActive = true; } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet/Compression/ZlibStream.cs b/src/Renci.SshNet/Compression/ZlibStream.cs index e1299694..5c9d3b84 100644 --- a/src/Renci.SshNet/Compression/ZlibStream.cs +++ b/src/Renci.SshNet/Compression/ZlibStream.cs @@ -14,7 +14,9 @@ namespace Renci.SshNet.Compression /// /// The stream. /// The mode. +#pragma warning disable IDE0060 // Remove unused parameter public ZlibStream(Stream stream, CompressionMode mode) +#pragma warning restore IDE0060 // Remove unused parameter { //switch (mode) //{ @@ -37,7 +39,9 @@ namespace Renci.SshNet.Compression /// The buffer. /// The offset. /// The count. +#pragma warning disable IDE0060 // Remove unused parameter public void Write(byte[] buffer, int offset, int count) +#pragma warning restore IDE0060 // Remove unused parameter { //this._baseStream.Write(buffer, offset, count); } diff --git a/src/Renci.SshNet/Connection/ConnectorBase.cs b/src/Renci.SshNet/Connection/ConnectorBase.cs index 6e9bed7a..bdea64a6 100644 --- a/src/Renci.SshNet/Connection/ConnectorBase.cs +++ b/src/Renci.SshNet/Connection/ConnectorBase.cs @@ -1,9 +1,12 @@ -using Renci.SshNet.Abstractions; -using Renci.SshNet.Common; -using Renci.SshNet.Messages.Transport; -using System; +using System; using System.Net; using System.Net.Sockets; +using System.Threading; +using System.Threading.Tasks; + +using Renci.SshNet.Abstractions; +using Renci.SshNet.Common; +using Renci.SshNet.Messages.Transport; namespace Renci.SshNet.Connection { @@ -11,8 +14,10 @@ namespace Renci.SshNet.Connection { protected ConnectorBase(ISocketFactory socketFactory) { - if (socketFactory == null) - throw new ArgumentNullException("socketFactory"); + if (socketFactory is null) + { + throw new ArgumentNullException(nameof(socketFactory)); + } SocketFactory = socketFactory; } @@ -21,6 +26,8 @@ namespace Renci.SshNet.Connection public abstract Socket Connect(IConnectionInfo connectionInfo); + public abstract Task ConnectAsync(IConnectionInfo connectionInfo, CancellationToken cancellationToken); + /// /// Establishes a socket connection to the specified host and port. /// @@ -54,17 +61,51 @@ namespace Renci.SshNet.Connection } } + /// + /// Establishes a socket connection to the specified host and port. + /// + /// The host name of the server to connect to. + /// The port to connect to. + /// The cancellation token to observe. + /// The connection failed to establish within the configured . + /// An error occurred trying to establish the connection. + protected async Task SocketConnectAsync(string host, int port, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + var ipAddress = (await DnsAbstraction.GetHostAddressesAsync(host).ConfigureAwait(false))[0]; + var ep = new IPEndPoint(ipAddress, port); + + DiagnosticAbstraction.Log(string.Format("Initiating connection to '{0}:{1}'.", host, port)); + + var socket = SocketFactory.Create(ep.AddressFamily, SocketType.Stream, ProtocolType.Tcp); + try + { + await SocketAbstraction.ConnectAsync(socket, ep, cancellationToken).ConfigureAwait(false); + + const int socketBufferSize = 2 * Session.MaximumSshPacketSize; + socket.SendBufferSize = socketBufferSize; + socket.ReceiveBufferSize = socketBufferSize; + return socket; + } + catch (Exception) + { + socket.Dispose(); + throw; + } + } + protected static byte SocketReadByte(Socket socket) { var buffer = new byte[1]; - SocketRead(socket, buffer, 0, 1, Session.InfiniteTimeSpan); + _ = SocketRead(socket, buffer, 0, 1, Session.InfiniteTimeSpan); return buffer[0]; } protected static byte SocketReadByte(Socket socket, TimeSpan readTimeout) { var buffer = new byte[1]; - SocketRead(socket, buffer, 0, 1, readTimeout); + _ = SocketRead(socket, buffer, 0, 1, readTimeout); return buffer[0]; } @@ -107,6 +148,7 @@ namespace Renci.SshNet.Connection throw new SshConnectionException("An established connection was aborted by the server.", DisconnectReason.ConnectionLost); } + return bytesRead; } } diff --git a/src/Renci.SshNet/Connection/DirectConnector.cs b/src/Renci.SshNet/Connection/DirectConnector.cs index ec846450..f0b1d6d6 100644 --- a/src/Renci.SshNet/Connection/DirectConnector.cs +++ b/src/Renci.SshNet/Connection/DirectConnector.cs @@ -1,10 +1,12 @@ using System.Net.Sockets; +using System.Threading; namespace Renci.SshNet.Connection { - internal class DirectConnector : ConnectorBase + internal sealed class DirectConnector : ConnectorBase { - public DirectConnector(ISocketFactory socketFactory) : base(socketFactory) + public DirectConnector(ISocketFactory socketFactory) + : base(socketFactory) { } @@ -12,5 +14,10 @@ namespace Renci.SshNet.Connection { return SocketConnect(connectionInfo.Host, connectionInfo.Port, connectionInfo.Timeout); } + + public override System.Threading.Tasks.Task ConnectAsync(IConnectionInfo connectionInfo, CancellationToken cancellationToken) + { + return SocketConnectAsync(connectionInfo.Host, connectionInfo.Port, cancellationToken); + } } } diff --git a/src/Renci.SshNet/Connection/HttpConnector.cs b/src/Renci.SshNet/Connection/HttpConnector.cs index b77f0734..832bb3dd 100644 --- a/src/Renci.SshNet/Connection/HttpConnector.cs +++ b/src/Renci.SshNet/Connection/HttpConnector.cs @@ -1,11 +1,13 @@ -using Renci.SshNet.Abstractions; -using Renci.SshNet.Common; -using System; +using System; using System.Collections.Generic; +using System.Globalization; using System.Net; using System.Net.Sockets; using System.Text.RegularExpressions; +using Renci.SshNet.Abstractions; +using Renci.SshNet.Common; + namespace Renci.SshNet.Connection { /// @@ -27,42 +29,29 @@ namespace Renci.SshNet.Connection /// /// /// - internal class HttpConnector : ConnectorBase + internal sealed class HttpConnector : ProxyConnector { - public HttpConnector(ISocketFactory socketFactory) : base(socketFactory) + public HttpConnector(ISocketFactory socketFactory) + : base(socketFactory) { } - public override Socket Connect(IConnectionInfo connectionInfo) - { - var socket = SocketConnect(connectionInfo.ProxyHost, connectionInfo.ProxyPort, connectionInfo.Timeout); - - try - { - HandleProxyConnect(connectionInfo, socket); - return socket; - } - catch (Exception) - { - socket.Shutdown(SocketShutdown.Both); - socket.Dispose(); - - throw; - } - } - - private void HandleProxyConnect(IConnectionInfo connectionInfo, Socket socket) + protected override void HandleProxyConnect(IConnectionInfo connectionInfo, Socket socket) { var httpResponseRe = new Regex(@"HTTP/(?\d[.]\d) (?\d{3}) (?.+)$"); var httpHeaderRe = new Regex(@"(?[^\[\]()<>@,;:\""/?={} \t]+):(?.+)?"); - SocketAbstraction.Send(socket, SshData.Ascii.GetBytes(string.Format("CONNECT {0}:{1} HTTP/1.0\r\n", connectionInfo.Host, connectionInfo.Port))); + SocketAbstraction.Send(socket, SshData.Ascii.GetBytes(string.Format(CultureInfo.InvariantCulture, + "CONNECT {0}:{1} HTTP/1.0\r\n", + connectionInfo.Host, + connectionInfo.Port))); - // Sent proxy authorization if specified + // Send proxy authorization if specified if (!string.IsNullOrEmpty(connectionInfo.ProxyUsername)) { - var authorization = string.Format("Proxy-Authorization: Basic {0}\r\n", - Convert.ToBase64String(SshData.Ascii.GetBytes(string.Format("{0}:{1}", connectionInfo.ProxyUsername, connectionInfo.ProxyPassword)))); + var authorization = string.Format(CultureInfo.InvariantCulture, + "Proxy-Authorization: Basic {0}\r\n", + Convert.ToBase64String(SshData.Ascii.GetBytes($"{connectionInfo.ProxyUsername}:{connectionInfo.ProxyPassword}"))); SocketAbstraction.Send(socket, SshData.Ascii.GetBytes(authorization)); } @@ -74,24 +63,22 @@ namespace Renci.SshNet.Connection while (true) { var response = SocketReadLine(socket, connectionInfo.Timeout); - if (response == null) + if (response is null) { // server shut down socket break; } - if (statusCode == null) + if (statusCode is null) { var statusMatch = httpResponseRe.Match(response); if (statusMatch.Success) { var httpStatusCode = statusMatch.Result("${statusCode}"); - statusCode = (HttpStatusCode)int.Parse(httpStatusCode); + statusCode = (HttpStatusCode) int.Parse(httpStatusCode, CultureInfo.InvariantCulture); if (statusCode != HttpStatusCode.OK) { - throw new ProxyException(string.Format("HTTP: Status code {0}, \"{1}\"", - httpStatusCode, - statusMatch.Result("${reasonPhrase}"))); + throw new ProxyException($"HTTP: Status code {httpStatusCode}, \"{statusMatch.Result("${reasonPhrase}")}\""); } } @@ -105,25 +92,27 @@ namespace Renci.SshNet.Connection var fieldName = headerMatch.Result("${fieldName}"); if (fieldName.Equals("Content-Length", StringComparison.OrdinalIgnoreCase)) { - contentLength = int.Parse(headerMatch.Result("${fieldValue}")); + contentLength = int.Parse(headerMatch.Result("${fieldValue}"), CultureInfo.InvariantCulture); } + continue; } // check if we've reached the CRLF which separates request line and headers from the message body if (response.Length == 0) { - // read response body if specified + // read response body if specified if (contentLength > 0) { var contentBody = new byte[contentLength]; - SocketRead(socket, contentBody, 0, contentLength, connectionInfo.Timeout); + _ = SocketRead(socket, contentBody, 0, contentLength, connectionInfo.Timeout); } + break; } } - if (statusCode == null) + if (statusCode is null) { throw new ProxyException("HTTP response does not contain status line."); } diff --git a/src/Renci.SshNet/Connection/IConnector.cs b/src/Renci.SshNet/Connection/IConnector.cs index 7efc5c5f..e49587b7 100644 --- a/src/Renci.SshNet/Connection/IConnector.cs +++ b/src/Renci.SshNet/Connection/IConnector.cs @@ -1,9 +1,12 @@ using System.Net.Sockets; +using System.Threading; namespace Renci.SshNet.Connection { internal interface IConnector { Socket Connect(IConnectionInfo connectionInfo); + + System.Threading.Tasks.Task ConnectAsync(IConnectionInfo connectionInfo, CancellationToken cancellationToken); } } diff --git a/src/Renci.SshNet/Connection/IProtocolVersionExchange.cs b/src/Renci.SshNet/Connection/IProtocolVersionExchange.cs index 77bcfbd3..252cda98 100644 --- a/src/Renci.SshNet/Connection/IProtocolVersionExchange.cs +++ b/src/Renci.SshNet/Connection/IProtocolVersionExchange.cs @@ -18,5 +18,7 @@ namespace Renci.SshNet.Connection /// The SSH identification of the server. /// SshIdentification Start(string clientVersion, Socket socket, TimeSpan timeout); + + System.Threading.Tasks.Task StartAsync(string clientVersion, Socket socket, System.Threading.CancellationToken cancellationToken); } } diff --git a/src/Renci.SshNet/Connection/ProtocolVersionExchange.cs b/src/Renci.SshNet/Connection/ProtocolVersionExchange.cs index 1d529a6d..068df230 100644 --- a/src/Renci.SshNet/Connection/ProtocolVersionExchange.cs +++ b/src/Renci.SshNet/Connection/ProtocolVersionExchange.cs @@ -1,12 +1,15 @@ -using Renci.SshNet.Abstractions; -using Renci.SshNet.Common; -using Renci.SshNet.Messages.Transport; -using System; +using System; using System.Collections.Generic; using System.Globalization; using System.Net.Sockets; using System.Text; using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; + +using Renci.SshNet.Abstractions; +using Renci.SshNet.Common; +using Renci.SshNet.Messages.Transport; namespace Renci.SshNet.Connection { @@ -14,17 +17,13 @@ namespace Renci.SshNet.Connection /// Handles the SSH protocol version exchange. /// /// - /// https://tools.ietf.org/html/rfc4253#section-4.2 + /// https://tools.ietf.org/html/rfc4253#section-4.2. /// - internal class ProtocolVersionExchange : IProtocolVersionExchange + internal sealed class ProtocolVersionExchange : IProtocolVersionExchange { private const byte Null = 0x00; -#if FEATURE_REGEX_COMPILE private static readonly Regex ServerVersionRe = new Regex("^SSH-(?[^-]+)-(?.+?)([ ](?.+))?$", RegexOptions.Compiled); -#else - private static readonly Regex ServerVersionRe = new Regex("^SSH-(?[^-]+)-(?.+?)([ ](?.+))?$"); -#endif /// /// Performs the SSH protocol version exchange. @@ -48,24 +47,47 @@ namespace Renci.SshNet.Connection while (true) { var line = SocketReadLine(socket, timeout, bytesReceived); - if (line == null) + if (line is null) { if (bytesReceived.Count == 0) { - throw new SshConnectionException(string.Format("The server response does not contain an SSH identification string.{0}" + - "The connection to the remote server was closed before any data was received.{0}{0}" + - "More information on the Protocol Version Exchange is available here:{0}" + - "https://tools.ietf.org/html/rfc4253#section-4.2", - Environment.NewLine), - DisconnectReason.ConnectionLost); + throw CreateConnectionLostException(); } - throw new SshConnectionException(string.Format("The server response does not contain an SSH identification string:{0}{0}{1}{0}{0}" + - "More information on the Protocol Version Exchange is available here:{0}" + - "https://tools.ietf.org/html/rfc4253#section-4.2", - Environment.NewLine, - PacketDump.Create(bytesReceived, 2)), - DisconnectReason.ProtocolError); + throw CreateServerResponseDoesNotContainIdentification(bytesReceived); + } + + var identificationMatch = ServerVersionRe.Match(line); + if (identificationMatch.Success) + { + return new SshIdentification(GetGroupValue(identificationMatch, "protoversion"), + GetGroupValue(identificationMatch, "softwareversion"), + GetGroupValue(identificationMatch, "comments")); + } + } + } + + public async Task StartAsync(string clientVersion, Socket socket, CancellationToken cancellationToken) + { + // Immediately send the identification string since the spec states both sides MUST send an identification string + // when the connection has been established + SocketAbstraction.Send(socket, Encoding.UTF8.GetBytes(clientVersion + "\x0D\x0A")); + + var bytesReceived = new List(); + + // Get server version from the server, + // ignore text lines which are sent before if any + while (true) + { + var line = await SocketReadLineAsync(socket, bytesReceived, cancellationToken).ConfigureAwait(false); + if (line is null) + { + if (bytesReceived.Count == 0) + { + throw CreateConnectionLostException(); + } + + throw CreateServerResponseDoesNotContainIdentification(bytesReceived); } var identificationMatch = ServerVersionRe.Match(line); @@ -121,16 +143,9 @@ namespace Renci.SshNet.Connection buffer.Add(byteRead); // The null character MUST NOT be sent - if (byteRead == Null) + if (byteRead is Null) { - throw new SshConnectionException(string.Format(CultureInfo.InvariantCulture, - "The server response contains a null character at position 0x{0:X8}:{1}{1}{2}{1}{1}" + - "A server must not send a null character before the Protocol Version Exchange is complete.{1}{1}" + - "More information is available here:{1}" + - "https://tools.ietf.org/html/rfc4253#section-4.2", - buffer.Count, - Environment.NewLine, - PacketDump.Create(buffer.ToArray(), 2))); + throw CreateServerResponseContainsNullCharacterException(buffer); } if (byteRead == Session.LineFeed) @@ -140,18 +155,102 @@ namespace Renci.SshNet.Connection // Return current line without CRLF return Encoding.UTF8.GetString(buffer.ToArray(), startPosition, buffer.Count - (startPosition + 2)); } - else - { - // Even though RFC4253 clearly indicates that the identification string should be terminated - // by a CR LF we also support banners and identification strings that are terminated by a LF - // Return current line without LF - return Encoding.UTF8.GetString(buffer.ToArray(), startPosition, buffer.Count - (startPosition + 1)); - } + // Even though RFC4253 clearly indicates that the identification string should be terminated + // by a CR LF we also support banners and identification strings that are terminated by a LF + + // Return current line without LF + return Encoding.UTF8.GetString(buffer.ToArray(), startPosition, buffer.Count - (startPosition + 1)); } } return null; } + + private static async Task SocketReadLineAsync(Socket socket, List buffer, CancellationToken cancellationToken) + { + var data = new byte[1]; + + var startPosition = buffer.Count; + + // Read data one byte at a time to find end of line and leave any unhandled information in the buffer + // to be processed by subsequent invocations. + while (true) + { + var bytesRead = await SocketAbstraction.ReadAsync(socket, data, 0, data.Length, cancellationToken).ConfigureAwait(false); + if (bytesRead == 0) + { + throw new SshConnectionException("The connection was closed by the remote host."); + } + + var byteRead = data[0]; + buffer.Add(byteRead); + + // The null character MUST NOT be sent + if (byteRead is Null) + { + throw CreateServerResponseContainsNullCharacterException(buffer); + } + + if (byteRead == Session.LineFeed) + { + if (buffer.Count > startPosition + 1 && buffer[buffer.Count - 2] == Session.CarriageReturn) + { + // Return current line without CRLF + return Encoding.UTF8.GetString(buffer.ToArray(), startPosition, buffer.Count - (startPosition + 2)); + } + + // Even though RFC4253 clearly indicates that the identification string should be terminated + // by a CR LF we also support banners and identification strings that are terminated by a LF + + // Return current line without LF + return Encoding.UTF8.GetString(buffer.ToArray(), startPosition, buffer.Count - (startPosition + 1)); + } + } + } + + private static SshConnectionException CreateConnectionLostException() + { +#pragma warning disable SA1118 // Parameter should not span multiple lines + var message = string.Format(CultureInfo.InvariantCulture, + "The server response does not contain an SSH identification string.{0}" + + "The connection to the remote server was closed before any data was received.{0}{0}" + + "More information on the Protocol Version Exchange is available here:{0}" + + "https://tools.ietf.org/html/rfc4253#section-4.2", + Environment.NewLine); +#pragma warning restore SA1118 // Parameter should not span multiple lines + + return new SshConnectionException(message, DisconnectReason.ConnectionLost); + } + + private static SshConnectionException CreateServerResponseContainsNullCharacterException(List buffer) + { +#pragma warning disable SA1118 // Parameter should not span multiple lines + var message = string.Format(CultureInfo.InvariantCulture, + "The server response contains a null character at position 0x{0:X8}:{1}{1}{2}{1}{1}" + + "A server must not send a null character before the Protocol Version Exchange is complete.{1}{1}" + + "More information is available here:{1}" + + "https://tools.ietf.org/html/rfc4253#section-4.2", + buffer.Count, + Environment.NewLine, + PacketDump.Create(buffer.ToArray(), 2)); +#pragma warning restore SA1118 // Parameter should not span multiple lines + + throw new SshConnectionException(message); + } + + private static SshConnectionException CreateServerResponseDoesNotContainIdentification(List bytesReceived) + { +#pragma warning disable SA1118 // Parameter should not span multiple lines + var message = string.Format(CultureInfo.InvariantCulture, + "The server response does not contain an SSH identification string:{0}{0}{1}{0}{0}" + + "More information on the Protocol Version Exchange is available here:{0}" + + "https://tools.ietf.org/html/rfc4253#section-4.2", + Environment.NewLine, + PacketDump.Create(bytesReceived, 2)); +#pragma warning restore SA1118 // Parameter should not span multiple lines + + throw new SshConnectionException(message, DisconnectReason.ProtocolError); + } } } diff --git a/src/Renci.SshNet/Connection/ProxyConnector.cs b/src/Renci.SshNet/Connection/ProxyConnector.cs new file mode 100644 index 00000000..089ee562 --- /dev/null +++ b/src/Renci.SshNet/Connection/ProxyConnector.cs @@ -0,0 +1,66 @@ +using System; +using System.Net.Sockets; +using System.Threading; +using System.Threading.Tasks; + +namespace Renci.SshNet.Connection +{ + internal abstract class ProxyConnector : ConnectorBase + { + protected ProxyConnector(ISocketFactory socketFactory) + : base(socketFactory) + { + } + + protected abstract void HandleProxyConnect(IConnectionInfo connectionInfo, Socket socket); + + // ToDo: Performs async/sync fallback, true async version should be implemented in derived classes + protected virtual Task HandleProxyConnectAsync(IConnectionInfo connectionInfo, Socket socket, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + using (cancellationToken.Register(o => ((Socket)o).Dispose(), socket, useSynchronizationContext: false)) + { + HandleProxyConnect(connectionInfo, socket); + } + + return Task.CompletedTask; + } + + public override Socket Connect(IConnectionInfo connectionInfo) + { + var socket = SocketConnect(connectionInfo.ProxyHost, connectionInfo.ProxyPort, connectionInfo.Timeout); + + try + { + HandleProxyConnect(connectionInfo, socket); + return socket; + } + catch (Exception) + { + socket.Shutdown(SocketShutdown.Both); + socket.Dispose(); + + throw; + } + } + + public override async Task ConnectAsync(IConnectionInfo connectionInfo, CancellationToken cancellationToken) + { + var socket = await SocketConnectAsync(connectionInfo.ProxyHost, connectionInfo.ProxyPort, cancellationToken).ConfigureAwait(false); + + try + { + await HandleProxyConnectAsync(connectionInfo, socket, cancellationToken).ConfigureAwait(false); + return socket; + } + catch (Exception) + { + socket.Shutdown(SocketShutdown.Both); + socket.Dispose(); + + throw; + } + } + } +} diff --git a/src/Renci.SshNet/Connection/SocketFactory.cs b/src/Renci.SshNet/Connection/SocketFactory.cs index 8c61b87c..d279da28 100644 --- a/src/Renci.SshNet/Connection/SocketFactory.cs +++ b/src/Renci.SshNet/Connection/SocketFactory.cs @@ -2,7 +2,7 @@ namespace Renci.SshNet.Connection { - internal class SocketFactory : ISocketFactory + internal sealed class SocketFactory : ISocketFactory { public Socket Create(AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType) { diff --git a/src/Renci.SshNet/Connection/Socks4Connector.cs b/src/Renci.SshNet/Connection/Socks4Connector.cs index 9154240f..4da6bf77 100644 --- a/src/Renci.SshNet/Connection/Socks4Connector.cs +++ b/src/Renci.SshNet/Connection/Socks4Connector.cs @@ -1,58 +1,42 @@ -using Renci.SshNet.Abstractions; -using Renci.SshNet.Common; -using System; +using System; using System.Net.Sockets; using System.Text; +using Renci.SshNet.Abstractions; +using Renci.SshNet.Common; + namespace Renci.SshNet.Connection { /// /// Establishes a tunnel via a SOCKS4 proxy server. /// /// - /// https://www.openssh.com/txt/socks4.protocol + /// https://www.openssh.com/txt/socks4.protocol. /// - internal class Socks4Connector : ConnectorBase + internal sealed class Socks4Connector : ProxyConnector { - public Socks4Connector(ISocketFactory socketFactory) : base(socketFactory) + public Socks4Connector(ISocketFactory socketFactory) + : base(socketFactory) { } - public override Socket Connect(IConnectionInfo connectionInfo) - { - var socket = SocketConnect(connectionInfo.ProxyHost, connectionInfo.ProxyPort, connectionInfo.Timeout); - - try - { - HandleProxyConnect(connectionInfo, socket); - return socket; - } - catch (Exception) - { - socket.Shutdown(SocketShutdown.Both); - socket.Dispose(); - - throw; - } - } - /// /// Establishes a connection to the server via a SOCKS5 proxy. /// /// The connection information. /// The . - private void HandleProxyConnect(IConnectionInfo connectionInfo, Socket socket) + protected override void HandleProxyConnect(IConnectionInfo connectionInfo, Socket socket) { var connectionRequest = CreateSocks4ConnectionRequest(connectionInfo.Host, (ushort)connectionInfo.Port, connectionInfo.ProxyUsername); SocketAbstraction.Send(socket, connectionRequest); - // Read reply version + // Read reply version if (SocketReadByte(socket, connectionInfo.Timeout) != 0x00) { throw new ProxyException("SOCKS4: Null is expected."); } - // Read response code + // Read response code var code = SocketReadByte(socket, connectionInfo.Timeout); switch (code) @@ -70,7 +54,7 @@ namespace Renci.SshNet.Connection } var destBuffer = new byte[6]; // destination port and IP address should be ignored - SocketRead(socket, destBuffer, 0, destBuffer.Length, connectionInfo.Timeout); + _ = SocketRead(socket, destBuffer, 0, destBuffer.Length, connectionInfo.Timeout); } private static byte[] CreateSocks4ConnectionRequest(string hostname, ushort port, string username) @@ -140,14 +124,10 @@ namespace Renci.SshNet.Connection { if (proxyUser == null) { - return Array.Empty; + return Array.Empty(); } -#if FEATURE_ENCODING_ASCII return Encoding.ASCII.GetBytes(proxyUser); -#else - return new ASCIIEncoding().GetBytes(proxyUser); -#endif } } } diff --git a/src/Renci.SshNet/Connection/Socks5Connector.cs b/src/Renci.SshNet/Connection/Socks5Connector.cs index 720ac500..ef9f9974 100644 --- a/src/Renci.SshNet/Connection/Socks5Connector.cs +++ b/src/Renci.SshNet/Connection/Socks5Connector.cs @@ -1,46 +1,30 @@ -using Renci.SshNet.Abstractions; -using Renci.SshNet.Common; -using System; +using System; using System.Net.Sockets; +using Renci.SshNet.Abstractions; +using Renci.SshNet.Common; + namespace Renci.SshNet.Connection { /// /// Establishes a tunnel via a SOCKS5 proxy server. /// /// - /// https://en.wikipedia.org/wiki/SOCKS#SOCKS5 + /// https://en.wikipedia.org/wiki/SOCKS#SOCKS5. /// - internal class Socks5Connector : ConnectorBase + internal sealed class Socks5Connector : ProxyConnector { - public Socks5Connector(ISocketFactory socketFactory) : base(socketFactory) + public Socks5Connector(ISocketFactory socketFactory) + : base(socketFactory) { } - public override Socket Connect(IConnectionInfo connectionInfo) - { - var socket = SocketConnect(connectionInfo.ProxyHost, connectionInfo.ProxyPort, connectionInfo.Timeout); - - try - { - HandleProxyConnect(connectionInfo, socket); - return socket; - } - catch (Exception) - { - socket.Shutdown(SocketShutdown.Both); - socket.Dispose(); - - throw; - } - } - /// /// Establishes a connection to the server via a SOCKS5 proxy. /// /// The connection information. /// The . - private void HandleProxyConnect(IConnectionInfo connectionInfo, Socket socket) + protected override void HandleProxyConnect(IConnectionInfo connectionInfo, Socket socket) { var greeting = new byte[] { @@ -65,34 +49,45 @@ namespace Renci.SshNet.Connection switch (authenticationMethod) { case 0x00: + // No authentication break; case 0x02: // Create username/password authentication request var authenticationRequest = CreateSocks5UserNameAndPasswordAuthenticationRequest(connectionInfo.ProxyUsername, connectionInfo.ProxyPassword); + // Send authentication request SocketAbstraction.Send(socket, authenticationRequest); + // Read authentication result var authenticationResult = SocketAbstraction.Read(socket, 2, connectionInfo.Timeout); if (authenticationResult[0] != 0x01) + { throw new ProxyException("SOCKS5: Server authentication version is not valid."); + } + if (authenticationResult[1] != 0x00) + { throw new ProxyException("SOCKS5: Username/Password authentication failed."); + } + break; case 0xFF: throw new ProxyException("SOCKS5: No acceptable authentication methods were offered."); + default: + throw new ProxyException($"SOCKS5: Chosen authentication method '0x{authenticationMethod:x2}' is not supported."); } var connectionRequest = CreateSocks5ConnectionRequest(connectionInfo.Host, (ushort) connectionInfo.Port); SocketAbstraction.Send(socket, connectionRequest); - // Read Server SOCKS5 version + // Read Server SOCKS5 version if (SocketReadByte(socket) != 5) { throw new ProxyException("SOCKS5: Version 5 is expected."); } - // Read response code + // Read response code var status = SocketReadByte(socket); switch (status) @@ -119,7 +114,7 @@ namespace Renci.SshNet.Connection throw new ProxyException("SOCKS5: Not valid response."); } - // Read reserved byte + // Read reserved byte if (SocketReadByte(socket) != 0) { throw new ProxyException("SOCKS5: 0 byte is expected."); @@ -130,11 +125,11 @@ namespace Renci.SshNet.Connection { case 0x01: var ipv4 = new byte[4]; - SocketRead(socket, ipv4, 0, 4); + _ = SocketRead(socket, ipv4, 0, 4); break; case 0x04: var ipv6 = new byte[16]; - SocketRead(socket, ipv6, 0, 16); + _ =SocketRead(socket, ipv6, 0, 16); break; default: throw new ProxyException(string.Format("Address type '{0}' is not supported.", addressType)); @@ -142,19 +137,24 @@ namespace Renci.SshNet.Connection var port = new byte[2]; - // Read 2 bytes to be ignored - SocketRead(socket, port, 0, 2); + // Read 2 bytes to be ignored + _ = SocketRead(socket, port, 0, 2); } /// - /// https://tools.ietf.org/html/rfc1929 + /// https://tools.ietf.org/html/rfc1929. /// private static byte[] CreateSocks5UserNameAndPasswordAuthenticationRequest(string username, string password) { if (username.Length > byte.MaxValue) + { throw new ProxyException("Proxy username is too long."); + } + if (password.Length > byte.MaxValue) + { throw new ProxyException("Proxy password is too long."); + } var authenticationRequest = new byte [ @@ -179,22 +179,21 @@ namespace Renci.SshNet.Connection authenticationRequest[index++] = (byte) username.Length; // Username - SshData.Ascii.GetBytes(username, 0, username.Length, authenticationRequest, index); + _ = SshData.Ascii.GetBytes(username, 0, username.Length, authenticationRequest, index); index += username.Length; // Length of the password authenticationRequest[index++] = (byte) password.Length; // Password - SshData.Ascii.GetBytes(password, 0, password.Length, authenticationRequest, index); + _ =SshData.Ascii.GetBytes(password, 0, password.Length, authenticationRequest, index); return authenticationRequest; } private static byte[] CreateSocks5ConnectionRequest(string hostname, ushort port) { - byte addressType; - var addressBytes = GetSocks5DestinationAddress(hostname, out addressType); + var addressBytes = GetSocks5DestinationAddress(hostname, out var addressType); var connectionRequest = new byte [ @@ -242,6 +241,7 @@ namespace Renci.SshNet.Connection byte[] address; +#pragma warning disable IDE0010 // Add missing cases switch (ip.AddressFamily) { case AddressFamily.InterNetwork: @@ -255,6 +255,7 @@ namespace Renci.SshNet.Connection default: throw new ProxyException(string.Format("SOCKS5: IP address '{0}' is not supported.", ip)); } +#pragma warning restore IDE0010 // Add missing cases return address; } diff --git a/src/Renci.SshNet/Connection/SshIdentification.cs b/src/Renci.SshNet/Connection/SshIdentification.cs index 90c00fe9..93165629 100644 --- a/src/Renci.SshNet/Connection/SshIdentification.cs +++ b/src/Renci.SshNet/Connection/SshIdentification.cs @@ -5,36 +5,41 @@ namespace Renci.SshNet.Connection /// /// Represents an SSH identification. /// - internal class SshIdentification + internal sealed class SshIdentification { /// - /// Initializes a new instance with the specified protocol version + /// Initializes a new instance of the class with the specified protocol version /// and software version. /// /// The SSH protocol version. - /// The software version of the implementation + /// The software version of the implementation. /// is . /// is . public SshIdentification(string protocolVersion, string softwareVersion) - : this(protocolVersion, softwareVersion, null) + : this(protocolVersion, softwareVersion, comments: null) { } /// - /// Initializes a new instance with the specified protocol version, + /// Initializes a new instance of the class with the specified protocol version, /// software version and comments. /// /// The SSH protocol version. - /// The software version of the implementation + /// The software version of the implementation. /// The comments. /// is . /// is . public SshIdentification(string protocolVersion, string softwareVersion, string comments) { - if (protocolVersion == null) - throw new ArgumentNullException("protocolVersion"); - if (softwareVersion == null) - throw new ArgumentNullException("softwareVersion"); + if (protocolVersion is null) + { + throw new ArgumentNullException(nameof(protocolVersion)); + } + + if (softwareVersion is null) + { + throw new ArgumentNullException(nameof(softwareVersion)); + } ProtocolVersion = protocolVersion; SoftwareVersion = softwareVersion; @@ -42,7 +47,7 @@ namespace Renci.SshNet.Connection } /// - /// Gets or sets the software version of the implementation. + /// Gets the software version of the implementation. /// /// /// The software version of the implementation. @@ -51,18 +56,18 @@ namespace Renci.SshNet.Connection /// This is primarily used to trigger compatibility extensions and to indicate /// the capabilities of an implementation. /// - public string SoftwareVersion { get; private set; } + public string SoftwareVersion { get; } /// - /// Gets or sets the SSH protocol version. + /// Gets the SSH protocol version. /// /// /// The SSH protocol version. /// - public string ProtocolVersion { get; private set; } + public string ProtocolVersion { get; } /// - /// Gets or sets the comments. + /// Gets the comments. /// /// /// The comments, or if there are no comments. @@ -71,7 +76,7 @@ namespace Renci.SshNet.Connection /// should contain additional information that might be useful /// in solving user problems. /// - public string Comments { get; private set; } + public string Comments { get; } /// /// Returns the SSH identification string. @@ -82,10 +87,12 @@ namespace Renci.SshNet.Connection public override string ToString() { var identificationString = "SSH-" + ProtocolVersion + "-" + SoftwareVersion; + if (Comments != null) { identificationString += " " + Comments; } + return identificationString; } } diff --git a/src/Renci.SshNet/ConnectionInfo.cs b/src/Renci.SshNet/ConnectionInfo.cs index af8e1ca5..a8089900 100644 --- a/src/Renci.SshNet/ConnectionInfo.cs +++ b/src/Renci.SshNet/ConnectionInfo.cs @@ -1,14 +1,18 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Net; +using System.Security.Cryptography; using System.Text; + using Renci.SshNet.Abstractions; -using Renci.SshNet.Security; -using Renci.SshNet.Messages.Connection; using Renci.SshNet.Common; using Renci.SshNet.Messages.Authentication; -using Renci.SshNet.Security.Cryptography.Ciphers.Modes; +using Renci.SshNet.Messages.Connection; +using Renci.SshNet.Security; +using Renci.SshNet.Security.Cryptography; using Renci.SshNet.Security.Cryptography.Ciphers; +using Renci.SshNet.Security.Cryptography.Ciphers.Modes; namespace Renci.SshNet { @@ -21,7 +25,7 @@ namespace Renci.SshNet /// public class ConnectionInfo : IConnectionInfoInternal { - internal static int DefaultPort = 22; + internal const int DefaultPort = 22; /// /// The default connection timeout. @@ -161,7 +165,7 @@ namespace Renci.SshNet /// Gets or sets the character encoding. /// /// - /// The character encoding. The default is . + /// The character encoding. The default is . /// public Encoding Encoding { get; set; } @@ -232,7 +236,7 @@ namespace Renci.SshNet public string ServerVersion { get; internal set; } /// - /// Get the client version. + /// Gets the client version. /// public string ClientVersion { get; internal set; } @@ -253,7 +257,7 @@ namespace Renci.SshNet /// is null. /// No specified. public ConnectionInfo(string host, string username, params AuthenticationMethod[] authenticationMethods) - : this(host, DefaultPort, username, ProxyTypes.None, null, 0, null, null, authenticationMethods) + : this(host, DefaultPort, username, ProxyTypes.None, proxyHost: null, 0, proxyUsername: null, proxyPassword: null, authenticationMethods) { } @@ -266,11 +270,11 @@ namespace Renci.SshNet /// The authentication methods. /// is null. /// is null, a zero-length string or contains only whitespace characters. - /// is not within and . + /// is not within and . /// is null. /// No specified. public ConnectionInfo(string host, int port, string username, params AuthenticationMethod[] authenticationMethods) - : this(host, port, username, ProxyTypes.None, null, 0, null, null, authenticationMethods) + : this(host, port, username, ProxyTypes.None, proxyHost: null, 0, proxyUsername: null, proxyPassword: null, authenticationMethods) { } @@ -288,35 +292,51 @@ namespace Renci.SshNet /// The authentication methods. /// is null. /// is null, a zero-length string or contains only whitespace characters. - /// is not within and . + /// is not within and . /// is not and is null. - /// is not and is not within and . + /// is not and is not within and . /// is null. /// No specified. public ConnectionInfo(string host, int port, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, string proxyPassword, params AuthenticationMethod[] authenticationMethods) { - if (host == null) - throw new ArgumentNullException("host"); + if (host is null) + { + throw new ArgumentNullException(nameof(host)); + } + port.ValidatePort("port"); - if (username == null) - throw new ArgumentNullException("username"); + if (username is null) + { + throw new ArgumentNullException(nameof(username)); + } + if (username.All(char.IsWhiteSpace)) - throw new ArgumentException("Cannot be empty or contain only whitespace.", "username"); + { + throw new ArgumentException("Cannot be empty or contain only whitespace.", nameof(username)); + } if (proxyType != ProxyTypes.None) { - if (proxyHost == null) - throw new ArgumentNullException("proxyHost"); + if (proxyHost is null) + { + throw new ArgumentNullException(nameof(proxyHost)); + } + proxyPort.ValidatePort("proxyPort"); } - if (authenticationMethods == null) - throw new ArgumentNullException("authenticationMethods"); - if (authenticationMethods.Length == 0) - throw new ArgumentException("At least one authentication method should be specified.", "authenticationMethods"); + if (authenticationMethods is null) + { + throw new ArgumentNullException(nameof(authenticationMethods)); + } - // Set default connection values + if (authenticationMethods.Length == 0) + { + throw new ArgumentException("At least one authentication method should be specified.", nameof(authenticationMethods)); + } + + // Set default connection values Timeout = DefaultTimeout; ChannelCloseTimeout = DefaultChannelCloseTimeout; RetryAttempts = 10; @@ -325,100 +345,85 @@ namespace Renci.SshNet KeyExchangeAlgorithms = new Dictionary { - {"curve25519-sha256", typeof(KeyExchangeECCurve25519)}, - {"curve25519-sha256@libssh.org", typeof(KeyExchangeECCurve25519)}, - {"ecdh-sha2-nistp256", typeof(KeyExchangeECDH256)}, - {"ecdh-sha2-nistp384", typeof(KeyExchangeECDH384)}, - {"ecdh-sha2-nistp521", typeof(KeyExchangeECDH521)}, - {"diffie-hellman-group-exchange-sha256", typeof (KeyExchangeDiffieHellmanGroupExchangeSha256)}, - {"diffie-hellman-group-exchange-sha1", typeof (KeyExchangeDiffieHellmanGroupExchangeSha1)}, - {"diffie-hellman-group16-sha512", typeof(KeyExchangeDiffieHellmanGroup16Sha512)}, - {"diffie-hellman-group14-sha256", typeof (KeyExchangeDiffieHellmanGroup14Sha256)}, - {"diffie-hellman-group14-sha1", typeof (KeyExchangeDiffieHellmanGroup14Sha1)}, - {"diffie-hellman-group1-sha1", typeof (KeyExchangeDiffieHellmanGroup1Sha1)}, + { "curve25519-sha256", typeof(KeyExchangeECCurve25519) }, + { "curve25519-sha256@libssh.org", typeof(KeyExchangeECCurve25519) }, + { "ecdh-sha2-nistp256", typeof(KeyExchangeECDH256) }, + { "ecdh-sha2-nistp384", typeof(KeyExchangeECDH384) }, + { "ecdh-sha2-nistp521", typeof(KeyExchangeECDH521) }, + { "diffie-hellman-group-exchange-sha256", typeof(KeyExchangeDiffieHellmanGroupExchangeSha256) }, + { "diffie-hellman-group-exchange-sha1", typeof(KeyExchangeDiffieHellmanGroupExchangeSha1) }, + { "diffie-hellman-group16-sha512", typeof(KeyExchangeDiffieHellmanGroup16Sha512) }, + { "diffie-hellman-group14-sha256", typeof(KeyExchangeDiffieHellmanGroup14Sha256) }, + { "diffie-hellman-group14-sha1", typeof(KeyExchangeDiffieHellmanGroup14Sha1) }, + { "diffie-hellman-group1-sha1", typeof(KeyExchangeDiffieHellmanGroup1Sha1) }, }; Encryptions = new Dictionary { - {"aes256-ctr", new CipherInfo(256, (key, iv) => new AesCipher(key, new CtrCipherMode(iv), null))}, - {"3des-cbc", new CipherInfo(192, (key, iv) => new TripleDesCipher(key, new CbcCipherMode(iv), null))}, - {"aes128-cbc", new CipherInfo(128, (key, iv) => new AesCipher(key, new CbcCipherMode(iv), null))}, - {"aes192-cbc", new CipherInfo(192, (key, iv) => new AesCipher(key, new CbcCipherMode(iv), null))}, - {"aes256-cbc", new CipherInfo(256, (key, iv) => new AesCipher(key, new CbcCipherMode(iv), null))}, - {"blowfish-cbc", new CipherInfo(128, (key, iv) => new BlowfishCipher(key, new CbcCipherMode(iv), null))}, - {"twofish-cbc", new CipherInfo(256, (key, iv) => new TwofishCipher(key, new CbcCipherMode(iv), null))}, - {"twofish192-cbc", new CipherInfo(192, (key, iv) => new TwofishCipher(key, new CbcCipherMode(iv), null))}, - {"twofish128-cbc", new CipherInfo(128, (key, iv) => new TwofishCipher(key, new CbcCipherMode(iv), null))}, - {"twofish256-cbc", new CipherInfo(256, (key, iv) => new TwofishCipher(key, new CbcCipherMode(iv), null))}, - ////{"serpent256-cbc", typeof(CipherSerpent256CBC)}, - ////{"serpent192-cbc", typeof(...)}, - ////{"serpent128-cbc", typeof(...)}, - {"arcfour", new CipherInfo(128, (key, iv) => new Arc4Cipher(key, false))}, - {"arcfour128", new CipherInfo(128, (key, iv) => new Arc4Cipher(key, true))}, - {"arcfour256", new CipherInfo(256, (key, iv) => new Arc4Cipher(key, true))}, - ////{"idea-cbc", typeof(...)}, - {"cast128-cbc", new CipherInfo(128, (key, iv) => new CastCipher(key, new CbcCipherMode(iv), null))}, - ////{"rijndael-cbc@lysator.liu.se", typeof(...)}, - {"aes128-ctr", new CipherInfo(128, (key, iv) => new AesCipher(key, new CtrCipherMode(iv), null))}, - {"aes192-ctr", new CipherInfo(192, (key, iv) => new AesCipher(key, new CtrCipherMode(iv), null))}, + { "aes256-ctr", new CipherInfo(256, (key, iv) => new AesCipher(key, new CtrCipherMode(iv), padding: null)) }, + { "3des-cbc", new CipherInfo(192, (key, iv) => new TripleDesCipher(key, new CbcCipherMode(iv), padding: null)) }, + { "aes128-cbc", new CipherInfo(128, (key, iv) => new AesCipher(key, new CbcCipherMode(iv), padding: null)) }, + { "aes192-cbc", new CipherInfo(192, (key, iv) => new AesCipher(key, new CbcCipherMode(iv), padding: null)) }, + { "aes256-cbc", new CipherInfo(256, (key, iv) => new AesCipher(key, new CbcCipherMode(iv), padding: null)) }, + { "blowfish-cbc", new CipherInfo(128, (key, iv) => new BlowfishCipher(key, new CbcCipherMode(iv), padding: null)) }, + { "twofish-cbc", new CipherInfo(256, (key, iv) => new TwofishCipher(key, new CbcCipherMode(iv), padding: null)) }, + { "twofish192-cbc", new CipherInfo(192, (key, iv) => new TwofishCipher(key, new CbcCipherMode(iv), padding: null)) }, + { "twofish128-cbc", new CipherInfo(128, (key, iv) => new TwofishCipher(key, new CbcCipherMode(iv), padding: null)) }, + { "twofish256-cbc", new CipherInfo(256, (key, iv) => new TwofishCipher(key, new CbcCipherMode(iv), padding: null)) }, + { "arcfour", new CipherInfo(128, (key, iv) => new Arc4Cipher(key, dischargeFirstBytes: false)) }, + { "arcfour128", new CipherInfo(128, (key, iv) => new Arc4Cipher(key, dischargeFirstBytes: true)) }, + { "arcfour256", new CipherInfo(256, (key, iv) => new Arc4Cipher(key, dischargeFirstBytes: true)) }, + { "cast128-cbc", new CipherInfo(128, (key, iv) => new CastCipher(key, new CbcCipherMode(iv), padding: null)) }, + { "aes128-ctr", new CipherInfo(128, (key, iv) => new AesCipher(key, new CtrCipherMode(iv), padding: null)) }, + { "aes192-ctr", new CipherInfo(192, (key, iv) => new AesCipher(key, new CtrCipherMode(iv), padding: null)) }, }; HmacAlgorithms = new Dictionary { - {"hmac-md5", new HashInfo(16*8, CryptoAbstraction.CreateHMACMD5)}, - {"hmac-md5-96", new HashInfo(16*8, key => CryptoAbstraction.CreateHMACMD5(key, 96))}, - {"hmac-sha1", new HashInfo(20*8, CryptoAbstraction.CreateHMACSHA1)}, - {"hmac-sha1-96", new HashInfo(20*8, key => CryptoAbstraction.CreateHMACSHA1(key, 96))}, - {"hmac-sha2-256", new HashInfo(32*8, CryptoAbstraction.CreateHMACSHA256)}, - {"hmac-sha2-256-96", new HashInfo(32*8, key => CryptoAbstraction.CreateHMACSHA256(key, 96))}, - {"hmac-sha2-512", new HashInfo(64 * 8, CryptoAbstraction.CreateHMACSHA512)}, - {"hmac-sha2-512-96", new HashInfo(64 * 8, key => CryptoAbstraction.CreateHMACSHA512(key, 96))}, - //{"umac-64@openssh.com", typeof(HMacSha1)}, - {"hmac-ripemd160", new HashInfo(160, CryptoAbstraction.CreateHMACRIPEMD160)}, - {"hmac-ripemd160@openssh.com", new HashInfo(160, CryptoAbstraction.CreateHMACRIPEMD160)}, - //{"none", typeof(...)}, + { "hmac-md5", new HashInfo(16*8, CryptoAbstraction.CreateHMACMD5) }, + { "hmac-md5-96", new HashInfo(16*8, key => CryptoAbstraction.CreateHMACMD5(key, 96)) }, + { "hmac-sha1", new HashInfo(20*8, CryptoAbstraction.CreateHMACSHA1) }, + { "hmac-sha1-96", new HashInfo(20*8, key => CryptoAbstraction.CreateHMACSHA1(key, 96)) }, + { "hmac-sha2-256", new HashInfo(32*8, CryptoAbstraction.CreateHMACSHA256) }, + { "hmac-sha2-256-96", new HashInfo(32*8, key => CryptoAbstraction.CreateHMACSHA256(key, 96)) }, + { "hmac-sha2-512", new HashInfo(64 * 8, CryptoAbstraction.CreateHMACSHA512) }, + { "hmac-sha2-512-96", new HashInfo(64 * 8, key => CryptoAbstraction.CreateHMACSHA512(key, 96)) }, + { "hmac-ripemd160", new HashInfo(160, CryptoAbstraction.CreateHMACRIPEMD160) }, + { "hmac-ripemd160@openssh.com", new HashInfo(160, CryptoAbstraction.CreateHMACRIPEMD160) }, }; HostKeyAlgorithms = new Dictionary> { - {"ssh-ed25519", data => new KeyHostAlgorithm("ssh-ed25519", new ED25519Key(), data)}, -#if FEATURE_ECDSA - {"ecdsa-sha2-nistp256", data => new KeyHostAlgorithm("ecdsa-sha2-nistp256", new EcdsaKey(), data)}, - {"ecdsa-sha2-nistp384", data => new KeyHostAlgorithm("ecdsa-sha2-nistp384", new EcdsaKey(), data)}, - {"ecdsa-sha2-nistp521", data => new KeyHostAlgorithm("ecdsa-sha2-nistp521", new EcdsaKey(), data)}, -#endif - {"ssh-rsa", data => new KeyHostAlgorithm("ssh-rsa", new RsaKey(), data)}, - {"ssh-dss", data => new KeyHostAlgorithm("ssh-dss", new DsaKey(), data)}, - //{"x509v3-sign-rsa", () => { ... }, - //{"x509v3-sign-dss", () => { ... }, - //{"spki-sign-rsa", () => { ... }, - //{"spki-sign-dss", () => { ... }, - //{"pgp-sign-rsa", () => { ... }, - //{"pgp-sign-dss", () => { ... }, + { "ssh-ed25519", data => new KeyHostAlgorithm("ssh-ed25519", new ED25519Key(), data) }, + { "ecdsa-sha2-nistp256", data => new KeyHostAlgorithm("ecdsa-sha2-nistp256", new EcdsaKey(), data) }, + { "ecdsa-sha2-nistp384", data => new KeyHostAlgorithm("ecdsa-sha2-nistp384", new EcdsaKey(), data) }, + { "ecdsa-sha2-nistp521", data => new KeyHostAlgorithm("ecdsa-sha2-nistp521", new EcdsaKey(), data) }, + { "rsa-sha2-512", data => { var key = new RsaKey(); return new KeyHostAlgorithm("rsa-sha2-512", key, data, new RsaDigitalSignature(key, HashAlgorithmName.SHA512)); }}, + { "rsa-sha2-256", data => { var key = new RsaKey(); return new KeyHostAlgorithm("rsa-sha2-256", key, data, new RsaDigitalSignature(key, HashAlgorithmName.SHA256)); }}, + { "ssh-rsa", data => new KeyHostAlgorithm("ssh-rsa", new RsaKey(), data) }, + { "ssh-dss", data => new KeyHostAlgorithm("ssh-dss", new DsaKey(), data) }, }; CompressionAlgorithms = new Dictionary { - //{"zlib@openssh.com", typeof(ZlibOpenSsh)}, - //{"zlib", typeof(Zlib)}, - {"none", null}, + { "none", null }, }; ChannelRequests = new Dictionary { - {EnvironmentVariableRequestInfo.Name, new EnvironmentVariableRequestInfo()}, - {ExecRequestInfo.Name, new ExecRequestInfo()}, - {ExitSignalRequestInfo.Name, new ExitSignalRequestInfo()}, - {ExitStatusRequestInfo.Name, new ExitStatusRequestInfo()}, - {PseudoTerminalRequestInfo.Name, new PseudoTerminalRequestInfo()}, - {ShellRequestInfo.Name, new ShellRequestInfo()}, - {SignalRequestInfo.Name, new SignalRequestInfo()}, - {SubsystemRequestInfo.Name, new SubsystemRequestInfo()}, - {WindowChangeRequestInfo.Name, new WindowChangeRequestInfo()}, - {X11ForwardingRequestInfo.Name, new X11ForwardingRequestInfo()}, - {XonXoffRequestInfo.Name, new XonXoffRequestInfo()}, - {EndOfWriteRequestInfo.Name, new EndOfWriteRequestInfo()}, - {KeepAliveRequestInfo.Name, new KeepAliveRequestInfo()}, + { EnvironmentVariableRequestInfo.Name, new EnvironmentVariableRequestInfo() }, + { ExecRequestInfo.Name, new ExecRequestInfo() }, + { ExitSignalRequestInfo.Name, new ExitSignalRequestInfo() }, + { ExitStatusRequestInfo.Name, new ExitStatusRequestInfo() }, + { PseudoTerminalRequestInfo.Name, new PseudoTerminalRequestInfo() }, + { ShellRequestInfo.Name, new ShellRequestInfo() }, + { SignalRequestInfo.Name, new SignalRequestInfo() }, + { SubsystemRequestInfo.Name, new SubsystemRequestInfo() }, + { WindowChangeRequestInfo.Name, new WindowChangeRequestInfo() }, + { X11ForwardingRequestInfo.Name, new X11ForwardingRequestInfo() }, + { XonXoffRequestInfo.Name, new XonXoffRequestInfo() }, + { EndOfWriteRequestInfo.Name, new EndOfWriteRequestInfo() }, + { KeepAliveRequestInfo.Name, new KeepAliveRequestInfo() }, }; Host = host; @@ -444,8 +449,10 @@ namespace Renci.SshNet /// No suitable authentication method found to complete authentication, or permission denied. internal void Authenticate(ISession session, IServiceFactory serviceFactory) { - if (serviceFactory == null) - throw new ArgumentNullException("serviceFactory"); + if (serviceFactory is null) + { + throw new ArgumentNullException(nameof(serviceFactory)); + } IsAuthenticated = false; var clientAuthentication = serviceFactory.CreateClientAuthentication(); @@ -457,15 +464,10 @@ namespace Renci.SshNet /// Signals that an authentication banner message was received from the server. /// /// The session in which the banner message was received. - /// The banner message.{ + /// The banner message. void IConnectionInfoInternal.UserAuthenticationBannerReceived(object sender, MessageEventArgs e) { - var authenticationBanner = AuthenticationBanner; - if (authenticationBanner != null) - { - authenticationBanner(this, - new AuthenticationBannerEventArgs(Username, e.Message.Message, e.Message.Language)); - } + AuthenticationBanner?.Invoke(this, new AuthenticationBannerEventArgs(Username, e.Message.Message, e.Message.Language)); } IAuthenticationMethod IConnectionInfoInternal.CreateNoneAuthenticationMethod() diff --git a/src/Renci.SshNet/ExpectAction.cs b/src/Renci.SshNet/ExpectAction.cs index c2ff3a6a..2274c52a 100644 --- a/src/Renci.SshNet/ExpectAction.cs +++ b/src/Renci.SshNet/ExpectAction.cs @@ -4,7 +4,7 @@ using System.Text.RegularExpressions; namespace Renci.SshNet { /// - /// Specifies behavior for expected expression + /// Specifies behavior for expected expression. /// public class ExpectAction { @@ -26,11 +26,15 @@ namespace Renci.SshNet /// or is null. public ExpectAction(Regex expect, Action action) { - if (expect == null) - throw new ArgumentNullException("expect"); + if (expect is null) + { + throw new ArgumentNullException(nameof(expect)); + } - if (action == null) - throw new ArgumentNullException("action"); + if (action is null) + { + throw new ArgumentNullException(nameof(action)); + } Expect = expect; Action = action; @@ -44,11 +48,15 @@ namespace Renci.SshNet /// or is null. public ExpectAction(string expect, Action action) { - if (expect == null) - throw new ArgumentNullException("expect"); + if (expect is null) + { + throw new ArgumentNullException(nameof(expect)); + } - if (action == null) - throw new ArgumentNullException("action"); + if (action is null) + { + throw new ArgumentNullException(nameof(action)); + } Expect = new Regex(Regex.Escape(expect)); Action = action; diff --git a/src/Renci.SshNet/ExpectAsyncResult.cs b/src/Renci.SshNet/ExpectAsyncResult.cs index 0f911c0b..f83d32f7 100644 --- a/src/Renci.SshNet/ExpectAsyncResult.cs +++ b/src/Renci.SshNet/ExpectAsyncResult.cs @@ -1,10 +1,11 @@ -using Renci.SshNet.Common; -using System; +using System; + +using Renci.SshNet.Common; namespace Renci.SshNet { /// - /// Provides additional information for asynchronous command execution + /// Provides additional information for asynchronous command execution. /// public class ExpectAsyncResult : AsyncResult { @@ -13,7 +14,7 @@ namespace Renci.SshNet /// /// The async callback. /// The state. - internal ExpectAsyncResult(AsyncCallback asyncCallback, Object state) + internal ExpectAsyncResult(AsyncCallback asyncCallback, object state) : base(asyncCallback, state) { } diff --git a/src/Renci.SshNet/ForwardedPort.cs b/src/Renci.SshNet/ForwardedPort.cs index 26baaa1d..f4544094 100644 --- a/src/Renci.SshNet/ForwardedPort.cs +++ b/src/Renci.SshNet/ForwardedPort.cs @@ -56,11 +56,19 @@ namespace Renci.SshNet CheckDisposed(); if (IsStarted) + { throw new InvalidOperationException("Forwarded port is already started."); - if (Session == null) + } + + if (Session is null) + { throw new InvalidOperationException("Forwarded port is not added to a client."); + } + if (!Session.IsConnected) + { throw new SshConnectionException("Client not connected."); + } Session.ErrorOccured += Session_ErrorOccured; StartPort(); @@ -127,11 +135,7 @@ namespace Renci.SshNet /// The exception. protected void RaiseExceptionEvent(Exception exception) { - var handlers = Exception; - if (handlers != null) - { - handlers(this, new ExceptionEventArgs(exception)); - } + Exception?.Invoke(this, new ExceptionEventArgs(exception)); } /// @@ -141,11 +145,7 @@ namespace Renci.SshNet /// Request originator port. protected void RaiseRequestReceived(string host, uint port) { - var handlers = RequestReceived; - if (handlers != null) - { - handlers(this, new PortForwardEventArgs(host, port)); - } + RequestReceived?.Invoke(this, new PortForwardEventArgs(host, port)); } /// @@ -153,11 +153,7 @@ namespace Renci.SshNet /// private void RaiseClosing() { - var handlers = Closing; - if (handlers != null) - { - handlers(this, EventArgs.Empty); - } + Closing?.Invoke(this, EventArgs.Empty); } /// diff --git a/src/Renci.SshNet/ForwardedPortDynamic.NET.cs b/src/Renci.SshNet/ForwardedPortDynamic.NET.cs index 36a80455..0ae0f431 100644 --- a/src/Renci.SshNet/ForwardedPortDynamic.NET.cs +++ b/src/Renci.SshNet/ForwardedPortDynamic.NET.cs @@ -1,9 +1,10 @@ using System; using System.Linq; -using System.Text; using System.Net; using System.Net.Sockets; +using System.Text; using System.Threading; + using Renci.SshNet.Abstractions; using Renci.SshNet.Channels; using Renci.SshNet.Common; @@ -37,12 +38,12 @@ namespace Renci.SshNet // consider port started when we're listening for inbound connections _status = ForwardedPortStatus.Started; - StartAccept(null); + StartAccept(e: null); } private void StartAccept(SocketAsyncEventArgs e) { - if (e == null) + if (e is null) { e = new SocketAsyncEventArgs(); e.Completed += AcceptCompleted; @@ -60,12 +61,12 @@ namespace Renci.SshNet { if (!_listener.AcceptAsync(e)) { - AcceptCompleted(null, e); + AcceptCompleted(sender: null, e); } } catch (ObjectDisposedException) { - if (_status == ForwardedPortStatus.Stopped || _status == ForwardedPortStatus.Stopped) + if (_status == ForwardedPortStatus.Stopping || _status == ForwardedPortStatus.Stopped) { // ignore ObjectDisposedException while stopping or stopped return; @@ -78,7 +79,7 @@ namespace Renci.SshNet private void AcceptCompleted(object sender, SocketAsyncEventArgs e) { - if (e.SocketError == SocketError.OperationAborted || e.SocketError == SocketError.NotSocket) + if (e.SocketError is SocketError.OperationAborted or SocketError.NotSocket) { // server was stopped return; @@ -91,6 +92,7 @@ namespace Renci.SshNet { // accept new connection StartAccept(e); + // dispose broken client socket CloseClientSocket(clientSocket); return; @@ -98,6 +100,7 @@ namespace Renci.SshNet // accept new connection StartAccept(e); + // process connection ProcessAccept(clientSocket); } @@ -146,7 +149,7 @@ namespace Renci.SshNet // the CountdownEvent will be disposed try { - pendingChannelCountdown.Signal(); + _ = pendingChannelCountdown.Signal(); } catch (ObjectDisposedException) { @@ -170,17 +173,17 @@ namespace Renci.SshNet private void InitializePendingChannelCountdown() { var original = Interlocked.Exchange(ref _pendingChannelCountdown, new CountdownEvent(1)); - if (original != null) - { - original.Dispose(); - } + original?.Dispose(); } private bool HandleSocks(IChannelDirectTcpip channel, Socket clientSocket, TimeSpan timeout) { - // create eventhandler which is to be invoked to interrupt a blocking receive - // when we're closing the forwarded port + +#pragma warning disable IDE0039 // Use lambda instead of local function to reduce allocations + // Create eventhandler which is to be invoked to interrupt a blocking receive + // when we're closing the forwarded port. EventHandler closeClientSocket = (_, args) => CloseClientSocket(clientSocket); +#pragma warning restore IDE0039 // Use lambda instead of local function to reduce allocations Closing += closeClientSocket; @@ -204,10 +207,19 @@ namespace Renci.SshNet { // ignore exception thrown by interrupting the blocking receive as part of closing // the forwarded port +#if NETFRAMEWORK if (ex.SocketErrorCode != SocketError.Interrupted) { RaiseExceptionEvent(ex); } +#else + // Since .NET 5 the exception has been changed. + // more info https://github.com/dotnet/runtime/issues/41585 + if (ex.SocketErrorCode != SocketError.ConnectionAborted) + { + RaiseExceptionEvent(ex); + } +#endif return false; } finally @@ -242,11 +254,7 @@ namespace Renci.SshNet partial void StopListener() { // close listener socket - var listener = _listener; - if (listener != null) - { - listener.Dispose(); - } + _listener?.Dispose(); // unsubscribe from session events var session = Session; @@ -263,7 +271,8 @@ namespace Renci.SshNet /// The maximum time to wait for the pending channels to close. partial void InternalStop(TimeSpan timeout) { - _pendingChannelCountdown.Signal(); + _ = _pendingChannelCountdown.Signal(); + if (!_pendingChannelCountdown.Wait(timeout)) { // TODO: log as warning @@ -345,7 +354,7 @@ namespace Renci.SshNet var ipAddress = new IPAddress(ipBuffer); var username = ReadString(socket, timeout); - if (username == null) + if (username is null) { // SOCKS client closed connection return false; @@ -392,12 +401,12 @@ namespace Renci.SshNet { // no user authentication is one of the authentication methods supported // by the SOCKS client - SocketAbstraction.Send(socket, new byte[] {0x05, 0x00}, 0, 2); + SocketAbstraction.Send(socket, new byte[] { 0x05, 0x00 }, 0, 2); } else { // the SOCKS client requires authentication, which we currently do not support - SocketAbstraction.Send(socket, new byte[] {0x05, 0xFF}, 0, 2); + SocketAbstraction.Send(socket, new byte[] { 0x05, 0xFF }, 0, 2); // we continue business as usual but expect the client to close the connection // so one of the subsequent reads should return -1 signaling that the client @@ -412,7 +421,9 @@ namespace Renci.SshNet } if (version != 5) + { throw new ProxyException("SOCKS5: Version 5 is expected."); + } var commandCode = SocketAbstraction.ReadByte(socket, timeout); if (commandCode == -1) @@ -441,7 +452,7 @@ namespace Renci.SshNet } var host = GetSocks5Host(addressType, socket, timeout); - if (host == null) + if (host is null) { // SOCKS client closed connection return false; @@ -583,11 +594,10 @@ namespace Renci.SshNet break; } - var c = (char) byteRead; - text.Append(c); + _ = text.Append((char) byteRead); } + return text.ToString(); } } } - diff --git a/src/Renci.SshNet/ForwardedPortDynamic.cs b/src/Renci.SshNet/ForwardedPortDynamic.cs index dbe9794d..1331557a 100644 --- a/src/Renci.SshNet/ForwardedPortDynamic.cs +++ b/src/Renci.SshNet/ForwardedPortDynamic.cs @@ -10,15 +10,23 @@ namespace Renci.SshNet { private ForwardedPortStatus _status; + /// + /// Holds a value indicating whether the current instance is disposed. + /// + /// + /// true if the current instance is disposed; otherwise, false. + /// + private bool _isDisposed; + /// /// Gets the bound host. /// - public string BoundHost { get; private set; } + public string BoundHost { get; } /// /// Gets the bound port. /// - public uint BoundPort { get; private set; } + public uint BoundPort { get; } /// /// Gets a value indicating whether port forwarding is started. @@ -35,7 +43,8 @@ namespace Renci.SshNet /// Initializes a new instance of the class. /// /// The port. - public ForwardedPortDynamic(uint port) : this(string.Empty, port) + public ForwardedPortDynamic(uint port) + : this(string.Empty, port) { } @@ -57,7 +66,9 @@ namespace Renci.SshNet protected override void StartPort() { if (!ForwardedPortStatus.ToStarting(ref _status)) + { return; + } try { @@ -78,14 +89,19 @@ namespace Renci.SshNet protected override void StopPort(TimeSpan timeout) { if (!ForwardedPortStatus.ToStopping(ref _status)) + { return; + } // signal existing channels that the port is closing base.StopPort(timeout); + // prevent new requests from getting processed StopListener(); + // wait for open channels to close InternalStop(timeout); + // mark port stopped _status = ForwardedPortStatus.Stopped; } @@ -97,7 +113,9 @@ namespace Renci.SshNet protected override void CheckDisposed() { if (_isDisposed) + { throw new ObjectDisposedException(GetType().FullName); + } } partial void InternalStart(); @@ -113,51 +131,40 @@ namespace Renci.SshNet /// The maximum time to wait for the forwarded port to stop. partial void InternalStop(TimeSpan timeout); - #region IDisposable Members - - /// - /// Holds a value indicating whether the current instance is disposed. - /// - /// - /// true if the current instance is disposed; otherwise, false. - /// - private bool _isDisposed; - /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// public void Dispose() { - Dispose(true); + Dispose(disposing: true); GC.SuppressFinalize(this); } partial void InternalDispose(bool disposing); /// - /// Releases unmanaged and - optionally - managed resources + /// Releases unmanaged and - optionally - managed resources. /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected override void Dispose(bool disposing) { if (_isDisposed) + { return; + } base.Dispose(disposing); - InternalDispose(disposing); + InternalDispose(disposing); _isDisposed = true; } /// - /// Releases unmanaged resources and performs other cleanup operations before the - /// is reclaimed by garbage collection. + /// Finalizes an instance of the class. /// ~ForwardedPortDynamic() { - Dispose(false); + Dispose(disposing: false); } - - #endregion } } diff --git a/src/Renci.SshNet/ForwardedPortLocal.NET.cs b/src/Renci.SshNet/ForwardedPortLocal.NET.cs index ba01ddbd..ca7ec626 100644 --- a/src/Renci.SshNet/ForwardedPortLocal.NET.cs +++ b/src/Renci.SshNet/ForwardedPortLocal.NET.cs @@ -1,7 +1,8 @@ using System; -using System.Net.Sockets; using System.Net; +using System.Net.Sockets; using System.Threading; + using Renci.SshNet.Abstractions; using Renci.SshNet.Common; @@ -32,12 +33,12 @@ namespace Renci.SshNet // consider port started when we're listening for inbound connections _status = ForwardedPortStatus.Started; - StartAccept(null); + StartAccept(e: null); } private void StartAccept(SocketAsyncEventArgs e) { - if (e == null) + if (e is null) { e = new SocketAsyncEventArgs(); e.Completed += AcceptCompleted; @@ -55,12 +56,12 @@ namespace Renci.SshNet { if (!_listener.AcceptAsync(e)) { - AcceptCompleted(null, e); + AcceptCompleted(sender: null, e); } } catch (ObjectDisposedException) { - if (_status == ForwardedPortStatus.Stopped || _status == ForwardedPortStatus.Stopped) + if (_status == ForwardedPortStatus.Stopping || _status == ForwardedPortStatus.Stopped) { // ignore ObjectDisposedException while stopping or stopped return; @@ -73,7 +74,7 @@ namespace Renci.SshNet private void AcceptCompleted(object sender, SocketAsyncEventArgs e) { - if (e.SocketError == SocketError.OperationAborted || e.SocketError == SocketError.NotSocket) + if (e.SocketError is SocketError.OperationAborted or SocketError.NotSocket) { // server was stopped return; @@ -86,6 +87,7 @@ namespace Renci.SshNet { // accept new connection StartAccept(e); + // dispose broken client socket CloseClientSocket(clientSocket); return; @@ -93,6 +95,7 @@ namespace Renci.SshNet // accept new connection StartAccept(e); + // process connection ProcessAccept(clientSocket); } @@ -139,7 +142,7 @@ namespace Renci.SshNet // the CountdownEvent will be disposed try { - pendingChannelCountdown.Signal(); + _ = pendingChannelCountdown.Signal(); } catch (ObjectDisposedException) { @@ -163,10 +166,7 @@ namespace Renci.SshNet private void InitializePendingChannelCountdown() { var original = Interlocked.Exchange(ref _pendingChannelCountdown, new CountdownEvent(1)); - if (original != null) - { - original.Dispose(); - } + original?.Dispose(); } private static void CloseClientSocket(Socket clientSocket) @@ -192,11 +192,7 @@ namespace Renci.SshNet partial void StopListener() { // close listener socket - var listener = _listener; - if (listener != null) - { - listener.Dispose(); - } + _listener?.Dispose(); // unsubscribe from session events var session = Session; @@ -213,7 +209,8 @@ namespace Renci.SshNet /// The maximum time to wait for the pending channels to close. partial void InternalStop(TimeSpan timeout) { - _pendingChannelCountdown.Signal(); + _ = _pendingChannelCountdown.Signal(); + if (!_pendingChannelCountdown.Wait(timeout)) { // TODO: log as warning diff --git a/src/Renci.SshNet/ForwardedPortLocal.cs b/src/Renci.SshNet/ForwardedPortLocal.cs index 79246bd7..8dabb09f 100644 --- a/src/Renci.SshNet/ForwardedPortLocal.cs +++ b/src/Renci.SshNet/ForwardedPortLocal.cs @@ -1,14 +1,17 @@ using System; +using System.Net; + using Renci.SshNet.Common; namespace Renci.SshNet { /// - /// Provides functionality for local port forwarding + /// Provides functionality for local port forwarding. /// public partial class ForwardedPortLocal : ForwardedPort, IDisposable { private ForwardedPortStatus _status; + private bool _isDisposed; /// /// Gets the bound host. @@ -47,9 +50,9 @@ namespace Renci.SshNet /// The bound port. /// The host. /// The port. - /// is greater than . + /// is greater than . /// is null. - /// is greater than . + /// is greater than . /// /// /// @@ -66,9 +69,9 @@ namespace Renci.SshNet /// The port. /// is null. /// is null. - /// is greater than . + /// is greater than . public ForwardedPortLocal(string boundHost, string host, uint port) - : this(boundHost, 0, host, port) + : this(boundHost, 0, host, port) { } @@ -81,15 +84,19 @@ namespace Renci.SshNet /// The port. /// is null. /// is null. - /// is greater than . - /// is greater than . + /// is greater than . + /// is greater than . public ForwardedPortLocal(string boundHost, uint boundPort, string host, uint port) { - if (boundHost == null) - throw new ArgumentNullException("boundHost"); + if (boundHost is null) + { + throw new ArgumentNullException(nameof(boundHost)); + } - if (host == null) - throw new ArgumentNullException("host"); + if (host is null) + { + throw new ArgumentNullException(nameof(host)); + } boundPort.ValidatePort("boundPort"); port.ValidatePort("port"); @@ -107,7 +114,9 @@ namespace Renci.SshNet protected override void StartPort() { if (!ForwardedPortStatus.ToStarting(ref _status)) + { return; + } try { @@ -128,14 +137,19 @@ namespace Renci.SshNet protected override void StopPort(TimeSpan timeout) { if (!ForwardedPortStatus.ToStopping(ref _status)) + { return; + } // signal existing channels that the port is closing base.StopPort(timeout); + // prevent new requests from getting processed StopListener(); + // wait for open channels to close InternalStop(timeout); + // mark port stopped _status = ForwardedPortStatus.Stopped; } @@ -147,7 +161,9 @@ namespace Renci.SshNet protected override void CheckDisposed() { if (_isDisposed) + { throw new ObjectDisposedException(GetType().FullName); + } } partial void InternalStart(); @@ -162,45 +178,40 @@ namespace Renci.SshNet partial void InternalStop(TimeSpan timeout); - #region IDisposable Members - - private bool _isDisposed; - /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// public void Dispose() { - Dispose(true); + Dispose(disposing: true); GC.SuppressFinalize(this); } partial void InternalDispose(bool disposing); /// - /// Releases unmanaged and - optionally - managed resources + /// Releases unmanaged and - optionally - managed resources. /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected override void Dispose(bool disposing) { if (_isDisposed) + { return; + } base.Dispose(disposing); - InternalDispose(disposing); + InternalDispose(disposing); _isDisposed = true; } /// - /// Releases unmanaged resources and performs other cleanup operations before the - /// is reclaimed by garbage collection. + /// Finalizes an instance of the class. /// ~ForwardedPortLocal() { - Dispose(false); + Dispose(disposing: false); } - - #endregion } } diff --git a/src/Renci.SshNet/ForwardedPortRemote.cs b/src/Renci.SshNet/ForwardedPortRemote.cs index b2ed15fe..ce465e12 100644 --- a/src/Renci.SshNet/ForwardedPortRemote.cs +++ b/src/Renci.SshNet/ForwardedPortRemote.cs @@ -1,23 +1,24 @@ using System; -using System.Threading; -using Renci.SshNet.Messages.Connection; -using Renci.SshNet.Common; using System.Globalization; using System.Net; +using System.Threading; + using Renci.SshNet.Abstractions; +using Renci.SshNet.Common; +using Renci.SshNet.Messages.Connection; namespace Renci.SshNet { /// - /// Provides functionality for remote port forwarding + /// Provides functionality for remote port forwarding. /// public class ForwardedPortRemote : ForwardedPort, IDisposable { private ForwardedPortStatus _status; private bool _requestStatus; - - private EventWaitHandle _globalRequestResponse = new AutoResetEvent(false); + private EventWaitHandle _globalRequestResponse = new AutoResetEvent(initialState: false); private CountdownEvent _pendingChannelCountdown; + private bool _isDisposed; /// /// Gets a value indicating whether port forwarding is started. @@ -81,14 +82,19 @@ namespace Renci.SshNet /// The port. /// is null. /// is null. - /// is greater than . - /// is greater than . + /// is greater than . + /// is greater than . public ForwardedPortRemote(IPAddress boundHostAddress, uint boundPort, IPAddress hostAddress, uint port) { - if (boundHostAddress == null) - throw new ArgumentNullException("boundHostAddress"); - if (hostAddress == null) - throw new ArgumentNullException("hostAddress"); + if (boundHostAddress is null) + { + throw new ArgumentNullException(nameof(boundHostAddress)); + } + + if (hostAddress is null) + { + throw new ArgumentNullException(nameof(hostAddress)); + } boundPort.ValidatePort("boundPort"); port.ValidatePort("port"); @@ -135,7 +141,9 @@ namespace Renci.SshNet protected override void StartPort() { if (!ForwardedPortStatus.ToStarting(ref _status)) + { return; + } InitializePendingChannelCountdown(); @@ -151,6 +159,7 @@ namespace Renci.SshNet // send global request to start forwarding Session.SendMessage(new TcpIpForwardGlobalRequestMessage(BoundHost, BoundPort)); + // wat for response on global request to start direct tcpip Session.WaitOnHandle(_globalRequestResponse); @@ -183,15 +192,18 @@ namespace Renci.SshNet protected override void StopPort(TimeSpan timeout) { if (!ForwardedPortStatus.ToStopping(ref _status)) + { return; + } base.StopPort(timeout); // send global request to cancel direct tcpip Session.SendMessage(new CancelTcpIpForwardGlobalRequestMessage(BoundHost, BoundPort)); + // wait for response on global request to cancel direct tcpip or completion of message // listener loop (in which case response on global request can never be received) - WaitHandle.WaitAny(new[] { _globalRequestResponse, Session.MessageListenerCompleted }, timeout); + _ = WaitHandle.WaitAny(new[] { _globalRequestResponse, Session.MessageListenerCompleted }, timeout); // unsubscribe from session events as either the tcpip forward is cancelled at the // server, or our session message loop has completed @@ -200,8 +212,8 @@ namespace Renci.SshNet Session.ChannelOpenReceived -= Session_ChannelOpening; // wait for pending channels to close - _pendingChannelCountdown.Signal(); - + _ = _pendingChannelCountdown.Signal(); + if (!_pendingChannelCountdown.Wait(timeout)) { // TODO: log as warning @@ -218,21 +230,24 @@ namespace Renci.SshNet protected override void CheckDisposed() { if (_isDisposed) + { throw new ObjectDisposedException(GetType().FullName); + } } private void Session_ChannelOpening(object sender, MessageEventArgs e) { var channelOpenMessage = e.Message; - var info = channelOpenMessage.Info as ForwardedTcpipChannelInfo; - if (info != null) + if (channelOpenMessage.Info is ForwardedTcpipChannelInfo info) { - // Ensure this is the corresponding request + // Ensure this is the corresponding request if (info.ConnectedAddress == BoundHost && info.ConnectedPort == BoundPort) { if (!IsStarted) { - Session.SendMessage(new ChannelOpenFailureMessage(channelOpenMessage.LocalChannelNumber, "", ChannelOpenFailureMessage.AdministrativelyProhibited)); + Session.SendMessage(new ChannelOpenFailureMessage(channelOpenMessage.LocalChannelNumber, + string.Empty, + ChannelOpenFailureMessage.AdministrativelyProhibited)); return; } @@ -266,7 +281,7 @@ namespace Renci.SshNet // the CountdownEvent will be disposed try { - pendingChannelCountdown.Signal(); + _ = pendingChannelCountdown.Signal(); } catch (ObjectDisposedException) { @@ -293,10 +308,7 @@ namespace Renci.SshNet private void InitializePendingChannelCountdown() { var original = Interlocked.Exchange(ref _pendingChannelCountdown, new CountdownEvent(1)); - if (original != null) - { - original.Dispose(); - } + original?.Dispose(); } private void Channel_Exception(object sender, ExceptionEventArgs exceptionEventArgs) @@ -307,30 +319,27 @@ namespace Renci.SshNet private void Session_RequestFailure(object sender, EventArgs e) { _requestStatus = false; - _globalRequestResponse.Set(); + _ = _globalRequestResponse.Set(); } private void Session_RequestSuccess(object sender, MessageEventArgs e) { _requestStatus = true; + if (BoundPort == 0) { - BoundPort = (e.Message.BoundPort == null) ? 0 : e.Message.BoundPort.Value; + BoundPort = (e.Message.BoundPort is null) ? 0 : e.Message.BoundPort.Value; } - _globalRequestResponse.Set(); + _ = _globalRequestResponse.Set(); } - #region IDisposable Members - - private bool _isDisposed; - /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// public void Dispose() { - Dispose(true); + Dispose(disposing: true); GC.SuppressFinalize(this); } @@ -341,7 +350,9 @@ namespace Renci.SshNet protected override void Dispose(bool disposing) { if (_isDisposed) + { return; + } base.Dispose(disposing); @@ -375,14 +386,11 @@ namespace Renci.SshNet } /// - /// Releases unmanaged resources and performs other cleanup operations before the - /// is reclaimed by garbage collection. + /// Finalizes an instance of the class. /// ~ForwardedPortRemote() { - Dispose(false); + Dispose(disposing: false); } - - #endregion } } diff --git a/src/Renci.SshNet/ForwardedPortStatus.cs b/src/Renci.SshNet/ForwardedPortStatus.cs index 7fce9ee7..2f151e84 100644 --- a/src/Renci.SshNet/ForwardedPortStatus.cs +++ b/src/Renci.SshNet/ForwardedPortStatus.cs @@ -3,33 +3,33 @@ using System.Threading; namespace Renci.SshNet { - internal class ForwardedPortStatus + internal sealed class ForwardedPortStatus { - private readonly int _value; - private readonly string _name; - public static readonly ForwardedPortStatus Stopped = new ForwardedPortStatus(1, "Stopped"); public static readonly ForwardedPortStatus Stopping = new ForwardedPortStatus(2, "Stopping"); public static readonly ForwardedPortStatus Started = new ForwardedPortStatus(3, "Started"); public static readonly ForwardedPortStatus Starting = new ForwardedPortStatus(4, "Starting"); + private readonly int _value; + private readonly string _name; + private ForwardedPortStatus(int value, string name) { _value = value; _name = name; } - public override bool Equals(object other) + public override bool Equals(object obj) { - if (ReferenceEquals(other, null)) - return false; - - if (ReferenceEquals(this, other)) + if (ReferenceEquals(this, obj)) + { return true; + } - var forwardedPortStatus = other as ForwardedPortStatus; - if (forwardedPortStatus == null) + if (obj is not ForwardedPortStatus forwardedPortStatus) + { return false; + } return forwardedPortStatus._value == _value; } @@ -37,10 +37,10 @@ namespace Renci.SshNet public static bool operator ==(ForwardedPortStatus left, ForwardedPortStatus right) { // check if lhs is null - if (ReferenceEquals(left, null)) + if (left is null) { // check if both lhs and rhs are null - return (ReferenceEquals(right, null)); + return right is null; } return left.Equals(right); @@ -85,7 +85,9 @@ namespace Renci.SshNet // we've successfully transitioned from Started to Stopping if (status == Stopping) + { return true; + } // attempt to transition from Starting to Stopping previousStatus = Interlocked.CompareExchange(ref status, Stopping, Starting); @@ -97,7 +99,9 @@ namespace Renci.SshNet // we've successfully transitioned from Starting to Stopping if (status == Stopping) + { return true; + } // there's no valid transition from status to Stopping throw new InvalidOperationException(string.Format("Forwarded port cannot transition from '{0}' to '{1}'.", @@ -129,7 +133,9 @@ namespace Renci.SshNet // we've successfully transitioned from Stopped to Starting if (status == Starting) + { return true; + } // there's no valid transition from status to Starting throw new InvalidOperationException(string.Format("Forwarded port cannot transition from '{0}' to '{1}'.", diff --git a/src/Renci.SshNet/HashInfo.cs b/src/Renci.SshNet/HashInfo.cs index 156c8456..60829f1e 100644 --- a/src/Renci.SshNet/HashInfo.cs +++ b/src/Renci.SshNet/HashInfo.cs @@ -30,7 +30,7 @@ namespace Renci.SshNet public HashInfo(int keySize, Func hash) { KeySize = keySize; - HashAlgorithm = key => (hash(key.Take(KeySize / 8))); + HashAlgorithm = key => hash(key.Take(KeySize / 8)); } } } diff --git a/src/Renci.SshNet/IBaseClient.cs b/src/Renci.SshNet/IBaseClient.cs new file mode 100644 index 00000000..56828f9b --- /dev/null +++ b/src/Renci.SshNet/IBaseClient.cs @@ -0,0 +1,105 @@ +using System; +using System.Net.Sockets; +using System.Threading; +using System.Threading.Tasks; + +using Renci.SshNet.Common; + +namespace Renci.SshNet +{ + /// + /// Serves as base class for client implementations, provides common client functionality. + /// + public interface IBaseClient + { + /// + /// Gets the connection info. + /// + /// + /// The connection info. + /// + /// The method was called after the client was disposed. + ConnectionInfo ConnectionInfo { get; } + + /// + /// Gets a value indicating whether this client is connected to the server. + /// + /// + /// true if this client is connected; otherwise, false. + /// + /// The method was called after the client was disposed. + bool IsConnected { get; } + + /// + /// Gets or sets the keep-alive interval. + /// + /// + /// The keep-alive interval. Specify negative one (-1) milliseconds to disable the + /// keep-alive. This is the default value. + /// + /// The method was called after the client was disposed. + TimeSpan KeepAliveInterval { get; set; } + + /// + /// Occurs when an error occurred. + /// + /// + /// + /// + event EventHandler ErrorOccurred; + + /// + /// Occurs when host key received. + /// + /// + /// + /// + event EventHandler HostKeyReceived; + + /// + /// Connects client to the server. + /// + /// The client is already connected. + /// The method was called after the client was disposed. + /// Socket connection to the SSH server or proxy server could not be established, or an error occurred while resolving the hostname. + /// SSH session could not be established. + /// Authentication of SSH session failed. + /// Failed to establish proxy connection. + void Connect(); + + /// + /// Asynchronously connects client to the server. + /// + /// The to observe. + /// A that represents the asynchronous connect operation. + /// + /// The client is already connected. + /// The method was called after the client was disposed. + /// Socket connection to the SSH server or proxy server could not be established, or an error occurred while resolving the hostname. + /// SSH session could not be established. + /// Authentication of SSH session failed. + /// Failed to establish proxy connection. + Task ConnectAsync(CancellationToken cancellationToken); + + /// + /// Disconnects client from the server. + /// + /// The method was called after the client was disposed. + void Disconnect(); + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + void Dispose(); + + /// + /// Sends a keep-alive message to the server. + /// + /// + /// Use to configure the client to send a keep-alive at regular + /// intervals. + /// + /// The method was called after the client was disposed. + void SendKeepAlive(); + } +} diff --git a/src/Renci.SshNet/IConnectionInfo.cs b/src/Renci.SshNet/IConnectionInfo.cs index 22abefda..77908d2d 100644 --- a/src/Renci.SshNet/IConnectionInfo.cs +++ b/src/Renci.SshNet/IConnectionInfo.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Text; + using Renci.SshNet.Common; using Renci.SshNet.Messages.Authentication; using Renci.SshNet.Messages.Connection; @@ -13,7 +14,7 @@ namespace Renci.SshNet /// Signals that an authentication banner message was received from the server. /// /// The session in which the banner message was received. - /// The banner message.{ + /// The banner message. void UserAuthenticationBannerReceived(object sender, MessageEventArgs e); /// @@ -41,7 +42,7 @@ namespace Renci.SshNet internal interface IConnectionInfo { /// - /// Gets or sets the timeout to used when waiting for a server to acknowledge closing a channel. + /// Gets the timeout to used when waiting for a server to acknowledge closing a channel. /// /// /// The channel close timeout. The default value is 1 second. @@ -121,7 +122,7 @@ namespace Renci.SshNet int RetryAttempts { get; } /// - /// Gets or sets connection timeout. + /// Gets the connection timeout. /// /// /// The connection timeout. The default value is 30 seconds. diff --git a/src/Renci.SshNet/IPrivateKeySource.cs b/src/Renci.SshNet/IPrivateKeySource.cs new file mode 100644 index 00000000..96ab4574 --- /dev/null +++ b/src/Renci.SshNet/IPrivateKeySource.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; + +using Renci.SshNet.Security; + +namespace Renci.SshNet +{ + /// + /// Represents private key source interface. + /// + public interface IPrivateKeySource + { + /// + /// Gets the host keys algorithms. + /// + /// + /// In situations where there is a preferred order of usage of the host algorithms, + /// the collection should be ordered from most preferred to least. + /// + IReadOnlyCollection HostKeyAlgorithms { get; } + } +} diff --git a/src/Renci.SshNet/IRemotePathTransformation.cs b/src/Renci.SshNet/IRemotePathTransformation.cs index 30c52421..d139db5d 100644 --- a/src/Renci.SshNet/IRemotePathTransformation.cs +++ b/src/Renci.SshNet/IRemotePathTransformation.cs @@ -14,6 +14,4 @@ /// string Transform(string path); } - - } diff --git a/src/Renci.SshNet/IServiceFactory.cs b/src/Renci.SshNet/IServiceFactory.cs index dad93e23..cb20ae06 100644 --- a/src/Renci.SshNet/IServiceFactory.cs +++ b/src/Renci.SshNet/IServiceFactory.cs @@ -75,10 +75,10 @@ namespace Renci.SshNet /// The TERM environment variable. /// The terminal width in columns. /// The terminal width in rows. - /// The terminal height in pixels. + /// The terminal width in pixels. /// The terminal height in pixels. - /// Size of the buffer. /// The terminal mode values. + /// Size of the buffer. /// /// The created instance. /// diff --git a/src/Renci.SshNet/ISession.cs b/src/Renci.SshNet/ISession.cs index cd950da5..bc0239b0 100644 --- a/src/Renci.SshNet/ISession.cs +++ b/src/Renci.SshNet/ISession.cs @@ -1,6 +1,8 @@ using System; using System.Net.Sockets; using System.Threading; +using System.Threading.Tasks; + using Renci.SshNet.Channels; using Renci.SshNet.Common; using Renci.SshNet.Messages; @@ -54,6 +56,17 @@ namespace Renci.SshNet /// Failed to establish proxy connection. void Connect(); + /// + /// Asynchronously connects to the server. + /// + /// The to observe. + /// A that represents the asynchronous connect operation. + /// Socket connection to the SSH server or proxy server could not be established, or an error occurred while resolving the hostname. + /// SSH session could not be established. + /// Authentication of SSH session failed. + /// Failed to establish proxy connection. + Task ConnectAsync(CancellationToken cancellationToken); + /// /// Create a new SSH session channel. /// diff --git a/src/Renci.SshNet/ISftpClient.cs b/src/Renci.SshNet/ISftpClient.cs index b1212cfe..29f958d9 100644 --- a/src/Renci.SshNet/ISftpClient.cs +++ b/src/Renci.SshNet/ISftpClient.cs @@ -2,15 +2,18 @@ using System.Collections.Generic; using System.IO; using System.Text; -using Renci.SshNet.Sftp; +using System.Threading; +using System.Threading.Tasks; + using Renci.SshNet.Common; +using Renci.SshNet.Sftp; namespace Renci.SshNet { /// /// Implementation of the SSH File Transfer Protocol (SFTP) over SSH. /// - public interface ISftpClient + public interface ISftpClient : IBaseClient, IDisposable { /// /// Gets or sets the maximum size of the buffer in bytes. @@ -37,7 +40,7 @@ namespace Renci.SshNet /// SSH_FXP_DATA protocol fields. /// /// - /// The size of the each indivual SSH_FXP_DATA message is limited to the + /// The size of the each individual SSH_FXP_DATA message is limited to the /// local maximum packet size of the channel, which is set to 64 KB /// for SSH.NET. However, the peer can limit this even further. /// @@ -53,7 +56,7 @@ namespace Renci.SshNet /// one (-1) milliseconds, which indicates an infinite timeout period. /// /// The method was called after the client was disposed. - /// represents a value that is less than -1 or greater than milliseconds. + /// represents a value that is less than -1 or greater than milliseconds. TimeSpan OperationTimeout { get; set; } /// @@ -236,6 +239,7 @@ namespace Renci.SshNet /// /// is null. /// is null or contains only whitespace. + /// If a problem occurs while copying the file IAsyncResult BeginSynchronizeDirectories(string sourcePath, string destinationPath, string searchPattern, AsyncCallback asyncCallback, object state); /// @@ -335,7 +339,7 @@ namespace Renci.SshNet /// /// /// When refers to an existing file, set to true to overwrite and truncate that file. - /// If is false, the upload will fail and will throw an + /// If is false, the upload will fail and will throw an /// . /// /// @@ -488,6 +492,20 @@ namespace Renci.SshNet /// The method was called after the client was disposed. void DeleteFile(string path); + /// + /// Asynchronously deletes remote file specified by path. + /// + /// File to be deleted path. + /// The to observe. + /// A that represents the asynchronous delete operation. + /// is null or contains only whitespace characters. + /// Client is not connected. + /// was not found on the remote host. + /// Permission to delete the file was denied by the remote host. -or- A SSH command was denied by the server. + /// A SSH error where is the message from the remote host. + /// The method was called after the client was disposed. + Task DeleteFileAsync(string path, CancellationToken cancellationToken); + /// /// Downloads remote file specified by the path into the stream. /// @@ -498,7 +516,7 @@ namespace Renci.SshNet /// is null or contains only whitespace characters. /// Client is not connected. /// Permission to perform the operation was denied by the remote host. -or- A SSH command was denied by the server. - /// was not found on the remote host./// + /// was not found on the remote host./// /// A SSH error where is the message from the remote host. /// The method was called after the client was disposed. /// @@ -510,7 +528,7 @@ namespace Renci.SshNet /// Ends an asynchronous file downloading into the stream. /// /// The pending asynchronous SFTP request. - /// The object did not come from the corresponding async method on this type.-or- was called multiple times with the same . + /// The object did not come from the corresponding async method on this type.-or- was called multiple times with the same . /// Client is not connected. /// Permission to perform the operation was denied by the remote host. -or- A SSH command was denied by the server. /// The path was not found on the remote host. @@ -524,8 +542,8 @@ namespace Renci.SshNet /// /// A list of files. /// - /// The object did not come from the corresponding async method on this type.-or- was called multiple times with the same . - IEnumerable EndListDirectory(IAsyncResult asyncResult); + /// The object did not come from the corresponding async method on this type.-or- was called multiple times with the same . + IEnumerable EndListDirectory(IAsyncResult asyncResult); /// /// Ends the synchronize directories. @@ -534,7 +552,7 @@ namespace Renci.SshNet /// /// A list of uploaded files. /// - /// The object did not come from the corresponding async method on this type.-or- was called multiple times with the same . + /// The object did not come from the corresponding async method on this type.-or- was called multiple times with the same . /// The destination path was not found on the remote host. IEnumerable EndSynchronizeDirectories(IAsyncResult asyncResult); @@ -542,7 +560,7 @@ namespace Renci.SshNet /// Ends an asynchronous uploading the stream into remote file. /// /// The pending asynchronous SFTP request. - /// The object did not come from the corresponding async method on this type.-or- was called multiple times with the same . + /// The object did not come from the corresponding async method on this type.-or- was called multiple times with the same . /// Client is not connected. /// The directory of the file was not found on the remote host. /// Permission to upload the file was denied by the remote host. -or- A SSH command was denied by the server. @@ -568,13 +586,13 @@ namespace Renci.SshNet /// /// The path. /// - /// A reference to file object. + /// A reference to file object. /// /// Client is not connected. /// was not found on the remote host. /// is null. /// The method was called after the client was disposed. - SftpFile Get(string path); + ISftpFile Get(string path); /// /// Gets the of the file on the path. @@ -653,6 +671,20 @@ namespace Renci.SshNet /// The method was called after the client was disposed. SftpFileSytemInformation GetStatus(string path); + /// + /// Asynchronously gets status using statvfs@openssh.com request. + /// + /// The path. + /// The to observe. + /// + /// A that represents the status operation. + /// The task result contains the instance that contains file status information. + /// + /// Client is not connected. + /// is null. + /// The method was called after the client was disposed. + Task GetStatusAsync(string path, CancellationToken cancellationToken); + /// /// Retrieves list of files in remote directory. /// @@ -666,7 +698,25 @@ namespace Renci.SshNet /// Permission to list the contents of the directory was denied by the remote host. -or- A SSH command was denied by the server. /// A SSH error where is the message from the remote host. /// The method was called after the client was disposed. - IEnumerable ListDirectory(string path, Action listCallback = null); + IEnumerable ListDirectory(string path, Action listCallback = null); + +#if FEATURE_ASYNC_ENUMERABLE + /// + /// Asynchronously enumerates the files in remote directory. + /// + /// The path. + /// The to observe. + /// + /// An of that represents the asynchronous enumeration operation. + /// The enumeration contains an async stream of for the files in the directory specified by . + /// + /// is null. + /// Client is not connected. + /// Permission to list the contents of the directory was denied by the remote host. -or- A SSH command was denied by the server. + /// A SSH error where is the message from the remote host. + /// The method was called after the client was disposed. + IAsyncEnumerable ListDirectoryAsync(string path, CancellationToken cancellationToken); +#endif //FEATURE_ASYNC_ENUMERABLE /// /// Opens a on the specified path with read/write access. @@ -695,6 +745,22 @@ namespace Renci.SshNet /// The method was called after the client was disposed. SftpFileStream Open(string path, FileMode mode, FileAccess access); + /// + /// Asynchronously opens a on the specified path, with the specified mode and access. + /// + /// The file to open. + /// A value that specifies whether a file is created if one does not exist, and determines whether the contents of existing files are retained or overwritten. + /// A value that specifies the operations that can be performed on the file. + /// The to observe. + /// + /// A that represents the asynchronous open operation. + /// The task result contains the that provides access to the specified file, with the specified mode and access. + /// + /// is null. + /// Client is not connected. + /// The method was called after the client was disposed. + Task OpenAsync(string path, FileMode mode, FileAccess access, CancellationToken cancellationToken); + /// /// Opens an existing file for reading. /// @@ -833,6 +899,20 @@ namespace Renci.SshNet /// The method was called after the client was disposed. void RenameFile(string oldPath, string newPath); + /// + /// Asynchronously renames remote file from old path to new path. + /// + /// Path to the old file location. + /// Path to the new file location. + /// The to observe. + /// A that represents the asynchronous rename operation. + /// is null. -or- or is null. + /// Client is not connected. + /// Permission to rename the file was denied by the remote host. -or- A SSH command was denied by the server. + /// A SSH error where is the message from the remote host. + /// The method was called after the client was disposed. + Task RenameFileAsync(string oldPath, string newPath, CancellationToken cancellationToken); + /// /// Renames remote file from old path to new path. /// @@ -846,6 +926,34 @@ namespace Renci.SshNet /// The method was called after the client was disposed. void RenameFile(string oldPath, string newPath, bool isPosix); + /// + /// Sets the date and time the specified file was last accessed. + /// + /// The file for which to set the access date and time information. + /// A containing the value to set for the last access date and time of path. This value is expressed in local time. + void SetLastAccessTime(string path, DateTime lastAccessTime); + + /// + /// Sets the date and time, in coordinated universal time (UTC), that the specified file was last accessed. + /// + /// The file for which to set the access date and time information. + /// A containing the value to set for the last access date and time of path. This value is expressed in UTC time. + void SetLastAccessTimeUtc(string path, DateTime lastAccessTimeUtc); + + /// + /// Sets the date and time that the specified file was last written to. + /// + /// The file for which to set the date and time information. + /// A containing the value to set for the last write date and time of path. This value is expressed in local time. + void SetLastWriteTime(string path, DateTime lastWriteTime); + + /// + /// Sets the date and time, in coordinated universal time (UTC), that the specified file was last written to. + /// + /// The file for which to set the date and time information. + /// A containing the value to set for the last write date and time of path. This value is expressed in UTC time. + void SetLastWriteTimeUtc(string path, DateTime lastWriteTimeUtc); + /// /// Sets the specified of the file on the specified path. /// @@ -880,6 +988,7 @@ namespace Renci.SshNet /// is null. /// is null or contains only whitespace. /// was not found on the remote host. + /// If a problem occurs while copying the file IEnumerable SynchronizeDirectories(string sourcePath, string destinationPath, string searchPattern); /// @@ -1062,4 +1171,4 @@ namespace Renci.SshNet /// void WriteAllText(string path, string contents, Encoding encoding); } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet/KeyboardInteractiveAuthenticationMethod.cs b/src/Renci.SshNet/KeyboardInteractiveAuthenticationMethod.cs index f8c78cbf..1e0a743c 100644 --- a/src/Renci.SshNet/KeyboardInteractiveAuthenticationMethod.cs +++ b/src/Renci.SshNet/KeyboardInteractiveAuthenticationMethod.cs @@ -1,10 +1,12 @@ using System; using System.Linq; +using System.Runtime.ExceptionServices; using System.Threading; + using Renci.SshNet.Abstractions; +using Renci.SshNet.Common; using Renci.SshNet.Messages; using Renci.SshNet.Messages.Authentication; -using Renci.SshNet.Common; namespace Renci.SshNet { @@ -13,16 +15,19 @@ namespace Renci.SshNet /// public class KeyboardInteractiveAuthenticationMethod : AuthenticationMethod, IDisposable { - private AuthenticationResult _authenticationResult = AuthenticationResult.Failure; - - private Session _session; - private EventWaitHandle _authenticationCompleted = new AutoResetEvent(false); - private Exception _exception; private readonly RequestMessage _requestMessage; + private AuthenticationResult _authenticationResult = AuthenticationResult.Failure; + private Session _session; + private EventWaitHandle _authenticationCompleted = new AutoResetEvent(initialState: false); + private Exception _exception; + private bool _isDisposed; /// - /// Gets authentication method name + /// Gets the name of the authentication method. /// + /// + /// The name of the authentication method. + /// public override string Name { get { return _requestMessage.MethodName; } @@ -73,7 +78,9 @@ namespace Renci.SshNet } if (_exception != null) - throw _exception; + { + ExceptionDispatchInfo.Capture(_exception).Throw(); + } return _authenticationResult; } @@ -81,20 +88,24 @@ namespace Renci.SshNet private void Session_UserAuthenticationSuccessReceived(object sender, MessageEventArgs e) { _authenticationResult = AuthenticationResult.Success; - _authenticationCompleted.Set(); + _ = _authenticationCompleted.Set(); } private void Session_UserAuthenticationFailureReceived(object sender, MessageEventArgs e) { if (e.Message.PartialSuccess) + { _authenticationResult = AuthenticationResult.PartialSuccess; + } else + { _authenticationResult = AuthenticationResult.Failure; + } // Copy allowed authentication methods AllowedAuthentications = e.Message.AllowedAuthentications; - _authenticationCompleted.Set(); + _ = _authenticationCompleted.Set(); } private void Session_UserAuthenticationInformationRequestReceived(object sender, MessageEventArgs e) @@ -110,10 +121,7 @@ namespace Renci.SshNet { try { - if (AuthenticationPrompt != null) - { - AuthenticationPrompt(this, eventArgs); - } + AuthenticationPrompt?.Invoke(this, eventArgs); var informationResponse = new InformationResponseMessage(); @@ -122,38 +130,36 @@ namespace Renci.SshNet informationResponse.Responses.Add(response); } - // Send information response message + // Send information response message _session.SendMessage(informationResponse); } catch (Exception exp) { _exception = exp; - _authenticationCompleted.Set(); + _ = _authenticationCompleted.Set(); } }); } - #region IDisposable Members - - private bool _isDisposed; - /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// public void Dispose() { - Dispose(true); + Dispose(disposing: true); GC.SuppressFinalize(this); } /// - /// Releases unmanaged and - optionally - managed resources + /// Releases unmanaged and - optionally - managed resources. /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected virtual void Dispose(bool disposing) { if (_isDisposed) + { return; + } if (disposing) { @@ -169,14 +175,11 @@ namespace Renci.SshNet } /// - /// Releases unmanaged resources and performs other cleanup operations before the - /// is reclaimed by garbage collection. + /// Finalizes an instance of the class. /// ~KeyboardInteractiveAuthenticationMethod() { - Dispose(false); + Dispose(disposing: false); } - - #endregion } } diff --git a/src/Renci.SshNet/KeyboardInteractiveConnectionInfo.cs b/src/Renci.SshNet/KeyboardInteractiveConnectionInfo.cs index d8780caa..af266784 100644 --- a/src/Renci.SshNet/KeyboardInteractiveConnectionInfo.cs +++ b/src/Renci.SshNet/KeyboardInteractiveConnectionInfo.cs @@ -4,13 +4,15 @@ using Renci.SshNet.Common; namespace Renci.SshNet { /// - /// Provides connection information when keyboard interactive authentication method is used + /// Provides connection information when keyboard interactive authentication method is used. /// /// /// /// public class KeyboardInteractiveConnectionInfo : ConnectionInfo, IDisposable { + private bool _isDisposed; + /// /// Occurs when server prompts for more authentication information. /// @@ -19,8 +21,6 @@ namespace Renci.SshNet /// public event EventHandler AuthenticationPrompt; - // TODO: DOCS Add exception documentation for this class. - /// /// Initializes a new instance of the class. /// @@ -29,7 +29,6 @@ namespace Renci.SshNet public KeyboardInteractiveConnectionInfo(string host, string username) : this(host, DefaultPort, username, ProxyTypes.None, string.Empty, 0, string.Empty, string.Empty) { - } /// @@ -41,7 +40,6 @@ namespace Renci.SshNet public KeyboardInteractiveConnectionInfo(string host, int port, string username) : this(host, port, username, ProxyTypes.None, string.Empty, 0, string.Empty, string.Empty) { - } /// @@ -131,8 +129,7 @@ namespace Renci.SshNet { foreach (var authenticationMethod in AuthenticationMethods) { - var kbdInteractive = authenticationMethod as KeyboardInteractiveAuthenticationMethod; - if (kbdInteractive != null) + if (authenticationMethod is KeyboardInteractiveAuthenticationMethod kbdInteractive) { kbdInteractive.AuthenticationPrompt += AuthenticationMethod_AuthenticationPrompt; } @@ -142,34 +139,28 @@ namespace Renci.SshNet private void AuthenticationMethod_AuthenticationPrompt(object sender, AuthenticationPromptEventArgs e) { - if (AuthenticationPrompt != null) - { - AuthenticationPrompt(sender, e); - } + AuthenticationPrompt?.Invoke(sender, e); } - - #region IDisposable Members - - private bool _isDisposed; - /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// public void Dispose() { - Dispose(true); + Dispose(disposing: true); GC.SuppressFinalize(this); } /// - /// Releases unmanaged and - optionally - managed resources + /// Releases unmanaged and - optionally - managed resources. /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected virtual void Dispose(bool disposing) { if (_isDisposed) + { return; + } if (disposing) { @@ -177,8 +168,7 @@ namespace Renci.SshNet { foreach (var authenticationMethods in AuthenticationMethods) { - var disposable = authenticationMethods as IDisposable; - if (disposable != null) + if (authenticationMethods is IDisposable disposable) { disposable.Dispose(); } @@ -190,14 +180,11 @@ namespace Renci.SshNet } /// - /// Releases unmanaged resources and performs other cleanup operations before the - /// is reclaimed by garbage collection. + /// Finalizes an instance of the class. /// ~KeyboardInteractiveConnectionInfo() { - Dispose(false); + Dispose(disposing: false); } - - #endregion } } diff --git a/src/Renci.SshNet/MessageEventArgs.cs b/src/Renci.SshNet/MessageEventArgs.cs index 216b8ada..c3886dce 100644 --- a/src/Renci.SshNet/MessageEventArgs.cs +++ b/src/Renci.SshNet/MessageEventArgs.cs @@ -5,7 +5,7 @@ namespace Renci.SshNet /// /// Provides data for message events. /// - /// Message type + /// Message type. public class MessageEventArgs : EventArgs { /// @@ -14,14 +14,16 @@ namespace Renci.SshNet public T Message { get; private set; } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The message. /// is null. public MessageEventArgs(T message) { - if (message == null) - throw new ArgumentNullException("message"); + if (message is null) + { + throw new ArgumentNullException(nameof(message)); + } Message = message; } diff --git a/src/Renci.SshNet/Messages/Authentication/InformationRequestMessage.cs b/src/Renci.SshNet/Messages/Authentication/InformationRequestMessage.cs index 910a0f55..c887cc88 100644 --- a/src/Renci.SshNet/Messages/Authentication/InformationRequestMessage.cs +++ b/src/Renci.SshNet/Messages/Authentication/InformationRequestMessage.cs @@ -9,7 +9,7 @@ namespace Renci.SshNet.Messages.Authentication /// Represents SSH_MSG_USERAUTH_INFO_REQUEST message. /// [Message("SSH_MSG_USERAUTH_INFO_REQUEST", 60)] - internal class InformationRequestMessage : Message + internal sealed class InformationRequestMessage : Message { /// /// Gets information request name. diff --git a/src/Renci.SshNet/Messages/Authentication/InformationResponseMessage.cs b/src/Renci.SshNet/Messages/Authentication/InformationResponseMessage.cs index 5e0c352f..4aa9cda4 100644 --- a/src/Renci.SshNet/Messages/Authentication/InformationResponseMessage.cs +++ b/src/Renci.SshNet/Messages/Authentication/InformationResponseMessage.cs @@ -7,7 +7,7 @@ namespace Renci.SshNet.Messages.Authentication /// Represents SSH_MSG_USERAUTH_INFO_RESPONSE message. /// [Message("SSH_MSG_USERAUTH_INFO_RESPONSE", 61)] - internal class InformationResponseMessage : Message + internal sealed class InformationResponseMessage : Message { /// /// Gets authentication responses. diff --git a/src/Renci.SshNet/Messages/Authentication/PasswordChangeRequiredMessage.cs b/src/Renci.SshNet/Messages/Authentication/PasswordChangeRequiredMessage.cs index ac05dffa..a87b53df 100644 --- a/src/Renci.SshNet/Messages/Authentication/PasswordChangeRequiredMessage.cs +++ b/src/Renci.SshNet/Messages/Authentication/PasswordChangeRequiredMessage.cs @@ -4,7 +4,7 @@ /// Represents SSH_MSG_USERAUTH_PASSWD_CHANGEREQ message. /// [Message("SSH_MSG_USERAUTH_PASSWD_CHANGEREQ", 60)] - internal class PasswordChangeRequiredMessage : Message + internal sealed class PasswordChangeRequiredMessage : Message { /// /// Gets password change request message as UTF-8 encoded byte array. diff --git a/src/Renci.SshNet/Messages/Authentication/PublicKeyMessage.cs b/src/Renci.SshNet/Messages/Authentication/PublicKeyMessage.cs index 9658a2e0..b5582b78 100644 --- a/src/Renci.SshNet/Messages/Authentication/PublicKeyMessage.cs +++ b/src/Renci.SshNet/Messages/Authentication/PublicKeyMessage.cs @@ -4,7 +4,7 @@ /// Represents SSH_MSG_USERAUTH_PK_OK message. /// [Message("SSH_MSG_USERAUTH_PK_OK", 60)] - internal class PublicKeyMessage : Message + internal sealed class PublicKeyMessage : Message { /// /// Gets the name of the public key algorithm as ASCII encoded byte array. diff --git a/src/Renci.SshNet/Messages/Authentication/RequestMessageHost.cs b/src/Renci.SshNet/Messages/Authentication/RequestMessageHost.cs index 5d5a22dc..ec9fd204 100644 --- a/src/Renci.SshNet/Messages/Authentication/RequestMessageHost.cs +++ b/src/Renci.SshNet/Messages/Authentication/RequestMessageHost.cs @@ -3,44 +3,44 @@ /// /// Represents "hostbased" SSH_MSG_USERAUTH_REQUEST message. /// - internal class RequestMessageHost : RequestMessage + internal sealed class RequestMessageHost : RequestMessage { /// /// Gets the public key algorithm for host key as ASCII encoded byte array. /// - public byte[] PublicKeyAlgorithm { get; private set; } + public byte[] PublicKeyAlgorithm { get; } /// - /// Gets or sets the public host key and certificates for client host. + /// Gets the public host key and certificates for client host. /// /// /// The public host key. /// - public byte[] PublicHostKey { get; private set; } + public byte[] PublicHostKey { get; } /// - /// Gets or sets the name of the client host as ASCII encoded byte array. + /// Gets the name of the client host as ASCII encoded byte array. /// /// /// The name of the client host. /// - public byte[] ClientHostName { get; private set; } + public byte[] ClientHostName { get; } /// - /// Gets or sets the client username on the client host as UTF-8 encoded byte array. + /// Gets the client username on the client host as UTF-8 encoded byte array. /// /// /// The client username. /// - public byte[] ClientUsername { get; private set; } + public byte[] ClientUsername { get; } /// - /// Gets or sets the signature. + /// Gets the signature. /// /// /// The signature. /// - public byte[] Signature { get; private set; } + public byte[] Signature { get; } /// /// Gets the size of the message in bytes. diff --git a/src/Renci.SshNet/Messages/Authentication/RequestMessageKeyboardInteractive.cs b/src/Renci.SshNet/Messages/Authentication/RequestMessageKeyboardInteractive.cs index 6961cb5a..6175511e 100644 --- a/src/Renci.SshNet/Messages/Authentication/RequestMessageKeyboardInteractive.cs +++ b/src/Renci.SshNet/Messages/Authentication/RequestMessageKeyboardInteractive.cs @@ -1,11 +1,11 @@ -using Renci.SshNet.Common; +using System; namespace Renci.SshNet.Messages.Authentication { /// /// Represents "keyboard-interactive" SSH_MSG_USERAUTH_REQUEST message. /// - internal class RequestMessageKeyboardInteractive : RequestMessage + internal sealed class RequestMessageKeyboardInteractive : RequestMessage { /// /// Gets message language. @@ -44,8 +44,8 @@ namespace Renci.SshNet.Messages.Authentication public RequestMessageKeyboardInteractive(ServiceName serviceName, string username) : base(serviceName, username, "keyboard-interactive") { - Language = Array.Empty; - SubMethods = Array.Empty; + Language = Array.Empty(); + SubMethods = Array.Empty(); } /// diff --git a/src/Renci.SshNet/Messages/Authentication/RequestMessageNone.cs b/src/Renci.SshNet/Messages/Authentication/RequestMessageNone.cs index 77cc55f3..1827c329 100644 --- a/src/Renci.SshNet/Messages/Authentication/RequestMessageNone.cs +++ b/src/Renci.SshNet/Messages/Authentication/RequestMessageNone.cs @@ -3,7 +3,7 @@ /// /// Represents "none" SSH_MSG_USERAUTH_REQUEST message. /// - internal class RequestMessageNone : RequestMessage + internal sealed class RequestMessageNone : RequestMessage { /// /// Initializes a new instance of the class. diff --git a/src/Renci.SshNet/Messages/Authentication/RequestMessagePassword.cs b/src/Renci.SshNet/Messages/Authentication/RequestMessagePassword.cs index 881617b8..603342c6 100644 --- a/src/Renci.SshNet/Messages/Authentication/RequestMessagePassword.cs +++ b/src/Renci.SshNet/Messages/Authentication/RequestMessagePassword.cs @@ -3,7 +3,7 @@ /// /// Represents "password" SSH_MSG_USERAUTH_REQUEST message. /// - internal class RequestMessagePassword : RequestMessage + internal sealed class RequestMessagePassword : RequestMessage { /// /// Gets authentication password. diff --git a/src/Renci.SshNet/Messages/Authentication/RequestMessagePublicKey.cs b/src/Renci.SshNet/Messages/Authentication/RequestMessagePublicKey.cs index 9a888e29..391e60e7 100644 --- a/src/Renci.SshNet/Messages/Authentication/RequestMessagePublicKey.cs +++ b/src/Renci.SshNet/Messages/Authentication/RequestMessagePublicKey.cs @@ -92,7 +92,9 @@ WriteBinaryString(PublicKeyAlgorithmName); WriteBinaryString(PublicKeyData); if (Signature != null) + { WriteBinaryString(Signature); + } } } } diff --git a/src/Renci.SshNet/Messages/Connection/CancelTcpIpForwardGlobalRequestMessage.cs b/src/Renci.SshNet/Messages/Connection/CancelTcpIpForwardGlobalRequestMessage.cs index eaf8c6be..0b3431c0 100644 --- a/src/Renci.SshNet/Messages/Connection/CancelTcpIpForwardGlobalRequestMessage.cs +++ b/src/Renci.SshNet/Messages/Connection/CancelTcpIpForwardGlobalRequestMessage.cs @@ -2,12 +2,12 @@ namespace Renci.SshNet.Messages.Connection { - internal class CancelTcpIpForwardGlobalRequestMessage : GlobalRequestMessage + internal sealed class CancelTcpIpForwardGlobalRequestMessage : GlobalRequestMessage { private byte[] _addressToBind; public CancelTcpIpForwardGlobalRequestMessage(string addressToBind, uint portToBind) - : base(Ascii.GetBytes("cancel-tcpip-forward"), true) + : base(Ascii.GetBytes("cancel-tcpip-forward"), wantReply: true) { AddressToBind = addressToBind; PortToBind = portToBind; diff --git a/src/Renci.SshNet/Messages/Connection/ChannelDataMessage.cs b/src/Renci.SshNet/Messages/Connection/ChannelDataMessage.cs index 76c98f36..8a764d79 100644 --- a/src/Renci.SshNet/Messages/Connection/ChannelDataMessage.cs +++ b/src/Renci.SshNet/Messages/Connection/ChannelDataMessage.cs @@ -11,7 +11,7 @@ namespace Renci.SshNet.Messages.Connection internal const byte MessageNumber = 94; /// - /// Gets or sets message data. + /// Gets the message data. /// /// /// The data. @@ -27,7 +27,7 @@ namespace Renci.SshNet.Messages.Connection /// /// The zero-based offset in at which the data begins. /// - public int Offset { get; set; } + public int Offset { get; private set; } /// /// Gets the number of bytes of to read or write. @@ -35,7 +35,7 @@ namespace Renci.SshNet.Messages.Connection /// /// The number of bytes of to read or write. /// - public int Size { get; set; } + public int Size { get; private set; } /// /// Gets the size of the message in bytes. @@ -74,8 +74,10 @@ namespace Renci.SshNet.Messages.Connection public ChannelDataMessage(uint localChannelNumber, byte[] data) : base(localChannelNumber) { - if (data == null) - throw new ArgumentNullException("data"); + if (data is null) + { + throw new ArgumentNullException(nameof(data)); + } Data = data; Offset = 0; @@ -92,8 +94,10 @@ namespace Renci.SshNet.Messages.Connection public ChannelDataMessage(uint localChannelNumber, byte[] data, int offset, int size) : base(localChannelNumber) { - if (data == null) - throw new ArgumentNullException("data"); + if (data is null) + { + throw new ArgumentNullException(nameof(data)); + } Data = data; Offset = offset; @@ -106,6 +110,7 @@ namespace Renci.SshNet.Messages.Connection protected override void LoadData() { base.LoadData(); + Data = ReadBinary(); Offset = 0; Size = Data.Length; @@ -117,6 +122,7 @@ namespace Renci.SshNet.Messages.Connection protected override void SaveData() { base.SaveData(); + WriteBinary(Data, Offset, Size); } } diff --git a/src/Renci.SshNet/Messages/Connection/ChannelMessage.cs b/src/Renci.SshNet/Messages/Connection/ChannelMessage.cs index b47852b9..e1d6ee99 100644 --- a/src/Renci.SshNet/Messages/Connection/ChannelMessage.cs +++ b/src/Renci.SshNet/Messages/Connection/ChannelMessage.cs @@ -32,14 +32,14 @@ namespace Renci.SshNet.Messages.Connection } /// - /// Initializes a new . + /// Initializes a new instance of the class. /// protected ChannelMessage() { } /// - /// Initializes a new with the specified local channel number. + /// Initializes a new instance of the class with the specified local channel number. /// /// The local channel number. protected ChannelMessage(uint localChannelNumber) @@ -64,10 +64,10 @@ namespace Renci.SshNet.Messages.Connection } /// - /// Returns a that represents this instance. + /// Returns a that represents this instance. /// /// - /// A that represents this instance. + /// A that represents this instance. /// public override string ToString() { diff --git a/src/Renci.SshNet/Messages/Connection/ChannelOpen/ChannelOpenMessage.cs b/src/Renci.SshNet/Messages/Connection/ChannelOpen/ChannelOpenMessage.cs index 1f8bb3e9..b4cac43b 100644 --- a/src/Renci.SshNet/Messages/Connection/ChannelOpen/ChannelOpenMessage.cs +++ b/src/Renci.SshNet/Messages/Connection/ChannelOpen/ChannelOpenMessage.cs @@ -76,7 +76,7 @@ namespace Renci.SshNet.Messages.Connection /// public ChannelOpenMessage() { - // Required for dynamicly loading request type when it comes from the server + // Required for dynamicly loading request type when it comes from the server } /// @@ -90,7 +90,9 @@ namespace Renci.SshNet.Messages.Connection public ChannelOpenMessage(uint channelNumber, uint initialWindowSize, uint maximumPacketSize, ChannelOpenInfo info) { if (info == null) - throw new ArgumentNullException("info"); + { + throw new ArgumentNullException(nameof(info)); + } ChannelType = Ascii.GetBytes(info.ChannelType); LocalChannelNumber = channelNumber; diff --git a/src/Renci.SshNet/Messages/Connection/ChannelOpen/DirectTcpipChannelInfo.cs b/src/Renci.SshNet/Messages/Connection/ChannelOpen/DirectTcpipChannelInfo.cs index 8ad47fbb..59604391 100644 --- a/src/Renci.SshNet/Messages/Connection/ChannelOpen/DirectTcpipChannelInfo.cs +++ b/src/Renci.SshNet/Messages/Connection/ChannelOpen/DirectTcpipChannelInfo.cs @@ -5,7 +5,7 @@ namespace Renci.SshNet.Messages.Connection /// /// Used to open "direct-tcpip" channel type /// - internal class DirectTcpipChannelInfo : ChannelOpenInfo + internal sealed class DirectTcpipChannelInfo : ChannelOpenInfo { private byte[] _hostToConnect; private byte[] _originatorAddress; diff --git a/src/Renci.SshNet/Messages/Connection/ChannelOpen/ForwardedTcpipChannelInfo.cs b/src/Renci.SshNet/Messages/Connection/ChannelOpen/ForwardedTcpipChannelInfo.cs index 64d0ebba..aec13dc0 100644 --- a/src/Renci.SshNet/Messages/Connection/ChannelOpen/ForwardedTcpipChannelInfo.cs +++ b/src/Renci.SshNet/Messages/Connection/ChannelOpen/ForwardedTcpipChannelInfo.cs @@ -5,7 +5,7 @@ namespace Renci.SshNet.Messages.Connection /// /// Used to open "forwarded-tcpip" channel type /// - internal class ForwardedTcpipChannelInfo : ChannelOpenInfo + internal sealed class ForwardedTcpipChannelInfo : ChannelOpenInfo { private byte[] _connectedAddress; private byte[] _originatorAddress; diff --git a/src/Renci.SshNet/Messages/Connection/ChannelOpen/SessionChannelOpenInfo.cs b/src/Renci.SshNet/Messages/Connection/ChannelOpen/SessionChannelOpenInfo.cs index bfe04768..edcc9dae 100644 --- a/src/Renci.SshNet/Messages/Connection/ChannelOpen/SessionChannelOpenInfo.cs +++ b/src/Renci.SshNet/Messages/Connection/ChannelOpen/SessionChannelOpenInfo.cs @@ -5,7 +5,7 @@ namespace Renci.SshNet.Messages.Connection /// /// Used to open "session" channel type /// - internal class SessionChannelOpenInfo : ChannelOpenInfo + internal sealed class SessionChannelOpenInfo : ChannelOpenInfo { /// /// Specifies channel open type diff --git a/src/Renci.SshNet/Messages/Connection/ChannelOpen/X11ChannelOpenInfo.cs b/src/Renci.SshNet/Messages/Connection/ChannelOpen/X11ChannelOpenInfo.cs index 6f62cfda..6eef2d51 100644 --- a/src/Renci.SshNet/Messages/Connection/ChannelOpen/X11ChannelOpenInfo.cs +++ b/src/Renci.SshNet/Messages/Connection/ChannelOpen/X11ChannelOpenInfo.cs @@ -5,7 +5,7 @@ namespace Renci.SshNet.Messages.Connection /// /// Used to open "x11" channel type /// - internal class X11ChannelOpenInfo : ChannelOpenInfo + internal sealed class X11ChannelOpenInfo : ChannelOpenInfo { private byte[] _originatorAddress; diff --git a/src/Renci.SshNet/Messages/Connection/ChannelRequest/BreakRequestInfo.cs b/src/Renci.SshNet/Messages/Connection/ChannelRequest/BreakRequestInfo.cs index 0cadd637..472f8cb8 100644 --- a/src/Renci.SshNet/Messages/Connection/ChannelRequest/BreakRequestInfo.cs +++ b/src/Renci.SshNet/Messages/Connection/ChannelRequest/BreakRequestInfo.cs @@ -3,7 +3,7 @@ /// /// Represents "break" type channel request information /// - internal class BreakRequestInfo : RequestInfo + internal sealed class BreakRequestInfo : RequestInfo { /// /// Channel request name diff --git a/src/Renci.SshNet/Messages/Connection/ChannelRequest/EnvironmentVariableRequestInfo.cs b/src/Renci.SshNet/Messages/Connection/ChannelRequest/EnvironmentVariableRequestInfo.cs index 7df59454..6901f324 100644 --- a/src/Renci.SshNet/Messages/Connection/ChannelRequest/EnvironmentVariableRequestInfo.cs +++ b/src/Renci.SshNet/Messages/Connection/ChannelRequest/EnvironmentVariableRequestInfo.cs @@ -3,7 +3,7 @@ /// /// Represents "env" type channel request information /// - internal class EnvironmentVariableRequestInfo : RequestInfo + internal sealed class EnvironmentVariableRequestInfo : RequestInfo { private byte[] _variableName; private byte[] _variableValue; diff --git a/src/Renci.SshNet/Messages/Connection/ChannelRequest/ExecRequestInfo.cs b/src/Renci.SshNet/Messages/Connection/ChannelRequest/ExecRequestInfo.cs index 673f4c2b..412a86d8 100644 --- a/src/Renci.SshNet/Messages/Connection/ChannelRequest/ExecRequestInfo.cs +++ b/src/Renci.SshNet/Messages/Connection/ChannelRequest/ExecRequestInfo.cs @@ -4,14 +4,14 @@ using System.Text; namespace Renci.SshNet.Messages.Connection { /// - /// Represents "exec" type channel request information + /// Represents "exec" type channel request information. /// - internal class ExecRequestInfo : RequestInfo + internal sealed class ExecRequestInfo : RequestInfo { private byte[] _command; /// - /// Channel request name + /// Channel request name. /// public const string Name = "exec"; @@ -79,10 +79,15 @@ namespace Renci.SshNet.Messages.Connection public ExecRequestInfo(string command, Encoding encoding) : this() { - if (command == null) - throw new ArgumentNullException("command"); - if (encoding == null) - throw new ArgumentNullException("encoding"); + if (command is null) + { + throw new ArgumentNullException(nameof(command)); + } + + if (encoding is null) + { + throw new ArgumentNullException(nameof(encoding)); + } _command = encoding.GetBytes(command); Encoding = encoding; diff --git a/src/Renci.SshNet/Messages/Connection/ChannelRequest/ExitSignalRequestInfo.cs b/src/Renci.SshNet/Messages/Connection/ChannelRequest/ExitSignalRequestInfo.cs index 512f3b97..114bd577 100644 --- a/src/Renci.SshNet/Messages/Connection/ChannelRequest/ExitSignalRequestInfo.cs +++ b/src/Renci.SshNet/Messages/Connection/ChannelRequest/ExitSignalRequestInfo.cs @@ -3,7 +3,7 @@ /// /// Represents "exit-signal" type channel request information /// - internal class ExitSignalRequestInfo : RequestInfo + internal sealed class ExitSignalRequestInfo : RequestInfo { private byte[] _signalName; private byte[] _errorMessage; diff --git a/src/Renci.SshNet/Messages/Connection/ChannelRequest/ExitStatusRequestInfo.cs b/src/Renci.SshNet/Messages/Connection/ChannelRequest/ExitStatusRequestInfo.cs index 96cc1d06..9aa92f96 100644 --- a/src/Renci.SshNet/Messages/Connection/ChannelRequest/ExitStatusRequestInfo.cs +++ b/src/Renci.SshNet/Messages/Connection/ChannelRequest/ExitStatusRequestInfo.cs @@ -3,7 +3,7 @@ /// /// Represents "exit-status" type channel request information /// - internal class ExitStatusRequestInfo : RequestInfo + internal sealed class ExitStatusRequestInfo : RequestInfo { /// /// Channel request name. diff --git a/src/Renci.SshNet/Messages/Connection/ChannelRequest/PseudoTerminalInfo.cs b/src/Renci.SshNet/Messages/Connection/ChannelRequest/PseudoTerminalInfo.cs index 6fdc681c..fbe5a11d 100644 --- a/src/Renci.SshNet/Messages/Connection/ChannelRequest/PseudoTerminalInfo.cs +++ b/src/Renci.SshNet/Messages/Connection/ChannelRequest/PseudoTerminalInfo.cs @@ -1,15 +1,16 @@ -using Renci.SshNet.Common; -using System.Collections.Generic; +using System.Collections.Generic; + +using Renci.SshNet.Common; namespace Renci.SshNet.Messages.Connection { /// - /// Represents "pty-req" type channel request information + /// Represents "pty-req" type channel request information. /// - internal class PseudoTerminalRequestInfo : RequestInfo + internal sealed class PseudoTerminalRequestInfo : RequestInfo { /// - /// Channel request name + /// Channel request name. /// public const string Name = "pty-req"; @@ -98,7 +99,7 @@ namespace Renci.SshNet.Messages.Connection /// The TERM environment variable which a identifier for the text window’s capabilities. /// The terminal width in columns. /// The terminal width in rows. - /// The terminal height in pixels. + /// The terminal width in pixels. /// The terminal height in pixels. /// The terminal mode values. /// @@ -153,7 +154,7 @@ namespace Renci.SshNet.Messages.Connection else { // when there are no terminal mode, the length of the string is zero - Write((uint) 0); + Write(0u); } } } diff --git a/src/Renci.SshNet/Messages/Connection/ChannelRequest/ShellRequestInfo.cs b/src/Renci.SshNet/Messages/Connection/ChannelRequest/ShellRequestInfo.cs index 4b570d70..42fc9cf1 100644 --- a/src/Renci.SshNet/Messages/Connection/ChannelRequest/ShellRequestInfo.cs +++ b/src/Renci.SshNet/Messages/Connection/ChannelRequest/ShellRequestInfo.cs @@ -3,7 +3,7 @@ /// /// Represents "shell" type channel request information /// - internal class ShellRequestInfo : RequestInfo + internal sealed class ShellRequestInfo : RequestInfo { /// /// Channel request name diff --git a/src/Renci.SshNet/Messages/Connection/ChannelRequest/SignalRequestInfo.cs b/src/Renci.SshNet/Messages/Connection/ChannelRequest/SignalRequestInfo.cs index 493681b4..bb69e5ed 100644 --- a/src/Renci.SshNet/Messages/Connection/ChannelRequest/SignalRequestInfo.cs +++ b/src/Renci.SshNet/Messages/Connection/ChannelRequest/SignalRequestInfo.cs @@ -3,7 +3,7 @@ /// /// Represents "signal" type channel request information /// - internal class SignalRequestInfo : RequestInfo + internal sealed class SignalRequestInfo : RequestInfo { private byte[] _signalName; diff --git a/src/Renci.SshNet/Messages/Connection/ChannelRequest/SubsystemRequestInfo.cs b/src/Renci.SshNet/Messages/Connection/ChannelRequest/SubsystemRequestInfo.cs index 1af72be8..6871b9fe 100644 --- a/src/Renci.SshNet/Messages/Connection/ChannelRequest/SubsystemRequestInfo.cs +++ b/src/Renci.SshNet/Messages/Connection/ChannelRequest/SubsystemRequestInfo.cs @@ -3,7 +3,7 @@ /// /// Represents "subsystem" type channel request information /// - internal class SubsystemRequestInfo : RequestInfo + internal sealed class SubsystemRequestInfo : RequestInfo { private byte[] _subsystemName; diff --git a/src/Renci.SshNet/Messages/Connection/ChannelRequest/WindowChangeRequestInfo.cs b/src/Renci.SshNet/Messages/Connection/ChannelRequest/WindowChangeRequestInfo.cs index 4f0813bd..edcd9af5 100644 --- a/src/Renci.SshNet/Messages/Connection/ChannelRequest/WindowChangeRequestInfo.cs +++ b/src/Renci.SshNet/Messages/Connection/ChannelRequest/WindowChangeRequestInfo.cs @@ -3,7 +3,7 @@ /// /// Represents "window-change" type channel request information /// - internal class WindowChangeRequestInfo : RequestInfo + internal sealed class WindowChangeRequestInfo : RequestInfo { /// /// Channe request name diff --git a/src/Renci.SshNet/Messages/Connection/ChannelRequest/X11ForwardingRequestInfo.cs b/src/Renci.SshNet/Messages/Connection/ChannelRequest/X11ForwardingRequestInfo.cs index 89ebfe4f..4d27872e 100644 --- a/src/Renci.SshNet/Messages/Connection/ChannelRequest/X11ForwardingRequestInfo.cs +++ b/src/Renci.SshNet/Messages/Connection/ChannelRequest/X11ForwardingRequestInfo.cs @@ -1,14 +1,14 @@ namespace Renci.SshNet.Messages.Connection { /// - /// Represents "x11-req" type channel request information + /// Represents "x11-req" type channel request information. /// - internal class X11ForwardingRequestInfo : RequestInfo + internal sealed class X11ForwardingRequestInfo : RequestInfo { private byte[] _authenticationProtocol; /// - /// Channel request name + /// Channel request name. /// public const string Name = "x11-req"; @@ -27,7 +27,7 @@ /// Gets or sets a value indicating whether it is a single connection. /// /// - /// true if it is a single connection; otherwise, false. + /// true if it is a single connection; otherwise, false. /// public bool IsSingleConnection { get; set; } diff --git a/src/Renci.SshNet/Messages/Connection/ChannelRequest/XonXoffRequestInfo.cs b/src/Renci.SshNet/Messages/Connection/ChannelRequest/XonXoffRequestInfo.cs index ff10a73a..b53578de 100644 --- a/src/Renci.SshNet/Messages/Connection/ChannelRequest/XonXoffRequestInfo.cs +++ b/src/Renci.SshNet/Messages/Connection/ChannelRequest/XonXoffRequestInfo.cs @@ -3,10 +3,10 @@ /// /// Represents "xon-xoff" type channel request information /// - internal class XonXoffRequestInfo : RequestInfo + internal sealed class XonXoffRequestInfo : RequestInfo { /// - /// Channel request type + /// Channel request type. /// public const string Name = "xon-xoff"; diff --git a/src/Renci.SshNet/Messages/Connection/ChannelWindowAdjustMessage.cs b/src/Renci.SshNet/Messages/Connection/ChannelWindowAdjustMessage.cs index 364b8130..315c98c8 100644 --- a/src/Renci.SshNet/Messages/Connection/ChannelWindowAdjustMessage.cs +++ b/src/Renci.SshNet/Messages/Connection/ChannelWindowAdjustMessage.cs @@ -32,7 +32,6 @@ /// public ChannelWindowAdjustMessage() { - } /// @@ -52,6 +51,7 @@ protected override void LoadData() { base.LoadData(); + BytesToAdd = ReadUInt32(); } @@ -61,6 +61,7 @@ protected override void SaveData() { base.SaveData(); + Write(BytesToAdd); } diff --git a/src/Renci.SshNet/Messages/Connection/RequestFailureMessage.cs b/src/Renci.SshNet/Messages/Connection/RequestFailureMessage.cs index af9df750..553f9547 100644 --- a/src/Renci.SshNet/Messages/Connection/RequestFailureMessage.cs +++ b/src/Renci.SshNet/Messages/Connection/RequestFailureMessage.cs @@ -1,5 +1,4 @@ - -namespace Renci.SshNet.Messages.Connection +namespace Renci.SshNet.Messages.Connection { /// /// Represents SSH_MSG_REQUEST_FAILURE message. diff --git a/src/Renci.SshNet/Messages/Connection/RequestSuccessMessage.cs b/src/Renci.SshNet/Messages/Connection/RequestSuccessMessage.cs index b456efe8..00ad1059 100644 --- a/src/Renci.SshNet/Messages/Connection/RequestSuccessMessage.cs +++ b/src/Renci.SshNet/Messages/Connection/RequestSuccessMessage.cs @@ -22,8 +22,12 @@ get { var capacity = base.BufferCapacity; + if (BoundPort.HasValue) + { capacity += 4; // BoundPort + } + return capacity; } } @@ -33,7 +37,6 @@ /// public RequestSuccessMessage() { - } /// @@ -51,7 +54,9 @@ protected override void LoadData() { if (!IsEndOfData) + { BoundPort = ReadUInt32(); + } } /// @@ -60,7 +65,9 @@ protected override void SaveData() { if (BoundPort.HasValue) + { Write(BoundPort.Value); + } } internal override void Process(Session session) diff --git a/src/Renci.SshNet/Messages/Connection/TcpIpForwardGlobalRequestMessage.cs b/src/Renci.SshNet/Messages/Connection/TcpIpForwardGlobalRequestMessage.cs index 83d0b013..edddcb56 100644 --- a/src/Renci.SshNet/Messages/Connection/TcpIpForwardGlobalRequestMessage.cs +++ b/src/Renci.SshNet/Messages/Connection/TcpIpForwardGlobalRequestMessage.cs @@ -2,12 +2,12 @@ namespace Renci.SshNet.Messages.Connection { - internal class TcpIpForwardGlobalRequestMessage : GlobalRequestMessage + internal sealed class TcpIpForwardGlobalRequestMessage : GlobalRequestMessage { private byte[] _addressToBind; public TcpIpForwardGlobalRequestMessage(string addressToBind, uint portToBind) - : base(Ascii.GetBytes("tcpip-forward"), true) + : base(Ascii.GetBytes("tcpip-forward"), wantReply: true) { AddressToBind = addressToBind; PortToBind = portToBind; diff --git a/src/Renci.SshNet/Messages/Message.cs b/src/Renci.SshNet/Messages/Message.cs index 2bbfb7b9..d2fc3274 100644 --- a/src/Renci.SshNet/Messages/Message.cs +++ b/src/Renci.SshNet/Messages/Message.cs @@ -1,7 +1,8 @@ -using System.IO; -using Renci.SshNet.Common; -using System.Globalization; +using System.Globalization; +using System.IO; + using Renci.SshNet.Abstractions; +using Renci.SshNet.Common; using Renci.SshNet.Compression; namespace Renci.SshNet.Messages @@ -30,7 +31,7 @@ namespace Renci.SshNet.Messages /// protected override void WriteBytes(SshDataStream stream) { - var enumerator = GetType().GetCustomAttributes(true).GetEnumerator(); + var enumerator = GetType().GetCustomAttributes(inherit: true).GetEnumerator(); try { if (!enumerator.MoveNext()) @@ -64,7 +65,7 @@ namespace Renci.SshNet.Messages // * 4 bytes for the outbound packet sequence // * 4 bytes for the packet data length // * one byte for the packet padding length - sshDataStream.Seek(outboundPacketSequenceSize + 4 + 1, SeekOrigin.Begin); + _ = sshDataStream.Seek(outboundPacketSequenceSize + 4 + 1, SeekOrigin.Begin); if (compressor != null) { @@ -99,12 +100,12 @@ namespace Renci.SshNet.Messages var packetDataLength = GetPacketDataLength(messageLength, paddingLength); // skip bytes for outbound packet sequence - sshDataStream.Seek(outboundPacketSequenceSize, SeekOrigin.Begin); + _ = sshDataStream.Seek(outboundPacketSequenceSize, SeekOrigin.Begin); // add packet data length sshDataStream.Write(packetDataLength); - // add packet padding length + // add packet padding length sshDataStream.WriteByte(paddingLength); } else @@ -120,12 +121,12 @@ namespace Renci.SshNet.Messages sshDataStream = new SshDataStream(packetLength + paddingLength + outboundPacketSequenceSize); // skip bytes for outbound packet sequenceSize - sshDataStream.Seek(outboundPacketSequenceSize, SeekOrigin.Begin); + _ = sshDataStream.Seek(outboundPacketSequenceSize, SeekOrigin.Begin); // add packet data length sshDataStream.Write(packetDataLength); - // add packet padding length + // add packet padding length sshDataStream.WriteByte(paddingLength); // add message payload @@ -148,10 +149,12 @@ namespace Renci.SshNet.Messages private static byte GetPaddingLength(byte paddingMultiplier, long packetLength) { var paddingLength = (byte)((-packetLength) & (paddingMultiplier - 1)); + if (paddingLength < paddingMultiplier) { paddingLength += paddingMultiplier; } + return paddingLength; } @@ -163,8 +166,7 @@ namespace Renci.SshNet.Messages /// public override string ToString() { - var enumerator = GetType().GetCustomAttributes(true).GetEnumerator(); - try + using (var enumerator = GetType().GetCustomAttributes(inherit: true).GetEnumerator()) { if (!enumerator.MoveNext()) { @@ -173,10 +175,6 @@ namespace Renci.SshNet.Messages return enumerator.Current.Name; } - finally - { - enumerator.Dispose(); - } } /// @@ -185,4 +183,4 @@ namespace Renci.SshNet.Messages /// The for which to process the current message. internal abstract void Process(Session session); } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet/Messages/Transport/IgnoreMessage.cs b/src/Renci.SshNet/Messages/Transport/IgnoreMessage.cs index 580cd3b2..5fc009a3 100644 --- a/src/Renci.SshNet/Messages/Transport/IgnoreMessage.cs +++ b/src/Renci.SshNet/Messages/Transport/IgnoreMessage.cs @@ -1,7 +1,6 @@ using System; using System.Globalization; using Renci.SshNet.Abstractions; -using Renci.SshNet.Common; namespace Renci.SshNet.Messages.Transport { @@ -23,7 +22,7 @@ namespace Renci.SshNet.Messages.Transport /// public IgnoreMessage() { - Data = Array.Empty; + Data = Array.Empty(); } /// @@ -49,8 +48,10 @@ namespace Renci.SshNet.Messages.Transport /// The data. public IgnoreMessage(byte[] data) { - if (data == null) - throw new ArgumentNullException("data"); + if (data is null) + { + throw new ArgumentNullException(nameof(data)); + } Data = data; } @@ -69,7 +70,7 @@ namespace Renci.SshNet.Messages.Transport if (dataLength > (DataStream.Length - DataStream.Position)) { DiagnosticAbstraction.Log("SSH_MSG_IGNORE: Length exceeds data bytes, data ignored."); - Data = Array.Empty; + Data = Array.Empty(); } else { diff --git a/src/Renci.SshNet/Messages/Transport/KeyExchangeDhGroupExchangeInit.cs b/src/Renci.SshNet/Messages/Transport/KeyExchangeDhGroupExchangeInit.cs index c8af4724..381c534b 100644 --- a/src/Renci.SshNet/Messages/Transport/KeyExchangeDhGroupExchangeInit.cs +++ b/src/Renci.SshNet/Messages/Transport/KeyExchangeDhGroupExchangeInit.cs @@ -4,7 +4,7 @@ /// Represents SSH_MSG_KEX_DH_GEX_INIT message. /// [Message("SSH_MSG_KEX_DH_GEX_INIT", 32)] - internal class KeyExchangeDhGroupExchangeInit : Message, IKeyExchangedAllowed + internal sealed class KeyExchangeDhGroupExchangeInit : Message, IKeyExchangedAllowed { /// /// Gets the E value. diff --git a/src/Renci.SshNet/Messages/Transport/KeyExchangeDhGroupExchangeReply.cs b/src/Renci.SshNet/Messages/Transport/KeyExchangeDhGroupExchangeReply.cs index 88bad74e..7b446d96 100644 --- a/src/Renci.SshNet/Messages/Transport/KeyExchangeDhGroupExchangeReply.cs +++ b/src/Renci.SshNet/Messages/Transport/KeyExchangeDhGroupExchangeReply.cs @@ -1,19 +1,19 @@ -using Renci.SshNet.Common; - -namespace Renci.SshNet.Messages.Transport +namespace Renci.SshNet.Messages.Transport { /// /// Represents SSH_MSG_KEX_DH_GEX_REPLY message. /// [Message("SSH_MSG_KEX_DH_GEX_REPLY", MessageNumber)] - internal class KeyExchangeDhGroupExchangeReply : Message + internal sealed class KeyExchangeDhGroupExchangeReply : Message { internal const byte MessageNumber = 33; /// - /// Gets server public host key and certificates + /// Gets server public host key and certificates. /// - /// The host key. + /// + /// The host key. + /// public byte[] HostKey { get; private set; } /// diff --git a/src/Renci.SshNet/Messages/Transport/KeyExchangeDhGroupExchangeRequest.cs b/src/Renci.SshNet/Messages/Transport/KeyExchangeDhGroupExchangeRequest.cs index d3133591..9bb6c6bc 100644 --- a/src/Renci.SshNet/Messages/Transport/KeyExchangeDhGroupExchangeRequest.cs +++ b/src/Renci.SshNet/Messages/Transport/KeyExchangeDhGroupExchangeRequest.cs @@ -4,12 +4,12 @@ /// Represents SSH_MSG_KEX_DH_GEX_REQUEST message. /// [Message("SSH_MSG_KEX_DH_GEX_REQUEST", MessageNumber)] - internal class KeyExchangeDhGroupExchangeRequest : Message, IKeyExchangedAllowed + internal sealed class KeyExchangeDhGroupExchangeRequest : Message, IKeyExchangedAllowed { internal const byte MessageNumber = 34; /// - /// Gets or sets the minimal size in bits of an acceptable group. + /// Gets the minimum size, in bits, of an acceptable group. /// /// /// The minimum. @@ -17,7 +17,7 @@ public uint Minimum { get; private set; } /// - /// Gets or sets the preferred size in bits of the group the server will send. + /// Gets the preferred size, in bits, of the group the server will send. /// /// /// The preferred. @@ -25,7 +25,7 @@ public uint Preferred { get; private set; } /// - /// Gets or sets the maximal size in bits of an acceptable group. + /// Gets the maximum size, in bits, of an acceptable group. /// /// /// The maximum. diff --git a/src/Renci.SshNet/Messages/Transport/KeyExchangeDhInitMessage.cs b/src/Renci.SshNet/Messages/Transport/KeyExchangeDhInitMessage.cs index 9d1d7685..5b681d1b 100644 --- a/src/Renci.SshNet/Messages/Transport/KeyExchangeDhInitMessage.cs +++ b/src/Renci.SshNet/Messages/Transport/KeyExchangeDhInitMessage.cs @@ -4,7 +4,7 @@ /// Represents SSH_MSG_KEXDH_INIT message. /// [Message("SSH_MSG_KEXDH_INIT", 30)] - internal class KeyExchangeDhInitMessage : Message, IKeyExchangedAllowed + internal sealed class KeyExchangeDhInitMessage : Message, IKeyExchangedAllowed { /// /// Gets the E value. diff --git a/src/Renci.SshNet/Messages/Transport/KeyExchangeEcdhInitMessage.cs b/src/Renci.SshNet/Messages/Transport/KeyExchangeEcdhInitMessage.cs index ddcf03f1..4bd508f5 100644 --- a/src/Renci.SshNet/Messages/Transport/KeyExchangeEcdhInitMessage.cs +++ b/src/Renci.SshNet/Messages/Transport/KeyExchangeEcdhInitMessage.cs @@ -7,7 +7,7 @@ namespace Renci.SshNet.Messages.Transport /// Represents SSH_MSG_KEXECDH_INIT message. /// [Message("SSH_MSG_KEX_ECDH_INIT", 30)] - internal class KeyExchangeEcdhInitMessage : Message, IKeyExchangedAllowed + internal sealed class KeyExchangeEcdhInitMessage : Message, IKeyExchangedAllowed { /// /// Gets the client's ephemeral contribution to the ECDH exchange, encoded as an octet string @@ -75,4 +75,4 @@ namespace Renci.SshNet.Messages.Transport throw new NotImplementedException(); } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet/NetConfClient.cs b/src/Renci.SshNet/NetConfClient.cs index b1877c60..bbedca3c 100644 --- a/src/Renci.SshNet/NetConfClient.cs +++ b/src/Renci.SshNet/NetConfClient.cs @@ -1,13 +1,13 @@ using System; -using Renci.SshNet.Common; -using Renci.SshNet.NetConf; -using System.Xml; using System.Diagnostics.CodeAnalysis; using System.Net; +using System.Xml; + +using Renci.SshNet.Common; +using Renci.SshNet.NetConf; namespace Renci.SshNet { - // TODO: Please help with documentation here, as I don't know the details, specially for the methods not documented. /// /// Contains operation for working with NetConf server. /// @@ -16,7 +16,7 @@ namespace Renci.SshNet private int _operationTimeout; /// - /// Holds instance that used to communicate to the server + /// Holds instance that used to communicate to the server. /// private INetConfSession _netConfSession; @@ -27,14 +27,20 @@ namespace Renci.SshNet /// The timeout to wait until an operation completes. The default value is negative /// one (-1) milliseconds, which indicates an infinite time-out period. /// - /// represents a value that is less than -1 or greater than milliseconds. - public TimeSpan OperationTimeout { - get { return TimeSpan.FromMilliseconds(_operationTimeout); } + /// represents a value that is less than -1 or greater than milliseconds. + public TimeSpan OperationTimeout + { + get + { + return TimeSpan.FromMilliseconds(_operationTimeout); + } set { var timeoutInMilliseconds = value.TotalMilliseconds; - if (timeoutInMilliseconds < -1d || timeoutInMilliseconds > int.MaxValue) - throw new ArgumentOutOfRangeException("value", "The timeout must represent a value between -1 and Int32.MaxValue, inclusive."); + if (timeoutInMilliseconds is < -1d or > int.MaxValue) + { + throw new ArgumentOutOfRangeException(nameof(value), "The timeout must represent a value between -1 and Int32.MaxValue, inclusive."); + } _operationTimeout = (int) timeoutInMilliseconds; } @@ -51,15 +57,13 @@ namespace Renci.SshNet get { return _netConfSession; } } - #region Constructors - /// /// Initializes a new instance of the class. /// /// The connection info. /// is null. public NetConfClient(ConnectionInfo connectionInfo) - : this(connectionInfo, false) + : this(connectionInfo, ownsConnectionInfo: false) { } @@ -75,7 +79,7 @@ namespace Renci.SshNet /// is not within and . [SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope", Justification = "Disposed in Dispose(bool) method.")] public NetConfClient(string host, int port, string username, string password) - : this(new PasswordConnectionInfo(host, port, username, password), true) + : this(new PasswordConnectionInfo(host, port, username, password), ownsConnectionInfo: true) { } @@ -103,8 +107,8 @@ namespace Renci.SshNet /// is invalid, -or- is null or contains only whitespace characters. /// is not within and . [SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope", Justification = "Disposed in Dispose(bool) method.")] - public NetConfClient(string host, int port, string username, params PrivateKeyFile[] keyFiles) - : this(new PrivateKeyConnectionInfo(host, port, username, keyFiles), true) + public NetConfClient(string host, int port, string username, params IPrivateKeySource[] keyFiles) + : this(new PrivateKeyConnectionInfo(host, port, username, keyFiles), ownsConnectionInfo: true) { } @@ -116,7 +120,7 @@ namespace Renci.SshNet /// Authentication private key file(s) . /// is null. /// is invalid, -or- is null or contains only whitespace characters. - public NetConfClient(string host, string username, params PrivateKeyFile[] keyFiles) + public NetConfClient(string host, string username, params IPrivateKeySource[] keyFiles) : this(host, ConnectionInfo.DefaultPort, username, keyFiles) { } @@ -155,15 +159,13 @@ namespace Renci.SshNet AutomaticMessageIdHandling = true; } - #endregion - /// /// Gets the NetConf server capabilities. /// /// /// The NetConf server capabilities. /// - public XmlDocument ServerCapabilities + public XmlDocument ServerCapabilities { get { return _netConfSession.ServerCapabilities; } } @@ -193,7 +195,9 @@ namespace Renci.SshNet /// Sends the receive RPC. /// /// The RPC. - /// Reply message to RPC request + /// + /// Reply message to RPC request. + /// /// Client is not connected. public XmlDocument SendReceiveRpc(XmlDocument rpc) { @@ -204,7 +208,9 @@ namespace Renci.SshNet /// Sends the receive RPC. /// /// The XML. - /// Reply message to RPC request + /// + /// Reply message to RPC request. + /// public XmlDocument SendReceiveRpc(string xml) { var rpc = new XmlDocument(); @@ -215,7 +221,9 @@ namespace Renci.SshNet /// /// Sends the close RPC. /// - /// Reply message to closing RPC request + /// + /// Reply message to closing RPC request. + /// /// Client is not connected. public XmlDocument SendCloseRpc() { @@ -245,7 +253,7 @@ namespace Renci.SshNet } /// - /// Releases unmanaged and - optionally - managed resources + /// Releases unmanaged and - optionally - managed resources. /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected override void Dispose(bool disposing) diff --git a/src/Renci.SshNet/Netconf/NetConfSession.cs b/src/Renci.SshNet/Netconf/NetConfSession.cs index 14ba00a6..f1252f53 100644 --- a/src/Renci.SshNet/Netconf/NetConfSession.cs +++ b/src/Renci.SshNet/Netconf/NetConfSession.cs @@ -1,21 +1,22 @@ using System; using System.Globalization; using System.Text; -using System.Threading; -using Renci.SshNet.Common; -using System.Xml; using System.Text.RegularExpressions; +using System.Threading; +using System.Xml; + +using Renci.SshNet.Common; namespace Renci.SshNet.NetConf { - internal class NetConfSession : SubsystemSession, INetConfSession + internal sealed class NetConfSession : SubsystemSession, INetConfSession { private const string Prompt = "]]>]]>"; private readonly StringBuilder _data = new StringBuilder(); private bool _usingFramingProtocol; - private EventWaitHandle _serverCapabilitiesConfirmed = new AutoResetEvent(false); - private EventWaitHandle _rpcReplyReceived = new AutoResetEvent(false); + private EventWaitHandle _serverCapabilitiesConfirmed = new AutoResetEvent(initialState: false); + private EventWaitHandle _rpcReplyReceived = new AutoResetEvent(initialState: false); private StringBuilder _rpcReply = new StringBuilder(); private int _messageId; @@ -46,12 +47,11 @@ namespace Renci.SshNet.NetConf "" + "" + ""); - } public XmlDocument SendReceiveRpc(XmlDocument rpc, bool automaticMessageIdHandling) { - _data.Clear(); + _ = _data.Clear(); XmlNamespaceManager nsMgr = null; if (automaticMessageIdHandling) @@ -61,15 +61,16 @@ namespace Renci.SshNet.NetConf nsMgr.AddNamespace("nc", "urn:ietf:params:xml:ns:netconf:base:1.0"); rpc.SelectSingleNode("/nc:rpc/@message-id", nsMgr).Value = _messageId.ToString(CultureInfo.InvariantCulture); } + _rpcReply = new StringBuilder(); - _rpcReplyReceived.Reset(); + _ = _rpcReplyReceived.Reset(); var reply = new XmlDocument(); if (_usingFramingProtocol) { var command = new StringBuilder(rpc.InnerXml.Length + 10); - command.AppendFormat("\n#{0}\n", rpc.InnerXml.Length); - command.Append(rpc.InnerXml); - command.Append("\n##\n"); + _ = command.AppendFormat(CultureInfo.InvariantCulture, "\n#{0}\n", rpc.InnerXml.Length); + _ = command.Append(rpc.InnerXml); + _ = command.Append("\n##\n"); SendData(Encoding.UTF8.GetBytes(command.ToString())); WaitOnHandle(_rpcReplyReceived, OperationTimeout); @@ -81,6 +82,7 @@ namespace Renci.SshNet.NetConf WaitOnHandle(_rpcReplyReceived, OperationTimeout); reply.LoadXml(_rpcReply.ToString()); } + if (automaticMessageIdHandling) { var replyId = rpc.SelectSingleNode("/nc:rpc/@message-id", nsMgr).Value; @@ -89,14 +91,15 @@ namespace Renci.SshNet.NetConf throw new NetConfServerException("The rpc message id does not match the rpc-reply message id."); } } + return reply; } protected override void OnChannelOpen() { - _data.Clear(); + _ = _data.Clear(); - var message = string.Format("{0}{1}", ClientCapabilities.InnerXml, Prompt); + var message = string.Concat(ClientCapabilities.InnerXml, Prompt); SendData(Encoding.UTF8.GetBytes(message)); @@ -107,21 +110,22 @@ namespace Renci.SshNet.NetConf { var chunk = Encoding.UTF8.GetString(data); - if (ServerCapabilities == null) // This must be server capabilities, old protocol + if (ServerCapabilities is null) { - _data.Append(chunk); + _ = _data.Append(chunk); if (!chunk.Contains(Prompt)) { return; } + try { - chunk = _data.ToString(); - _data.Clear(); + chunk = _data.ToString(); + _ = _data.Clear(); ServerCapabilities = new XmlDocument(); - ServerCapabilities.LoadXml(chunk.Replace(Prompt, "")); + ServerCapabilities.LoadXml(chunk.Replace(Prompt, string.Empty)); } catch (XmlException e) { @@ -131,9 +135,9 @@ namespace Renci.SshNet.NetConf var nsMgr = new XmlNamespaceManager(ServerCapabilities.NameTable); nsMgr.AddNamespace("nc", "urn:ietf:params:xml:ns:netconf:base:1.0"); - _usingFramingProtocol = (ServerCapabilities.SelectSingleNode("/nc:hello/nc:capabilities/nc:capability[text()='urn:ietf:params:netconf:base:1.1']", nsMgr) != null); + _usingFramingProtocol = ServerCapabilities.SelectSingleNode("/nc:hello/nc:capabilities/nc:capability[text()='urn:ietf:params:netconf:base:1.1']", nsMgr) != null; - _serverCapabilitiesConfirmed.Set(); + _ = _serverCapabilitiesConfirmed.Set(); } else if (_usingFramingProtocol) { @@ -146,30 +150,31 @@ namespace Renci.SshNet.NetConf { break; } - var fractionLength = Convert.ToInt32(match.Groups["length"].Value); - _rpcReply.Append(chunk, position + match.Index + match.Length, fractionLength); + + var fractionLength = Convert.ToInt32(match.Groups["length"].Value, CultureInfo.InvariantCulture); + _ = _rpcReply.Append(chunk, position + match.Index + match.Length, fractionLength); position += match.Index + match.Length + fractionLength; } + if (Regex.IsMatch(chunk.Substring(position), @"\n##\n")) { - _rpcReplyReceived.Set(); + _ = _rpcReplyReceived.Set(); } } - else // Old protocol + else { - _data.Append(chunk); + _ = _data.Append(chunk); if (!chunk.Contains(Prompt)) { return; - //throw new NetConfServerException("Server XML message does not end with the prompt " + _prompt); } - - chunk = _data.ToString(); - _data.Clear(); - _rpcReply.Append(chunk.Replace(Prompt, "")); - _rpcReplyReceived.Set(); + chunk = _data.ToString(); + _ = _data.Clear(); + + _ = _rpcReply.Append(chunk.Replace(Prompt, string.Empty)); + _ = _rpcReplyReceived.Set(); } } diff --git a/src/Renci.SshNet/NoneAuthenticationMethod.cs b/src/Renci.SshNet/NoneAuthenticationMethod.cs index a5d842fd..99d02524 100644 --- a/src/Renci.SshNet/NoneAuthenticationMethod.cs +++ b/src/Renci.SshNet/NoneAuthenticationMethod.cs @@ -1,23 +1,22 @@ using System; using System.Threading; -#if !FEATURE_WAITHANDLE_DISPOSE -using Renci.SshNet.Common; -#endif // !FEATURE_WAITHANDLE_DISPOSE -using Renci.SshNet.Messages.Authentication; + using Renci.SshNet.Messages; +using Renci.SshNet.Messages.Authentication; namespace Renci.SshNet { /// - /// Provides functionality for "none" authentication method + /// Provides functionality for "none" authentication method. /// public class NoneAuthenticationMethod : AuthenticationMethod, IDisposable { private AuthenticationResult _authenticationResult = AuthenticationResult.Failure; - private EventWaitHandle _authenticationCompleted = new AutoResetEvent(false); + private EventWaitHandle _authenticationCompleted = new AutoResetEvent(initialState: false); + private bool _isDisposed; /// - /// Gets connection name + /// Gets the name of the authentication method. /// public override string Name { @@ -44,8 +43,10 @@ namespace Renci.SshNet /// is null. public override AuthenticationResult Authenticate(Session session) { - if (session == null) - throw new ArgumentNullException("session"); + if (session is null) + { + throw new ArgumentNullException(nameof(session)); + } session.UserAuthenticationSuccessReceived += Session_UserAuthenticationSuccessReceived; session.UserAuthenticationFailureReceived += Session_UserAuthenticationFailureReceived; @@ -68,43 +69,45 @@ namespace Renci.SshNet { _authenticationResult = AuthenticationResult.Success; - _authenticationCompleted.Set(); + _ = _authenticationCompleted.Set(); } private void Session_UserAuthenticationFailureReceived(object sender, MessageEventArgs e) { if (e.Message.PartialSuccess) + { _authenticationResult = AuthenticationResult.PartialSuccess; + } else + { _authenticationResult = AuthenticationResult.Failure; + } // Copy allowed authentication methods AllowedAuthentications = e.Message.AllowedAuthentications; - _authenticationCompleted.Set(); + _ = _authenticationCompleted.Set(); } - - #region IDisposable Members - - private bool _isDisposed; /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// public void Dispose() { - Dispose(true); + Dispose(disposing: true); GC.SuppressFinalize(this); } /// - /// Releases unmanaged and - optionally - managed resources + /// Releases unmanaged and - optionally - managed resources. /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected virtual void Dispose(bool disposing) { if (_isDisposed) + { return; + } if (disposing) { @@ -120,15 +123,11 @@ namespace Renci.SshNet } /// - /// Releases unmanaged resources and performs other cleanup operations before the - /// is reclaimed by garbage collection. + /// Finalizes an instance of the class. /// ~NoneAuthenticationMethod() { - Dispose(false); + Dispose(disposing: false); } - - #endregion - } } diff --git a/src/Renci.SshNet/PasswordAuthenticationMethod.cs b/src/Renci.SshNet/PasswordAuthenticationMethod.cs index 48e46bef..bebfd3c4 100644 --- a/src/Renci.SshNet/PasswordAuthenticationMethod.cs +++ b/src/Renci.SshNet/PasswordAuthenticationMethod.cs @@ -1,10 +1,12 @@ using System; +using System.Runtime.ExceptionServices; using System.Text; using System.Threading; + using Renci.SshNet.Abstractions; using Renci.SshNet.Common; -using Renci.SshNet.Messages.Authentication; using Renci.SshNet.Messages; +using Renci.SshNet.Messages.Authentication; namespace Renci.SshNet { @@ -13,15 +15,16 @@ namespace Renci.SshNet /// public class PasswordAuthenticationMethod : AuthenticationMethod, IDisposable { - private AuthenticationResult _authenticationResult = AuthenticationResult.Failure; - private Session _session; - private EventWaitHandle _authenticationCompleted = new AutoResetEvent(false); - private Exception _exception; private readonly RequestMessage _requestMessage; private readonly byte[] _password; + private AuthenticationResult _authenticationResult = AuthenticationResult.Failure; + private Session _session; + private EventWaitHandle _authenticationCompleted = new AutoResetEvent(initialState: false); + private Exception _exception; + private bool _isDisposed; /// - /// Gets authentication method name + /// Gets the name of the authentication method. /// public override string Name { @@ -66,8 +69,10 @@ namespace Renci.SshNet public PasswordAuthenticationMethod(string username, byte[] password) : base(username) { - if (password == null) - throw new ArgumentNullException("password"); + if (password is null) + { + throw new ArgumentNullException(nameof(password)); + } _password = password; _requestMessage = new RequestMessagePassword(ServiceName.Connection, Username, _password); @@ -83,8 +88,10 @@ namespace Renci.SshNet /// is null. public override AuthenticationResult Authenticate(Session session) { - if (session == null) - throw new ArgumentNullException("session"); + if (session is null) + { + throw new ArgumentNullException(nameof(session)); + } _session = session; @@ -107,7 +114,9 @@ namespace Renci.SshNet } if (_exception != null) - throw _exception; + { + ExceptionDispatchInfo.Capture(_exception).Throw(); + } return _authenticationResult; } @@ -116,20 +125,24 @@ namespace Renci.SshNet { _authenticationResult = AuthenticationResult.Success; - _authenticationCompleted.Set(); + _ = _authenticationCompleted.Set(); } private void Session_UserAuthenticationFailureReceived(object sender, MessageEventArgs e) { if (e.Message.PartialSuccess) + { _authenticationResult = AuthenticationResult.PartialSuccess; + } else + { _authenticationResult = AuthenticationResult.Failure; + } - // Copy allowed authentication methods + // Copy allowed authentication methods AllowedAuthentications = e.Message.AllowedAuthentications; - _authenticationCompleted.Set(); + _ = _authenticationCompleted.Set(); } private void Session_UserAuthenticationPasswordChangeRequiredReceived(object sender, MessageEventArgs e) @@ -142,45 +155,40 @@ namespace Renci.SshNet { var eventArgs = new AuthenticationPasswordChangeEventArgs(Username); - // Raise an event to allow user to supply a new password - if (PasswordExpired != null) - { - PasswordExpired(this, eventArgs); - } + // Raise an event to allow user to supply a new password + PasswordExpired?.Invoke(this, eventArgs); - // Send new authentication request with new password + // Send new authentication request with new password _session.SendMessage(new RequestMessagePassword(ServiceName.Connection, Username, _password, eventArgs.NewPassword)); } catch (Exception exp) { _exception = exp; - _authenticationCompleted.Set(); + _ = _authenticationCompleted.Set(); } }); } - #region IDisposable Members - - private bool _isDisposed; - /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// public void Dispose() { - Dispose(true); + Dispose(disposing: true); GC.SuppressFinalize(this); } /// - /// Releases unmanaged and - optionally - managed resources + /// Releases unmanaged and - optionally - managed resources. /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected virtual void Dispose(bool disposing) { if (_isDisposed) + { return; - + } + if (disposing) { var authenticationCompleted = _authenticationCompleted; @@ -195,14 +203,11 @@ namespace Renci.SshNet } /// - /// Releases unmanaged resources and performs other cleanup operations before the - /// is reclaimed by garbage collection. + /// Finalizes an instance of the class. /// ~PasswordAuthenticationMethod() { - Dispose(false); + Dispose(disposing: false); } - - #endregion } } diff --git a/src/Renci.SshNet/PasswordConnectionInfo.cs b/src/Renci.SshNet/PasswordConnectionInfo.cs index d42ff509..28317411 100644 --- a/src/Renci.SshNet/PasswordConnectionInfo.cs +++ b/src/Renci.SshNet/PasswordConnectionInfo.cs @@ -15,6 +15,8 @@ namespace Renci.SshNet /// public class PasswordConnectionInfo : ConnectionInfo, IDisposable { + private bool _isDisposed; + /// /// Occurs when user's password has expired and needs to be changed. /// @@ -249,8 +251,7 @@ namespace Renci.SshNet { foreach (var authenticationMethod in AuthenticationMethods) { - var pwdAuthentication = authenticationMethod as PasswordAuthenticationMethod; - if (pwdAuthentication != null) + if (authenticationMethod is PasswordAuthenticationMethod pwdAuthentication) { pwdAuthentication.PasswordExpired += AuthenticationMethod_PasswordExpired; } @@ -259,33 +260,28 @@ namespace Renci.SshNet private void AuthenticationMethod_PasswordExpired(object sender, AuthenticationPasswordChangeEventArgs e) { - if (PasswordExpired != null) - { - PasswordExpired(sender, e); - } + PasswordExpired?.Invoke(sender, e); } - #region IDisposable Members - - private bool _isDisposed; - /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// public void Dispose() { - Dispose(true); + Dispose(disposing: true); GC.SuppressFinalize(this); } /// - /// Releases unmanaged and - optionally - managed resources + /// Releases unmanaged and - optionally - managed resources. /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected virtual void Dispose(bool disposing) { if (_isDisposed) + { return; + } if (disposing) { @@ -293,8 +289,7 @@ namespace Renci.SshNet { foreach (var authenticationMethod in AuthenticationMethods) { - var disposable = authenticationMethod as IDisposable; - if (disposable != null) + if (authenticationMethod is IDisposable disposable) { disposable.Dispose(); } @@ -306,17 +301,11 @@ namespace Renci.SshNet } /// - /// Releases unmanaged resources and performs other cleanup operations before the - /// is reclaimed by garbage collection. + /// Finalizes an instance of the class. /// ~PasswordConnectionInfo() { - // Do not re-create Dispose clean-up code here. - // Calling Dispose(false) is optimal in terms of - // readability and maintainability. - Dispose(false); + Dispose(disposing: false); } - - #endregion } } diff --git a/src/Renci.SshNet/PrivateKeyAuthenticationMethod.cs b/src/Renci.SshNet/PrivateKeyAuthenticationMethod.cs index 93ebbe19..224f8d29 100644 --- a/src/Renci.SshNet/PrivateKeyAuthenticationMethod.cs +++ b/src/Renci.SshNet/PrivateKeyAuthenticationMethod.cs @@ -1,11 +1,13 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; -using Renci.SshNet.Messages.Authentication; -using Renci.SshNet.Messages; -using Renci.SshNet.Common; +using System.Linq; using System.Threading; +using Renci.SshNet.Common; +using Renci.SshNet.Messages; +using Renci.SshNet.Messages.Authentication; + namespace Renci.SshNet { /// @@ -14,11 +16,12 @@ namespace Renci.SshNet public class PrivateKeyAuthenticationMethod : AuthenticationMethod, IDisposable { private AuthenticationResult _authenticationResult = AuthenticationResult.Failure; - private EventWaitHandle _authenticationCompleted = new ManualResetEvent(false); + private EventWaitHandle _authenticationCompleted = new ManualResetEvent(initialState: false); private bool _isSignatureRequired; + private bool _isDisposed; /// - /// Gets authentication method name + /// Gets the name of the authentication method. /// public override string Name { @@ -28,7 +31,7 @@ namespace Renci.SshNet /// /// Gets the key files used for authentication. /// - public ICollection KeyFiles { get; private set; } + public ICollection KeyFiles { get; private set; } /// /// Initializes a new instance of the class. @@ -36,13 +39,15 @@ namespace Renci.SshNet /// The username. /// The key files. /// is whitespace or null. - public PrivateKeyAuthenticationMethod(string username, params PrivateKeyFile[] keyFiles) + public PrivateKeyAuthenticationMethod(string username, params IPrivateKeySource[] keyFiles) : base(username) { - if (keyFiles == null) - throw new ArgumentNullException("keyFiles"); + if (keyFiles is null) + { + throw new ArgumentNullException(nameof(keyFiles)); + } - KeyFiles = new Collection(keyFiles); + KeyFiles = new Collection(keyFiles); } /// @@ -60,51 +65,53 @@ namespace Renci.SshNet session.RegisterMessage("SSH_MSG_USERAUTH_PK_OK"); + var hostAlgorithms = KeyFiles.SelectMany(x => x.HostKeyAlgorithms).ToList(); + try { - foreach (var keyFile in KeyFiles) + foreach (var hostAlgorithm in hostAlgorithms) { - _authenticationCompleted.Reset(); + _ = _authenticationCompleted.Reset(); _isSignatureRequired = false; var message = new RequestMessagePublicKey(ServiceName.Connection, Username, - keyFile.HostKey.Name, - keyFile.HostKey.Data); + hostAlgorithm.Name, + hostAlgorithm.Data); - if (KeyFiles.Count < 2) + if (hostAlgorithms.Count == 1) { - // If only one key file provided then send signature for very first request + // If only one key file provided then send signature for very first request var signatureData = new SignatureData(message, session.SessionId).GetBytes(); - message.Signature = keyFile.HostKey.Sign(signatureData); + message.Signature = hostAlgorithm.Sign(signatureData); } - // Send public key authentication request + // Send public key authentication request session.SendMessage(message); session.WaitOnHandle(_authenticationCompleted); if (_isSignatureRequired) { - _authenticationCompleted.Reset(); + _ = _authenticationCompleted.Reset(); var signatureMessage = new RequestMessagePublicKey(ServiceName.Connection, Username, - keyFile.HostKey.Name, - keyFile.HostKey.Data); + hostAlgorithm.Name, + hostAlgorithm.Data); var signatureData = new SignatureData(message, session.SessionId).GetBytes(); - signatureMessage.Signature = keyFile.HostKey.Sign(signatureData); + signatureMessage.Signature = hostAlgorithm.Sign(signatureData); - // Send public key authentication request with signature + // Send public key authentication request with signature session.SendMessage(signatureMessage); } session.WaitOnHandle(_authenticationCompleted); - if (_authenticationResult == AuthenticationResult.Success) + if (_authenticationResult is AuthenticationResult.Success or AuthenticationResult.PartialSuccess) { break; } @@ -125,38 +132,38 @@ namespace Renci.SshNet { _authenticationResult = AuthenticationResult.Success; - _authenticationCompleted.Set(); + _ = _authenticationCompleted.Set(); } private void Session_UserAuthenticationFailureReceived(object sender, MessageEventArgs e) { if (e.Message.PartialSuccess) + { _authenticationResult = AuthenticationResult.PartialSuccess; + } else + { _authenticationResult = AuthenticationResult.Failure; + } - // Copy allowed authentication methods + // Copy allowed authentication methods AllowedAuthentications = e.Message.AllowedAuthentications; - _authenticationCompleted.Set(); + _ = _authenticationCompleted.Set(); } private void Session_UserAuthenticationPublicKeyReceived(object sender, MessageEventArgs e) { _isSignatureRequired = true; - _authenticationCompleted.Set(); + _ = _authenticationCompleted.Set(); } - #region IDisposable Members - - private bool _isDisposed; - /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// public void Dispose() { - Dispose(true); + Dispose(disposing: true); GC.SuppressFinalize(this); } @@ -167,7 +174,9 @@ namespace Renci.SshNet protected virtual void Dispose(bool disposing) { if (_isDisposed) + { return; + } if (disposing) { @@ -183,17 +192,14 @@ namespace Renci.SshNet } /// - /// Releases unmanaged resources and performs other cleanup operations before the - /// is reclaimed by garbage collection. + /// Finalizes an instance of the class. /// ~PrivateKeyAuthenticationMethod() { - Dispose(false); + Dispose(disposing: false); } - #endregion - - private class SignatureData : SshData + private sealed class SignatureData : SshData { private readonly RequestMessagePublicKey _message; diff --git a/src/Renci.SshNet/PrivateKeyConnectionInfo.cs b/src/Renci.SshNet/PrivateKeyConnectionInfo.cs index 7f0c4f65..29b48ffa 100644 --- a/src/Renci.SshNet/PrivateKeyConnectionInfo.cs +++ b/src/Renci.SshNet/PrivateKeyConnectionInfo.cs @@ -5,17 +5,19 @@ using System.Collections.ObjectModel; namespace Renci.SshNet { /// - /// Provides connection information when private key authentication method is used + /// Provides connection information when private key authentication method is used. /// /// /// /// public class PrivateKeyConnectionInfo : ConnectionInfo, IDisposable { + private bool _isDisposed; + /// /// Gets the key files used for authentication. /// - public ICollection KeyFiles { get; private set; } + public ICollection KeyFiles { get; private set; } /// /// Initializes a new instance of the class. @@ -30,7 +32,6 @@ namespace Renci.SshNet public PrivateKeyConnectionInfo(string host, string username, params PrivateKeyFile[] keyFiles) : this(host, DefaultPort, username, ProxyTypes.None, string.Empty, 0, string.Empty, string.Empty, keyFiles) { - } /// @@ -40,13 +41,13 @@ namespace Renci.SshNet /// Connection port. /// Connection username. /// Connection key files. - public PrivateKeyConnectionInfo(string host, int port, string username, params PrivateKeyFile[] keyFiles) + public PrivateKeyConnectionInfo(string host, int port, string username, params IPrivateKeySource[] keyFiles) : this(host, port, username, ProxyTypes.None, string.Empty, 0, string.Empty, string.Empty, keyFiles) { } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// Connection host. /// The port. @@ -55,13 +56,13 @@ namespace Renci.SshNet /// The proxy host. /// The proxy port. /// The key files. - public PrivateKeyConnectionInfo(string host, int port, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, params PrivateKeyFile[] keyFiles) + public PrivateKeyConnectionInfo(string host, int port, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, params IPrivateKeySource[] keyFiles) : this(host, port, username, proxyType, proxyHost, proxyPort, string.Empty, string.Empty, keyFiles) { } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// Connection host. /// The port. @@ -71,13 +72,13 @@ namespace Renci.SshNet /// The proxy port. /// The proxy username. /// The key files. - public PrivateKeyConnectionInfo(string host, int port, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, params PrivateKeyFile[] keyFiles) + public PrivateKeyConnectionInfo(string host, int port, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, params IPrivateKeySource[] keyFiles) : this(host, port, username, proxyType, proxyHost, proxyPort, proxyUsername, string.Empty, keyFiles) { } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// Connection host. /// Connection username. @@ -85,13 +86,13 @@ namespace Renci.SshNet /// The proxy host. /// The proxy port. /// The key files. - public PrivateKeyConnectionInfo(string host, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, params PrivateKeyFile[] keyFiles) + public PrivateKeyConnectionInfo(string host, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, params IPrivateKeySource[] keyFiles) : this(host, DefaultPort, username, proxyType, proxyHost, proxyPort, string.Empty, string.Empty, keyFiles) { } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// Connection host. /// Connection username. @@ -100,13 +101,13 @@ namespace Renci.SshNet /// The proxy port. /// The proxy username. /// The key files. - public PrivateKeyConnectionInfo(string host, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, params PrivateKeyFile[] keyFiles) + public PrivateKeyConnectionInfo(string host, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, params IPrivateKeySource[] keyFiles) : this(host, DefaultPort, username, proxyType, proxyHost, proxyPort, proxyUsername, string.Empty, keyFiles) { } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// Connection host. /// Connection username. @@ -116,13 +117,13 @@ namespace Renci.SshNet /// The proxy username. /// The proxy password. /// The key files. - public PrivateKeyConnectionInfo(string host, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, string proxyPassword, params PrivateKeyFile[] keyFiles) + public PrivateKeyConnectionInfo(string host, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, string proxyPassword, params IPrivateKeySource[] keyFiles) : this(host, DefaultPort, username, proxyType, proxyHost, proxyPort, proxyUsername, proxyPassword, keyFiles) { } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// Connection host. /// The port. @@ -133,33 +134,31 @@ namespace Renci.SshNet /// The proxy username. /// The proxy password. /// The key files. - public PrivateKeyConnectionInfo(string host, int port, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, string proxyPassword, params PrivateKeyFile[] keyFiles) + public PrivateKeyConnectionInfo(string host, int port, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, string proxyPassword, params IPrivateKeySource[] keyFiles) : base(host, port, username, proxyType, proxyHost, proxyPort, proxyUsername, proxyPassword, new PrivateKeyAuthenticationMethod(username, keyFiles)) { - KeyFiles = new Collection(keyFiles); + KeyFiles = new Collection(keyFiles); } - #region IDisposable Members - - private bool _isDisposed; - /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// public void Dispose() { - Dispose(true); + Dispose(disposing: true); GC.SuppressFinalize(this); } /// - /// Releases unmanaged and - optionally - managed resources + /// Releases unmanaged and - optionally - managed resources. /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected virtual void Dispose(bool disposing) { if (_isDisposed) + { return; + } if (disposing) { @@ -168,8 +167,7 @@ namespace Renci.SshNet { foreach (var authenticationMethod in AuthenticationMethods) { - var disposable = authenticationMethod as IDisposable; - if (disposable != null) + if (authenticationMethod is IDisposable disposable) { disposable.Dispose(); } @@ -181,17 +179,11 @@ namespace Renci.SshNet } /// - /// Releases unmanaged resources and performs other cleanup operations before the - /// is reclaimed by garbage collection. + /// Finalizes an instance of the class. /// ~PrivateKeyConnectionInfo() { - // Do not re-create Dispose clean-up code here. - // Calling Dispose(false) is optimal in terms of - // readability and maintainability. - Dispose(false); + Dispose(disposing: false); } - - #endregion } } diff --git a/src/Renci.SshNet/PrivateKeyFile.cs b/src/Renci.SshNet/PrivateKeyFile.cs index 31fac7db..6dc8d427 100644 --- a/src/Renci.SshNet/PrivateKeyFile.cs +++ b/src/Renci.SshNet/PrivateKeyFile.cs @@ -1,17 +1,19 @@ using System; using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; using System.IO; +using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; + using Renci.SshNet.Abstractions; -using Renci.SshNet.Security; using Renci.SshNet.Common; -using System.Globalization; +using Renci.SshNet.Security; +using Renci.SshNet.Security.Cryptography; using Renci.SshNet.Security.Cryptography.Ciphers; using Renci.SshNet.Security.Cryptography.Ciphers.Modes; using Renci.SshNet.Security.Cryptography.Ciphers.Paddings; -using System.Diagnostics.CodeAnalysis; -using Renci.SshNet.Security.Cryptography; namespace Renci.SshNet { @@ -26,13 +28,13 @@ namespace Renci.SshNet /// The following private keys are supported: /// /// - /// RSA in OpenSSL PEM and ssh.com format + /// RSA in OpenSSL PEM, ssh.com and OpenSSH key format /// /// /// DSA in OpenSSL PEM and ssh.com format /// /// - /// ECDSA 256/384/521 in OpenSSL PEM format + /// ECDSA 256/384/521 in OpenSSL PEM and OpenSSH key format /// /// /// ED25519 in OpenSSH key format @@ -63,21 +65,46 @@ namespace Renci.SshNet /// /// /// - public class PrivateKeyFile : IDisposable + public class PrivateKeyFile : IPrivateKeySource, IDisposable { private static readonly Regex PrivateKeyRegex = new Regex(@"^-+ *BEGIN (?\w+( \w+)*) PRIVATE KEY *-+\r?\n((Proc-Type: 4,ENCRYPTED\r?\nDEK-Info: (?[A-Z0-9-]+),(?[A-F0-9]+)\r?\n\r?\n)|(Comment: ""?[^\r\n]*""?\r?\n))?(?([a-zA-Z0-9/+=]{1,80}\r?\n)+)-+ *END \k PRIVATE KEY *-+", -#if FEATURE_REGEX_COMPILE RegexOptions.Compiled | RegexOptions.Multiline); -#else - RegexOptions.Multiline); -#endif + private readonly List _hostAlgorithms = new List(); private Key _key; + private bool _isDisposed; /// - /// Gets the host key. + /// The supported host algorithms for this key file. /// - public HostAlgorithm HostKey { get; private set; } + public IReadOnlyCollection HostKeyAlgorithms + { + get + { + return _hostAlgorithms; + } + } + + /// + /// Gets the key. + /// + public Key Key + { + get + { + return _key; + } + } + + /// + /// Initializes a new instance of the class. + /// + /// The key. + public PrivateKeyFile(Key key) + { + _key = key; + _hostAlgorithms.Add(new KeyHostAlgorithm(key.ToString(), key)); + } /// /// Initializes a new instance of the class. @@ -85,7 +112,8 @@ namespace Renci.SshNet /// The private key. public PrivateKeyFile(Stream privateKey) { - Open(privateKey, null); + Open(privateKey, passPhrase: null); + Debug.Assert(_hostAlgorithms.Count > 0, $"{nameof(HostKeyAlgorithms)} is not set."); } /// @@ -93,9 +121,11 @@ namespace Renci.SshNet /// /// Name of the file. /// is null or empty. - /// This method calls internally, this method does not catch exceptions from . + /// + /// This method calls internally, this method does not catch exceptions from . + /// public PrivateKeyFile(string fileName) - : this(fileName, null) + : this(fileName, passPhrase: null) { } @@ -105,16 +135,22 @@ namespace Renci.SshNet /// Name of the file. /// The pass phrase. /// is null or empty, or is null. - /// This method calls internally, this method does not catch exceptions from . + /// + /// This method calls internally, this method does not catch exceptions from . + /// public PrivateKeyFile(string fileName, string passPhrase) { if (string.IsNullOrEmpty(fileName)) - throw new ArgumentNullException("fileName"); + { + throw new ArgumentNullException(nameof(fileName)); + } using (var keyFile = File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.Read)) { Open(keyFile, passPhrase); } + + Debug.Assert(_hostAlgorithms.Count > 0, $"{nameof(HostKeyAlgorithms)} is not set."); } /// @@ -126,6 +162,8 @@ namespace Renci.SshNet public PrivateKeyFile(Stream privateKey, string passPhrase) { Open(privateKey, passPhrase); + + Debug.Assert(_hostAlgorithms.Count > 0, $"{nameof(HostKeyAlgorithms)} is not set."); } /// @@ -133,11 +171,12 @@ namespace Renci.SshNet /// /// The private key. /// The pass phrase. - [SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope", Justification = "this._key disposed in Dispose(bool) method.")] private void Open(Stream privateKey, string passPhrase) { - if (privateKey == null) - throw new ArgumentNullException("privateKey"); + if (privateKey is null) + { + throw new ArgumentNullException(nameof(privateKey)); + } Match privateKeyMatch; @@ -164,11 +203,15 @@ namespace Renci.SshNet if (!string.IsNullOrEmpty(cipherName) && !string.IsNullOrEmpty(salt)) { if (string.IsNullOrEmpty(passPhrase)) + { throw new SshPassPhraseNullOrEmptyException("Private key is encrypted but passphrase is empty."); + } var binarySalt = new byte[salt.Length / 2]; for (var i = 0; i < binarySalt.Length; i++) + { binarySalt[i] = Convert.ToByte(salt.Substring(i * 2, 2), 16); + } CipherInfo cipher; switch (cipherName) @@ -205,22 +248,32 @@ namespace Renci.SshNet switch (keyName) { case "RSA": - _key = new RsaKey(decryptedData); - HostKey = new KeyHostAlgorithm("ssh-rsa", _key); + var rsaKey = new RsaKey(decryptedData); + _key = rsaKey; + _hostAlgorithms.Add(new KeyHostAlgorithm("rsa-sha2-512", _key, new RsaDigitalSignature(rsaKey, HashAlgorithmName.SHA512))); + _hostAlgorithms.Add(new KeyHostAlgorithm("rsa-sha2-256", _key, new RsaDigitalSignature(rsaKey, HashAlgorithmName.SHA256))); + _hostAlgorithms.Add(new KeyHostAlgorithm("ssh-rsa", _key)); break; case "DSA": _key = new DsaKey(decryptedData); - HostKey = new KeyHostAlgorithm("ssh-dss", _key); + _hostAlgorithms.Add(new KeyHostAlgorithm("ssh-dss", _key)); break; -#if FEATURE_ECDSA case "EC": _key = new EcdsaKey(decryptedData); - HostKey = new KeyHostAlgorithm(_key.ToString(), _key); + _hostAlgorithms.Add(new KeyHostAlgorithm(_key.ToString(), _key)); break; -#endif case "OPENSSH": _key = ParseOpenSshV1Key(decryptedData, passPhrase); - HostKey = new KeyHostAlgorithm(_key.ToString(), _key); + if (_key is RsaKey parsedRsaKey) + { + _hostAlgorithms.Add(new KeyHostAlgorithm("rsa-sha2-512", _key, new RsaDigitalSignature(parsedRsaKey, HashAlgorithmName.SHA512))); + _hostAlgorithms.Add(new KeyHostAlgorithm("rsa-sha2-256", _key, new RsaDigitalSignature(parsedRsaKey, HashAlgorithmName.SHA256))); + _hostAlgorithms.Add(new KeyHostAlgorithm("ssh-rsa", _key)); + } + else + { + _hostAlgorithms.Add(new KeyHostAlgorithm(_key.ToString(), _key)); + } break; case "SSH2 ENCRYPTED": var reader = new SshDataReader(decryptedData); @@ -230,7 +283,7 @@ namespace Renci.SshNet throw new SshException("Invalid SSH2 private key."); } - reader.ReadUInt32(); // Read total bytes length including magic number + _ = reader.ReadUInt32(); // Read total bytes length including magic number var keyType = reader.ReadString(SshData.Ascii); var ssh2CipherName = reader.ReadString(SshData.Ascii); var blobSize = (int)reader.ReadUInt32(); @@ -243,7 +296,9 @@ namespace Renci.SshNet else if (ssh2CipherName == "3des-cbc") { if (string.IsNullOrEmpty(passPhrase)) + { throw new SshPassPhraseNullOrEmptyException("Private key is encrypted but passphrase is empty."); + } var key = GetCipherKey(passPhrase, 192 / 8); var ssh2Сipher = new TripleDesCipher(key, new CbcCipherMode(new byte[8]), new PKCS7Padding()); @@ -254,25 +309,30 @@ namespace Renci.SshNet throw new SshException(string.Format("Cipher method '{0}' is not supported.", cipherName)); } - // TODO: Create two specific data types to avoid using SshDataReader class + // TODO: Create two specific data types to avoid using SshDataReader class reader = new SshDataReader(keyData); var decryptedLength = reader.ReadUInt32(); if (decryptedLength > blobSize - 4) + { throw new SshException("Invalid passphrase."); - + } + if (keyType == "if-modn{sign{rsa-pkcs1-sha1},encrypt{rsa-pkcs1v2-oaep}}") { - var exponent = reader.ReadBigIntWithBits();//e - var d = reader.ReadBigIntWithBits();//d - var modulus = reader.ReadBigIntWithBits();//n - var inverseQ = reader.ReadBigIntWithBits();//u - var q = reader.ReadBigIntWithBits();//p - var p = reader.ReadBigIntWithBits();//q - _key = new RsaKey(modulus, exponent, d, p, q, inverseQ); - HostKey = new KeyHostAlgorithm("ssh-rsa", _key); + var exponent = reader.ReadBigIntWithBits(); // e + var d = reader.ReadBigIntWithBits(); // d + var modulus = reader.ReadBigIntWithBits(); // n + var inverseQ = reader.ReadBigIntWithBits(); // u + var q = reader.ReadBigIntWithBits(); // p + var p = reader.ReadBigIntWithBits(); // q + var decryptedRsaKey = new RsaKey(modulus, exponent, d, p, q, inverseQ); + _key = decryptedRsaKey; + _hostAlgorithms.Add(new KeyHostAlgorithm("rsa-sha2-512", _key, new RsaDigitalSignature(decryptedRsaKey, HashAlgorithmName.SHA512))); + _hostAlgorithms.Add(new KeyHostAlgorithm("rsa-sha2-256", _key, new RsaDigitalSignature(decryptedRsaKey, HashAlgorithmName.SHA256))); + _hostAlgorithms.Add(new KeyHostAlgorithm("ssh-rsa", _key)); } else if (keyType == "dl-modp{sign{dsa-nist-sha1},dh{plain}}") { @@ -287,7 +347,7 @@ namespace Renci.SshNet var y = reader.ReadBigIntWithBits(); var x = reader.ReadBigIntWithBits(); _key = new DsaKey(p, q, g, y, x); - HostKey = new KeyHostAlgorithm("ssh-dss", _key); + _hostAlgorithms.Add(new KeyHostAlgorithm("ssh-dss", _key)); } else { @@ -332,14 +392,20 @@ namespace Renci.SshNet /// , , or is null. private static byte[] DecryptKey(CipherInfo cipherInfo, byte[] cipherData, string passPhrase, byte[] binarySalt) { - if (cipherInfo == null) - throw new ArgumentNullException("cipherInfo"); + if (cipherInfo is null) + { + throw new ArgumentNullException(nameof(cipherInfo)); + } - if (cipherData == null) - throw new ArgumentNullException("cipherData"); + if (cipherData is null) + { + throw new ArgumentNullException(nameof(cipherData)); + } - if (binarySalt == null) - throw new ArgumentNullException("binarySalt"); + if (binarySalt is null) + { + throw new ArgumentNullException(nameof(binarySalt)); + } var cipherKey = new List(); @@ -347,7 +413,7 @@ namespace Renci.SshNet { var passwordBytes = Encoding.UTF8.GetBytes(passPhrase); - // Use 8 bytes binary salt + // Use 8 bytes binary salt var initVector = passwordBytes.Concat(binarySalt.Take(8)); var hash = md5.ComputeHash(initVector); @@ -372,12 +438,14 @@ namespace Renci.SshNet /// /// the key file data (i.e. base64 encoded data between the header/footer) /// passphrase or null if there isn't one - /// - private ED25519Key ParseOpenSshV1Key(byte [] keyFileData, string passPhrase) + /// + /// The OpenSSH V1 key. + /// + private static Key ParseOpenSshV1Key(byte[] keyFileData, string passPhrase) { var keyReader = new SshDataReader(keyFileData); - //check magic header + // check magic header var authMagic = Encoding.UTF8.GetBytes("openssh-key-v1\0"); var keyHeaderBytes = keyReader.ReadBytes(authMagic.Length); if (!authMagic.IsEqualTo(keyHeaderBytes)) @@ -385,146 +453,174 @@ namespace Renci.SshNet throw new SshException("This openssh key does not contain the 'openssh-key-v1' format magic header"); } - //cipher will be "aes256-cbc" if using a passphrase, "none" otherwise + // cipher will be "aes256-cbc" if using a passphrase, "none" otherwise var cipherName = keyReader.ReadString(Encoding.UTF8); - //key derivation function (kdf): bcrypt or nothing + + // key derivation function (kdf): bcrypt or nothing var kdfName = keyReader.ReadString(Encoding.UTF8); - //kdf options length: 24 if passphrase, 0 if no passphrase + + // kdf options length: 24 if passphrase, 0 if no passphrase var kdfOptionsLen = (int)keyReader.ReadUInt32(); byte[] salt = null; - int rounds = 0; + var rounds = 0; if (kdfOptionsLen > 0) { - var saltLength = (int)keyReader.ReadUInt32(); + var saltLength = (int) keyReader.ReadUInt32(); salt = keyReader.ReadBytes(saltLength); - rounds = (int)keyReader.ReadUInt32(); + rounds = (int) keyReader.ReadUInt32(); } - //number of public keys, only supporting 1 for now + // number of public keys, only supporting 1 for now var numberOfPublicKeys = (int)keyReader.ReadUInt32(); if (numberOfPublicKeys != 1) { throw new SshException("At this time only one public key in the openssh key is supported."); } - //length of first public key section - keyReader.ReadUInt32(); - var keyType = keyReader.ReadString(Encoding.UTF8); - if(keyType != "ssh-ed25519") - { - throw new SshException("openssh key type: " + keyType + " is not supported"); - } + // read public key in ssh-format, but we dont need it + _ = keyReader.ReadString(Encoding.UTF8); - //read public key - var publicKeyLength = (int)keyReader.ReadUInt32(); //32 - var publicKey = keyReader.ReadBytes(publicKeyLength); - - //possibly encrypted private key - var privateKeyLength = (int)keyReader.ReadUInt32(); + // possibly encrypted private key + var privateKeyLength = (int) keyReader.ReadUInt32(); var privateKeyBytes = keyReader.ReadBytes(privateKeyLength); - //decrypt private key if necessary - if (cipherName == "aes256-cbc") + // decrypt private key if necessary + if (cipherName != "none") { if (string.IsNullOrEmpty(passPhrase)) { throw new SshPassPhraseNullOrEmptyException("Private key is encrypted but passphrase is empty."); } + if (string.IsNullOrEmpty(kdfName) || kdfName != "bcrypt") { throw new SshException("kdf " + kdfName + " is not supported for openssh key file"); } - //inspired by the SSHj library (https://github.com/hierynomus/sshj) - //apply the kdf to derive a key and iv from the passphrase + // inspired by the SSHj library (https://github.com/hierynomus/sshj) + // apply the kdf to derive a key and iv from the passphrase var passPhraseBytes = Encoding.UTF8.GetBytes(passPhrase); - byte[] keyiv = new byte[48]; + var keyiv = new byte[48]; new BCrypt().Pbkdf(passPhraseBytes, salt, rounds, keyiv); - byte[] key = new byte[32]; + var key = new byte[32]; Array.Copy(keyiv, 0, key, 0, 32); - byte[] iv = new byte[16]; + var iv = new byte[16]; Array.Copy(keyiv, 32, iv, 0, 16); - //now that we have the key/iv, use a cipher to decrypt the bytes - var cipher = new AesCipher(key, new CbcCipherMode(iv), new PKCS7Padding()); + AesCipher cipher; + switch (cipherName) + { + case "aes256-cbc": + cipher = new AesCipher(key, new CbcCipherMode(iv), new PKCS7Padding()); + break; + case "aes256-ctr": + cipher = new AesCipher(key, new CtrCipherMode(iv), new PKCS7Padding()); + break; + default: + throw new SshException("Cipher '" + cipherName + "' is not supported for an OpenSSH key."); + } + privateKeyBytes = cipher.Decrypt(privateKeyBytes); } - else if (cipherName != "none") - { - throw new SshException("cipher name " + cipherName + " for openssh key file is not supported"); - } - //validate private key length + // validate private key length privateKeyLength = privateKeyBytes.Length; if (privateKeyLength % 8 != 0) { throw new SshException("The private key section must be a multiple of the block size (8)"); } - //now parse the data we called the private key, it actually contains the public key again - //so we need to parse through it to get the private key bytes, plus there's some - //validation we need to do. + // now parse the data we called the private key, it actually contains the public key again + // so we need to parse through it to get the private key bytes, plus there's some + // validation we need to do. var privateKeyReader = new SshDataReader(privateKeyBytes); - //check ints should match, they wouldn't match for example if the wrong passphrase was supplied - int checkInt1 = (int)privateKeyReader.ReadUInt32(); - int checkInt2 = (int)privateKeyReader.ReadUInt32(); + // check ints should match, they wouldn't match for example if the wrong passphrase was supplied + var checkInt1 = (int) privateKeyReader.ReadUInt32(); + var checkInt2 = (int) privateKeyReader.ReadUInt32(); if (checkInt1 != checkInt2) { - throw new SshException("The checkints differed, the openssh key was not correctly decoded."); + throw new SshException("The random check bytes of the OpenSSH key do not match (" + checkInt1 + " <->" + checkInt2 + ")."); } - //key type, we already know it is ssh-ed25519 - privateKeyReader.ReadString(Encoding.UTF8); + // key type + var keyType = privateKeyReader.ReadString(Encoding.UTF8); - //public key length/bytes (again) - var publicKeyLength2 = (int)privateKeyReader.ReadUInt32(); - privateKeyReader.ReadBytes(publicKeyLength2); - - //length of private and public key (64) - privateKeyReader.ReadUInt32(); - var unencryptedPrivateKey = privateKeyReader.ReadBytes(32); - //public key (again) - privateKeyReader.ReadBytes(32); - - //comment, we don't need this but we could log it, not sure if necessary - var comment = privateKeyReader.ReadString(Encoding.UTF8); - - //The list of privatekey/comment pairs is padded with the bytes 1, 2, 3, ... - //until the total length is a multiple of the cipher block size. - var padding = privateKeyReader.ReadBytes(); - for (int i = 0; i < padding.Length; i++) + Key parsedKey; + byte[] publicKey; + byte[] unencryptedPrivateKey; + switch (keyType) { - if ((int)padding[i] != i + 1) + case "ssh-ed25519": + // public key + publicKey = privateKeyReader.ReadBignum2(); + + // private key + unencryptedPrivateKey = privateKeyReader.ReadBignum2(); + parsedKey = new ED25519Key(publicKey.Reverse(), unencryptedPrivateKey); + break; + case "ecdsa-sha2-nistp256": + case "ecdsa-sha2-nistp384": + case "ecdsa-sha2-nistp521": + // curve + var len = (int) privateKeyReader.ReadUInt32(); + var curve = Encoding.ASCII.GetString(privateKeyReader.ReadBytes(len)); + + // public key + publicKey = privateKeyReader.ReadBignum2(); + + // private key + unencryptedPrivateKey = privateKeyReader.ReadBignum2(); + parsedKey = new EcdsaKey(curve, publicKey, unencryptedPrivateKey.TrimLeadingZeros()); + break; + case "ssh-rsa": + var modulus = privateKeyReader.ReadBignum(); // n + var exponent = privateKeyReader.ReadBignum(); // e + var d = privateKeyReader.ReadBignum(); // d + var inverseQ = privateKeyReader.ReadBignum(); // iqmp + var p = privateKeyReader.ReadBignum(); // p + var q = privateKeyReader.ReadBignum(); // q + parsedKey = new RsaKey(modulus, exponent, d, p, q, inverseQ); + break; + default: + throw new SshException("OpenSSH key type '" + keyType + "' is not supported."); + } + + parsedKey.Comment = privateKeyReader.ReadString(Encoding.UTF8); + + // The list of privatekey/comment pairs is padded with the bytes 1, 2, 3, ... + // until the total length is a multiple of the cipher block size. + var padding = privateKeyReader.ReadBytes(); + for (var i = 0; i < padding.Length; i++) + { + if ((int) padding[i] != i + 1) { throw new SshException("Padding of openssh key format contained wrong byte at position: " + i); } } - return new ED25519Key(publicKey.Reverse(), unencryptedPrivateKey); + return parsedKey; } - #region IDisposable Members - - private bool _isDisposed; - /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// public void Dispose() { - Dispose(true); + Dispose(disposing: true); GC.SuppressFinalize(this); } /// - /// Releases unmanaged and - optionally - managed resources + /// Releases unmanaged and - optionally - managed resources. /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected virtual void Dispose(bool disposing) { if (_isDisposed) + { return; + } if (disposing) { @@ -540,17 +636,14 @@ namespace Renci.SshNet } /// - /// Releases unmanaged resources and performs other cleanup operations before the - /// is reclaimed by garbage collection. + /// Finalizes an instance of the class. /// ~PrivateKeyFile() { - Dispose(false); + Dispose(disposing: false); } - #endregion - - private class SshDataReader : SshData + private sealed class SshDataReader : SshData { public SshDataReader(byte[] data) { @@ -594,6 +687,17 @@ namespace Renci.SshNet return new BigInteger(bytesArray.Reverse()); } + public BigInteger ReadBignum() + { + return new BigInteger(ReadBignum2().Reverse()); + } + + public byte[] ReadBignum2() + { + var length = (int)base.ReadUInt32(); + return base.ReadBytes(length); + } + protected override void LoadData() { } diff --git a/src/Renci.SshNet/Properties/AssemblyInfo.cs b/src/Renci.SshNet/Properties/AssemblyInfo.cs index 22e4125c..07f66e5f 100644 --- a/src/Renci.SshNet/Properties/AssemblyInfo.cs +++ b/src/Renci.SshNet/Properties/AssemblyInfo.cs @@ -1,8 +1,9 @@ using System.Reflection; -using System.Runtime.InteropServices; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; [assembly: AssemblyTitle("SSH.NET")] [assembly: Guid("ad816c5e-6f13-4589-9f3e-59523f8b77a4")] [assembly: InternalsVisibleTo("Renci.SshNet.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f9194e1eb66b7e2575aaee115ee1d27bc100920e7150e43992d6f668f9737de8b9c7ae892b62b8a36dd1d57929ff1541665d101dc476d6e02390846efae7e5186eec409710fdb596e3f83740afef0d4443055937649bc5a773175b61c57615dac0f0fd10f52b52fedf76c17474cc567b3f7a79de95dde842509fb39aaf69c6c2")] +[assembly: InternalsVisibleTo("Renci.SshNet.IntegrationTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f9194e1eb66b7e2575aaee115ee1d27bc100920e7150e43992d6f668f9737de8b9c7ae892b62b8a36dd1d57929ff1541665d101dc476d6e02390846efae7e5186eec409710fdb596e3f83740afef0d4443055937649bc5a773175b61c57615dac0f0fd10f52b52fedf76c17474cc567b3f7a79de95dde842509fb39aaf69c6c2")] [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] diff --git a/src/Renci.SshNet/Properties/CommonAssemblyInfo.cs b/src/Renci.SshNet/Properties/CommonAssemblyInfo.cs index 0883060c..d99338fe 100644 --- a/src/Renci.SshNet/Properties/CommonAssemblyInfo.cs +++ b/src/Renci.SshNet/Properties/CommonAssemblyInfo.cs @@ -5,13 +5,13 @@ using System.Runtime.InteropServices; [assembly: AssemblyDescription("SSH.NET is a Secure Shell (SSH) library for .NET, optimized for parallelism.")] [assembly: AssemblyCompany("Renci")] [assembly: AssemblyProduct("SSH.NET")] -[assembly: AssemblyCopyright("Copyright Renci 2010-2021")] +[assembly: AssemblyCopyright("Copyright Renci 2010-2023")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] -[assembly: AssemblyVersion("2020.0.1")] -[assembly: AssemblyFileVersion("2020.0.1")] -[assembly: AssemblyInformationalVersion("2020.0.1")] +[assembly: AssemblyVersion("2023.0.0")] +[assembly: AssemblyFileVersion("2023.0.0")] +[assembly: AssemblyInformationalVersion("2023.0.0")] [assembly: CLSCompliant(false)] // Setting ComVisible to false makes the types in this assembly not visible @@ -24,4 +24,4 @@ using System.Runtime.InteropServices; [assembly: AssemblyConfiguration("Debug")] #else [assembly: AssemblyConfiguration("Release")] -#endif \ No newline at end of file +#endif diff --git a/src/Renci.SshNet/RemotePathDoubleQuoteTransformation.cs b/src/Renci.SshNet/RemotePathDoubleQuoteTransformation.cs index 70af535a..74413eac 100644 --- a/src/Renci.SshNet/RemotePathDoubleQuoteTransformation.cs +++ b/src/Renci.SshNet/RemotePathDoubleQuoteTransformation.cs @@ -6,7 +6,7 @@ namespace Renci.SshNet /// /// Encloses a path in double quotes, and escapes any embedded double quote with a backslash. /// - internal class RemotePathDoubleQuoteTransformation : IRemotePathTransformation + internal sealed class RemotePathDoubleQuoteTransformation : IRemotePathTransformation { /// /// Encloses a path in double quotes, and escapes any embedded double quote with a backslash. @@ -50,21 +50,26 @@ namespace Renci.SshNet /// public string Transform(string path) { - if (path == null) + if (path is null) { - throw new ArgumentNullException("path"); + throw new ArgumentNullException(nameof(path)); } var transformed = new StringBuilder(path.Length); - transformed.Append('"'); + _ = transformed.Append('"'); + foreach (var c in path) { if (c == '"') - transformed.Append('\\'); - transformed.Append(c); + { + _ = transformed.Append('\\'); + } + + _ = transformed.Append(c); } - transformed.Append('"'); + + _ = transformed.Append('"'); return transformed.ToString(); } diff --git a/src/Renci.SshNet/RemotePathNoneTransformation.cs b/src/Renci.SshNet/RemotePathNoneTransformation.cs index ef51531d..d1937514 100644 --- a/src/Renci.SshNet/RemotePathNoneTransformation.cs +++ b/src/Renci.SshNet/RemotePathNoneTransformation.cs @@ -5,7 +5,7 @@ namespace Renci.SshNet /// /// Performs no transformation. /// - internal class RemotePathNoneTransformation : IRemotePathTransformation + internal sealed class RemotePathNoneTransformation : IRemotePathTransformation { /// /// Returns the specified path without applying a transformation. @@ -21,9 +21,9 @@ namespace Renci.SshNet /// public string Transform(string path) { - if (path == null) + if (path is null) { - throw new ArgumentNullException("path"); + throw new ArgumentNullException(nameof(path)); } return path; diff --git a/src/Renci.SshNet/RemotePathShellQuoteTransformation.cs b/src/Renci.SshNet/RemotePathShellQuoteTransformation.cs index 5093cafd..7d02d29d 100644 --- a/src/Renci.SshNet/RemotePathShellQuoteTransformation.cs +++ b/src/Renci.SshNet/RemotePathShellQuoteTransformation.cs @@ -6,7 +6,7 @@ namespace Renci.SshNet /// /// Quotes a path in a way to be suitable to be used with a shell-based server. /// - internal class RemotePathShellQuoteTransformation : IRemotePathTransformation + internal sealed class RemotePathShellQuoteTransformation : IRemotePathTransformation { /// /// Quotes a path in a way to be suitable to be used with a shell-based server. @@ -80,9 +80,9 @@ namespace Renci.SshNet /// public string Transform(string path) { - if (path == null) + if (path is null) { - throw new ArgumentNullException("path"); + throw new ArgumentNullException(nameof(path)); } // result is at least value and (likely) leading/trailing single-quotes @@ -99,42 +99,52 @@ namespace Renci.SshNet { case ShellQuoteState.Unquoted: // Start quoted string - sb.Append('"'); + _ = sb.Append('"'); break; case ShellQuoteState.Quoted: // Continue quoted string break; case ShellQuoteState.SingleQuoted: // Close single-quoted string - sb.Append('\''); + _ = sb.Append('\''); + // Start quoted string - sb.Append('"'); + _ = sb.Append('"'); + break; + default: break; } + state = ShellQuoteState.Quoted; break; case '!': - // In C-Shell, an exclamatation point can only be protected from shell interpretation - // when escaped by a backslash - // Source: - // https://earthsci.stanford.edu/computing/unix/shell/specialchars.php + /* + * In C-Shell, an exclamatation point can only be protected from shell interpretation + * when escaped by a backslash. + * + * Source: + * https://earthsci.stanford.edu/computing/unix/shell/specialchars.php + */ switch (state) { case ShellQuoteState.Unquoted: - sb.Append('\\'); + _ = sb.Append('\\'); break; case ShellQuoteState.Quoted: // Close quoted string - sb.Append('"'); - sb.Append('\\'); + _ = sb.Append('"'); + _ = sb.Append('\\'); break; case ShellQuoteState.SingleQuoted: // Close single quoted string - sb.Append('\''); - sb.Append('\\'); + _ = sb.Append('\''); + _ = sb.Append('\\'); + break; + default: break; } + state = ShellQuoteState.Unquoted; break; default: @@ -142,23 +152,27 @@ namespace Renci.SshNet { case ShellQuoteState.Unquoted: // Start single-quoted string - sb.Append('\''); + _ = sb.Append('\''); break; case ShellQuoteState.Quoted: // Close quoted string - sb.Append('"'); + _ = sb.Append('"'); + // Start single-quoted string - sb.Append('\''); + _ = sb.Append('\''); break; case ShellQuoteState.SingleQuoted: // Continue single-quoted string break; + default: + break; } + state = ShellQuoteState.SingleQuoted; break; } - sb.Append(c); + _ = sb.Append(c); } switch (state) @@ -167,17 +181,19 @@ namespace Renci.SshNet break; case ShellQuoteState.Quoted: // Close quoted string - sb.Append('"'); + _ = sb.Append('"'); break; case ShellQuoteState.SingleQuoted: // Close single-quoted string - sb.Append('\''); + _ = sb.Append('\''); + break; + default: break; } if (sb.Length == 0) { - sb.Append("''"); + _ = sb.Append("''"); } return sb.ToString(); diff --git a/src/Renci.SshNet/Renci.SshNet.csproj b/src/Renci.SshNet/Renci.SshNet.csproj index 124ce9d4..e55911d5 100644 --- a/src/Renci.SshNet/Renci.SshNet.csproj +++ b/src/Renci.SshNet/Renci.SshNet.csproj @@ -1,47 +1,23 @@  - true - true false Renci.SshNet - ../Renci.SshNet.snk - 5 - true - net35;net40;netstandard1.3;netstandard2.0 + net462;netstandard2.0;netstandard2.1;net6.0;net7.0 - - - - - - - - - - - - - - + - - FEATURE_REGEX_COMPILE;FEATURE_BINARY_SERIALIZATION;FEATURE_RNG_CREATE;FEATURE_SOCKET_SYNC;FEATURE_SOCKET_EAP;FEATURE_SOCKET_APM;FEATURE_SOCKET_POLL;FEATURE_STREAM_APM;FEATURE_DNS_SYNC;FEATURE_THREAD_THREADPOOL;FEATURE_THREAD_SLEEP;FEATURE_HASH_MD5;FEATURE_HASH_SHA1_CREATE;FEATURE_HASH_SHA256_CREATE;FEATURE_HASH_SHA384_CREATE;FEATURE_HASH_SHA512_CREATE;FEATURE_HASH_RIPEMD160_CREATE;FEATURE_HMAC_MD5;FEATURE_HMAC_SHA1;FEATURE_HMAC_SHA256;FEATURE_HMAC_SHA384;FEATURE_HMAC_SHA512;FEATURE_HMAC_RIPEMD160;FEATURE_MEMORYSTREAM_GETBUFFER;FEATURE_DIAGNOSTICS_TRACESOURCE;FEATURE_ENCODING_ASCII;FEATURE_ECDSA + + FEATURE_SOCKET_TAP;FEATURE_SOCKET_APM;FEATURE_SOCKET_EAP;FEATURE_DNS_SYNC;FEATURE_DNS_APM;FEATURE_DNS_TAP - - FEATURE_STRINGBUILDER_CLEAR;FEATURE_HASHALGORITHM_DISPOSE;FEATURE_REGEX_COMPILE;FEATURE_BINARY_SERIALIZATION;FEATURE_RNG_CREATE;FEATURE_SOCKET_SYNC;FEATURE_SOCKET_EAP;FEATURE_SOCKET_APM;FEATURE_SOCKET_SELECT;FEATURE_SOCKET_POLL;FEATURE_SOCKET_DISPOSE;FEATURE_STREAM_APM;FEATURE_DNS_SYNC;FEATURE_THREAD_COUNTDOWNEVENT;FEATURE_THREAD_THREADPOOL;FEATURE_THREAD_SLEEP;FEATURE_WAITHANDLE_DISPOSE;FEATURE_HASH_MD5;FEATURE_HASH_SHA1_CREATE;FEATURE_HASH_SHA256_CREATE;FEATURE_HASH_SHA384_CREATE;FEATURE_HASH_SHA512_CREATE;FEATURE_HASH_RIPEMD160_CREATE;FEATURE_HMAC_MD5;FEATURE_HMAC_SHA1;FEATURE_HMAC_SHA256;FEATURE_HMAC_SHA384;FEATURE_HMAC_SHA512;FEATURE_HMAC_RIPEMD160;FEATURE_MEMORYSTREAM_GETBUFFER;FEATURE_DIAGNOSTICS_TRACESOURCE;FEATURE_ENCODING_ASCII;FEATURE_ECDSA - - - FEATURE_STRINGBUILDER_CLEAR;FEATURE_HASHALGORITHM_DISPOSE;FEATURE_ENCODING_ASCII;FEATURE_DIAGNOSTICS_TRACESOURCE;FEATURE_DIRECTORYINFO_ENUMERATEFILES;FEATURE_MEMORYSTREAM_TRYGETBUFFER;FEATURE_REFLECTION_TYPEINFO;FEATURE_RNG_CREATE;FEATURE_SOCKET_TAP;FEATURE_SOCKET_EAP;FEATURE_SOCKET_SYNC;FEATURE_SOCKET_SELECT;FEATURE_SOCKET_POLL;FEATURE_SOCKET_DISPOSE;FEATURE_DNS_TAP;FEATURE_STREAM_TAP;FEATURE_THREAD_COUNTDOWNEVENT;FEATURE_THREAD_TAP;FEATURE_THREAD_THREADPOOL;FEATURE_THREAD_SLEEP;FEATURE_WAITHANDLE_DISPOSE;FEATURE_HASH_MD5;FEATURE_HASH_SHA1_CREATE;FEATURE_HASH_SHA256_CREATE;FEATURE_HASH_SHA384_CREATE;FEATURE_HASH_SHA512_CREATE;FEATURE_HMAC_MD5;FEATURE_HMAC_SHA1;FEATURE_HMAC_SHA256;FEATURE_HMAC_SHA384;FEATURE_HMAC_SHA512 - - - FEATURE_STRINGBUILDER_CLEAR;FEATURE_HASHALGORITHM_DISPOSE;FEATURE_ENCODING_ASCII;FEATURE_DIAGNOSTICS_TRACESOURCE;FEATURE_DIRECTORYINFO_ENUMERATEFILES;FEATURE_MEMORYSTREAM_GETBUFFER;FEATURE_MEMORYSTREAM_TRYGETBUFFER;FEATURE_RNG_CREATE;FEATURE_SOCKET_TAP;FEATURE_SOCKET_APM;FEATURE_SOCKET_EAP;FEATURE_SOCKET_SYNC;FEATURE_SOCKET_SELECT;FEATURE_SOCKET_POLL;FEATURE_SOCKET_DISPOSE;FEATURE_DNS_SYNC;FEATURE_DNS_APM;FEATURE_DNS_TAP;FEATURE_STREAM_APM;FEATURE_STREAM_TAP;FEATURE_THREAD_COUNTDOWNEVENT;FEATURE_THREAD_TAP;FEATURE_THREAD_THREADPOOL;FEATURE_THREAD_SLEEP;FEATURE_WAITHANDLE_DISPOSE;FEATURE_HASH_MD5;FEATURE_HASH_SHA1_CREATE;FEATURE_HASH_SHA256_CREATE;FEATURE_HASH_SHA384_CREATE;FEATURE_HASH_SHA512_CREATE;FEATURE_HMAC_MD5;FEATURE_HMAC_SHA1;FEATURE_HMAC_SHA256;FEATURE_HMAC_SHA384;FEATURE_HMAC_SHA512;FEATURE_ECDSA + + + $(DefineConstants);FEATURE_ASYNC_ENUMERABLE diff --git a/src/Renci.SshNet/ScpClient.NET.cs b/src/Renci.SshNet/ScpClient.NET.cs index cb23cb69..ede00194 100644 --- a/src/Renci.SshNet/ScpClient.NET.cs +++ b/src/Renci.SshNet/ScpClient.NET.cs @@ -1,9 +1,10 @@ using System; -using Renci.SshNet.Channels; using System.IO; -using Renci.SshNet.Common; using System.Text.RegularExpressions; +using Renci.SshNet.Channels; +using Renci.SshNet.Common; + namespace Renci.SshNet { /// @@ -26,8 +27,10 @@ namespace Renci.SshNet /// The secure copy execution request was rejected by the server. public void Upload(FileInfo fileInfo, string path) { - if (fileInfo == null) - throw new ArgumentNullException("fileInfo"); + if (fileInfo is null) + { + throw new ArgumentNullException(nameof(fileInfo)); + } var posixPath = PosixPath.CreateAbsoluteOrRelativeFilePath(path); @@ -43,6 +46,7 @@ namespace Renci.SshNet { throw SecureExecutionRequestRejectedException(); } + CheckReturnCode(input); using (var source = fileInfo.OpenRead()) @@ -66,12 +70,20 @@ namespace Renci.SshNet /// The secure copy execution request was rejected by the server. public void Upload(DirectoryInfo directoryInfo, string path) { - if (directoryInfo == null) - throw new ArgumentNullException("directoryInfo"); - if (path == null) - throw new ArgumentNullException("path"); + if (directoryInfo is null) + { + throw new ArgumentNullException(nameof(directoryInfo)); + } + + if (path is null) + { + throw new ArgumentNullException(nameof(path)); + } + if (path.Length == 0) - throw new ArgumentException("The path cannot be a zero-length string.", "path"); + { + throw new ArgumentException("The path cannot be a zero-length string.", nameof(path)); + } using (var input = ServiceFactory.CreatePipeStream()) using (var channel = Session.CreateChannelSession()) @@ -107,9 +119,14 @@ namespace Renci.SshNet public void Download(string filename, FileInfo fileInfo) { if (string.IsNullOrEmpty(filename)) + { throw new ArgumentException("filename"); - if (fileInfo == null) - throw new ArgumentNullException("fileInfo"); + } + + if (fileInfo is null) + { + throw new ArgumentNullException(nameof(fileInfo)); + } using (var input = ServiceFactory.CreatePipeStream()) using (var channel = Session.CreateChannelSession()) @@ -122,6 +139,7 @@ namespace Renci.SshNet { throw SecureExecutionRequestRejectedException(); } + // Send reply SendSuccessConfirmation(channel); @@ -141,9 +159,14 @@ namespace Renci.SshNet public void Download(string directoryName, DirectoryInfo directoryInfo) { if (string.IsNullOrEmpty(directoryName)) + { throw new ArgumentException("directoryName"); - if (directoryInfo == null) - throw new ArgumentNullException("directoryInfo"); + } + + if (directoryInfo is null) + { + throw new ArgumentNullException(nameof(directoryInfo)); + } using (var input = ServiceFactory.CreatePipeStream()) using (var channel = Session.CreateChannelSession()) @@ -156,6 +179,7 @@ namespace Renci.SshNet { throw SecureExecutionRequestRejectedException(); } + // Send reply SendSuccessConfirmation(channel); @@ -187,7 +211,7 @@ namespace Renci.SshNet /// The directory to upload. private void UploadDirectoryContent(IChannelSession channel, Stream input, DirectoryInfo directoryInfo) { - // Upload files + // Upload files var files = directoryInfo.GetFiles(); foreach (var file in files) { @@ -199,7 +223,7 @@ namespace Renci.SshNet } } - // Upload directories + // Upload directories var directories = directoryInfo.GetDirectories(); foreach (var directory in directories) { @@ -237,23 +261,26 @@ namespace Renci.SshNet if (message == "E") { - SendSuccessConfirmation(channel); // Send reply + SendSuccessConfirmation(channel); // Send reply directoryCounter--; currentDirectoryFullName = new DirectoryInfo(currentDirectoryFullName).Parent.FullName; if (directoryCounter == 0) + { break; + } + continue; } var match = DirectoryInfoRe.Match(message); if (match.Success) { - SendSuccessConfirmation(channel); // Send reply + SendSuccessConfirmation(channel); // Send reply - // Read directory + // Read directory var filename = match.Result("${filename}"); DirectoryInfo newDirectoryInfo; @@ -265,7 +292,7 @@ namespace Renci.SshNet } else { - // Don't create directory for first level + // Don't create directory for first level newDirectoryInfo = fileSystemInfo as DirectoryInfo; } @@ -278,16 +305,16 @@ namespace Renci.SshNet match = FileInfoRe.Match(message); if (match.Success) { - // Read file + // Read file SendSuccessConfirmation(channel); // Send reply var length = long.Parse(match.Result("${length}")); var fileName = match.Result("${filename}"); - var fileInfo = fileSystemInfo as FileInfo; - - if (fileInfo == null) + if (fileSystemInfo is not FileInfo fileInfo) + { fileInfo = new FileInfo(Path.Combine(currentDirectoryFullName, fileName)); + } using (var output = fileInfo.OpenWrite()) { @@ -298,14 +325,17 @@ namespace Renci.SshNet fileInfo.LastWriteTime = modifiedTime; if (directoryCounter == 0) + { break; + } + continue; } match = TimestampRe.Match(message); if (match.Success) { - // Read timestamp + // Read timestamp SendSuccessConfirmation(channel); // Send reply var mtime = long.Parse(match.Result("${mtime}")); @@ -320,39 +350,5 @@ namespace Renci.SshNet SendErrorConfirmation(channel, string.Format("\"{0}\" is not valid protocol message.", message)); } } - - /// - /// Return a value indicating whether the specified path is a valid SCP file path. - /// - /// The path to verify. - /// - /// if is a valid SCP file path; otherwise, . - /// - /// - /// To match OpenSSH behavior (introduced as a result of CVE-2018-20685), a file path is considered - /// invalid in any of the following conditions: - /// - /// - /// is a zero-length string. - /// - /// - /// is ".". - /// - /// - /// is "..". - /// - /// - /// contains a forward slash (/). - /// - /// - /// - private static bool IsValidScpFilePath(string path) - { - return path != null && - path.Length != 0 && - path != "." && - path != ".." && - path.IndexOf('/') == -1; - } } } diff --git a/src/Renci.SshNet/ScpClient.cs b/src/Renci.SshNet/ScpClient.cs index 8a252cac..177a83bb 100644 --- a/src/Renci.SshNet/ScpClient.cs +++ b/src/Renci.SshNet/ScpClient.cs @@ -1,11 +1,13 @@ using System; -using Renci.SshNet.Channels; -using System.IO; -using Renci.SshNet.Common; -using System.Text.RegularExpressions; -using System.Diagnostics.CodeAnalysis; -using System.Net; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.IO; +using System.Net; +using System.Text.RegularExpressions; + +using Renci.SshNet.Channels; +using Renci.SshNet.Common; namespace Renci.SshNet { @@ -29,8 +31,9 @@ namespace Renci.SshNet /// public partial class ScpClient : BaseClient { + private const string Message = "filename"; private static readonly Regex FileInfoRe = new Regex(@"C(?\d{4}) (?\d+) (?.+)"); - private static readonly byte[] SuccessConfirmationCode = {0}; + private static readonly byte[] SuccessConfirmationCode = { 0 }; private static readonly byte[] ErrorConfirmationCode = { 1 }; private IRemotePathTransformation _remotePathTransformation; @@ -56,7 +59,7 @@ namespace Renci.SshNet /// Gets or sets the transformation to apply to remote paths. /// /// - /// The transformation to apply to remote paths. The default is . + /// The transformation to apply to remote paths. The default is . /// /// is null. /// @@ -74,8 +77,11 @@ namespace Renci.SshNet get { return _remotePathTransformation; } set { - if (value == null) - throw new ArgumentNullException("value"); + if (value is null) + { + throw new ArgumentNullException(nameof(value)); + } + _remotePathTransformation = value; } } @@ -90,15 +96,13 @@ namespace Renci.SshNet /// public event EventHandler Uploading; - #region Constructors - /// /// Initializes a new instance of the class. /// /// The connection info. /// is null. public ScpClient(ConnectionInfo connectionInfo) - : this(connectionInfo, false) + : this(connectionInfo, ownsConnectionInfo: false) { } @@ -114,7 +118,7 @@ namespace Renci.SshNet /// is not within and . [SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope", Justification = "Disposed in Dispose(bool) method.")] public ScpClient(string host, int port, string username, string password) - : this(new PasswordConnectionInfo(host, port, username, password), true) + : this(new PasswordConnectionInfo(host, port, username, password), ownsConnectionInfo: true) { } @@ -142,8 +146,8 @@ namespace Renci.SshNet /// is invalid, -or- is null or contains only whitespace characters. /// is not within and . [SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope", Justification = "Disposed in Dispose(bool) method.")] - public ScpClient(string host, int port, string username, params PrivateKeyFile[] keyFiles) - : this(new PrivateKeyConnectionInfo(host, port, username, keyFiles), true) + public ScpClient(string host, int port, string username, params IPrivateKeySource[] keyFiles) + : this(new PrivateKeyConnectionInfo(host, port, username, keyFiles), ownsConnectionInfo: true) { } @@ -155,7 +159,7 @@ namespace Renci.SshNet /// Authentication private key file(s) . /// is null. /// is invalid, -or- is null or contains only whitespace characters. - public ScpClient(string host, string username, params PrivateKeyFile[] keyFiles) + public ScpClient(string host, string username, params IPrivateKeySource[] keyFiles) : this(host, ConnectionInfo.DefaultPort, username, keyFiles) { } @@ -195,8 +199,6 @@ namespace Renci.SshNet _remotePathTransformation = serviceFactory.CreateRemotePathDoubleQuoteTransformation(); } - #endregion - /// /// Uploads the specified stream to the remote host. /// @@ -222,6 +224,7 @@ namespace Renci.SshNet { throw SecureExecutionRequestRejectedException(); } + CheckReturnCode(input); UploadFileModeAndName(channel, input, source.Length, posixPath.File); @@ -240,11 +243,15 @@ namespace Renci.SshNet /// The secure copy execution request was rejected by the server. public void Download(string filename, Stream destination) { - if (filename.IsNullOrWhiteSpace()) - throw new ArgumentException("filename"); + if (string.IsNullOrWhiteSpace(filename)) + { + throw new ArgumentException(Message); + } - if (destination == null) - throw new ArgumentNullException("destination"); + if (destination is null) + { + throw new ArgumentNullException(nameof(destination)); + } using (var input = ServiceFactory.CreatePipeStream()) using (var channel = Session.CreateChannelSession()) @@ -252,22 +259,23 @@ namespace Renci.SshNet channel.DataReceived += (sender, e) => input.Write(e.Data, 0, e.Data.Length); channel.Open(); - // Send channel command request - if (!channel.SendExecRequest(string.Format("scp -f {0}", _remotePathTransformation.Transform(filename)))) + // Send channel command request + if (!channel.SendExecRequest(string.Concat("scp -f ", _remotePathTransformation.Transform(filename)))) { throw SecureExecutionRequestRejectedException(); } - SendSuccessConfirmation(channel); // Send reply + + SendSuccessConfirmation(channel); // Send reply var message = ReadString(input); var match = FileInfoRe.Match(message); if (match.Success) { - // Read file + // Read file SendSuccessConfirmation(channel); // Send reply - var length = long.Parse(match.Result("${length}")); + var length = long.Parse(match.Result("${length}"), CultureInfo.InvariantCulture); var fileName = match.Result("${filename}"); InternalDownload(channel, input, destination, fileName, length); @@ -364,18 +372,12 @@ namespace Renci.SshNet private void RaiseDownloadingEvent(string filename, long size, long downloaded) { - if (Downloading != null) - { - Downloading(this, new ScpDownloadEventArgs(filename, size, downloaded)); - } + Downloading?.Invoke(this, new ScpDownloadEventArgs(filename, size, downloaded)); } private void RaiseUploadingEvent(string filename, long size, long uploaded) { - if (Uploading != null) - { - Uploading(this, new ScpUploadEventArgs(filename, size, uploaded)); - } + Uploading?.Invoke(this, new ScpUploadEventArgs(filename, size, uploaded)); } private static void SendSuccessConfirmation(IChannel channel) @@ -423,8 +425,12 @@ namespace Renci.SshNet private static int ReadByte(Stream stream) { var b = stream.ReadByte(); + if (b == -1) + { throw new SshException("Stream has been closed."); + } + return b; } @@ -442,7 +448,7 @@ namespace Renci.SshNet var buffer = new List(); var b = ReadByte(stream); - if (b == 1 || b == 2) + if (b is 1 or 2) { hasError = true; b = ReadByte(stream); @@ -457,7 +463,10 @@ namespace Renci.SshNet var readBytes = buffer.ToArray(); if (hasError) + { throw new ScpException(ConnectionInfo.Encoding.GetString(readBytes, 0, readBytes.Length)); + } + return ConnectionInfo.Encoding.GetString(readBytes, 0, readBytes.Length); } diff --git a/src/Renci.SshNet/Security/BouncyCastle/.editorconfig b/src/Renci.SshNet/Security/BouncyCastle/.editorconfig new file mode 100644 index 00000000..9440813d --- /dev/null +++ b/src/Renci.SshNet/Security/BouncyCastle/.editorconfig @@ -0,0 +1,6 @@ +[*.cs] + +generated_code = true + +# Do not reported any diagnostics for "imported" code +dotnet_analyzer_diagnostic.severity = none diff --git a/src/Renci.SshNet/Security/BouncyCastle/crypto/prng/CryptoApiRandomGenerator.cs b/src/Renci.SshNet/Security/BouncyCastle/crypto/prng/CryptoApiRandomGenerator.cs index 5dd468b0..50ae6f38 100644 --- a/src/Renci.SshNet/Security/BouncyCastle/crypto/prng/CryptoApiRandomGenerator.cs +++ b/src/Renci.SshNet/Security/BouncyCastle/crypto/prng/CryptoApiRandomGenerator.cs @@ -9,9 +9,7 @@ namespace Renci.SshNet.Security.Org.BouncyCastle.Crypto.Prng private readonly RandomNumberGenerator rndProv; public CryptoApiRandomGenerator() -#if FEATURE_RNG_CREATE || FEATURE_RNG_CSP : this(Abstractions.CryptoAbstraction.CreateRandomNumberGenerator()) -#endif { } @@ -34,15 +32,7 @@ namespace Renci.SshNet.Security.Org.BouncyCastle.Crypto.Prng public virtual void NextBytes(byte[] bytes) { -#if FEATURE_RNG_CREATE || FEATURE_RNG_CSP rndProv.GetBytes(bytes); -#else - if (bytes == null) - throw new ArgumentNullException("bytes"); - - var buffer = Windows.Security.Cryptography.CryptographicBuffer.GenerateRandom((uint)bytes.Length); - System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeBufferExtensions.CopyTo(buffer, bytes); -#endif } public virtual void NextBytes(byte[] bytes, int start, int len) diff --git a/src/Renci.SshNet/Security/BouncyCastle/crypto/prng/IRandomGenerator.cs b/src/Renci.SshNet/Security/BouncyCastle/crypto/prng/IRandomGenerator.cs index 5bb0c354..57e49ea7 100644 --- a/src/Renci.SshNet/Security/BouncyCastle/crypto/prng/IRandomGenerator.cs +++ b/src/Renci.SshNet/Security/BouncyCastle/crypto/prng/IRandomGenerator.cs @@ -23,4 +23,4 @@ namespace Renci.SshNet.Security.Org.BouncyCastle.Crypto.Prng /// Length of segment to fill. void NextBytes(byte[] bytes, int start, int len); } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet/Security/BouncyCastle/math/ec/endo/GlvEndomorphism.cs b/src/Renci.SshNet/Security/BouncyCastle/math/ec/endo/GlvEndomorphism.cs index 143a369b..acfae526 100644 --- a/src/Renci.SshNet/Security/BouncyCastle/math/ec/endo/GlvEndomorphism.cs +++ b/src/Renci.SshNet/Security/BouncyCastle/math/ec/endo/GlvEndomorphism.cs @@ -3,7 +3,7 @@ namespace Renci.SshNet.Security.Org.BouncyCastle.Math.EC.Endo { internal interface GlvEndomorphism - : ECEndomorphism + : ECEndomorphism { BigInteger[] DecomposeScalar(BigInteger k); } diff --git a/src/Renci.SshNet/Security/Chaos.NaCl/.editorconfig b/src/Renci.SshNet/Security/Chaos.NaCl/.editorconfig new file mode 100644 index 00000000..9440813d --- /dev/null +++ b/src/Renci.SshNet/Security/Chaos.NaCl/.editorconfig @@ -0,0 +1,6 @@ +[*.cs] + +generated_code = true + +# Do not reported any diagnostics for "imported" code +dotnet_analyzer_diagnostic.severity = none diff --git a/src/Renci.SshNet/Security/Cryptography/.editorconfig b/src/Renci.SshNet/Security/Cryptography/.editorconfig new file mode 100644 index 00000000..7e36b6e5 --- /dev/null +++ b/src/Renci.SshNet/Security/Cryptography/.editorconfig @@ -0,0 +1,12 @@ +[Bcrypt.cs] + +generated_code = true + +# IDE0005: Remove unnecessary using directives +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0005 +dotnet_diagnostic.IDE0005.severity = none + +# IDE0007: Use var instead of explicit type +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0007-ide0008 +# +dotnet_diagnostic.IDE0007.severity = none \ No newline at end of file diff --git a/src/Renci.SshNet/Security/Cryptography/Bcrypt.cs b/src/Renci.SshNet/Security/Cryptography/Bcrypt.cs index 837d0031..14f6169a 100644 --- a/src/Renci.SshNet/Security/Cryptography/Bcrypt.cs +++ b/src/Renci.SshNet/Security/Cryptography/Bcrypt.cs @@ -499,14 +499,10 @@ namespace Renci.SshNet.Security.Cryptography throw new ArgumentOutOfRangeException("workFactor", "The work factor must be between 4 and 31 (inclusive)"); byte[] rnd = new byte[BCRYPT_SALT_LEN]; -#if FEATURE_RNG_CREATE + RandomNumberGenerator rng = RandomNumberGenerator.Create(); -#elif FEATURE_RNG_CSP - RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider(); -#endif -#if FEATURE_RNG_CREATE || FEATURE_RNG_CSP + rng.GetBytes(rnd); -#endif StringBuilder rs = new StringBuilder(); rs.AppendFormat("$2a${0:00}$", workFactor); diff --git a/src/Renci.SshNet/Security/Cryptography/BlockCipher.cs b/src/Renci.SshNet/Security/Cryptography/BlockCipher.cs index 18cf81cb..62050068 100644 --- a/src/Renci.SshNet/Security/Cryptography/BlockCipher.cs +++ b/src/Renci.SshNet/Security/Cryptography/BlockCipher.cs @@ -60,27 +60,27 @@ namespace Renci.SshNet.Security.Cryptography _mode = mode; _padding = padding; - if (_mode != null) - _mode.Init(this); + _mode?.Init(this); } /// /// Encrypts the specified data. /// - /// The data. - /// The zero-based offset in at which to begin encrypting. - /// The number of bytes to encrypt from . + /// The data. + /// The zero-based offset in at which to begin encrypting. + /// The number of bytes to encrypt from . /// Encrypted data - public override byte[] Encrypt(byte[] data, int offset, int length) + public override byte[] Encrypt(byte[] input, int offset, int length) { if (length % _blockSize > 0) { - if (_padding == null) + if (_padding is null) { throw new ArgumentException("data"); } + var paddingLength = _blockSize - (length % _blockSize); - data = _padding.Pad(data, offset, length, paddingLength); + input = _padding.Pad(input, offset, length, paddingLength); length += paddingLength; offset = 0; } @@ -90,13 +90,13 @@ namespace Renci.SshNet.Security.Cryptography for (var i = 0; i < length / _blockSize; i++) { - if (_mode == null) + if (_mode is null) { - writtenBytes += EncryptBlock(data, offset + (i * _blockSize), _blockSize, output, i * _blockSize); + writtenBytes += EncryptBlock(input, offset + (i * _blockSize), _blockSize, output, i * _blockSize); } else { - writtenBytes += _mode.EncryptBlock(data, offset + (i * _blockSize), _blockSize, output, i * _blockSize); + writtenBytes += _mode.EncryptBlock(input, offset + (i * _blockSize), _blockSize, output, i * _blockSize); } } @@ -111,33 +111,34 @@ namespace Renci.SshNet.Security.Cryptography /// /// Decrypts the specified data. /// - /// The data. + /// The data. /// Decrypted data - public override byte[] Decrypt(byte[] data) + public override byte[] Decrypt(byte[] input) { - return Decrypt(data, 0, data.Length); + return Decrypt(input, 0, input.Length); } /// /// Decrypts the specified input. /// - /// The input. - /// The zero-based offset in at which to begin decrypting. - /// The number of bytes to decrypt from . + /// The input. + /// The zero-based offset in at which to begin decrypting. + /// The number of bytes to decrypt from . /// /// The decrypted data. /// - public override byte[] Decrypt(byte[] data, int offset, int length) + public override byte[] Decrypt(byte[] input, int offset, int length) { if (length % _blockSize > 0) { - if (_padding == null) + if (_padding is null) { throw new ArgumentException("data"); } - data = _padding.Pad(_blockSize, data, offset, length); + + input = _padding.Pad(_blockSize, input, offset, length); offset = 0; - length = data.Length; + length = input.Length; } var output = new byte[length]; @@ -145,13 +146,13 @@ namespace Renci.SshNet.Security.Cryptography var writtenBytes = 0; for (var i = 0; i < length / _blockSize; i++) { - if (_mode == null) + if (_mode is null) { - writtenBytes += DecryptBlock(data, offset + (i * _blockSize), _blockSize, output, i * _blockSize); + writtenBytes += DecryptBlock(input, offset + (i * _blockSize), _blockSize, output, i * _blockSize); } else { - writtenBytes += _mode.DecryptBlock(data, offset + (i * _blockSize), _blockSize, output, i * _blockSize); + writtenBytes += _mode.DecryptBlock(input, offset + (i * _blockSize), _blockSize, output, i * _blockSize); } } diff --git a/src/Renci.SshNet/Security/Cryptography/CipherDigitalSignature.cs b/src/Renci.SshNet/Security/Cryptography/CipherDigitalSignature.cs index 752f9701..ec9243fe 100644 --- a/src/Renci.SshNet/Security/Cryptography/CipherDigitalSignature.cs +++ b/src/Renci.SshNet/Security/Cryptography/CipherDigitalSignature.cs @@ -18,8 +18,10 @@ namespace Renci.SshNet.Security.Cryptography /// The cipher. protected CipherDigitalSignature(ObjectIdentifier oid, AsymmetricCipher cipher) { - if (cipher == null) - throw new ArgumentNullException("cipher"); + if (cipher is null) + { + throw new ArgumentNullException(nameof(cipher)); + } _cipher = cipher; _oid = oid; @@ -50,10 +52,10 @@ namespace Renci.SshNet.Security.Cryptography /// public override byte[] Sign(byte[] input) { - // Calculate hash value + // Calculate hash value var hashData = Hash(input); - // Calculate DER string + // Calculate DER string var derEncodedHash = DerEncode(hashData); return _cipher.Encrypt(derEncodedHash).TrimLeadingZeros(); @@ -70,7 +72,9 @@ namespace Renci.SshNet.Security.Cryptography /// Encodes hash using DER. /// /// The hash data. - /// DER Encoded byte array + /// + /// DER Encoded byte array. + /// protected byte[] DerEncode(byte[] hashData) { var alg = new DerData(); diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.cs index b4225862..19fd1cd7 100644 --- a/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.cs +++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.cs @@ -1,5 +1,6 @@ using System; using System.Globalization; + using Renci.SshNet.Common; namespace Renci.SshNet.Security.Cryptography.Ciphers @@ -9,19 +10,14 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers /// public sealed class AesCipher : BlockCipher { - private const uint m1 = 0x80808080; - - private const uint m2 = 0x7f7f7f7f; - - private const uint m3 = 0x0000001b; + private const uint M1 = 0x80808080; + private const uint M2 = 0x7f7f7f7f; + private const uint M3 = 0x0000001b; private int _rounds; - private uint[] _encryptionKey; - private uint[] _decryptionKey; - - private uint C0, C1, C2, C3; + private uint _c0, _c1, _c2, _c3; #region Static Definition Tables @@ -99,7 +95,7 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers }; // vector used in calculating key schedule (powers of x in GF(256)) - private static readonly byte[] rcon = + private static readonly byte[] Rcon = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 @@ -569,8 +565,10 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers { var keySize = key.Length * 8; - if (!(keySize == 256 || keySize == 192 || keySize == 128)) + if (keySize is not (256 or 192 or 128)) + { throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "KeySize '{0}' is not valid for this algorithm.", keySize)); + } } /// @@ -588,11 +586,15 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers /// or is too short. public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { - if (inputBuffer == null) - throw new ArgumentNullException("inputBuffer"); + if (inputBuffer is null) + { + throw new ArgumentNullException(nameof(inputBuffer)); + } - if (outputBuffer == null) - throw new ArgumentNullException("outputBuffer"); + if (outputBuffer is null) + { + throw new ArgumentNullException(nameof(outputBuffer)); + } if ((inputOffset + (32 / 2)) > inputBuffer.Length) { @@ -604,10 +606,7 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers throw new IndexOutOfRangeException("output buffer too short"); } - if (_encryptionKey == null) - { - _encryptionKey = GenerateWorkingKey(true, Key); - } + _encryptionKey ??= GenerateWorkingKey(isEncryption: true, Key); UnPackBlock(inputBuffer, inputOffset); @@ -633,11 +632,15 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers /// or is too short. public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { - if (inputBuffer == null) - throw new ArgumentNullException("inputBuffer"); + if (inputBuffer is null) + { + throw new ArgumentNullException(nameof(inputBuffer)); + } - if (outputBuffer == null) - throw new ArgumentNullException("outputBuffer"); + if (outputBuffer is null) + { + throw new ArgumentNullException(nameof(outputBuffer)); + } if ((inputOffset + (32 / 2)) > inputBuffer.Length) { @@ -649,10 +652,7 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers throw new IndexOutOfRangeException("output buffer too short"); } - if (_decryptionKey == null) - { - _decryptionKey = GenerateWorkingKey(false, Key); - } + _decryptionKey ??= GenerateWorkingKey(isEncryption: false, Key); UnPackBlock(inputBuffer, inputOffset); @@ -665,21 +665,23 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers private uint[] GenerateWorkingKey(bool isEncryption, byte[] key) { - int KC = key.Length / 4; // key length in words + var KC = key.Length / 4; // key length in words if (((KC != 4) && (KC != 6) && (KC != 8)) || ((KC * 4) != key.Length)) + { throw new ArgumentException("Key length not 128/192/256 bits."); + } _rounds = KC + 6; // This is not always true for the generalized Rijndael that allows larger block sizes - uint[] W = new uint[(_rounds + 1) * 4]; // 4 words in a block + var W = new uint[(_rounds + 1) * 4]; // 4 words in a block // // copy the key into the round key array // - int t = 0; + var t = 0; - for (int i = 0; i < key.Length; t++) + for (var i = 0; i < key.Length; t++) { W[(t >> 2) * 4 + (t & 3)] = Pack.LittleEndianToUInt32(key, i); i += 4; @@ -689,13 +691,13 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers // while not enough round key material calculated // calculate new values // - int k = (_rounds + 1) << 2; - for (int i = KC; (i < k); i++) + var k = (_rounds + 1) << 2; + for (var i = KC; i < k; i++) { - uint temp = W[((i - 1) >> 2) * 4 + ((i - 1) & 3)]; + var temp = W[((i - 1) >> 2) * 4 + ((i - 1) & 3)]; if ((i % KC) == 0) { - temp = SubWord(Shift(temp, 8)) ^ rcon[(i / KC) - 1]; + temp = SubWord(Shift(temp, 8)) ^ Rcon[(i / KC) - 1]; } else if ((KC > 6) && ((i % KC) == 4)) { @@ -707,9 +709,9 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers if (!isEncryption) { - for (int j = 1; j < _rounds; j++) + for (var j = 1; j < _rounds; j++) { - for (int i = 0; i < 4; i++) + for (var i = 0; i < 4; i++) { W[j * 4 + i] = InvMcol(W[j * 4 + i]); } @@ -726,15 +728,15 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers private static uint FFmulX(uint x) { - return ((x & m2) << 1) ^ (((x & m1) >> 7) * m3); + return ((x & M2) << 1) ^ (((x & M1) >> 7) * M3); } private static uint InvMcol(uint x) { - uint f2 = FFmulX(x); - uint f4 = FFmulX(f2); - uint f8 = FFmulX(f4); - uint f9 = x ^ f8; + var f2 = FFmulX(x); + var f4 = FFmulX(f2); + var f8 = FFmulX(f4); + var f9 = x ^ f8; return f2 ^ f4 ^ f8 ^ Shift(f2 ^ f9, 8) ^ Shift(f4 ^ f9, 16) ^ Shift(f9, 24); } @@ -749,18 +751,18 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers private void UnPackBlock(byte[] bytes, int off) { - C0 = Pack.LittleEndianToUInt32(bytes, off); - C1 = Pack.LittleEndianToUInt32(bytes, off + 4); - C2 = Pack.LittleEndianToUInt32(bytes, off + 8); - C3 = Pack.LittleEndianToUInt32(bytes, off + 12); + _c0 = Pack.LittleEndianToUInt32(bytes, off); + _c1 = Pack.LittleEndianToUInt32(bytes, off + 4); + _c2 = Pack.LittleEndianToUInt32(bytes, off + 8); + _c3 = Pack.LittleEndianToUInt32(bytes, off + 12); } private void PackBlock(byte[] bytes, int off) { - Pack.UInt32ToLittleEndian(C0, bytes, off); - Pack.UInt32ToLittleEndian(C1, bytes, off + 4); - Pack.UInt32ToLittleEndian(C2, bytes, off + 8); - Pack.UInt32ToLittleEndian(C3, bytes, off + 12); + Pack.UInt32ToLittleEndian(_c0, bytes, off); + Pack.UInt32ToLittleEndian(_c1, bytes, off + 4); + Pack.UInt32ToLittleEndian(_c2, bytes, off + 8); + Pack.UInt32ToLittleEndian(_c3, bytes, off + 12); } private void EncryptBlock(uint[] KW) @@ -768,34 +770,34 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers int r; uint r0, r1, r2, r3; - C0 ^= KW[0 * 4 + 0]; - C1 ^= KW[0 * 4 + 1]; - C2 ^= KW[0 * 4 + 2]; - C3 ^= KW[0 * 4 + 3]; + _c0 ^= KW[0 * 4 + 0]; + _c1 ^= KW[0 * 4 + 1]; + _c2 ^= KW[0 * 4 + 2]; + _c3 ^= KW[0 * 4 + 3]; for (r = 1; r < _rounds - 1;) { - r0 = T0[C0 & 255] ^ T1[(C1 >> 8) & 255] ^ T2[(C2 >> 16) & 255] ^ T3[C3 >> 24] ^ KW[r * 4 + 0]; - r1 = T0[C1 & 255] ^ T1[(C2 >> 8) & 255] ^ T2[(C3 >> 16) & 255] ^ T3[C0 >> 24] ^ KW[r * 4 + 1]; - r2 = T0[C2 & 255] ^ T1[(C3 >> 8) & 255] ^ T2[(C0 >> 16) & 255] ^ T3[C1 >> 24] ^ KW[r * 4 + 2]; - r3 = T0[C3 & 255] ^ T1[(C0 >> 8) & 255] ^ T2[(C1 >> 16) & 255] ^ T3[C2 >> 24] ^ KW[r++ * 4 + 3]; - C0 = T0[r0 & 255] ^ T1[(r1 >> 8) & 255] ^ T2[(r2 >> 16) & 255] ^ T3[r3 >> 24] ^ KW[r * 4 + 0]; - C1 = T0[r1 & 255] ^ T1[(r2 >> 8) & 255] ^ T2[(r3 >> 16) & 255] ^ T3[r0 >> 24] ^ KW[r * 4 + 1]; - C2 = T0[r2 & 255] ^ T1[(r3 >> 8) & 255] ^ T2[(r0 >> 16) & 255] ^ T3[r1 >> 24] ^ KW[r * 4 + 2]; - C3 = T0[r3 & 255] ^ T1[(r0 >> 8) & 255] ^ T2[(r1 >> 16) & 255] ^ T3[r2 >> 24] ^ KW[r++ * 4 + 3]; + r0 = T0[_c0 & 255] ^ T1[(_c1 >> 8) & 255] ^ T2[(_c2 >> 16) & 255] ^ T3[_c3 >> 24] ^ KW[r * 4 + 0]; + r1 = T0[_c1 & 255] ^ T1[(_c2 >> 8) & 255] ^ T2[(_c3 >> 16) & 255] ^ T3[_c0 >> 24] ^ KW[r * 4 + 1]; + r2 = T0[_c2 & 255] ^ T1[(_c3 >> 8) & 255] ^ T2[(_c0 >> 16) & 255] ^ T3[_c1 >> 24] ^ KW[r * 4 + 2]; + r3 = T0[_c3 & 255] ^ T1[(_c0 >> 8) & 255] ^ T2[(_c1 >> 16) & 255] ^ T3[_c2 >> 24] ^ KW[r++ * 4 + 3]; + _c0 = T0[r0 & 255] ^ T1[(r1 >> 8) & 255] ^ T2[(r2 >> 16) & 255] ^ T3[r3 >> 24] ^ KW[r * 4 + 0]; + _c1 = T0[r1 & 255] ^ T1[(r2 >> 8) & 255] ^ T2[(r3 >> 16) & 255] ^ T3[r0 >> 24] ^ KW[r * 4 + 1]; + _c2 = T0[r2 & 255] ^ T1[(r3 >> 8) & 255] ^ T2[(r0 >> 16) & 255] ^ T3[r1 >> 24] ^ KW[r * 4 + 2]; + _c3 = T0[r3 & 255] ^ T1[(r0 >> 8) & 255] ^ T2[(r1 >> 16) & 255] ^ T3[r2 >> 24] ^ KW[r++ * 4 + 3]; } - r0 = T0[C0 & 255] ^ T1[(C1 >> 8) & 255] ^ T2[(C2 >> 16) & 255] ^ T3[C3 >> 24] ^ KW[r * 4 + 0]; - r1 = T0[C1 & 255] ^ T1[(C2 >> 8) & 255] ^ T2[(C3 >> 16) & 255] ^ T3[C0 >> 24] ^ KW[r * 4 + 1]; - r2 = T0[C2 & 255] ^ T1[(C3 >> 8) & 255] ^ T2[(C0 >> 16) & 255] ^ T3[C1 >> 24] ^ KW[r * 4 + 2]; - r3 = T0[C3 & 255] ^ T1[(C0 >> 8) & 255] ^ T2[(C1 >> 16) & 255] ^ T3[C2 >> 24] ^ KW[r++ * 4 + 3]; + r0 = T0[_c0 & 255] ^ T1[(_c1 >> 8) & 255] ^ T2[(_c2 >> 16) & 255] ^ T3[_c3 >> 24] ^ KW[r * 4 + 0]; + r1 = T0[_c1 & 255] ^ T1[(_c2 >> 8) & 255] ^ T2[(_c3 >> 16) & 255] ^ T3[_c0 >> 24] ^ KW[r * 4 + 1]; + r2 = T0[_c2 & 255] ^ T1[(_c3 >> 8) & 255] ^ T2[(_c0 >> 16) & 255] ^ T3[_c1 >> 24] ^ KW[r * 4 + 2]; + r3 = T0[_c3 & 255] ^ T1[(_c0 >> 8) & 255] ^ T2[(_c1 >> 16) & 255] ^ T3[_c2 >> 24] ^ KW[r++ * 4 + 3]; // the final round's table is a simple function of S so we don't use a whole other four tables for it - C0 = (uint)S[r0 & 255] ^ (((uint)S[(r1 >> 8) & 255]) << 8) ^ (((uint)S[(r2 >> 16) & 255]) << 16) ^ (((uint)S[r3 >> 24]) << 24) ^ KW[r * 4 + 0]; - C1 = (uint)S[r1 & 255] ^ (((uint)S[(r2 >> 8) & 255]) << 8) ^ (((uint)S[(r3 >> 16) & 255]) << 16) ^ (((uint)S[r0 >> 24]) << 24) ^ KW[r * 4 + 1]; - C2 = (uint)S[r2 & 255] ^ (((uint)S[(r3 >> 8) & 255]) << 8) ^ (((uint)S[(r0 >> 16) & 255]) << 16) ^ (((uint)S[r1 >> 24]) << 24) ^ KW[r * 4 + 2]; - C3 = (uint)S[r3 & 255] ^ (((uint)S[(r0 >> 8) & 255]) << 8) ^ (((uint)S[(r1 >> 16) & 255]) << 16) ^ (((uint)S[r2 >> 24]) << 24) ^ KW[r * 4 + 3]; + _c0 = (uint)S[r0 & 255] ^ (((uint)S[(r1 >> 8) & 255]) << 8) ^ (((uint)S[(r2 >> 16) & 255]) << 16) ^ (((uint)S[r3 >> 24]) << 24) ^ KW[r * 4 + 0]; + _c1 = (uint)S[r1 & 255] ^ (((uint)S[(r2 >> 8) & 255]) << 8) ^ (((uint)S[(r3 >> 16) & 255]) << 16) ^ (((uint)S[r0 >> 24]) << 24) ^ KW[r * 4 + 1]; + _c2 = (uint)S[r2 & 255] ^ (((uint)S[(r3 >> 8) & 255]) << 8) ^ (((uint)S[(r0 >> 16) & 255]) << 16) ^ (((uint)S[r1 >> 24]) << 24) ^ KW[r * 4 + 2]; + _c3 = (uint)S[r3 & 255] ^ (((uint)S[(r0 >> 8) & 255]) << 8) ^ (((uint)S[(r1 >> 16) & 255]) << 16) ^ (((uint)S[r2 >> 24]) << 24) ^ KW[r * 4 + 3]; } private void DecryptBlock(uint[] KW) @@ -803,34 +805,34 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers int r; uint r0, r1, r2, r3; - C0 ^= KW[_rounds * 4 + 0]; - C1 ^= KW[_rounds * 4 + 1]; - C2 ^= KW[_rounds * 4 + 2]; - C3 ^= KW[_rounds * 4 + 3]; + _c0 ^= KW[_rounds * 4 + 0]; + _c1 ^= KW[_rounds * 4 + 1]; + _c2 ^= KW[_rounds * 4 + 2]; + _c3 ^= KW[_rounds * 4 + 3]; for (r = _rounds - 1; r > 1;) { - r0 = Tinv0[C0 & 255] ^ Tinv1[(C3 >> 8) & 255] ^ Tinv2[(C2 >> 16) & 255] ^ Tinv3[C1 >> 24] ^ KW[r * 4 + 0]; - r1 = Tinv0[C1 & 255] ^ Tinv1[(C0 >> 8) & 255] ^ Tinv2[(C3 >> 16) & 255] ^ Tinv3[C2 >> 24] ^ KW[r * 4 + 1]; - r2 = Tinv0[C2 & 255] ^ Tinv1[(C1 >> 8) & 255] ^ Tinv2[(C0 >> 16) & 255] ^ Tinv3[C3 >> 24] ^ KW[r * 4 + 2]; - r3 = Tinv0[C3 & 255] ^ Tinv1[(C2 >> 8) & 255] ^ Tinv2[(C1 >> 16) & 255] ^ Tinv3[C0 >> 24] ^ KW[r-- * 4 + 3]; - C0 = Tinv0[r0 & 255] ^ Tinv1[(r3 >> 8) & 255] ^ Tinv2[(r2 >> 16) & 255] ^ Tinv3[r1 >> 24] ^ KW[r * 4 + 0]; - C1 = Tinv0[r1 & 255] ^ Tinv1[(r0 >> 8) & 255] ^ Tinv2[(r3 >> 16) & 255] ^ Tinv3[r2 >> 24] ^ KW[r * 4 + 1]; - C2 = Tinv0[r2 & 255] ^ Tinv1[(r1 >> 8) & 255] ^ Tinv2[(r0 >> 16) & 255] ^ Tinv3[r3 >> 24] ^ KW[r * 4 + 2]; - C3 = Tinv0[r3 & 255] ^ Tinv1[(r2 >> 8) & 255] ^ Tinv2[(r1 >> 16) & 255] ^ Tinv3[r0 >> 24] ^ KW[r-- * 4 + 3]; + r0 = Tinv0[_c0 & 255] ^ Tinv1[(_c3 >> 8) & 255] ^ Tinv2[(_c2 >> 16) & 255] ^ Tinv3[_c1 >> 24] ^ KW[r * 4 + 0]; + r1 = Tinv0[_c1 & 255] ^ Tinv1[(_c0 >> 8) & 255] ^ Tinv2[(_c3 >> 16) & 255] ^ Tinv3[_c2 >> 24] ^ KW[r * 4 + 1]; + r2 = Tinv0[_c2 & 255] ^ Tinv1[(_c1 >> 8) & 255] ^ Tinv2[(_c0 >> 16) & 255] ^ Tinv3[_c3 >> 24] ^ KW[r * 4 + 2]; + r3 = Tinv0[_c3 & 255] ^ Tinv1[(_c2 >> 8) & 255] ^ Tinv2[(_c1 >> 16) & 255] ^ Tinv3[_c0 >> 24] ^ KW[r-- * 4 + 3]; + _c0 = Tinv0[r0 & 255] ^ Tinv1[(r3 >> 8) & 255] ^ Tinv2[(r2 >> 16) & 255] ^ Tinv3[r1 >> 24] ^ KW[r * 4 + 0]; + _c1 = Tinv0[r1 & 255] ^ Tinv1[(r0 >> 8) & 255] ^ Tinv2[(r3 >> 16) & 255] ^ Tinv3[r2 >> 24] ^ KW[r * 4 + 1]; + _c2 = Tinv0[r2 & 255] ^ Tinv1[(r1 >> 8) & 255] ^ Tinv2[(r0 >> 16) & 255] ^ Tinv3[r3 >> 24] ^ KW[r * 4 + 2]; + _c3 = Tinv0[r3 & 255] ^ Tinv1[(r2 >> 8) & 255] ^ Tinv2[(r1 >> 16) & 255] ^ Tinv3[r0 >> 24] ^ KW[r-- * 4 + 3]; } - r0 = Tinv0[C0 & 255] ^ Tinv1[(C3 >> 8) & 255] ^ Tinv2[(C2 >> 16) & 255] ^ Tinv3[C1 >> 24] ^ KW[r * 4 + 0]; - r1 = Tinv0[C1 & 255] ^ Tinv1[(C0 >> 8) & 255] ^ Tinv2[(C3 >> 16) & 255] ^ Tinv3[C2 >> 24] ^ KW[r * 4 + 1]; - r2 = Tinv0[C2 & 255] ^ Tinv1[(C1 >> 8) & 255] ^ Tinv2[(C0 >> 16) & 255] ^ Tinv3[C3 >> 24] ^ KW[r * 4 + 2]; - r3 = Tinv0[C3 & 255] ^ Tinv1[(C2 >> 8) & 255] ^ Tinv2[(C1 >> 16) & 255] ^ Tinv3[C0 >> 24] ^ KW[r * 4 + 3]; + r0 = Tinv0[_c0 & 255] ^ Tinv1[(_c3 >> 8) & 255] ^ Tinv2[(_c2 >> 16) & 255] ^ Tinv3[_c1 >> 24] ^ KW[r * 4 + 0]; + r1 = Tinv0[_c1 & 255] ^ Tinv1[(_c0 >> 8) & 255] ^ Tinv2[(_c3 >> 16) & 255] ^ Tinv3[_c2 >> 24] ^ KW[r * 4 + 1]; + r2 = Tinv0[_c2 & 255] ^ Tinv1[(_c1 >> 8) & 255] ^ Tinv2[(_c0 >> 16) & 255] ^ Tinv3[_c3 >> 24] ^ KW[r * 4 + 2]; + r3 = Tinv0[_c3 & 255] ^ Tinv1[(_c2 >> 8) & 255] ^ Tinv2[(_c1 >> 16) & 255] ^ Tinv3[_c0 >> 24] ^ KW[r * 4 + 3]; // the final round's table is a simple function of Si so we don't use a whole other four tables for it - C0 = (uint)Si[r0 & 255] ^ (((uint)Si[(r3 >> 8) & 255]) << 8) ^ (((uint)Si[(r2 >> 16) & 255]) << 16) ^ (((uint)Si[r1 >> 24]) << 24) ^ KW[0 * 4 + 0]; - C1 = (uint)Si[r1 & 255] ^ (((uint)Si[(r0 >> 8) & 255]) << 8) ^ (((uint)Si[(r3 >> 16) & 255]) << 16) ^ (((uint)Si[r2 >> 24]) << 24) ^ KW[0 * 4 + 1]; - C2 = (uint)Si[r2 & 255] ^ (((uint)Si[(r1 >> 8) & 255]) << 8) ^ (((uint)Si[(r0 >> 16) & 255]) << 16) ^ (((uint)Si[r3 >> 24]) << 24) ^ KW[0 * 4 + 2]; - C3 = (uint)Si[r3 & 255] ^ (((uint)Si[(r2 >> 8) & 255]) << 8) ^ (((uint)Si[(r1 >> 16) & 255]) << 16) ^ (((uint)Si[r0 >> 24]) << 24) ^ KW[0 * 4 + 3]; + _c0 = (uint)Si[r0 & 255] ^ (((uint)Si[(r3 >> 8) & 255]) << 8) ^ (((uint)Si[(r2 >> 16) & 255]) << 16) ^ (((uint)Si[r1 >> 24]) << 24) ^ KW[0 * 4 + 0]; + _c1 = (uint)Si[r1 & 255] ^ (((uint)Si[(r0 >> 8) & 255]) << 8) ^ (((uint)Si[(r3 >> 16) & 255]) << 16) ^ (((uint)Si[r2 >> 24]) << 24) ^ KW[0 * 4 + 1]; + _c2 = (uint)Si[r2 & 255] ^ (((uint)Si[(r1 >> 8) & 255]) << 8) ^ (((uint)Si[(r0 >> 16) & 255]) << 16) ^ (((uint)Si[r3 >> 24]) << 24) ^ KW[0 * 4 + 2]; + _c3 = (uint)Si[r3 & 255] ^ (((uint)Si[(r2 >> 8) & 255]) << 8) ^ (((uint)Si[(r1 >> 16) & 255]) << 16) ^ (((uint)Si[r0 >> 24]) << 24) ^ KW[0 * 4 + 3]; } } } diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/Arc4Cipher.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/Arc4Cipher.cs index 0707ce2e..9a256287 100644 --- a/src/Renci.SshNet/Security/Cryptography/Ciphers/Arc4Cipher.cs +++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/Arc4Cipher.cs @@ -18,8 +18,6 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers private int _y; - private byte[] _workingKey; - /// /// Gets the minimum data size. /// @@ -40,14 +38,14 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers public Arc4Cipher(byte[] key, bool dischargeFirstBytes) : base(key) { - _workingKey = key; - SetKey(_workingKey); - // The first 1536 bytes of keystream - // generated by the cipher MUST be discarded, and the first byte of the - // first encrypted packet MUST be encrypted using the 1537th byte of - // keystream. + SetKey(key); + + // The first 1536 bytes of keystream generated by the cipher MUST be discarded, and the first byte of the + // first encrypted packet MUST be encrypted using the 1537th byte of keystream. if (dischargeFirstBytes) - Encrypt(new byte[1536]); + { + _ = Encrypt(new byte[1536]); + } } /// @@ -94,7 +92,7 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers public override byte[] Encrypt(byte[] input, int offset, int length) { var output = new byte[length]; - ProcessBytes(input, offset, length, output, 0); + _ = ProcessBytes(input, offset, length, output, 0); return output; } @@ -122,7 +120,7 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers public override byte[] Decrypt(byte[] input, int offset, int length) { var output = new byte[length]; - ProcessBytes(input, offset, length, output, 0); + _ = ProcessBytes(input, offset, length, output, 0); return output; } @@ -151,20 +149,16 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers // xor outputBuffer[i + outputOffset] = (byte)(inputBuffer[i + inputOffset] ^ _engineState[(_engineState[_x] + _engineState[_y]) & 0xff]); } + return inputCount; } private void SetKey(byte[] keyBytes) { - _workingKey = keyBytes; - _x = 0; _y = 0; - if (_engineState == null) - { - _engineState = new byte[STATE_LENGTH]; - } + _engineState ??= new byte[STATE_LENGTH]; // reset the state of the engine for (var i = 0; i < STATE_LENGTH; i++) @@ -178,6 +172,7 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers for (var i = 0; i < STATE_LENGTH; i++) { i2 = ((keyBytes[i1] & 0xff) + _engineState[i] + i2) & 0xff; + // do the byte-swap inline var tmp = _engineState[i]; _engineState[i] = _engineState[i2]; diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/BlowfishCipher.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/BlowfishCipher.cs index d68d3988..f43940c5 100644 --- a/src/Renci.SshNet/Security/Cryptography/Ciphers/BlowfishCipher.cs +++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/BlowfishCipher.cs @@ -322,8 +322,10 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers { var keySize = key.Length * 8; - if (keySize < 1 || keySize > 448) + if (keySize is < 1 or > 448) + { throw new ArgumentException(string.Format("KeySize '{0}' is not valid for this algorithm.", keySize)); + } _s0 = new uint[SboxSk]; _s1 = new uint[SboxSk]; @@ -348,14 +350,16 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { if (inputCount != BlockSize) + { throw new ArgumentException("inputCount"); + } - uint xl = Pack.BigEndianToUInt32(inputBuffer, inputOffset); - uint xr = Pack.BigEndianToUInt32(inputBuffer, inputOffset + 4); + var xl = Pack.BigEndianToUInt32(inputBuffer, inputOffset); + var xr = Pack.BigEndianToUInt32(inputBuffer, inputOffset + 4); xl ^= _p[0]; - for (int i = 1; i < Rounds; i += 2) + for (var i = 1; i < Rounds; i += 2) { xr ^= F(xl) ^ _p[i]; xl ^= F(xr) ^ _p[i + 1]; @@ -383,7 +387,9 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { if (inputCount != BlockSize) + { throw new ArgumentException("inputCount"); + } var xl = Pack.BigEndianToUInt32(inputBuffer, inputOffset); var xr = Pack.BigEndianToUInt32(inputBuffer, inputOffset + 4); @@ -406,7 +412,7 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers private uint F(uint x) { - return (((_s0[x >> 24] + _s1[(x >> 16) & 0xff]) ^ _s2[(x >> 8) & 0xff]) + _s3[x & 0xff]); + return ((_s0[x >> 24] + _s1[(x >> 16) & 0xff]) ^ _s2[(x >> 8) & 0xff]) + _s3[x & 0xff]; } private void SetKey(byte[] key) @@ -451,6 +457,7 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers keyIndex = 0; } } + // XOR the newly created 32 bit chunk onto the P-array _p[i] ^= data; } diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/CastCipher.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/CastCipher.cs index e7577f39..e2b0779e 100644 --- a/src/Renci.SshNet/Security/Cryptography/Ciphers/CastCipher.cs +++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/CastCipher.cs @@ -38,7 +38,9 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers var keySize = key.Length * 8; if (!(keySize >= 40 && keySize <= 128 && keySize % 8 == 0)) + { throw new ArgumentException(string.Format("KeySize '{0}' is not valid for this algorithm.", keySize)); + } SetKey(key); } @@ -625,6 +627,7 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers var rp = ri; // equivalent to R[i-1] li = rp; + switch (i) { case 1: @@ -649,6 +652,9 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers case 15: ri = lp ^ F3(rp, _km[i], _kr[i]); break; + default: + // We should never get here as max. rounds is 16 + break; } } @@ -666,6 +672,7 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers var rp = ri; // equivalent to R[i-1] li = rp; + switch (i) { case 1: @@ -690,6 +697,9 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers case 15: ri = lp ^ F3(rp, _km[i], _kr[i]); break; + default: + // We should never get here as max. rounds is 16 + break; } } diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/DesCipher.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/DesCipher.cs index 1f6a2aa9..29097eea 100644 --- a/src/Renci.SshNet/Security/Cryptography/Ciphers/DesCipher.cs +++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/DesCipher.cs @@ -12,8 +12,6 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers private int[] _decryptionKey; - #region Static tables - private static readonly short[] Bytebit = {128, 64, 32, 16, 8, 4, 2, 1}; private static readonly int[] Bigbyte = @@ -212,8 +210,6 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers 0x00001040, 0x00040040, 0x10000000, 0x10041000 }; - #endregion - /// /// Initializes a new instance of the class. /// @@ -240,16 +236,17 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { if ((inputOffset + BlockSize) > inputBuffer.Length) + { throw new IndexOutOfRangeException("input buffer too short"); + } if ((outputOffset + BlockSize) > outputBuffer.Length) - throw new IndexOutOfRangeException("output buffer too short"); - - if (_encryptionKey == null) { - _encryptionKey = GenerateWorkingKey(true, Key); + throw new IndexOutOfRangeException("output buffer too short"); } + _encryptionKey ??= GenerateWorkingKey(encrypting: true, Key); + DesFunc(_encryptionKey, inputBuffer, inputOffset, outputBuffer, outputOffset); return BlockSize; @@ -269,16 +266,17 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { if ((inputOffset + BlockSize) > inputBuffer.Length) + { throw new IndexOutOfRangeException("input buffer too short"); + } if ((outputOffset + BlockSize) > outputBuffer.Length) - throw new IndexOutOfRangeException("output buffer too short"); - - if (_decryptionKey == null) { - _decryptionKey = GenerateWorkingKey(false, Key); + throw new IndexOutOfRangeException("output buffer too short"); } + _decryptionKey ??= GenerateWorkingKey(encrypting: false, Key); + DesFunc(_decryptionKey, inputBuffer, inputOffset, outputBuffer, outputOffset); return BlockSize; @@ -294,18 +292,18 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers { ValidateKey(); - int[] newKey = new int[32]; - bool[] pc1m = new bool[56]; - bool[] pcr = new bool[56]; + var newKey = new int[32]; + var pc1m = new bool[56]; + var pcr = new bool[56]; - for (int j = 0; j < 56; j++) + for (var j = 0; j < 56; j++) { int l = Pc1[j]; pc1m[j] = ((key[(uint)l >> 3] & Bytebit[l & 07]) != 0); } - for (int i = 0; i < 16; i++) + for (var i = 0; i < 16; i++) { int l, m; @@ -321,7 +319,7 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers var n = m + 1; newKey[m] = newKey[n] = 0; - for (int j = 0; j < 28; j++) + for (var j = 0; j < 28; j++) { l = j + Totrot[i]; if (l < 28) @@ -334,7 +332,7 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers } } - for (int j = 28; j < 56; j++) + for (var j = 28; j < 56; j++) { l = j + Totrot[i]; if (l < 56) @@ -347,7 +345,7 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers } } - for (int j = 0; j < 24; j++) + for (var j = 0; j < 24; j++) { if (pcr[Pc2[j]]) { @@ -364,7 +362,7 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers // // store the processed key // - for (int i = 0; i != 32; i += 2) + for (var i = 0; i != 32; i += 2) { var i1 = newKey[i]; var i2 = newKey[i + 1]; @@ -391,7 +389,9 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers var keySize = Key.Length * 8; if (keySize != 64) + { throw new ArgumentException(string.Format("KeySize '{0}' is not valid for this algorithm.", keySize)); + } } /// @@ -409,16 +409,16 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers var work = ((left >> 4) ^ right) & 0x0f0f0f0f; right ^= work; - left ^= (work << 4); + left ^= work << 4; work = ((left >> 16) ^ right) & 0x0000ffff; right ^= work; - left ^= (work << 16); + left ^= work << 16; work = ((right >> 2) ^ left) & 0x33333333; left ^= work; - right ^= (work << 2); + right ^= work << 2; work = ((right >> 8) ^ left) & 0x00ff00ff; left ^= work; - right ^= (work << 8); + right ^= work << 8; right = (right << 1) | (right >> 31); work = (left ^ right) & 0xaaaaaaaa; left ^= work; @@ -460,16 +460,16 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers left = (left << 31) | (left >> 1); work = ((left >> 8) ^ right) & 0x00ff00ff; right ^= work; - left ^= (work << 8); + left ^= work << 8; work = ((left >> 2) ^ right) & 0x33333333; right ^= work; - left ^= (work << 2); + left ^= work << 2; work = ((right >> 16) ^ left) & 0x0000ffff; left ^= work; - right ^= (work << 16); + right ^= work << 16; work = ((right >> 4) ^ left) & 0x0f0f0f0f; left ^= work; - right ^= (work << 4); + right ^= work << 4; Pack.UInt32ToBigEndian(right, outBytes, outOff); Pack.UInt32ToBigEndian(left, outBytes, outOff + 4); diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CbcCipherMode.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CbcCipherMode.cs index c6ac2f0a..de07141c 100644 --- a/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CbcCipherMode.cs +++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CbcCipherMode.cs @@ -31,20 +31,26 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers.Modes public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { if (inputBuffer.Length - inputOffset < _blockSize) + { throw new ArgumentException("Invalid input buffer"); + } if (outputBuffer.Length - outputOffset < _blockSize) + { throw new ArgumentException("Invalid output buffer"); + } if (inputCount != _blockSize) + { throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "inputCount must be {0}.", _blockSize)); + } - for (int i = 0; i < _blockSize; i++) + for (var i = 0; i < _blockSize; i++) { IV[i] ^= inputBuffer[inputOffset + i]; } - Cipher.EncryptBlock(IV, 0, inputCount, outputBuffer, outputOffset); + _ = Cipher.EncryptBlock(IV, 0, inputCount, outputBuffer, outputOffset); Buffer.BlockCopy(outputBuffer, outputOffset, IV, 0, IV.Length); @@ -65,17 +71,23 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers.Modes public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { if (inputBuffer.Length - inputOffset < _blockSize) + { throw new ArgumentException("Invalid input buffer"); + } if (outputBuffer.Length - outputOffset < _blockSize) + { throw new ArgumentException("Invalid output buffer"); + } if (inputCount != _blockSize) + { throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "inputCount must be {0}.", _blockSize)); + } - Cipher.DecryptBlock(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset); + _ = Cipher.DecryptBlock(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset); - for (int i = 0; i < _blockSize; i++) + for (var i = 0; i < _blockSize; i++) { outputBuffer[outputOffset + i] ^= IV[i]; } diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CfbCipherMode.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CfbCipherMode.cs index 3171e2bb..ae889e87 100644 --- a/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CfbCipherMode.cs +++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CfbCipherMode.cs @@ -34,17 +34,23 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers.Modes public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { if (inputBuffer.Length - inputOffset < _blockSize) + { throw new ArgumentException("Invalid input buffer"); + } if (outputBuffer.Length - outputOffset < _blockSize) + { throw new ArgumentException("Invalid output buffer"); + } if (inputCount != _blockSize) + { throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "inputCount must be {0}.", _blockSize)); + } - Cipher.EncryptBlock(IV, 0, IV.Length, _ivOutput, 0); + _ = Cipher.EncryptBlock(IV, 0, IV.Length, _ivOutput, 0); - for (int i = 0; i < _blockSize; i++) + for (var i = 0; i < _blockSize; i++) { outputBuffer[outputOffset + i] = (byte)(_ivOutput[i] ^ inputBuffer[inputOffset + i]); } @@ -69,20 +75,26 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers.Modes public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { if (inputBuffer.Length - inputOffset < _blockSize) + { throw new ArgumentException("Invalid input buffer"); + } if (outputBuffer.Length - outputOffset < _blockSize) + { throw new ArgumentException("Invalid output buffer"); + } if (inputCount != _blockSize) + { throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "inputCount must be {0}.", _blockSize)); + } - Cipher.EncryptBlock(IV, 0, IV.Length, _ivOutput, 0); + _ = Cipher.EncryptBlock(IV, 0, IV.Length, _ivOutput, 0); Buffer.BlockCopy(IV, _blockSize, IV, 0, IV.Length - _blockSize); Buffer.BlockCopy(inputBuffer, inputOffset, IV, IV.Length - _blockSize, _blockSize); - for (int i = 0; i < _blockSize; i++) + for (var i = 0; i < _blockSize; i++) { outputBuffer[outputOffset + i] = (byte)(_ivOutput[i] ^ inputBuffer[inputOffset + i]); } diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CtrCipherMode.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CtrCipherMode.cs index cbe5d7e6..90149f57 100644 --- a/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CtrCipherMode.cs +++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CtrCipherMode.cs @@ -34,23 +34,32 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers.Modes public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { if (inputBuffer.Length - inputOffset < _blockSize) + { throw new ArgumentException("Invalid input buffer"); + } if (outputBuffer.Length - outputOffset < _blockSize) + { throw new ArgumentException("Invalid output buffer"); + } if (inputCount != _blockSize) + { throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "inputCount must be {0}.", _blockSize)); + } - Cipher.EncryptBlock(IV, 0, IV.Length, _ivOutput, 0); + _ = Cipher.EncryptBlock(IV, 0, IV.Length, _ivOutput, 0); - for (int i = 0; i < _blockSize; i++) + for (var i = 0; i < _blockSize; i++) { outputBuffer[outputOffset + i] = (byte)(_ivOutput[i] ^ inputBuffer[inputOffset + i]); } - int j = IV.Length; - while (--j >= 0 && ++IV[j] == 0) ; + var j = IV.Length; + while (--j >= 0 && ++IV[j] == 0) + { + // Intentionally empty block + } return _blockSize; } @@ -69,23 +78,32 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers.Modes public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { if (inputBuffer.Length - inputOffset < _blockSize) + { throw new ArgumentException("Invalid input buffer"); + } if (outputBuffer.Length - outputOffset < _blockSize) + { throw new ArgumentException("Invalid output buffer"); + } if (inputCount != _blockSize) + { throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "inputCount must be {0}.", _blockSize)); + } - Cipher.EncryptBlock(IV, 0, IV.Length, _ivOutput, 0); + _ = Cipher.EncryptBlock(IV, 0, IV.Length, _ivOutput, 0); - for (int i = 0; i < _blockSize; i++) + for (var i = 0; i < _blockSize; i++) { outputBuffer[outputOffset + i] = (byte)(_ivOutput[i] ^ inputBuffer[inputOffset + i]); } - int j = IV.Length; - while (--j >= 0 && ++IV[j] == 0) ; + var j = IV.Length; + while (--j >= 0 && ++IV[j] == 0) + { + // Intentionally empty block + } return _blockSize; } diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/OfbCipherMode.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/OfbCipherMode.cs index a13cfb0a..1f321f45 100644 --- a/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/OfbCipherMode.cs +++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/OfbCipherMode.cs @@ -34,17 +34,23 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers.Modes public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { if (inputBuffer.Length - inputOffset < _blockSize) + { throw new ArgumentException("Invalid input buffer"); + } if (outputBuffer.Length - outputOffset < _blockSize) + { throw new ArgumentException("Invalid output buffer"); + } if (inputCount != _blockSize) + { throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "inputCount must be {0}.", _blockSize)); + } - Cipher.EncryptBlock(IV, 0, IV.Length, _ivOutput, 0); + _ = Cipher.EncryptBlock(IV, 0, IV.Length, _ivOutput, 0); - for (int i = 0; i < _blockSize; i++) + for (var i = 0; i < _blockSize; i++) { outputBuffer[outputOffset + i] = (byte)(_ivOutput[i] ^ inputBuffer[inputOffset + i]); } @@ -69,17 +75,23 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers.Modes public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { if (inputBuffer.Length - inputOffset < _blockSize) + { throw new ArgumentException("Invalid input buffer"); + } if (outputBuffer.Length - outputOffset < _blockSize) + { throw new ArgumentException("Invalid output buffer"); + } if (inputCount != _blockSize) + { throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "inputCount must be {0}.", _blockSize)); + } - Cipher.EncryptBlock(IV, 0, IV.Length, _ivOutput, 0); + _ = Cipher.EncryptBlock(IV, 0, IV.Length, _ivOutput, 0); - for (int i = 0; i < _blockSize; i++) + for (var i = 0; i < _blockSize; i++) { outputBuffer[outputOffset + i] = (byte)(_ivOutput[i] ^ inputBuffer[inputOffset + i]); } diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/Paddings/PKCS5Padding.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/Paddings/PKCS5Padding.cs index 70d94c20..18e85c59 100644 --- a/src/Renci.SshNet/Security/Cryptography/Ciphers/Paddings/PKCS5Padding.cs +++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/Paddings/PKCS5Padding.cs @@ -3,7 +3,7 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers.Paddings { /// - /// Implements PKCS5 cipher padding + /// Implements PKCS5 cipher padding. /// public class PKCS5Padding : CipherPadding { @@ -37,10 +37,12 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers.Paddings { var output = new byte[length + paddinglength]; Buffer.BlockCopy(input, offset, output, 0, length); + for (var i = 0; i < paddinglength; i++) { output[length + i] = (byte) paddinglength; } + return output; } } diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/Paddings/PKCS7Padding.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/Paddings/PKCS7Padding.cs index 2623d8fd..cfe0ae9a 100644 --- a/src/Renci.SshNet/Security/Cryptography/Ciphers/Paddings/PKCS7Padding.cs +++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/Paddings/PKCS7Padding.cs @@ -37,10 +37,12 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers.Paddings { var output = new byte[length + paddinglength]; Buffer.BlockCopy(input, offset, output, 0, length); + for (var i = 0; i < paddinglength; i++) { output[length + i] = (byte) paddinglength; } + return output; } } diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/RsaCipher.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/RsaCipher.cs index 116471bd..749083c0 100644 --- a/src/Renci.SshNet/Security/Cryptography/Ciphers/RsaCipher.cs +++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/RsaCipher.cs @@ -8,8 +8,6 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers /// public class RsaCipher : AsymmetricCipher { - private readonly bool _isPrivate; - private readonly RsaKey _key; /// @@ -18,23 +16,24 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers /// The RSA key. public RsaCipher(RsaKey key) { - if (key == null) - throw new ArgumentNullException("key"); + if (key is null) + { + throw new ArgumentNullException(nameof(key)); + } _key = key; - _isPrivate = !_key.D.IsZero; } /// /// Encrypts the specified data. /// - /// The data. - /// The zero-based offset in at which to begin encrypting. - /// The number of bytes to encrypt from . + /// The data. + /// The zero-based offset in at which to begin encrypting. + /// The number of bytes to encrypt from . /// Encrypted data. - public override byte[] Encrypt(byte[] data, int offset, int length) + public override byte[] Encrypt(byte[] input, int offset, int length) { - // Calculate signature + // Calculate signature var bitLength = _key.Modulus.BitLength; var paddedBlock = new byte[bitLength / 8 + (bitLength % 8 > 0 ? 1 : 0) - 1]; @@ -45,7 +44,7 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers paddedBlock[i] = 0xFF; } - Buffer.BlockCopy(data, offset, paddedBlock, paddedBlock.Length - length, length); + Buffer.BlockCopy(input, offset, paddedBlock, paddedBlock.Length - length, length); return Transform(paddedBlock); } @@ -53,38 +52,44 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers /// /// Decrypts the specified data. /// - /// The data. + /// The data. /// /// The decrypted data. /// /// Only block type 01 or 02 are supported. /// Thrown when decrypted block type is not supported. - public override byte[] Decrypt(byte[] data) + public override byte[] Decrypt(byte[] input) { - return Decrypt(data, 0, data.Length); + return Decrypt(input, 0, input.Length); } /// /// Decrypts the specified input. /// - /// The input. - /// The zero-based offset in at which to begin decrypting. - /// The number of bytes to decrypt from . + /// The input. + /// The zero-based offset in at which to begin decrypting. + /// The number of bytes to decrypt from . /// /// The decrypted data. /// /// Only block type 01 or 02 are supported. /// Thrown when decrypted block type is not supported. - public override byte[] Decrypt(byte[] data, int offset, int length) + public override byte[] Decrypt(byte[] input, int offset, int length) { - var paddedBlock = Transform(data, offset, length); + var paddedBlock = Transform(input, offset, length); - if (paddedBlock[0] != 1 && paddedBlock[0] != 2) + if (paddedBlock[0] is not 1 and not 2) + { throw new NotSupportedException("Only block type 01 or 02 are supported."); + } var position = 1; + while (position < paddedBlock.Length && paddedBlock[position] != 0) + { position++; + } + position++; var result = new byte[paddedBlock.Length - position]; @@ -108,35 +113,39 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers BigInteger result; - if (_isPrivate) + var isPrivate = !_key.D.IsZero; + + if (isPrivate) { var random = BigInteger.One; var max = _key.Modulus - 1; var bitLength = _key.Modulus.BitLength; if (max < BigInteger.One) + { throw new SshException("Invalid RSA key."); + } while (random <= BigInteger.One || random >= max) { random = BigInteger.Random(bitLength); } - var blindedInput = BigInteger.PositiveMod((BigInteger.ModPow(random, _key.Exponent, _key.Modulus) * input), _key.Modulus); + var blindedInput = BigInteger.PositiveMod(BigInteger.ModPow(random, _key.Exponent, _key.Modulus) * input, _key.Modulus); // mP = ((input Mod p) ^ dP)) Mod p - var mP = BigInteger.ModPow((blindedInput % _key.P), _key.DP, _key.P); + var mP = BigInteger.ModPow(blindedInput % _key.P, _key.DP, _key.P); // mQ = ((input Mod q) ^ dQ)) Mod q - var mQ = BigInteger.ModPow((blindedInput % _key.Q), _key.DQ, _key.Q); + var mQ = BigInteger.ModPow(blindedInput % _key.Q, _key.DQ, _key.Q); - var h = BigInteger.PositiveMod(((mP - mQ) * _key.InverseQ), _key.P); + var h = BigInteger.PositiveMod((mP - mQ) * _key.InverseQ, _key.P); var m = h * _key.Q + mQ; var rInv = BigInteger.ModInverse(random, _key.Modulus); - result = BigInteger.PositiveMod((m * rInv), _key.Modulus); + result = BigInteger.PositiveMod(m * rInv, _key.Modulus); } else { diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/SerpentCipher.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/SerpentCipher.cs index 61305946..0f67ac20 100644 --- a/src/Renci.SshNet/Security/Cryptography/Ciphers/SerpentCipher.cs +++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/SerpentCipher.cs @@ -11,7 +11,12 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers private const int Phi = unchecked((int)0x9E3779B9); // (Sqrt(5) - 1) * 2**31 private readonly int[] _workingKey; - private int _x0, _x1, _x2, _x3; // registers + + // registers + private int _x0; + private int _x1; + private int _x2; + private int _x3; /// /// Initializes a new instance of the class. @@ -26,8 +31,10 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers { var keySize = key.Length * 8; - if (!(keySize == 128 || keySize == 192 || keySize == 256)) + if (keySize is not (128 or 192 or 256)) + { throw new ArgumentException(string.Format("KeySize '{0}' is not valid for this algorithm.", keySize)); + } _workingKey = MakeWorkingKey(key); } @@ -46,44 +53,77 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { if (inputCount != BlockSize) + { throw new ArgumentException("inputCount"); + } _x3 = BytesToWord(inputBuffer, inputOffset); _x2 = BytesToWord(inputBuffer, inputOffset + 4); _x1 = BytesToWord(inputBuffer, inputOffset + 8); _x0 = BytesToWord(inputBuffer, inputOffset + 12); - Sb0(_workingKey[0] ^ _x0, _workingKey[1] ^ _x1, _workingKey[2] ^ _x2, _workingKey[3] ^ _x3); LT(); - Sb1(_workingKey[4] ^ _x0, _workingKey[5] ^ _x1, _workingKey[6] ^ _x2, _workingKey[7] ^ _x3); LT(); - Sb2(_workingKey[8] ^ _x0, _workingKey[9] ^ _x1, _workingKey[10] ^ _x2, _workingKey[11] ^ _x3); LT(); - Sb3(_workingKey[12] ^ _x0, _workingKey[13] ^ _x1, _workingKey[14] ^ _x2, _workingKey[15] ^ _x3); LT(); - Sb4(_workingKey[16] ^ _x0, _workingKey[17] ^ _x1, _workingKey[18] ^ _x2, _workingKey[19] ^ _x3); LT(); - Sb5(_workingKey[20] ^ _x0, _workingKey[21] ^ _x1, _workingKey[22] ^ _x2, _workingKey[23] ^ _x3); LT(); - Sb6(_workingKey[24] ^ _x0, _workingKey[25] ^ _x1, _workingKey[26] ^ _x2, _workingKey[27] ^ _x3); LT(); - Sb7(_workingKey[28] ^ _x0, _workingKey[29] ^ _x1, _workingKey[30] ^ _x2, _workingKey[31] ^ _x3); LT(); - Sb0(_workingKey[32] ^ _x0, _workingKey[33] ^ _x1, _workingKey[34] ^ _x2, _workingKey[35] ^ _x3); LT(); - Sb1(_workingKey[36] ^ _x0, _workingKey[37] ^ _x1, _workingKey[38] ^ _x2, _workingKey[39] ^ _x3); LT(); - Sb2(_workingKey[40] ^ _x0, _workingKey[41] ^ _x1, _workingKey[42] ^ _x2, _workingKey[43] ^ _x3); LT(); - Sb3(_workingKey[44] ^ _x0, _workingKey[45] ^ _x1, _workingKey[46] ^ _x2, _workingKey[47] ^ _x3); LT(); - Sb4(_workingKey[48] ^ _x0, _workingKey[49] ^ _x1, _workingKey[50] ^ _x2, _workingKey[51] ^ _x3); LT(); - Sb5(_workingKey[52] ^ _x0, _workingKey[53] ^ _x1, _workingKey[54] ^ _x2, _workingKey[55] ^ _x3); LT(); - Sb6(_workingKey[56] ^ _x0, _workingKey[57] ^ _x1, _workingKey[58] ^ _x2, _workingKey[59] ^ _x3); LT(); - Sb7(_workingKey[60] ^ _x0, _workingKey[61] ^ _x1, _workingKey[62] ^ _x2, _workingKey[63] ^ _x3); LT(); - Sb0(_workingKey[64] ^ _x0, _workingKey[65] ^ _x1, _workingKey[66] ^ _x2, _workingKey[67] ^ _x3); LT(); - Sb1(_workingKey[68] ^ _x0, _workingKey[69] ^ _x1, _workingKey[70] ^ _x2, _workingKey[71] ^ _x3); LT(); - Sb2(_workingKey[72] ^ _x0, _workingKey[73] ^ _x1, _workingKey[74] ^ _x2, _workingKey[75] ^ _x3); LT(); - Sb3(_workingKey[76] ^ _x0, _workingKey[77] ^ _x1, _workingKey[78] ^ _x2, _workingKey[79] ^ _x3); LT(); - Sb4(_workingKey[80] ^ _x0, _workingKey[81] ^ _x1, _workingKey[82] ^ _x2, _workingKey[83] ^ _x3); LT(); - Sb5(_workingKey[84] ^ _x0, _workingKey[85] ^ _x1, _workingKey[86] ^ _x2, _workingKey[87] ^ _x3); LT(); - Sb6(_workingKey[88] ^ _x0, _workingKey[89] ^ _x1, _workingKey[90] ^ _x2, _workingKey[91] ^ _x3); LT(); - Sb7(_workingKey[92] ^ _x0, _workingKey[93] ^ _x1, _workingKey[94] ^ _x2, _workingKey[95] ^ _x3); LT(); - Sb0(_workingKey[96] ^ _x0, _workingKey[97] ^ _x1, _workingKey[98] ^ _x2, _workingKey[99] ^ _x3); LT(); - Sb1(_workingKey[100] ^ _x0, _workingKey[101] ^ _x1, _workingKey[102] ^ _x2, _workingKey[103] ^ _x3); LT(); - Sb2(_workingKey[104] ^ _x0, _workingKey[105] ^ _x1, _workingKey[106] ^ _x2, _workingKey[107] ^ _x3); LT(); - Sb3(_workingKey[108] ^ _x0, _workingKey[109] ^ _x1, _workingKey[110] ^ _x2, _workingKey[111] ^ _x3); LT(); - Sb4(_workingKey[112] ^ _x0, _workingKey[113] ^ _x1, _workingKey[114] ^ _x2, _workingKey[115] ^ _x3); LT(); - Sb5(_workingKey[116] ^ _x0, _workingKey[117] ^ _x1, _workingKey[118] ^ _x2, _workingKey[119] ^ _x3); LT(); - Sb6(_workingKey[120] ^ _x0, _workingKey[121] ^ _x1, _workingKey[122] ^ _x2, _workingKey[123] ^ _x3); LT(); + Sb0(_workingKey[0] ^ _x0, _workingKey[1] ^ _x1, _workingKey[2] ^ _x2, _workingKey[3] ^ _x3); + LT(); + Sb1(_workingKey[4] ^ _x0, _workingKey[5] ^ _x1, _workingKey[6] ^ _x2, _workingKey[7] ^ _x3); + LT(); + Sb2(_workingKey[8] ^ _x0, _workingKey[9] ^ _x1, _workingKey[10] ^ _x2, _workingKey[11] ^ _x3); + LT(); + Sb3(_workingKey[12] ^ _x0, _workingKey[13] ^ _x1, _workingKey[14] ^ _x2, _workingKey[15] ^ _x3); + LT(); + Sb4(_workingKey[16] ^ _x0, _workingKey[17] ^ _x1, _workingKey[18] ^ _x2, _workingKey[19] ^ _x3); + LT(); + Sb5(_workingKey[20] ^ _x0, _workingKey[21] ^ _x1, _workingKey[22] ^ _x2, _workingKey[23] ^ _x3); + LT(); + Sb6(_workingKey[24] ^ _x0, _workingKey[25] ^ _x1, _workingKey[26] ^ _x2, _workingKey[27] ^ _x3); + LT(); + Sb7(_workingKey[28] ^ _x0, _workingKey[29] ^ _x1, _workingKey[30] ^ _x2, _workingKey[31] ^ _x3); + LT(); + Sb0(_workingKey[32] ^ _x0, _workingKey[33] ^ _x1, _workingKey[34] ^ _x2, _workingKey[35] ^ _x3); + LT(); + Sb1(_workingKey[36] ^ _x0, _workingKey[37] ^ _x1, _workingKey[38] ^ _x2, _workingKey[39] ^ _x3); + LT(); + Sb2(_workingKey[40] ^ _x0, _workingKey[41] ^ _x1, _workingKey[42] ^ _x2, _workingKey[43] ^ _x3); + LT(); + Sb3(_workingKey[44] ^ _x0, _workingKey[45] ^ _x1, _workingKey[46] ^ _x2, _workingKey[47] ^ _x3); + LT(); + Sb4(_workingKey[48] ^ _x0, _workingKey[49] ^ _x1, _workingKey[50] ^ _x2, _workingKey[51] ^ _x3); + LT(); + Sb5(_workingKey[52] ^ _x0, _workingKey[53] ^ _x1, _workingKey[54] ^ _x2, _workingKey[55] ^ _x3); + LT(); + Sb6(_workingKey[56] ^ _x0, _workingKey[57] ^ _x1, _workingKey[58] ^ _x2, _workingKey[59] ^ _x3); + LT(); + Sb7(_workingKey[60] ^ _x0, _workingKey[61] ^ _x1, _workingKey[62] ^ _x2, _workingKey[63] ^ _x3); + LT(); + Sb0(_workingKey[64] ^ _x0, _workingKey[65] ^ _x1, _workingKey[66] ^ _x2, _workingKey[67] ^ _x3); + LT(); + Sb1(_workingKey[68] ^ _x0, _workingKey[69] ^ _x1, _workingKey[70] ^ _x2, _workingKey[71] ^ _x3); + LT(); + Sb2(_workingKey[72] ^ _x0, _workingKey[73] ^ _x1, _workingKey[74] ^ _x2, _workingKey[75] ^ _x3); + LT(); + Sb3(_workingKey[76] ^ _x0, _workingKey[77] ^ _x1, _workingKey[78] ^ _x2, _workingKey[79] ^ _x3); + LT(); + Sb4(_workingKey[80] ^ _x0, _workingKey[81] ^ _x1, _workingKey[82] ^ _x2, _workingKey[83] ^ _x3); + LT(); + Sb5(_workingKey[84] ^ _x0, _workingKey[85] ^ _x1, _workingKey[86] ^ _x2, _workingKey[87] ^ _x3); + LT(); + Sb6(_workingKey[88] ^ _x0, _workingKey[89] ^ _x1, _workingKey[90] ^ _x2, _workingKey[91] ^ _x3); + LT(); + Sb7(_workingKey[92] ^ _x0, _workingKey[93] ^ _x1, _workingKey[94] ^ _x2, _workingKey[95] ^ _x3); + LT(); + Sb0(_workingKey[96] ^ _x0, _workingKey[97] ^ _x1, _workingKey[98] ^ _x2, _workingKey[99] ^ _x3); + LT(); + Sb1(_workingKey[100] ^ _x0, _workingKey[101] ^ _x1, _workingKey[102] ^ _x2, _workingKey[103] ^ _x3); + LT(); + Sb2(_workingKey[104] ^ _x0, _workingKey[105] ^ _x1, _workingKey[106] ^ _x2, _workingKey[107] ^ _x3); + LT(); + Sb3(_workingKey[108] ^ _x0, _workingKey[109] ^ _x1, _workingKey[110] ^ _x2, _workingKey[111] ^ _x3); + LT(); + Sb4(_workingKey[112] ^ _x0, _workingKey[113] ^ _x1, _workingKey[114] ^ _x2, _workingKey[115] ^ _x3); + LT(); + Sb5(_workingKey[116] ^ _x0, _workingKey[117] ^ _x1, _workingKey[118] ^ _x2, _workingKey[119] ^ _x3); + LT(); + Sb6(_workingKey[120] ^ _x0, _workingKey[121] ^ _x1, _workingKey[122] ^ _x2, _workingKey[123] ^ _x3); + LT(); Sb7(_workingKey[124] ^ _x0, _workingKey[125] ^ _x1, _workingKey[126] ^ _x2, _workingKey[127] ^ _x3); WordToBytes(_workingKey[131] ^ _x3, outputBuffer, outputOffset); @@ -108,7 +148,9 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { if (inputCount != BlockSize) + { throw new ArgumentException("inputCount"); + } _x3 = _workingKey[131] ^ BytesToWord(inputBuffer, inputOffset); _x2 = _workingKey[130] ^ BytesToWord(inputBuffer, inputOffset + 4); @@ -116,68 +158,253 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers _x0 = _workingKey[128] ^ BytesToWord(inputBuffer, inputOffset + 12); Ib7(_x0, _x1, _x2, _x3); - _x0 ^= _workingKey[124]; _x1 ^= _workingKey[125]; _x2 ^= _workingKey[126]; _x3 ^= _workingKey[127]; - InverseLT(); Ib6(_x0, _x1, _x2, _x3); - _x0 ^= _workingKey[120]; _x1 ^= _workingKey[121]; _x2 ^= _workingKey[122]; _x3 ^= _workingKey[123]; - InverseLT(); Ib5(_x0, _x1, _x2, _x3); - _x0 ^= _workingKey[116]; _x1 ^= _workingKey[117]; _x2 ^= _workingKey[118]; _x3 ^= _workingKey[119]; - InverseLT(); Ib4(_x0, _x1, _x2, _x3); - _x0 ^= _workingKey[112]; _x1 ^= _workingKey[113]; _x2 ^= _workingKey[114]; _x3 ^= _workingKey[115]; - InverseLT(); Ib3(_x0, _x1, _x2, _x3); - _x0 ^= _workingKey[108]; _x1 ^= _workingKey[109]; _x2 ^= _workingKey[110]; _x3 ^= _workingKey[111]; - InverseLT(); Ib2(_x0, _x1, _x2, _x3); - _x0 ^= _workingKey[104]; _x1 ^= _workingKey[105]; _x2 ^= _workingKey[106]; _x3 ^= _workingKey[107]; - InverseLT(); Ib1(_x0, _x1, _x2, _x3); - _x0 ^= _workingKey[100]; _x1 ^= _workingKey[101]; _x2 ^= _workingKey[102]; _x3 ^= _workingKey[103]; - InverseLT(); Ib0(_x0, _x1, _x2, _x3); - _x0 ^= _workingKey[96]; _x1 ^= _workingKey[97]; _x2 ^= _workingKey[98]; _x3 ^= _workingKey[99]; - InverseLT(); Ib7(_x0, _x1, _x2, _x3); - _x0 ^= _workingKey[92]; _x1 ^= _workingKey[93]; _x2 ^= _workingKey[94]; _x3 ^= _workingKey[95]; - InverseLT(); Ib6(_x0, _x1, _x2, _x3); - _x0 ^= _workingKey[88]; _x1 ^= _workingKey[89]; _x2 ^= _workingKey[90]; _x3 ^= _workingKey[91]; - InverseLT(); Ib5(_x0, _x1, _x2, _x3); - _x0 ^= _workingKey[84]; _x1 ^= _workingKey[85]; _x2 ^= _workingKey[86]; _x3 ^= _workingKey[87]; - InverseLT(); Ib4(_x0, _x1, _x2, _x3); - _x0 ^= _workingKey[80]; _x1 ^= _workingKey[81]; _x2 ^= _workingKey[82]; _x3 ^= _workingKey[83]; - InverseLT(); Ib3(_x0, _x1, _x2, _x3); - _x0 ^= _workingKey[76]; _x1 ^= _workingKey[77]; _x2 ^= _workingKey[78]; _x3 ^= _workingKey[79]; - InverseLT(); Ib2(_x0, _x1, _x2, _x3); - _x0 ^= _workingKey[72]; _x1 ^= _workingKey[73]; _x2 ^= _workingKey[74]; _x3 ^= _workingKey[75]; - InverseLT(); Ib1(_x0, _x1, _x2, _x3); - _x0 ^= _workingKey[68]; _x1 ^= _workingKey[69]; _x2 ^= _workingKey[70]; _x3 ^= _workingKey[71]; - InverseLT(); Ib0(_x0, _x1, _x2, _x3); - _x0 ^= _workingKey[64]; _x1 ^= _workingKey[65]; _x2 ^= _workingKey[66]; _x3 ^= _workingKey[67]; - InverseLT(); Ib7(_x0, _x1, _x2, _x3); - _x0 ^= _workingKey[60]; _x1 ^= _workingKey[61]; _x2 ^= _workingKey[62]; _x3 ^= _workingKey[63]; - InverseLT(); Ib6(_x0, _x1, _x2, _x3); - _x0 ^= _workingKey[56]; _x1 ^= _workingKey[57]; _x2 ^= _workingKey[58]; _x3 ^= _workingKey[59]; - InverseLT(); Ib5(_x0, _x1, _x2, _x3); - _x0 ^= _workingKey[52]; _x1 ^= _workingKey[53]; _x2 ^= _workingKey[54]; _x3 ^= _workingKey[55]; - InverseLT(); Ib4(_x0, _x1, _x2, _x3); - _x0 ^= _workingKey[48]; _x1 ^= _workingKey[49]; _x2 ^= _workingKey[50]; _x3 ^= _workingKey[51]; - InverseLT(); Ib3(_x0, _x1, _x2, _x3); - _x0 ^= _workingKey[44]; _x1 ^= _workingKey[45]; _x2 ^= _workingKey[46]; _x3 ^= _workingKey[47]; - InverseLT(); Ib2(_x0, _x1, _x2, _x3); - _x0 ^= _workingKey[40]; _x1 ^= _workingKey[41]; _x2 ^= _workingKey[42]; _x3 ^= _workingKey[43]; - InverseLT(); Ib1(_x0, _x1, _x2, _x3); - _x0 ^= _workingKey[36]; _x1 ^= _workingKey[37]; _x2 ^= _workingKey[38]; _x3 ^= _workingKey[39]; - InverseLT(); Ib0(_x0, _x1, _x2, _x3); - _x0 ^= _workingKey[32]; _x1 ^= _workingKey[33]; _x2 ^= _workingKey[34]; _x3 ^= _workingKey[35]; - InverseLT(); Ib7(_x0, _x1, _x2, _x3); - _x0 ^= _workingKey[28]; _x1 ^= _workingKey[29]; _x2 ^= _workingKey[30]; _x3 ^= _workingKey[31]; - InverseLT(); Ib6(_x0, _x1, _x2, _x3); - _x0 ^= _workingKey[24]; _x1 ^= _workingKey[25]; _x2 ^= _workingKey[26]; _x3 ^= _workingKey[27]; - InverseLT(); Ib5(_x0, _x1, _x2, _x3); - _x0 ^= _workingKey[20]; _x1 ^= _workingKey[21]; _x2 ^= _workingKey[22]; _x3 ^= _workingKey[23]; - InverseLT(); Ib4(_x0, _x1, _x2, _x3); - _x0 ^= _workingKey[16]; _x1 ^= _workingKey[17]; _x2 ^= _workingKey[18]; _x3 ^= _workingKey[19]; - InverseLT(); Ib3(_x0, _x1, _x2, _x3); - _x0 ^= _workingKey[12]; _x1 ^= _workingKey[13]; _x2 ^= _workingKey[14]; _x3 ^= _workingKey[15]; - InverseLT(); Ib2(_x0, _x1, _x2, _x3); - _x0 ^= _workingKey[8]; _x1 ^= _workingKey[9]; _x2 ^= _workingKey[10]; _x3 ^= _workingKey[11]; - InverseLT(); Ib1(_x0, _x1, _x2, _x3); - _x0 ^= _workingKey[4]; _x1 ^= _workingKey[5]; _x2 ^= _workingKey[6]; _x3 ^= _workingKey[7]; - InverseLT(); Ib0(_x0, _x1, _x2, _x3); + + _x0 ^= _workingKey[124]; + _x1 ^= _workingKey[125]; + _x2 ^= _workingKey[126]; + _x3 ^= _workingKey[127]; + + InverseLT(); + Ib6(_x0, _x1, _x2, _x3); + + _x0 ^= _workingKey[120]; + _x1 ^= _workingKey[121]; + _x2 ^= _workingKey[122]; + _x3 ^= _workingKey[123]; + + InverseLT(); + Ib5(_x0, _x1, _x2, _x3); + + _x0 ^= _workingKey[116]; + _x1 ^= _workingKey[117]; + _x2 ^= _workingKey[118]; + _x3 ^= _workingKey[119]; + + InverseLT(); + Ib4(_x0, _x1, _x2, _x3); + + _x0 ^= _workingKey[112]; + _x1 ^= _workingKey[113]; + _x2 ^= _workingKey[114]; + _x3 ^= _workingKey[115]; + InverseLT(); + Ib3(_x0, _x1, _x2, _x3); + + _x0 ^= _workingKey[108]; + _x1 ^= _workingKey[109]; + _x2 ^= _workingKey[110]; + _x3 ^= _workingKey[111]; + + InverseLT(); + Ib2(_x0, _x1, _x2, _x3); + + _x0 ^= _workingKey[104]; + _x1 ^= _workingKey[105]; + _x2 ^= _workingKey[106]; + _x3 ^= _workingKey[107]; + + InverseLT(); + Ib1(_x0, _x1, _x2, _x3); + + _x0 ^= _workingKey[100]; + _x1 ^= _workingKey[101]; + _x2 ^= _workingKey[102]; + _x3 ^= _workingKey[103]; + + InverseLT(); + Ib0(_x0, _x1, _x2, _x3); + + _x0 ^= _workingKey[96]; + _x1 ^= _workingKey[97]; + _x2 ^= _workingKey[98]; + _x3 ^= _workingKey[99]; + + InverseLT(); + Ib7(_x0, _x1, _x2, _x3); + + _x0 ^= _workingKey[92]; + _x1 ^= _workingKey[93]; + _x2 ^= _workingKey[94]; + _x3 ^= _workingKey[95]; + + InverseLT(); + Ib6(_x0, _x1, _x2, _x3); + + _x0 ^= _workingKey[88]; + _x1 ^= _workingKey[89]; + _x2 ^= _workingKey[90]; + _x3 ^= _workingKey[91]; + + InverseLT(); + Ib5(_x0, _x1, _x2, _x3); + + _x0 ^= _workingKey[84]; + _x1 ^= _workingKey[85]; + _x2 ^= _workingKey[86]; + _x3 ^= _workingKey[87]; + + InverseLT(); + Ib4(_x0, _x1, _x2, _x3); + + _x0 ^= _workingKey[80]; + _x1 ^= _workingKey[81]; + _x2 ^= _workingKey[82]; + _x3 ^= _workingKey[83]; + + InverseLT(); + Ib3(_x0, _x1, _x2, _x3); + + _x0 ^= _workingKey[76]; + _x1 ^= _workingKey[77]; + _x2 ^= _workingKey[78]; + _x3 ^= _workingKey[79]; + + InverseLT(); + Ib2(_x0, _x1, _x2, _x3); + + _x0 ^= _workingKey[72]; + _x1 ^= _workingKey[73]; + _x2 ^= _workingKey[74]; + _x3 ^= _workingKey[75]; + + InverseLT(); + Ib1(_x0, _x1, _x2, _x3); + + _x0 ^= _workingKey[68]; + _x1 ^= _workingKey[69]; + _x2 ^= _workingKey[70]; + _x3 ^= _workingKey[71]; + + InverseLT(); + Ib0(_x0, _x1, _x2, _x3); + + _x0 ^= _workingKey[64]; + _x1 ^= _workingKey[65]; + _x2 ^= _workingKey[66]; + _x3 ^= _workingKey[67]; + + InverseLT(); + Ib7(_x0, _x1, _x2, _x3); + + _x0 ^= _workingKey[60]; + _x1 ^= _workingKey[61]; + _x2 ^= _workingKey[62]; + _x3 ^= _workingKey[63]; + + InverseLT(); + Ib6(_x0, _x1, _x2, _x3); + + _x0 ^= _workingKey[56]; + _x1 ^= _workingKey[57]; + _x2 ^= _workingKey[58]; + _x3 ^= _workingKey[59]; + + InverseLT(); + Ib5(_x0, _x1, _x2, _x3); + + _x0 ^= _workingKey[52]; + _x1 ^= _workingKey[53]; + _x2 ^= _workingKey[54]; + _x3 ^= _workingKey[55]; + + InverseLT(); + Ib4(_x0, _x1, _x2, _x3); + + _x0 ^= _workingKey[48]; + _x1 ^= _workingKey[49]; + _x2 ^= _workingKey[50]; + _x3 ^= _workingKey[51]; + + InverseLT(); + Ib3(_x0, _x1, _x2, _x3); + + _x0 ^= _workingKey[44]; + _x1 ^= _workingKey[45]; + _x2 ^= _workingKey[46]; + _x3 ^= _workingKey[47]; + + InverseLT(); + Ib2(_x0, _x1, _x2, _x3); + + _x0 ^= _workingKey[40]; + _x1 ^= _workingKey[41]; + _x2 ^= _workingKey[42]; + _x3 ^= _workingKey[43]; + + InverseLT(); + Ib1(_x0, _x1, _x2, _x3); + + _x0 ^= _workingKey[36]; + _x1 ^= _workingKey[37]; + _x2 ^= _workingKey[38]; + _x3 ^= _workingKey[39]; + + InverseLT(); + Ib0(_x0, _x1, _x2, _x3); + + _x0 ^= _workingKey[32]; + _x1 ^= _workingKey[33]; + _x2 ^= _workingKey[34]; + _x3 ^= _workingKey[35]; + + InverseLT(); + Ib7(_x0, _x1, _x2, _x3); + + _x0 ^= _workingKey[28]; + _x1 ^= _workingKey[29]; + _x2 ^= _workingKey[30]; + _x3 ^= _workingKey[31]; + + InverseLT(); + Ib6(_x0, _x1, _x2, _x3); + + _x0 ^= _workingKey[24]; + _x1 ^= _workingKey[25]; + _x2 ^= _workingKey[26]; + _x3 ^= _workingKey[27]; + + InverseLT(); + Ib5(_x0, _x1, _x2, _x3); + + _x0 ^= _workingKey[20]; + _x1 ^= _workingKey[21]; + _x2 ^= _workingKey[22]; + _x3 ^= _workingKey[23]; + + InverseLT(); + Ib4(_x0, _x1, _x2, _x3); + + _x0 ^= _workingKey[16]; + _x1 ^= _workingKey[17]; + _x2 ^= _workingKey[18]; + _x3 ^= _workingKey[19]; + + InverseLT(); + Ib3(_x0, _x1, _x2, _x3); + + _x0 ^= _workingKey[12]; + _x1 ^= _workingKey[13]; + _x2 ^= _workingKey[14]; + _x3 ^= _workingKey[15]; + + InverseLT(); + Ib2(_x0, _x1, _x2, _x3); + + _x0 ^= _workingKey[8]; + _x1 ^= _workingKey[9]; + _x2 ^= _workingKey[10]; + _x3 ^= _workingKey[11]; + + InverseLT(); + Ib1(_x0, _x1, _x2, _x3); + + _x0 ^= _workingKey[4]; + _x1 ^= _workingKey[5]; + _x2 ^= _workingKey[6]; + _x3 ^= _workingKey[7]; + + InverseLT(); + Ib0(_x0, _x1, _x2, _x3); WordToBytes(_x3 ^ _workingKey[3], outputBuffer, outputOffset); WordToBytes(_x2 ^ _workingKey[2], outputBuffer, outputOffset + 4); @@ -187,7 +414,6 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers return BlockSize; } - /// /// Expand a user-supplied key material into a session key. /// @@ -198,9 +424,7 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers /// is not multiple of 4 bytes. private int[] MakeWorkingKey(byte[] key) { - // // pad key to 256 bits - // var kPad = new int[16]; int off; var length = 0; @@ -223,15 +447,11 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers throw new ArgumentException("key must be a multiple of 4 bytes"); } - // // expand the padded key up to 33 x 128 bits of key material - // const int amount = (Rounds + 1) * 4; var w = new int[amount]; - // // compute w0 to w7 from w-8 to w-1 - // for (var i = 8; i < 16; i++) { kPad[i] = RotateLeft(kPad[i - 8] ^ kPad[i - 5] ^ kPad[i - 3] ^ kPad[i - 1] ^ Phi ^ (i - 8), 11); @@ -239,134 +459,263 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers Buffer.BlockCopy(kPad, 8, w, 0, 8); - // // compute w8 to w136 - // for (var i = 8; i < amount; i++) { w[i] = RotateLeft(w[i - 8] ^ w[i - 5] ^ w[i - 3] ^ w[i - 1] ^ Phi ^ i, 11); } - // // create the working keys by processing w with the Sbox and IP - // Sb3(w[0], w[1], w[2], w[3]); - w[0] = _x0; w[1] = _x1; w[2] = _x2; w[3] = _x3; + w[0] = _x0; + w[1] = _x1; + w[2] = _x2; + w[3] = _x3; + Sb2(w[4], w[5], w[6], w[7]); - w[4] = _x0; w[5] = _x1; w[6] = _x2; w[7] = _x3; + w[4] = _x0; + w[5] = _x1; + w[6] = _x2; + w[7] = _x3; + Sb1(w[8], w[9], w[10], w[11]); - w[8] = _x0; w[9] = _x1; w[10] = _x2; w[11] = _x3; + w[8] = _x0; + w[9] = _x1; + w[10] = _x2; + w[11] = _x3; + Sb0(w[12], w[13], w[14], w[15]); - w[12] = _x0; w[13] = _x1; w[14] = _x2; w[15] = _x3; + w[12] = _x0; + w[13] = _x1; + w[14] = _x2; + w[15] = _x3; + Sb7(w[16], w[17], w[18], w[19]); - w[16] = _x0; w[17] = _x1; w[18] = _x2; w[19] = _x3; + w[16] = _x0; + w[17] = _x1; + w[18] = _x2; + w[19] = _x3; + Sb6(w[20], w[21], w[22], w[23]); - w[20] = _x0; w[21] = _x1; w[22] = _x2; w[23] = _x3; + w[20] = _x0; + w[21] = _x1; + w[22] = _x2; + w[23] = _x3; + Sb5(w[24], w[25], w[26], w[27]); - w[24] = _x0; w[25] = _x1; w[26] = _x2; w[27] = _x3; + w[24] = _x0; + w[25] = _x1; + w[26] = _x2; + w[27] = _x3; + Sb4(w[28], w[29], w[30], w[31]); - w[28] = _x0; w[29] = _x1; w[30] = _x2; w[31] = _x3; + w[28] = _x0; + w[29] = _x1; + w[30] = _x2; + w[31] = _x3; + Sb3(w[32], w[33], w[34], w[35]); - w[32] = _x0; w[33] = _x1; w[34] = _x2; w[35] = _x3; + w[32] = _x0; + w[33] = _x1; + w[34] = _x2; + w[35] = _x3; + Sb2(w[36], w[37], w[38], w[39]); - w[36] = _x0; w[37] = _x1; w[38] = _x2; w[39] = _x3; + w[36] = _x0; + w[37] = _x1; + w[38] = _x2; + w[39] = _x3; + Sb1(w[40], w[41], w[42], w[43]); - w[40] = _x0; w[41] = _x1; w[42] = _x2; w[43] = _x3; + w[40] = _x0; + w[41] = _x1; + w[42] = _x2; + w[43] = _x3; + Sb0(w[44], w[45], w[46], w[47]); - w[44] = _x0; w[45] = _x1; w[46] = _x2; w[47] = _x3; + w[44] = _x0; + w[45] = _x1; + w[46] = _x2; + w[47] = _x3; + Sb7(w[48], w[49], w[50], w[51]); - w[48] = _x0; w[49] = _x1; w[50] = _x2; w[51] = _x3; + w[48] = _x0; + w[49] = _x1; + w[50] = _x2; + w[51] = _x3; + Sb6(w[52], w[53], w[54], w[55]); - w[52] = _x0; w[53] = _x1; w[54] = _x2; w[55] = _x3; + w[52] = _x0; + w[53] = _x1; + w[54] = _x2; + w[55] = _x3; + Sb5(w[56], w[57], w[58], w[59]); - w[56] = _x0; w[57] = _x1; w[58] = _x2; w[59] = _x3; + w[56] = _x0; + w[57] = _x1; + w[58] = _x2; + w[59] = _x3; + Sb4(w[60], w[61], w[62], w[63]); - w[60] = _x0; w[61] = _x1; w[62] = _x2; w[63] = _x3; + w[60] = _x0; + w[61] = _x1; + w[62] = _x2; + w[63] = _x3; + Sb3(w[64], w[65], w[66], w[67]); - w[64] = _x0; w[65] = _x1; w[66] = _x2; w[67] = _x3; + w[64] = _x0; + w[65] = _x1; + w[66] = _x2; + w[67] = _x3; + Sb2(w[68], w[69], w[70], w[71]); - w[68] = _x0; w[69] = _x1; w[70] = _x2; w[71] = _x3; + w[68] = _x0; + w[69] = _x1; + w[70] = _x2; + w[71] = _x3; + Sb1(w[72], w[73], w[74], w[75]); - w[72] = _x0; w[73] = _x1; w[74] = _x2; w[75] = _x3; + w[72] = _x0; + w[73] = _x1; + w[74] = _x2; + w[75] = _x3; + Sb0(w[76], w[77], w[78], w[79]); - w[76] = _x0; w[77] = _x1; w[78] = _x2; w[79] = _x3; + w[76] = _x0; + w[77] = _x1; + w[78] = _x2; + w[79] = _x3; + Sb7(w[80], w[81], w[82], w[83]); - w[80] = _x0; w[81] = _x1; w[82] = _x2; w[83] = _x3; + w[80] = _x0; + w[81] = _x1; + w[82] = _x2; + w[83] = _x3; + Sb6(w[84], w[85], w[86], w[87]); - w[84] = _x0; w[85] = _x1; w[86] = _x2; w[87] = _x3; + w[84] = _x0; + w[85] = _x1; + w[86] = _x2; + w[87] = _x3; + Sb5(w[88], w[89], w[90], w[91]); - w[88] = _x0; w[89] = _x1; w[90] = _x2; w[91] = _x3; + w[88] = _x0; + w[89] = _x1; + w[90] = _x2; + w[91] = _x3; + Sb4(w[92], w[93], w[94], w[95]); - w[92] = _x0; w[93] = _x1; w[94] = _x2; w[95] = _x3; + w[92] = _x0; + w[93] = _x1; + w[94] = _x2; + w[95] = _x3; + Sb3(w[96], w[97], w[98], w[99]); - w[96] = _x0; w[97] = _x1; w[98] = _x2; w[99] = _x3; + w[96] = _x0; + w[97] = _x1; + w[98] = _x2; + w[99] = _x3; + Sb2(w[100], w[101], w[102], w[103]); - w[100] = _x0; w[101] = _x1; w[102] = _x2; w[103] = _x3; + w[100] = _x0; + w[101] = _x1; + w[102] = _x2; + w[103] = _x3; + Sb1(w[104], w[105], w[106], w[107]); - w[104] = _x0; w[105] = _x1; w[106] = _x2; w[107] = _x3; + w[104] = _x0; + w[105] = _x1; + w[106] = _x2; + w[107] = _x3; + Sb0(w[108], w[109], w[110], w[111]); - w[108] = _x0; w[109] = _x1; w[110] = _x2; w[111] = _x3; + w[108] = _x0; + w[109] = _x1; + w[110] = _x2; + w[111] = _x3; + Sb7(w[112], w[113], w[114], w[115]); - w[112] = _x0; w[113] = _x1; w[114] = _x2; w[115] = _x3; + w[112] = _x0; + w[113] = _x1; + w[114] = _x2; + w[115] = _x3; + Sb6(w[116], w[117], w[118], w[119]); - w[116] = _x0; w[117] = _x1; w[118] = _x2; w[119] = _x3; + w[116] = _x0; + w[117] = _x1; + w[118] = _x2; + w[119] = _x3; + Sb5(w[120], w[121], w[122], w[123]); - w[120] = _x0; w[121] = _x1; w[122] = _x2; w[123] = _x3; + w[120] = _x0; + w[121] = _x1; + w[122] = _x2; + w[123] = _x3; + Sb4(w[124], w[125], w[126], w[127]); - w[124] = _x0; w[125] = _x1; w[126] = _x2; w[127] = _x3; + w[124] = _x0; + w[125] = _x1; + w[126] = _x2; + w[127] = _x3; + Sb3(w[128], w[129], w[130], w[131]); - w[128] = _x0; w[129] = _x1; w[130] = _x2; w[131] = _x3; + w[128] = _x0; + w[129] = _x1; + w[130] = _x2; + w[131] = _x3; return w; } private static int RotateLeft(int x, int bits) { - return ((x << bits) | (int)((uint)x >> (32 - bits))); + return (x << bits) | (int) ((uint) x >> (32 - bits)); } private static int RotateRight(int x, int bits) { - return ((int)((uint)x >> bits) | (x << (32 - bits))); + return (int) ((uint) x >> bits) | (x << (32 - bits)); } private static int BytesToWord(byte[] src, int srcOff) { - return (((src[srcOff] & 0xff) << 24) | ((src[srcOff + 1] & 0xff) << 16) | - ((src[srcOff + 2] & 0xff) << 8) | ((src[srcOff + 3] & 0xff))); + return ((src[srcOff] & 0xff) << 24) | ((src[srcOff + 1] & 0xff) << 16) | + ((src[srcOff + 2] & 0xff) << 8) | (src[srcOff + 3] & 0xff); } private static void WordToBytes(int word, byte[] dst, int dstOff) { - dst[dstOff + 3] = (byte)(word); - dst[dstOff + 2] = (byte)((uint)word >> 8); - dst[dstOff + 1] = (byte)((uint)word >> 16); - dst[dstOff] = (byte)((uint)word >> 24); + dst[dstOff + 3] = (byte) word; + dst[dstOff + 2] = (byte) ((uint)word >> 8); + dst[dstOff + 1] = (byte) ((uint)word >> 16); + dst[dstOff] = (byte) ((uint)word >> 24); } /* - * The sboxes below are based on the work of Brian Gladman and - * Sam Simpson, whose original notice appears below. - *

- * For further details see: - * http://fp.gladman.plus.com/cryptography_technology/serpent/ - *

- */ + * The sboxes below are based on the work of Brian Gladman and + * Sam Simpson, whose original notice appears below. + * + * For further details see: + * http://fp.gladman.plus.com/cryptography_technology/serpent/ + * + */ - /* Partially optimised Serpent S Box bool functions derived */ - /* using a recursive descent analyser but without a full search */ - /* of all subtrees. This set of S boxes is the result of work */ - /* by Sam Simpson and Brian Gladman using the spare time on a */ - /* cluster of high capacity servers to search for S boxes with */ - /* this customised search engine. There are now an average of */ - /* 15.375 terms per S box. */ - /* */ - /* Copyright: Dr B. R Gladman (gladman@seven77.demon.co.uk) */ - /* and Sam Simpson (s.simpson@mia.co.uk) */ - /* 17th December 1998 */ - /* */ - /* We hereby give permission for information in this file to be */ - /* used freely subject only to acknowledgement of its origin. */ + /* + * Partially optimised Serpent S Box bool functions derived + * using a recursive descent analyser but without a full search + * of all subtrees. This set of S boxes is the result of work + * by Sam Simpson and Brian Gladman using the spare time on a + * cluster of high capacity servers to search for S boxes with + * this customised search engine. There are now an average of + * 15.375 terms per S box. + * + * Copyright: Dr B. R Gladman (gladman@seven77.demon.co.uk) + * and Sam Simpson (s.simpson@mia.co.uk) + * 17th December 1998 + * + * We hereby give permission for information in this file to be + * used freely subject only to acknowledgement of its origin. + */ /// /// S0 - { 3, 8,15, 1,10, 6, 5,11,14,13, 4, 2, 7, 0, 9,12 } - 15 terms. @@ -377,13 +726,13 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers /// The d. private void Sb0(int a, int b, int c, int d) { - int t1 = a ^ d; - int t3 = c ^ t1; - int t4 = b ^ t3; + var t1 = a ^ d; + var t3 = c ^ t1; + var t4 = b ^ t3; _x3 = (a & d) ^ t4; - int t7 = a ^ (b & t1); + var t7 = a ^ (b & t1); _x2 = t4 ^ (c | t7); - int t12 = _x3 & (t3 ^ t7); + var t12 = _x3 & (t3 ^ t7); _x1 = (~t3) ^ t12; _x0 = t12 ^ (~t7); } @@ -397,12 +746,12 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers /// The d. private void Ib0(int a, int b, int c, int d) { - int t1 = ~a; - int t2 = a ^ b; - int t4 = d ^ (t1 | t2); - int t5 = c ^ t4; + var t1 = ~a; + var t2 = a ^ b; + var t4 = d ^ (t1 | t2); + var t5 = c ^ t4; _x2 = t2 ^ t5; - int t8 = t1 ^ (d & t2); + var t8 = t1 ^ (d & t2); _x1 = t4 ^ (_x2 & t8); _x3 = (a & t4) ^ (t5 | _x1); _x0 = _x3 ^ (t5 ^ t8); @@ -417,13 +766,13 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers /// The d. private void Sb1(int a, int b, int c, int d) { - int t2 = b ^ (~a); - int t5 = c ^ (a | t2); + var t2 = b ^ (~a); + var t5 = c ^ (a | t2); _x2 = d ^ t5; - int t7 = b ^ (d | t2); - int t8 = t2 ^ _x2; + var t7 = b ^ (d | t2); + var t8 = t2 ^ _x2; _x3 = t8 ^ (t5 & t7); - int t11 = t5 ^ t7; + var t11 = t5 ^ t7; _x1 = _x3 ^ t11; _x0 = t5 ^ (t8 & t11); } @@ -437,15 +786,15 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers /// The d. private void Ib1(int a, int b, int c, int d) { - int t1 = b ^ d; - int t3 = a ^ (b & t1); - int t4 = t1 ^ t3; + var t1 = b ^ d; + var t3 = a ^ (b & t1); + var t4 = t1 ^ t3; _x3 = c ^ t4; - int t7 = b ^ (t1 & t3); - int t8 = _x3 | t7; + var t7 = b ^ (t1 & t3); + var t8 = _x3 | t7; _x1 = t3 ^ t8; - int t10 = ~_x1; - int t11 = _x3 ^ t7; + var t10 = ~_x1; + var t11 = _x3 ^ t7; _x0 = t10 ^ t11; _x2 = t4 ^ (t10 | t11); } @@ -459,13 +808,13 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers /// The d. private void Sb2(int a, int b, int c, int d) { - int t1 = ~a; - int t2 = b ^ d; - int t3 = c & t1; + var t1 = ~a; + var t2 = b ^ d; + var t3 = c & t1; _x0 = t2 ^ t3; - int t5 = c ^ t1; - int t6 = c ^ _x0; - int t7 = b & t6; + var t5 = c ^ t1; + var t6 = c ^ _x0; + var t7 = b & t6; _x3 = t5 ^ t7; _x2 = a ^ ((d | t7) & (_x0 | t5)); _x1 = (t2 ^ _x3) ^ (_x2 ^ (d | t1)); @@ -480,18 +829,18 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers /// The d. private void Ib2(int a, int b, int c, int d) { - int t1 = b ^ d; - int t2 = ~t1; - int t3 = a ^ c; - int t4 = c ^ t1; - int t5 = b & t4; + var t1 = b ^ d; + var t2 = ~t1; + var t3 = a ^ c; + var t4 = c ^ t1; + var t5 = b & t4; _x0 = t3 ^ t5; - int t7 = a | t2; - int t8 = d ^ t7; - int t9 = t3 | t8; + var t7 = a | t2; + var t8 = d ^ t7; + var t9 = t3 | t8; _x3 = t1 ^ t9; - int t11 = ~t4; - int t12 = _x0 | _x3; + var t11 = ~t4; + var t12 = _x0 | _x3; _x1 = t11 ^ t12; _x2 = (d & t11) ^ (t3 ^ t12); } @@ -505,18 +854,18 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers /// The d. private void Sb3(int a, int b, int c, int d) { - int t1 = a ^ b; - int t2 = a & c; - int t3 = a | d; - int t4 = c ^ d; - int t5 = t1 & t3; - int t6 = t2 | t5; + var t1 = a ^ b; + var t2 = a & c; + var t3 = a | d; + var t4 = c ^ d; + var t5 = t1 & t3; + var t6 = t2 | t5; _x2 = t4 ^ t6; - int t8 = b ^ t3; - int t9 = t6 ^ t8; - int t10 = t4 & t9; + var t8 = b ^ t3; + var t9 = t6 ^ t8; + var t10 = t4 & t9; _x0 = t1 ^ t10; - int t12 = _x2 & _x0; + var t12 = _x2 & _x0; _x1 = t9 ^ t12; _x3 = (b | d) ^ (t4 ^ t12); } @@ -530,18 +879,18 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers /// The d. private void Ib3(int a, int b, int c, int d) { - int t1 = a | b; - int t2 = b ^ c; - int t3 = b & t2; - int t4 = a ^ t3; - int t5 = c ^ t4; - int t6 = d | t4; + var t1 = a | b; + var t2 = b ^ c; + var t3 = b & t2; + var t4 = a ^ t3; + var t5 = c ^ t4; + var t6 = d | t4; _x0 = t2 ^ t6; - int t8 = t2 | t6; - int t9 = d ^ t8; + var t8 = t2 | t6; + var t9 = d ^ t8; _x2 = t5 ^ t9; - int t11 = t1 ^ t9; - int t12 = _x0 & t11; + var t11 = t1 ^ t9; + var t12 = _x0 & t11; _x3 = t4 ^ t12; _x1 = _x3 ^ (_x0 ^ t11); } @@ -555,17 +904,17 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers /// The d. private void Sb4(int a, int b, int c, int d) { - int t1 = a ^ d; - int t2 = d & t1; - int t3 = c ^ t2; - int t4 = b | t3; + var t1 = a ^ d; + var t2 = d & t1; + var t3 = c ^ t2; + var t4 = b | t3; _x3 = t1 ^ t4; - int t6 = ~b; - int t7 = t1 | t6; + var t6 = ~b; + var t7 = t1 | t6; _x0 = t3 ^ t7; - int t9 = a & _x0; - int t10 = t1 ^ t6; - int t11 = t4 & t10; + var t9 = a & _x0; + var t10 = t1 ^ t6; + var t11 = t4 & t10; _x2 = t9 ^ t11; _x1 = (a ^ t3) ^ (t10 & _x2); } @@ -579,17 +928,17 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers /// The d. private void Ib4(int a, int b, int c, int d) { - int t1 = c | d; - int t2 = a & t1; - int t3 = b ^ t2; - int t4 = a & t3; - int t5 = c ^ t4; + var t1 = c | d; + var t2 = a & t1; + var t3 = b ^ t2; + var t4 = a & t3; + var t5 = c ^ t4; _x1 = d ^ t5; - int t7 = ~a; - int t8 = t5 & _x1; + var t7 = ~a; + var t8 = t5 & _x1; _x3 = t3 ^ t8; - int t10 = _x1 | t7; - int t11 = d ^ t10; + var t10 = _x1 | t7; + var t11 = d ^ t10; _x0 = _x3 ^ t11; _x2 = (t3 & t11) ^ (_x1 ^ t7); } @@ -603,18 +952,18 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers /// The d. private void Sb5(int a, int b, int c, int d) { - int t1 = ~a; - int t2 = a ^ b; - int t3 = a ^ d; - int t4 = c ^ t1; - int t5 = t2 | t3; + var t1 = ~a; + var t2 = a ^ b; + var t3 = a ^ d; + var t4 = c ^ t1; + var t5 = t2 | t3; _x0 = t4 ^ t5; - int t7 = d & _x0; - int t8 = t2 ^ _x0; + var t7 = d & _x0; + var t8 = t2 ^ _x0; _x1 = t7 ^ t8; - int t10 = t1 | _x0; - int t11 = t2 | t7; - int t12 = t3 ^ t10; + var t10 = t1 | _x0; + var t11 = t2 | t7; + var t12 = t3 ^ t10; _x2 = t11 ^ t12; _x3 = (b ^ t7) ^ (_x1 & t12); } @@ -628,17 +977,17 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers /// The d. private void Ib5(int a, int b, int c, int d) { - int t1 = ~c; - int t2 = b & t1; - int t3 = d ^ t2; - int t4 = a & t3; - int t5 = b ^ t1; + var t1 = ~c; + var t2 = b & t1; + var t3 = d ^ t2; + var t4 = a & t3; + var t5 = b ^ t1; _x3 = t4 ^ t5; - int t7 = b | _x3; - int t8 = a & t7; + var t7 = b | _x3; + var t8 = a & t7; _x1 = t3 ^ t8; - int t10 = a | d; - int t11 = t1 ^ t7; + var t10 = a | d; + var t11 = t1 ^ t7; _x0 = t10 ^ t11; _x2 = (b & t10) ^ (t4 | (a ^ c)); } @@ -652,17 +1001,17 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers /// The d. private void Sb6(int a, int b, int c, int d) { - int t1 = ~a; - int t2 = a ^ d; - int t3 = b ^ t2; - int t4 = t1 | t2; - int t5 = c ^ t4; + var t1 = ~a; + var t2 = a ^ d; + var t3 = b ^ t2; + var t4 = t1 | t2; + var t5 = c ^ t4; _x1 = b ^ t5; - int t7 = t2 | _x1; - int t8 = d ^ t7; - int t9 = t5 & t8; + var t7 = t2 | _x1; + var t8 = d ^ t7; + var t9 = t5 & t8; _x2 = t3 ^ t9; - int t11 = t5 ^ t8; + var t11 = t5 ^ t8; _x0 = _x2 ^ t11; _x3 = (~t5) ^ (t3 & t11); } @@ -676,17 +1025,17 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers /// The d. private void Ib6(int a, int b, int c, int d) { - int t1 = ~a; - int t2 = a ^ b; - int t3 = c ^ t2; - int t4 = c | t1; - int t5 = d ^ t4; + var t1 = ~a; + var t2 = a ^ b; + var t3 = c ^ t2; + var t4 = c | t1; + var t5 = d ^ t4; _x1 = t3 ^ t5; - int t7 = t3 & t5; - int t8 = t2 ^ t7; - int t9 = b | t8; + var t7 = t3 & t5; + var t8 = t2 ^ t7; + var t9 = b | t8; _x3 = t5 ^ t9; - int t11 = b | _x3; + var t11 = b | _x3; _x0 = t8 ^ t11; _x2 = (d & t1) ^ (t3 ^ t11); } @@ -700,18 +1049,18 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers /// The d. private void Sb7(int a, int b, int c, int d) { - int t1 = b ^ c; - int t2 = c & t1; - int t3 = d ^ t2; - int t4 = a ^ t3; - int t5 = d | t1; - int t6 = t4 & t5; + var t1 = b ^ c; + var t2 = c & t1; + var t3 = d ^ t2; + var t4 = a ^ t3; + var t5 = d | t1; + var t6 = t4 & t5; _x1 = b ^ t6; - int t8 = t3 | _x1; - int t9 = a & t4; + var t8 = t3 | _x1; + var t9 = a & t4; _x3 = t1 ^ t9; - int t11 = t4 ^ t8; - int t12 = _x3 & t11; + var t11 = t4 ^ t8; + var t12 = _x3 & t11; _x2 = t3 ^ t12; _x0 = (~t11) ^ (_x3 & _x2); } @@ -725,12 +1074,12 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers /// The d. private void Ib7(int a, int b, int c, int d) { - int t3 = c | (a & b); - int t4 = d & (a | b); + var t3 = c | (a & b); + var t4 = d & (a | b); _x3 = t3 ^ t4; - int t6 = ~d; - int t7 = b ^ t4; - int t9 = t7 | (_x3 ^ t6); + var t6 = ~d; + var t7 = b ^ t4; + var t9 = t7 | (_x3 ^ t6); _x1 = a ^ t9; _x0 = (c ^ t7) ^ (d | _x1); _x2 = (t3 ^ _x1) ^ (_x0 ^ (a & _x3)); @@ -741,10 +1090,10 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers /// private void LT() { - int x0 = RotateLeft(_x0, 13); - int x2 = RotateLeft(_x2, 3); - int x1 = _x1 ^ x0 ^ x2; - int x3 = _x3 ^ x2 ^ x0 << 3; + var x0 = RotateLeft(_x0, 13); + var x2 = RotateLeft(_x2, 3); + var x1 = _x1 ^ x0 ^ x2; + var x3 = _x3 ^ x2 ^ x0 << 3; _x1 = RotateLeft(x1, 1); _x3 = RotateLeft(x3, 7); @@ -757,10 +1106,10 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers ///
private void InverseLT() { - int x2 = RotateRight(_x2, 22) ^ _x3 ^ (_x1 << 7); - int x0 = RotateRight(_x0, 5) ^ _x1 ^ _x3; - int x3 = RotateRight(_x3, 7); - int x1 = RotateRight(_x1, 1); + var x2 = RotateRight(_x2, 22) ^ _x3 ^ (_x1 << 7); + var x0 = RotateRight(_x0, 5) ^ _x1 ^ _x3; + var x3 = RotateRight(_x3, 7); + var x1 = RotateRight(_x1, 1); _x3 = x3 ^ x2 ^ x0 << 3; _x1 = x1 ^ x0 ^ x2; _x2 = RotateRight(x2, 3); diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/TripleDesCipher.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/TripleDesCipher.cs index a9db5db5..644d4472 100644 --- a/src/Renci.SshNet/Security/Cryptography/Ciphers/TripleDesCipher.cs +++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/TripleDesCipher.cs @@ -40,12 +40,16 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { if ((inputOffset + BlockSize) > inputBuffer.Length) + { throw new IndexOutOfRangeException("input buffer too short"); + } if ((outputOffset + BlockSize) > outputBuffer.Length) + { throw new IndexOutOfRangeException("output buffer too short"); + } - if (_encryptionKey1 == null || _encryptionKey2 == null || _encryptionKey3 == null) + if (_encryptionKey1 is null || _encryptionKey2 is null || _encryptionKey3 is null) { var part1 = new byte[8]; var part2 = new byte[8]; @@ -53,16 +57,15 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers Buffer.BlockCopy(Key, 0, part1, 0, 8); Buffer.BlockCopy(Key, 8, part2, 0, 8); - _encryptionKey1 = GenerateWorkingKey(true, part1); - - _encryptionKey2 = GenerateWorkingKey(false, part2); + _encryptionKey1 = GenerateWorkingKey(encrypting: true, part1); + _encryptionKey2 = GenerateWorkingKey(encrypting: false, part2); if (Key.Length == 24) { var part3 = new byte[8]; Buffer.BlockCopy(Key, 16, part3, 0, 8); - _encryptionKey3 = GenerateWorkingKey(true, part3); + _encryptionKey3 = GenerateWorkingKey(encrypting: true, part3); } else { @@ -70,7 +73,7 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers } } - byte[] temp = new byte[BlockSize]; + var temp = new byte[BlockSize]; DesFunc(_encryptionKey1, inputBuffer, inputOffset, temp, 0); DesFunc(_encryptionKey2, temp, 0, temp, 0); @@ -93,12 +96,16 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { if ((inputOffset + BlockSize) > inputBuffer.Length) + { throw new IndexOutOfRangeException("input buffer too short"); + } if ((outputOffset + BlockSize) > outputBuffer.Length) + { throw new IndexOutOfRangeException("output buffer too short"); + } - if (_decryptionKey1 == null || _decryptionKey2 == null || _decryptionKey3 == null) + if (_decryptionKey1 is null || _decryptionKey2 is null || _decryptionKey3 is null) { var part1 = new byte[8]; var part2 = new byte[8]; @@ -106,15 +113,15 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers Buffer.BlockCopy(Key, 0, part1, 0, 8); Buffer.BlockCopy(Key, 8, part2, 0, 8); - _decryptionKey1 = GenerateWorkingKey(false, part1); - _decryptionKey2 = GenerateWorkingKey(true, part2); + _decryptionKey1 = GenerateWorkingKey(encrypting: false, part1); + _decryptionKey2 = GenerateWorkingKey(encrypting: true, part2); if (Key.Length == 24) { var part3 = new byte[8]; Buffer.BlockCopy(Key, 16, part3, 0, 8); - _decryptionKey3 = GenerateWorkingKey(false, part3); + _decryptionKey3 = GenerateWorkingKey(encrypting: false, part3); } else { @@ -122,7 +129,7 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers } } - byte[] temp = new byte[BlockSize]; + var temp = new byte[BlockSize]; DesFunc(_decryptionKey3, inputBuffer, inputOffset, temp, 0); DesFunc(_decryptionKey2, temp, 0, temp, 0); @@ -138,8 +145,10 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers { var keySize = Key.Length * 8; - if (!(keySize == 128 || keySize == 128 + 64)) + if (keySize is not (128 or 128 + 64)) + { throw new ArgumentException(string.Format("KeySize '{0}' is not valid for this algorithm.", keySize)); + } } } } diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/TwofishCipher.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/TwofishCipher.cs index 8f7c8bba..1a4bf2cc 100644 --- a/src/Renci.SshNet/Security/Cryptography/Ciphers/TwofishCipher.cs +++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/TwofishCipher.cs @@ -3,7 +3,7 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers { /// - /// Implements Twofish cipher algorithm + /// Implements Twofish cipher algorithm. /// public sealed class TwofishCipher : BlockCipher { @@ -20,10 +20,10 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers { var keySize = key.Length * 8; - if (!(keySize == 128 || keySize == 192 || keySize == 256)) + if (keySize is not (128 or 192 or 256)) + { throw new ArgumentException(string.Format("KeySize '{0}' is not valid for this algorithm.", keySize)); - - // TODO: Refactor this algorithm + } // calculate the MDS matrix var m1 = new int[2]; @@ -42,13 +42,13 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers mX[1] = Mx_X(j) & 0xff; mY[1] = Mx_Y(j) & 0xff; - gMDS0[i] = m1[P_00] | mX[P_00] << 8 | mY[P_00] << 16 | mY[P_00] << 24; + _gMDS0[i] = m1[P_00] | mX[P_00] << 8 | mY[P_00] << 16 | mY[P_00] << 24; - gMDS1[i] = mY[P_10] | mY[P_10] << 8 | mX[P_10] << 16 | m1[P_10] << 24; + _gMDS1[i] = mY[P_10] | mY[P_10] << 8 | mX[P_10] << 16 | m1[P_10] << 24; - gMDS2[i] = mX[P_20] | mY[P_20] << 8 | m1[P_20] << 16 | mY[P_20] << 24; + _gMDS2[i] = mX[P_20] | mY[P_20] << 8 | m1[P_20] << 16 | mY[P_20] << 24; - gMDS3[i] = mX[P_30] | m1[P_30] << 8 | mY[P_30] << 16 | mX[P_30] << 24; + _gMDS3[i] = mX[P_30] | m1[P_30] << 8 | mY[P_30] << 16 | mX[P_30] << 24; } _k64Cnt = key.Length / 8; // pre-padded ? @@ -68,31 +68,31 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers /// public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { - var x0 = BytesTo32Bits(inputBuffer, inputOffset) ^ gSubKeys[INPUT_WHITEN]; - var x1 = BytesTo32Bits(inputBuffer, inputOffset + 4) ^ gSubKeys[INPUT_WHITEN + 1]; - var x2 = BytesTo32Bits(inputBuffer, inputOffset + 8) ^ gSubKeys[INPUT_WHITEN + 2]; - var x3 = BytesTo32Bits(inputBuffer, inputOffset + 12) ^ gSubKeys[INPUT_WHITEN + 3]; + var x0 = BytesTo32Bits(inputBuffer, inputOffset) ^ _gSubKeys[INPUT_WHITEN]; + var x1 = BytesTo32Bits(inputBuffer, inputOffset + 4) ^ _gSubKeys[INPUT_WHITEN + 1]; + var x2 = BytesTo32Bits(inputBuffer, inputOffset + 8) ^ _gSubKeys[INPUT_WHITEN + 2]; + var x3 = BytesTo32Bits(inputBuffer, inputOffset + 12) ^ _gSubKeys[INPUT_WHITEN + 3]; var k = ROUND_SUBKEYS; for (var r = 0; r < ROUNDS; r += 2) { - var t0 = Fe32_0(gSBox, x0); - var t1 = Fe32_3(gSBox, x1); - x2 ^= t0 + t1 + gSubKeys[k++]; + var t0 = Fe32_0(_gSBox, x0); + var t1 = Fe32_3(_gSBox, x1); + x2 ^= t0 + t1 + _gSubKeys[k++]; x2 = (int)((uint)x2 >> 1) | x2 << 31; - x3 = (x3 << 1 | (int)((uint)x3 >> 31)) ^ (t0 + 2 * t1 + gSubKeys[k++]); + x3 = (x3 << 1 | (int)((uint)x3 >> 31)) ^ (t0 + 2 * t1 + _gSubKeys[k++]); - t0 = Fe32_0(gSBox, x2); - t1 = Fe32_3(gSBox, x3); - x0 ^= t0 + t1 + gSubKeys[k++]; + t0 = Fe32_0(_gSBox, x2); + t1 = Fe32_3(_gSBox, x3); + x0 ^= t0 + t1 + _gSubKeys[k++]; x0 = (int)((uint)x0 >> 1) | x0 << 31; - x1 = (x1 << 1 | (int)((uint)x1 >> 31)) ^ (t0 + 2 * t1 + gSubKeys[k++]); + x1 = (x1 << 1 | (int)((uint)x1 >> 31)) ^ (t0 + 2 * t1 + _gSubKeys[k++]); } - Bits32ToBytes(x2 ^ gSubKeys[OUTPUT_WHITEN], outputBuffer, outputOffset); - Bits32ToBytes(x3 ^ gSubKeys[OUTPUT_WHITEN + 1], outputBuffer, outputOffset + 4); - Bits32ToBytes(x0 ^ gSubKeys[OUTPUT_WHITEN + 2], outputBuffer, outputOffset + 8); - Bits32ToBytes(x1 ^ gSubKeys[OUTPUT_WHITEN + 3], outputBuffer, outputOffset + 12); + Bits32ToBytes(x2 ^ _gSubKeys[OUTPUT_WHITEN], outputBuffer, outputOffset); + Bits32ToBytes(x3 ^ _gSubKeys[OUTPUT_WHITEN + 1], outputBuffer, outputOffset + 4); + Bits32ToBytes(x0 ^ _gSubKeys[OUTPUT_WHITEN + 2], outputBuffer, outputOffset + 8); + Bits32ToBytes(x1 ^ _gSubKeys[OUTPUT_WHITEN + 3], outputBuffer, outputOffset + 12); return BlockSize; } @@ -110,37 +110,35 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers /// public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { - var x2 = BytesTo32Bits(inputBuffer, inputOffset) ^ gSubKeys[OUTPUT_WHITEN]; - var x3 = BytesTo32Bits(inputBuffer, inputOffset + 4) ^ gSubKeys[OUTPUT_WHITEN + 1]; - var x0 = BytesTo32Bits(inputBuffer, inputOffset + 8) ^ gSubKeys[OUTPUT_WHITEN + 2]; - var x1 = BytesTo32Bits(inputBuffer, inputOffset + 12) ^ gSubKeys[OUTPUT_WHITEN + 3]; + var x2 = BytesTo32Bits(inputBuffer, inputOffset) ^ _gSubKeys[OUTPUT_WHITEN]; + var x3 = BytesTo32Bits(inputBuffer, inputOffset + 4) ^ _gSubKeys[OUTPUT_WHITEN + 1]; + var x0 = BytesTo32Bits(inputBuffer, inputOffset + 8) ^ _gSubKeys[OUTPUT_WHITEN + 2]; + var x1 = BytesTo32Bits(inputBuffer, inputOffset + 12) ^ _gSubKeys[OUTPUT_WHITEN + 3]; var k = ROUND_SUBKEYS + 2 * ROUNDS - 1; for (var r = 0; r < ROUNDS; r += 2) { - var t0 = Fe32_0(gSBox, x2); - var t1 = Fe32_3(gSBox, x3); - x1 ^= t0 + 2 * t1 + gSubKeys[k--]; - x0 = (x0 << 1 | (int)((uint)x0 >> 31)) ^ (t0 + t1 + gSubKeys[k--]); + var t0 = Fe32_0(_gSBox, x2); + var t1 = Fe32_3(_gSBox, x3); + x1 ^= t0 + 2 * t1 + _gSubKeys[k--]; + x0 = (x0 << 1 | (int)((uint)x0 >> 31)) ^ (t0 + t1 + _gSubKeys[k--]); x1 = (int)((uint)x1 >> 1) | x1 << 31; - t0 = Fe32_0(gSBox, x0); - t1 = Fe32_3(gSBox, x1); - x3 ^= t0 + 2 * t1 + gSubKeys[k--]; - x2 = (x2 << 1 | (int)((uint)x2 >> 31)) ^ (t0 + t1 + gSubKeys[k--]); + t0 = Fe32_0(_gSBox, x0); + t1 = Fe32_3(_gSBox, x1); + x3 ^= t0 + 2 * t1 + _gSubKeys[k--]; + x2 = (x2 << 1 | (int)((uint)x2 >> 31)) ^ (t0 + t1 + _gSubKeys[k--]); x3 = (int)((uint)x3 >> 1) | x3 << 31; } - Bits32ToBytes(x0 ^ gSubKeys[INPUT_WHITEN], outputBuffer, outputOffset); - Bits32ToBytes(x1 ^ gSubKeys[INPUT_WHITEN + 1], outputBuffer, outputOffset + 4); - Bits32ToBytes(x2 ^ gSubKeys[INPUT_WHITEN + 2], outputBuffer, outputOffset + 8); - Bits32ToBytes(x3 ^ gSubKeys[INPUT_WHITEN + 3], outputBuffer, outputOffset + 12); + Bits32ToBytes(x0 ^ _gSubKeys[INPUT_WHITEN], outputBuffer, outputOffset); + Bits32ToBytes(x1 ^ _gSubKeys[INPUT_WHITEN + 1], outputBuffer, outputOffset + 4); + Bits32ToBytes(x2 ^ _gSubKeys[INPUT_WHITEN + 2], outputBuffer, outputOffset + 8); + Bits32ToBytes(x3 ^ _gSubKeys[INPUT_WHITEN + 3], outputBuffer, outputOffset + 12); return BlockSize; } - #region Static Definition Tables - private static readonly byte[] P = { // p0 @@ -160,6 +158,7 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers 0x57, 0xC7, 0x8D, 0x74, 0xB7, 0xC4, 0x9F, 0x72, 0x7E, 0x15, 0x22, 0x12, 0x58, 0x07, 0x99, 0x34, 0x6E, 0x50, 0xDE, 0x68, 0x65, 0xBC, 0xDB, 0xF8, 0xC8, 0xA8, 0x2B, 0x40, 0xDC, 0xFE, 0x32, 0xA4, 0xCA, 0x10, 0x21, 0xF0, 0xD3, 0x5D, 0x0F, 0x00, 0x6F, 0x9D, 0x36, 0x42, 0x4A, 0x5E, 0xC1, 0xE0, + // p1 0x75, 0xF3, 0xC6, 0xF4, 0xDB, 0x7B, 0xFB, 0xC8, 0x4A, 0xD3, 0xE6, 0x6B, 0x45, 0x7D, 0xE8, 0x4B, 0xD6, 0x32, 0xD8, 0xFD, 0x37, 0x71, 0xF1, 0xE1, 0x30, 0x0F, 0xF8, 0x1B, 0x87, 0xFA, 0x06, 0x3F, @@ -179,13 +178,11 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers 0xD7, 0x61, 0x1E, 0xB4, 0x50, 0x04, 0xF6, 0xC2, 0x16, 0x25, 0x86, 0x56, 0x55, 0x09, 0xBE, 0x91 }; - #endregion - /** - * Define the fixed p0/p1 permutations used in keyed S-box lookup. - * By changing the following constant definitions, the S-boxes will - * automatically Get changed in the Twofish engine. - */ + * Define the fixed p0/p1 permutations used in keyed S-box lookup. + * By changing the following constant definitions, the S-boxes will + * automatically Get changed in the Twofish engine. + */ private const int P_00 = 1; private const int P_01 = 0; private const int P_02 = 0; @@ -217,10 +214,6 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers private const int RS_GF_FDBK = 0x14D; // field generator - //==================================== - // Useful constants - //==================================== - private const int ROUNDS = 16; private const int MAX_ROUNDS = 16; // bytes = 128 bits private const int MAX_KEY_BITS = 256; @@ -235,27 +228,27 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers private const int SK_BUMP = 0x01010101; private const int SK_ROTL = 9; - private readonly int[] gMDS0 = new int[MAX_KEY_BITS]; - private readonly int[] gMDS1 = new int[MAX_KEY_BITS]; - private readonly int[] gMDS2 = new int[MAX_KEY_BITS]; - private readonly int[] gMDS3 = new int[MAX_KEY_BITS]; - - /** - * gSubKeys[] and gSBox[] are eventually used in the - * encryption and decryption methods. - */ - private int[] gSubKeys; - private int[] gSBox; + private readonly int[] _gMDS0 = new int[MAX_KEY_BITS]; + private readonly int[] _gMDS1 = new int[MAX_KEY_BITS]; + private readonly int[] _gMDS2 = new int[MAX_KEY_BITS]; + private readonly int[] _gMDS3 = new int[MAX_KEY_BITS]; private readonly int _k64Cnt; + /** + * _gSubKeys[] and _gSBox[] are eventually used in the + * encryption and decryption methods. + */ + private int[] _gSubKeys; + private int[] _gSBox; + private void SetKey(byte[] key) { var k32e = new int[MAX_KEY_BITS / 64]; // 4 var k32o = new int[MAX_KEY_BITS / 64]; // 4 var sBoxKeys = new int[MAX_KEY_BITS / 64]; // 4 - gSubKeys = new int[TOTAL_SUBKEYS]; + _gSubKeys = new int[TOTAL_SUBKEYS]; if (_k64Cnt < 1) { @@ -290,9 +283,9 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers var b = F32(q + SK_BUMP, k32o); b = b << 8 | (int)((uint)b >> 24); a += b; - gSubKeys[i * 2] = a; + _gSubKeys[i * 2] = a; a += b; - gSubKeys[i * 2 + 1] = a << SK_ROTL | (int)((uint)a >> (32 - SK_ROTL)); + _gSubKeys[i * 2 + 1] = a << SK_ROTL | (int)((uint)a >> (32 - SK_ROTL)); } /* @@ -302,18 +295,20 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers var k1 = sBoxKeys[1]; var k2 = sBoxKeys[2]; var k3 = sBoxKeys[3]; - gSBox = new int[4 * MAX_KEY_BITS]; + _gSBox = new int[4 * MAX_KEY_BITS]; for (var i = 0; i < MAX_KEY_BITS; i++) { int b1, b2, b3; var b0 = b1 = b2 = b3 = i; + +#pragma warning disable IDE0010 // Add missing cases switch (_k64Cnt & 3) { case 1: - gSBox[i * 2] = gMDS0[(P[P_01 * 256 + b0] & 0xff) ^ M_b0(k0)]; - gSBox[i * 2 + 1] = gMDS1[(P[P_11 * 256 + b1] & 0xff) ^ M_b1(k0)]; - gSBox[i * 2 + 0x200] = gMDS2[(P[P_21 * 256 + b2] & 0xff) ^ M_b2(k0)]; - gSBox[i * 2 + 0x201] = gMDS3[(P[P_31 * 256 + b3] & 0xff) ^ M_b3(k0)]; + _gSBox[i * 2] = _gMDS0[(P[P_01 * 256 + b0] & 0xff) ^ M_b0(k0)]; + _gSBox[i * 2 + 1] = _gMDS1[(P[P_11 * 256 + b1] & 0xff) ^ M_b1(k0)]; + _gSBox[i * 2 + 0x200] = _gMDS2[(P[P_21 * 256 + b2] & 0xff) ^ M_b2(k0)]; + _gSBox[i * 2 + 0x201] = _gMDS3[(P[P_31 * 256 + b3] & 0xff) ^ M_b3(k0)]; break; case 0: /* 256 bits of key */ b0 = (P[P_04 * 256 + b0] & 0xff) ^ M_b0(k3); @@ -328,12 +323,13 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers b3 = (P[P_33 * 256 + b3] & 0xff) ^ M_b3(k2); goto case 2; case 2: - gSBox[i * 2] = gMDS0[(P[P_01 * 256 + (P[P_02 * 256 + b0] & 0xff) ^ M_b0(k1)] & 0xff) ^ M_b0(k0)]; - gSBox[i * 2 + 1] = gMDS1[(P[P_11 * 256 + (P[P_12 * 256 + b1] & 0xff) ^ M_b1(k1)] & 0xff) ^ M_b1(k0)]; - gSBox[i * 2 + 0x200] = gMDS2[(P[P_21 * 256 + (P[P_22 * 256 + b2] & 0xff) ^ M_b2(k1)] & 0xff) ^ M_b2(k0)]; - gSBox[i * 2 + 0x201] = gMDS3[(P[P_31 * 256 + (P[P_32 * 256 + b3] & 0xff) ^ M_b3(k1)] & 0xff) ^ M_b3(k0)]; + _gSBox[i * 2] = _gMDS0[(P[P_01 * 256 + (P[P_02 * 256 + b0] & 0xff) ^ M_b0(k1)] & 0xff) ^ M_b0(k0)]; + _gSBox[i * 2 + 1] = _gMDS1[(P[P_11 * 256 + (P[P_12 * 256 + b1] & 0xff) ^ M_b1(k1)] & 0xff) ^ M_b1(k0)]; + _gSBox[i * 2 + 0x200] = _gMDS2[(P[P_21 * 256 + (P[P_22 * 256 + b2] & 0xff) ^ M_b2(k1)] & 0xff) ^ M_b2(k0)]; + _gSBox[i * 2 + 0x201] = _gMDS3[(P[P_31 * 256 + (P[P_32 * 256 + b3] & 0xff) ^ M_b3(k1)] & 0xff) ^ M_b3(k0)]; break; } +#pragma warning restore IDE0010 // Add missing cases } /* @@ -359,13 +355,15 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers var k3 = k32[3]; var result = 0; + +#pragma warning disable IDE0010 // Add missing cases switch (_k64Cnt & 3) { case 1: - result = gMDS0[(P[P_01 * 256 + b0] & 0xff) ^ M_b0(k0)] ^ - gMDS1[(P[P_11 * 256 + b1] & 0xff) ^ M_b1(k0)] ^ - gMDS2[(P[P_21 * 256 + b2] & 0xff) ^ M_b2(k0)] ^ - gMDS3[(P[P_31 * 256 + b3] & 0xff) ^ M_b3(k0)]; + result = _gMDS0[(P[P_01 * 256 + b0] & 0xff) ^ M_b0(k0)] ^ + _gMDS1[(P[P_11 * 256 + b1] & 0xff) ^ M_b1(k0)] ^ + _gMDS2[(P[P_21 * 256 + b2] & 0xff) ^ M_b2(k0)] ^ + _gMDS3[(P[P_31 * 256 + b3] & 0xff) ^ M_b3(k0)]; break; case 0: /* 256 bits of key */ b0 = (P[P_04 * 256 + b0] & 0xff) ^ M_b0(k3); @@ -381,12 +379,14 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers goto case 2; case 2: result = - gMDS0[(P[P_01 * 256 + (P[P_02 * 256 + b0] & 0xff) ^ M_b0(k1)] & 0xff) ^ M_b0(k0)] ^ - gMDS1[(P[P_11 * 256 + (P[P_12 * 256 + b1] & 0xff) ^ M_b1(k1)] & 0xff) ^ M_b1(k0)] ^ - gMDS2[(P[P_21 * 256 + (P[P_22 * 256 + b2] & 0xff) ^ M_b2(k1)] & 0xff) ^ M_b2(k0)] ^ - gMDS3[(P[P_31 * 256 + (P[P_32 * 256 + b3] & 0xff) ^ M_b3(k1)] & 0xff) ^ M_b3(k0)]; + _gMDS0[(P[P_01 * 256 + (P[P_02 * 256 + b0] & 0xff) ^ M_b0(k1)] & 0xff) ^ M_b0(k0)] ^ + _gMDS1[(P[P_11 * 256 + (P[P_12 * 256 + b1] & 0xff) ^ M_b1(k1)] & 0xff) ^ M_b1(k0)] ^ + _gMDS2[(P[P_21 * 256 + (P[P_22 * 256 + b2] & 0xff) ^ M_b2(k1)] & 0xff) ^ M_b2(k0)] ^ + _gMDS3[(P[P_31 * 256 + (P[P_32 * 256 + b3] & 0xff) ^ M_b3(k1)] & 0xff) ^ M_b3(k0)]; break; } +#pragma warning restore IDE0010 // Add missing cases + return result; } @@ -402,6 +402,7 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers private static int RS_MDS_Encode(int k0, int k1) { var r = k1; + // shift 1 byte at a time r = RS_rem(r); r = RS_rem(r); @@ -428,24 +429,19 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers private static int RS_rem(int x) { var b = (int)(((uint)x >> 24) & 0xff); - var g2 = ((b << 1) ^ - ((b & 0x80) != 0 ? RS_GF_FDBK : 0)) & 0xff; - var g3 = ((int)((uint)b >> 1) ^ - ((b & 0x01) != 0 ? (int)((uint)RS_GF_FDBK >> 1) : 0)) ^ g2; - return ((x << 8) ^ (g3 << 24) ^ (g2 << 16) ^ (g3 << 8) ^ b); + var g2 = ((b << 1) ^ ((b & 0x80) != 0 ? RS_GF_FDBK : 0)) & 0xff; + var g3 = ((int)((uint)b >> 1) ^ ((b & 0x01) != 0 ? (int)((uint)RS_GF_FDBK >> 1) : 0)) ^ g2; + return (x << 8) ^ (g3 << 24) ^ (g2 << 16) ^ (g3 << 8) ^ b; } private static int LFSR1(int x) { - return (x >> 1) ^ - (((x & 0x01) != 0) ? GF256_FDBK_2 : 0); + return (x >> 1) ^ (((x & 0x01) != 0) ? GF256_FDBK_2 : 0); } private static int LFSR2(int x) { - return (x >> 2) ^ - (((x & 0x02) != 0) ? GF256_FDBK_2 : 0) ^ - (((x & 0x01) != 0) ? GF256_FDBK_4 : 0); + return (x >> 2) ^ (((x & 0x02) != 0) ? GF256_FDBK_2 : 0) ^ (((x & 0x01) != 0) ? GF256_FDBK_4 : 0); } private static int Mx_X(int x) @@ -458,22 +454,30 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers return x ^ LFSR1(x) ^ LFSR2(x); } // EF +#pragma warning disable IDE1006 // Naming Styles private static int M_b0(int x) +#pragma warning restore IDE1006 // Naming Styles { return x & 0xff; } +#pragma warning disable IDE1006 // Naming Styles private static int M_b1(int x) +#pragma warning restore IDE1006 // Naming Styles { return (int)((uint)x >> 8) & 0xff; } +#pragma warning disable IDE1006 // Naming Styles private static int M_b2(int x) +#pragma warning restore IDE1006 // Naming Styles { return (int)((uint)x >> 16) & 0xff; } +#pragma warning disable IDE1006 // Naming Styles private static int M_b3(int x) +#pragma warning restore IDE1006 // Naming Styles { return (int)((uint)x >> 24) & 0xff; } @@ -496,7 +500,7 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers private static int BytesTo32Bits(byte[] b, int p) { - return ((b[p] & 0xff)) | + return (b[p] & 0xff) | ((b[p + 1] & 0xff) << 8) | ((b[p + 2] & 0xff) << 16) | ((b[p + 3] & 0xff) << 24); diff --git a/src/Renci.SshNet/Security/Cryptography/DsaDigitalSignature.cs b/src/Renci.SshNet/Security/Cryptography/DsaDigitalSignature.cs index 06275bda..467a5e0a 100644 --- a/src/Renci.SshNet/Security/Cryptography/DsaDigitalSignature.cs +++ b/src/Renci.SshNet/Security/Cryptography/DsaDigitalSignature.cs @@ -1,5 +1,6 @@ using System; using System.Security.Cryptography; + using Renci.SshNet.Abstractions; using Renci.SshNet.Common; @@ -10,9 +11,9 @@ namespace Renci.SshNet.Security.Cryptography ///
public class DsaDigitalSignature : DigitalSignature, IDisposable { - private HashAlgorithm _hash; - private readonly DsaKey _key; + private HashAlgorithm _hash; + private bool _isDisposed; /// /// Initializes a new instance of the class. @@ -21,8 +22,10 @@ namespace Renci.SshNet.Security.Cryptography /// is null. public DsaDigitalSignature(DsaKey key) { - if (key == null) - throw new ArgumentNullException("key"); + if (key is null) + { + throw new ArgumentNullException(nameof(key)); + } _key = key; @@ -45,9 +48,11 @@ namespace Renci.SshNet.Security.Cryptography var hm = new BigInteger(hashInput.Reverse().Concat(new byte[] { 0 })); if (signature.Length != 40) + { throw new InvalidOperationException("Invalid signature."); + } - // Extract r and s numbers from the signature + // Extract r and s numbers from the signature var rBytes = new byte[21]; var sBytes = new byte[21]; @@ -60,29 +65,33 @@ namespace Renci.SshNet.Security.Cryptography var r = new BigInteger(rBytes); var s = new BigInteger(sBytes); - // Reject the signature if 0 < r < q or 0 < s < q is not satisfied. + // Reject the signature if 0 < r < q or 0 < s < q is not satisfied. if (r <= 0 || r >= _key.Q) + { return false; + } if (s <= 0 || s >= _key.Q) + { return false; + } - // Calculate w = s−1 mod q + // Calculate w = s−1 mod q var w = BigInteger.ModInverse(s, _key.Q); - // Calculate u1 = H(m)·w mod q + // Calculate u1 = H(m)·w mod q var u1 = hm * w % _key.Q; - // Calculate u2 = r * w mod q + // Calculate u2 = r * w mod q var u2 = r * w % _key.Q; u1 = BigInteger.ModPow(_key.G, u1, _key.P); u2 = BigInteger.ModPow(_key.Y, u2, _key.P); - // Calculate v = ((g pow u1 * y pow u2) mod p) mod q + // Calculate v = ((g pow u1 * y pow u2) mod p) mod q var v = ((u1 * u2) % _key.P) % _key.Q; - // The signature is valid if v = r + // The signature is valid if v = r return v == r; } @@ -105,37 +114,40 @@ namespace Renci.SshNet.Security.Cryptography do { - BigInteger k = BigInteger.Zero; + var k = BigInteger.Zero; do { - // Generate a random per-message value k where 0 < k < q + // Generate a random per-message value k where 0 < k < q var bitLength = _key.Q.BitLength; if (_key.Q < BigInteger.Zero) + { throw new SshException("Invalid DSA key."); + } while (k <= 0 || k >= _key.Q) { k = BigInteger.Random(bitLength); } - // Calculate r = ((g pow k) mod p) mod q + // Calculate r = ((g pow k) mod p) mod q r = BigInteger.ModPow(_key.G, k, _key.P) % _key.Q; - // In the unlikely case that r = 0, start again with a different random k - } while (r.IsZero); + // In the unlikely case that r = 0, start again with a different random k + } + while (r.IsZero); - - // Calculate s = ((k pow −1)(H(m) + x*r)) mod q - k = (BigInteger.ModInverse(k, _key.Q) * (m + _key.X * r)); + // Calculate s = ((k pow −1)(H(m) + x*r)) mod q + k = BigInteger.ModInverse(k, _key.Q) * (m + (_key.X * r)); s = k % _key.Q; - // In the unlikely case that s = 0, start again with a different random k - } while (s.IsZero); + // In the unlikely case that s = 0, start again with a different random k + } + while (s.IsZero); - // The signature is (r, s) + // The signature is (r, s) var signature = new byte[40]; // issue #1918: pad part with zero's on the left if length is less than 20 @@ -149,27 +161,25 @@ namespace Renci.SshNet.Security.Cryptography return signature; } - #region IDisposable Members - - private bool _isDisposed; - /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// public void Dispose() { - Dispose(true); + Dispose(disposing: true); GC.SuppressFinalize(this); } /// - /// Releases unmanaged and - optionally - managed resources + /// Releases unmanaged and - optionally - managed resources. /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected virtual void Dispose(bool disposing) { if (_isDisposed) + { return; + } if (disposing) { @@ -185,14 +195,11 @@ namespace Renci.SshNet.Security.Cryptography } /// - /// Releases unmanaged resources and performs other cleanup operations before the - /// is reclaimed by garbage collection. + /// Finalizes an instance of the class. /// ~DsaDigitalSignature() { - Dispose(false); + Dispose(disposing: false); } - - #endregion } } diff --git a/src/Renci.SshNet/Security/Cryptography/DsaKey.cs b/src/Renci.SshNet/Security/Cryptography/DsaKey.cs index 5e9907f7..1c239aeb 100644 --- a/src/Renci.SshNet/Security/Cryptography/DsaKey.cs +++ b/src/Renci.SshNet/Security/Cryptography/DsaKey.cs @@ -5,10 +5,13 @@ using Renci.SshNet.Security.Cryptography; namespace Renci.SshNet.Security { /// - /// Contains DSA private and public key + /// Contains DSA private and public key. /// public class DsaKey : Key, IDisposable { + private DsaDigitalSignature _digitalSignature; + private bool _isDisposed; + /// /// Gets the P. /// @@ -78,18 +81,14 @@ namespace Renci.SshNet.Security } } - private DsaDigitalSignature _digitalSignature; /// /// Gets the digital signature. /// - protected override DigitalSignature DigitalSignature + protected internal override DigitalSignature DigitalSignature { get { - if (_digitalSignature == null) - { - _digitalSignature = new DsaDigitalSignature(this); - } + _digitalSignature ??= new DsaDigitalSignature(this); return _digitalSignature; } } @@ -109,7 +108,9 @@ namespace Renci.SshNet.Security set { if (value.Length != 4) + { throw new InvalidOperationException("Invalid public key."); + } _privateKey = value; } @@ -131,7 +132,9 @@ namespace Renci.SshNet.Security : base(data) { if (_privateKey.Length != 5) + { throw new InvalidOperationException("Invalid private key."); + } } /// @@ -144,24 +147,15 @@ namespace Renci.SshNet.Security /// The x. public DsaKey(BigInteger p, BigInteger q, BigInteger g, BigInteger y, BigInteger x) { - _privateKey = new BigInteger[5]; - _privateKey[0] = p; - _privateKey[1] = q; - _privateKey[2] = g; - _privateKey[3] = y; - _privateKey[4] = x; + _privateKey = new BigInteger[5] { p, q, g, y, x }; } - #region IDisposable Members - - private bool _isDisposed; - /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// public void Dispose() { - Dispose(true); + Dispose(disposing: true); GC.SuppressFinalize(this); } @@ -172,7 +166,9 @@ namespace Renci.SshNet.Security protected virtual void Dispose(bool disposing) { if (_isDisposed) + { return; + } if (disposing) { @@ -188,14 +184,11 @@ namespace Renci.SshNet.Security } /// - /// Releases unmanaged resources and performs other cleanup operations before the - /// is reclaimed by garbage collection. + /// Finalizes an instance of the class. /// ~DsaKey() { - Dispose(false); + Dispose(disposing: false); } - - #endregion } } diff --git a/src/Renci.SshNet/Security/Cryptography/ED25519DigitalSignature.cs b/src/Renci.SshNet/Security/Cryptography/ED25519DigitalSignature.cs index be68fd48..c777f4d4 100644 --- a/src/Renci.SshNet/Security/Cryptography/ED25519DigitalSignature.cs +++ b/src/Renci.SshNet/Security/Cryptography/ED25519DigitalSignature.cs @@ -10,6 +10,7 @@ namespace Renci.SshNet.Security.Cryptography public class ED25519DigitalSignature : DigitalSignature, IDisposable { private readonly ED25519Key _key; + private bool _isDisposed; /// /// Initializes a new instance of the class. @@ -18,8 +19,10 @@ namespace Renci.SshNet.Security.Cryptography /// is null. public ED25519DigitalSignature(ED25519Key key) { - if (key == null) - throw new ArgumentNullException("key"); + if (key is null) + { + throw new ArgumentNullException(nameof(key)); + } _key = key; } @@ -51,16 +54,12 @@ namespace Renci.SshNet.Security.Cryptography return Ed25519.Sign(input, _key.PrivateKey); } - #region IDisposable Members - - private bool _isDisposed; - /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// public void Dispose() { - Dispose(true); + Dispose(disposing: true); GC.SuppressFinalize(this); } @@ -71,7 +70,9 @@ namespace Renci.SshNet.Security.Cryptography protected virtual void Dispose(bool disposing) { if (_isDisposed) + { return; + } if (disposing) { @@ -80,14 +81,11 @@ namespace Renci.SshNet.Security.Cryptography } /// - /// Releases unmanaged resources and performs other cleanup operations before the - /// is reclaimed by garbage collection. + /// Finalizes an instance of the class. /// ~ED25519DigitalSignature() { - Dispose(false); + Dispose(disposing: false); } - - #endregion } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet/Security/Cryptography/ED25519Key.cs b/src/Renci.SshNet/Security/Cryptography/ED25519Key.cs index 83ac1c8c..84dc6117 100644 --- a/src/Renci.SshNet/Security/Cryptography/ED25519Key.cs +++ b/src/Renci.SshNet/Security/Cryptography/ED25519Key.cs @@ -1,19 +1,23 @@ using System; + using Renci.SshNet.Common; -using Renci.SshNet.Security.Cryptography; using Renci.SshNet.Security.Chaos.NaCl; +using Renci.SshNet.Security.Cryptography; namespace Renci.SshNet.Security { /// - /// Contains ED25519 private and public key + /// Contains ED25519 private and public key. /// public class ED25519Key : Key, IDisposable { private ED25519DigitalSignature _digitalSignature; - private byte[] publicKey = new byte[Ed25519.PublicKeySizeInBytes]; - private byte[] privateKey = new byte[Ed25519.ExpandedPrivateKeySizeInBytes]; + private byte[] _publicKey = new byte[Ed25519.PublicKeySizeInBytes]; +#pragma warning disable IDE1006 // Naming Styles + private readonly byte[] privateKey = new byte[Ed25519.ExpandedPrivateKeySizeInBytes]; +#pragma warning restore IDE1006 // Naming Styles + private bool _isDisposed; /// /// Gets the Key String. @@ -33,11 +37,11 @@ namespace Renci.SshNet.Security { get { - return new BigInteger[] { publicKey.ToBigInteger() }; + return new BigInteger[] { _publicKey.ToBigInteger2() }; } set { - publicKey = value[0].ToByteArray().Reverse().TrimLeadingZeros().Pad(Ed25519.PublicKeySizeInBytes); + _publicKey = value[0].ToByteArray().Reverse().TrimLeadingZeros().Pad(Ed25519.PublicKeySizeInBytes); } } @@ -51,38 +55,35 @@ namespace Renci.SshNet.Security { get { - return PublicKey.Length; + return PublicKey.Length * 8; } } /// /// Gets the digital signature. /// - protected override DigitalSignature DigitalSignature + protected internal override DigitalSignature DigitalSignature { get { - if (_digitalSignature == null) - { - _digitalSignature = new ED25519DigitalSignature(this); - } + _digitalSignature ??= new ED25519DigitalSignature(this); return _digitalSignature; } } /// - /// Gets the PublicKey Bytes + /// Gets the PublicKey Bytes. /// public byte[] PublicKey { get { - return publicKey; + return _publicKey; } } /// - /// Gets the PrivateKey Bytes + /// Gets the PrivateKey Bytes. /// public byte[] PrivateKey { @@ -99,6 +100,15 @@ namespace Renci.SshNet.Security { } + /// + /// Initializes a new instance of the class. + /// + /// pk data. + public ED25519Key(byte[] pk) + { + _publicKey = pk.TrimLeadingZeros().Pad(Ed25519.PublicKeySizeInBytes); + } + /// /// Initializes a new instance of the class. /// @@ -106,22 +116,18 @@ namespace Renci.SshNet.Security /// sk data. public ED25519Key(byte[] pk, byte[] sk) { - publicKey = pk.TrimLeadingZeros().Pad(Ed25519.PublicKeySizeInBytes); + _publicKey = pk.TrimLeadingZeros().Pad(Ed25519.PublicKeySizeInBytes); var seed = new byte[Ed25519.PrivateKeySeedSizeInBytes]; Buffer.BlockCopy(sk, 0, seed, 0, seed.Length); - Ed25519.KeyPairFromSeed(out publicKey, out privateKey, seed); + Ed25519.KeyPairFromSeed(out _publicKey, out privateKey, seed); } - #region IDisposable Members - - private bool _isDisposed; - /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// public void Dispose() { - Dispose(true); + Dispose(disposing: true); GC.SuppressFinalize(this); } @@ -132,7 +138,9 @@ namespace Renci.SshNet.Security protected virtual void Dispose(bool disposing) { if (_isDisposed) + { return; + } if (disposing) { @@ -141,14 +149,11 @@ namespace Renci.SshNet.Security } /// - /// Releases unmanaged resources and performs other cleanup operations before the - /// is reclaimed by garbage collection. + /// Finalizes an instance of the class. /// ~ED25519Key() { - Dispose(false); + Dispose(disposing: false); } - - #endregion } } diff --git a/src/Renci.SshNet/Security/Cryptography/EcdsaDigitalSignature.cs b/src/Renci.SshNet/Security/Cryptography/EcdsaDigitalSignature.cs index 38d60966..93fc6ba4 100644 --- a/src/Renci.SshNet/Security/Cryptography/EcdsaDigitalSignature.cs +++ b/src/Renci.SshNet/Security/Cryptography/EcdsaDigitalSignature.cs @@ -1,8 +1,10 @@ -#if FEATURE_ECDSA -using System; -using Renci.SshNet.Common; +using System; using System.Globalization; +#if NETFRAMEWORK using System.Security.Cryptography; +#endif // NETFRAMEWORK + +using Renci.SshNet.Common; namespace Renci.SshNet.Security.Cryptography { @@ -12,6 +14,7 @@ namespace Renci.SshNet.Security.Cryptography public class EcdsaDigitalSignature : DigitalSignature, IDisposable { private readonly EcdsaKey _key; + private bool _isDisposed; /// /// Initializes a new instance of the class. @@ -20,8 +23,10 @@ namespace Renci.SshNet.Security.Cryptography /// is null. public EcdsaDigitalSignature(EcdsaKey key) { - if (key == null) - throw new ArgumentNullException("key"); + if (key is null) + { + throw new ArgumentNullException(nameof(key)); + } _key = key; } @@ -39,12 +44,12 @@ namespace Renci.SshNet.Security.Cryptography // for 521 sig_size is 132 var sig_size = _key.KeyLength == 521 ? 132 : _key.KeyLength / 4; var ssh_data = new SshDataSignature(signature, sig_size); -#if NETSTANDARD2_0 - return _key.Ecdsa.VerifyData(input, ssh_data.Signature, _key.HashAlgorithm); -#else +#if NETFRAMEWORK var ecdsa = (ECDsaCng)_key.Ecdsa; ecdsa.HashAlgorithm = _key.HashAlgorithm; return ecdsa.VerifyData(input, ssh_data.Signature); +#else + return _key.Ecdsa.VerifyData(input, ssh_data.Signature, _key.HashAlgorithm); #endif } @@ -57,28 +62,23 @@ namespace Renci.SshNet.Security.Cryptography /// public override byte[] Sign(byte[] input) { -#if NETSTANDARD2_0 - var signed = _key.Ecdsa.SignData(input, _key.HashAlgorithm); -#else +#if NETFRAMEWORK var ecdsa = (ECDsaCng)_key.Ecdsa; ecdsa.HashAlgorithm = _key.HashAlgorithm; var signed = ecdsa.SignData(input); +#else + var signed = _key.Ecdsa.SignData(input, _key.HashAlgorithm); #endif - var ssh_data = new SshDataSignature(signed.Length); - ssh_data.Signature = signed; + var ssh_data = new SshDataSignature(signed.Length) { Signature = signed }; return ssh_data.GetBytes(); } - #region IDisposable Members - - private bool _isDisposed; - /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// public void Dispose() { - Dispose(true); + Dispose(disposing: true); GC.SuppressFinalize(this); } @@ -89,7 +89,9 @@ namespace Renci.SshNet.Security.Cryptography protected virtual void Dispose(bool disposing) { if (_isDisposed) + { return; + } if (disposing) { @@ -98,20 +100,17 @@ namespace Renci.SshNet.Security.Cryptography } /// - /// Releases unmanaged resources and performs other cleanup operations before the - /// is reclaimed by garbage collection. + /// Finalizes an instance of the class. /// ~EcdsaDigitalSignature() { - Dispose(false); + Dispose(disposing: false); } - - #endregion } - class SshDataSignature : SshData + internal sealed class SshDataSignature : SshData { - private int _signature_size; + private readonly int _signature_size; private byte[] _signature_r; private byte[] _signature_s; @@ -186,4 +185,3 @@ namespace Renci.SshNet.Security.Cryptography } } } -#endif \ No newline at end of file diff --git a/src/Renci.SshNet/Security/Cryptography/EcdsaKey.cs b/src/Renci.SshNet/Security/Cryptography/EcdsaKey.cs index 58861f02..f6bd50bd 100644 --- a/src/Renci.SshNet/Security/Cryptography/EcdsaKey.cs +++ b/src/Renci.SshNet/Security/Cryptography/EcdsaKey.cs @@ -1,16 +1,20 @@ -#if FEATURE_ECDSA -using System; +using System; +#if NETFRAMEWORK using System.IO; +#endif // NETFRAMEWORK using System.Text; +#if NETFRAMEWORK using System.Runtime.InteropServices; +#endif // NETFRAMEWORK using System.Security.Cryptography; + using Renci.SshNet.Common; using Renci.SshNet.Security.Cryptography; namespace Renci.SshNet.Security { /// - /// Contains ECDSA (ecdsa-sha2-nistp{256,384,521}) private and public key + /// Contains ECDSA (ecdsa-sha2-nistp{256,384,521}) private and public key. /// public class EcdsaKey : Key, IDisposable { @@ -18,8 +22,13 @@ namespace Renci.SshNet.Security internal const string ECDSA_P384_OID_VALUE = "1.3.132.0.34"; // Also called nistP384 or secP384r1 internal const string ECDSA_P521_OID_VALUE = "1.3.132.0.35"; // Also called nistP521or secP521r1 -#if !NETSTANDARD2_0 - internal enum KeyBlobMagicNumber : int + private EcdsaDigitalSignature _digitalSignature; + private bool _isDisposed; + +#if NETFRAMEWORK + private CngKey _key; + + internal enum KeyBlobMagicNumber { BCRYPT_ECDSA_PUBLIC_P256_MAGIC = 0x31534345, BCRYPT_ECDSA_PRIVATE_P256_MAGIC = 0x32534345, @@ -45,39 +54,20 @@ namespace Renci.SshNet.Security internal KeyBlobMagicNumber Magic; internal int cbKey; } - - private CngKey key; #endif /// - /// Gets the SSH name of the ECDSA Key + /// Gets the SSH name of the ECDSA Key. /// + /// + /// The SSH name of the ECDSA Key. + /// public override string ToString() { return string.Format("ecdsa-sha2-nistp{0}", KeyLength); } -#if NETSTANDARD2_0 - /// - /// Gets the HashAlgorithm to use - /// - public HashAlgorithmName HashAlgorithm - { - get - { - switch (KeyLength) - { - case 256: - return HashAlgorithmName.SHA256; - case 384: - return HashAlgorithmName.SHA384; - case 521: - return HashAlgorithmName.SHA512; - } - return HashAlgorithmName.SHA256; - } - } -#else +#if NETFRAMEWORK /// /// Gets the HashAlgorithm to use /// @@ -98,6 +88,27 @@ namespace Renci.SshNet.Security } } } +#else + /// + /// Gets the HashAlgorithm to use. + /// + public HashAlgorithmName HashAlgorithm + { + get + { + switch (KeyLength) + { + case 256: + return HashAlgorithmName.SHA256; + case 384: + return HashAlgorithmName.SHA384; + case 521: + return HashAlgorithmName.SHA512; + default: + return HashAlgorithmName.SHA256; + } + } + } #endif /// @@ -114,19 +125,15 @@ namespace Renci.SshNet.Security } } - private EcdsaDigitalSignature _digitalSignature; - /// /// Gets the digital signature. /// - protected override DigitalSignature DigitalSignature + protected internal override DigitalSignature DigitalSignature { get { - if (_digitalSignature == null) - { - _digitalSignature = new EcdsaDigitalSignature(this); - } + _digitalSignature ??= new EcdsaDigitalSignature(this); + return _digitalSignature; } } @@ -144,8 +151,36 @@ namespace Renci.SshNet.Security byte[] curve; byte[] qx; byte[] qy; -#if NETSTANDARD2_0 - var parameter = Ecdsa.ExportParameters(false); +#if NETFRAMEWORK + var blob = _key.Export(CngKeyBlobFormat.EccPublicBlob); + + KeyBlobMagicNumber magic; + using (var br = new BinaryReader(new MemoryStream(blob))) + { + magic = (KeyBlobMagicNumber)br.ReadInt32(); + var cbKey = br.ReadInt32(); + qx = br.ReadBytes(cbKey); + qy = br.ReadBytes(cbKey); + } + +#pragma warning disable IDE0010 // Add missing cases + switch (magic) + { + case KeyBlobMagicNumber.BCRYPT_ECDSA_PUBLIC_P256_MAGIC: + curve = Encoding.ASCII.GetBytes("nistp256"); + break; + case KeyBlobMagicNumber.BCRYPT_ECDSA_PUBLIC_P384_MAGIC: + curve = Encoding.ASCII.GetBytes("nistp384"); + break; + case KeyBlobMagicNumber.BCRYPT_ECDSA_PUBLIC_P521_MAGIC: + curve = Encoding.ASCII.GetBytes("nistp521"); + break; + default: + throw new SshException("Unexpected Curve Magic: " + magic); + } +#pragma warning restore IDE0010 // Add missing cases +#else + var parameter = Ecdsa.ExportParameters(includePrivateParameters: false); qx = parameter.Q.X; qy = parameter.Q.Y; switch (parameter.Curve.Oid.FriendlyName) @@ -165,33 +200,8 @@ namespace Renci.SshNet.Security default: throw new SshException("Unexpected Curve Name: " + parameter.Curve.Oid.FriendlyName); } -#else - var blob = key.Export(CngKeyBlobFormat.EccPublicBlob); - - KeyBlobMagicNumber magic; - using (var br = new BinaryReader(new MemoryStream(blob))) - { - magic = (KeyBlobMagicNumber)br.ReadInt32(); - int cbKey = br.ReadInt32(); - qx = br.ReadBytes(cbKey); - qy = br.ReadBytes(cbKey); - } - - switch (magic) - { - case KeyBlobMagicNumber.BCRYPT_ECDSA_PUBLIC_P256_MAGIC: - curve = Encoding.ASCII.GetBytes("nistp256"); - break; - case KeyBlobMagicNumber.BCRYPT_ECDSA_PUBLIC_P384_MAGIC: - curve = Encoding.ASCII.GetBytes("nistp384"); - break; - case KeyBlobMagicNumber.BCRYPT_ECDSA_PUBLIC_P521_MAGIC: - curve = Encoding.ASCII.GetBytes("nistp521"); - break; - default: - throw new SshException("Unexpected Curve Magic: " + magic); - } #endif + // Make ECPoint from x and y // Prepend 04 (uncompressed format) + qx-bytes + qy-bytes var q = new byte[1 + qx.Length + qy.Length]; @@ -205,15 +215,20 @@ namespace Renci.SshNet.Security set { var curve_s = Encoding.ASCII.GetString(value[0].ToByteArray().Reverse()); - string curve_oid = GetCurveOid(curve_s); + var curve_oid = GetCurveOid(curve_s); var publickey = value[1].ToByteArray().Reverse(); - Import(curve_oid, publickey, null); + Import(curve_oid, publickey, privatekey: null); } } /// - /// Gets ECDsa Object + /// Gets the PrivateKey Bytes. + /// + public byte[] PrivateKey { get; private set; } + + /// + /// Gets the object. /// public ECDsa Ecdsa { get; private set; } @@ -227,9 +242,9 @@ namespace Renci.SshNet.Security /// /// Initializes a new instance of the class. /// - /// The curve name - /// Value of publickey - /// Value of privatekey + /// The curve name. + /// Value of publickey. + /// Value of privatekey. public EcdsaKey(string curve, byte[] publickey, byte[] privatekey) { Import(GetCurveOid(curve), publickey, privatekey); @@ -242,7 +257,7 @@ namespace Renci.SshNet.Security public EcdsaKey(byte[] data) { var der = new DerData(data); - var version = der.ReadBigInteger(); // skip version + _ = der.ReadBigInteger(); // skip version // PrivateKey var privatekey = der.ReadOctetString().TrimLeadingZeros(); @@ -250,27 +265,39 @@ namespace Renci.SshNet.Security // Construct var s0 = der.ReadByte(); if ((s0 & 0xe0) != 0xa0) + { throw new SshException(string.Format("UnexpectedDER: wanted constructed tag (0xa0-0xbf), got: {0:X}", s0)); + } + var tag = s0 & 0x1f; if (tag != 0) + { throw new SshException(string.Format("expected tag 0 in DER privkey, got: {0}", tag)); + } + var construct = der.ReadBytes(der.ReadLength()); // object length // curve OID - var curve_der = new DerData(construct, true); + var curve_der = new DerData(construct, construct: true); var curve = curve_der.ReadObject(); // Construct s0 = der.ReadByte(); if ((s0 & 0xe0) != 0xa0) + { throw new SshException(string.Format("UnexpectedDER: wanted constructed tag (0xa0-0xbf), got: {0:X}", s0)); + } + tag = s0 & 0x1f; if (tag != 1) + { throw new SshException(string.Format("expected tag 1 in DER privkey, got: {0}", tag)); + } + construct = der.ReadBytes(der.ReadLength()); // object length // PublicKey - var pubkey_der = new DerData(construct, true); + var pubkey_der = new DerData(construct, construct: true); var pubkey = pubkey_der.ReadBitString().TrimLeadingZeros(); Import(OidByteArrayToString(curve), pubkey, privatekey); @@ -278,7 +305,85 @@ namespace Renci.SshNet.Security private void Import(string curve_oid, byte[] publickey, byte[] privatekey) { -#if NETSTANDARD2_0 +#if NETFRAMEWORK + KeyBlobMagicNumber curve_magic; + + switch (GetCurveName(curve_oid)) + { + case "nistp256": + if (privatekey != null) + { + curve_magic = KeyBlobMagicNumber.BCRYPT_ECDSA_PRIVATE_P256_MAGIC; + } + else + { + curve_magic = KeyBlobMagicNumber.BCRYPT_ECDSA_PUBLIC_P256_MAGIC; + } + + break; + case "nistp384": + if (privatekey != null) + { + curve_magic = KeyBlobMagicNumber.BCRYPT_ECDSA_PRIVATE_P384_MAGIC; + } + else + { + curve_magic = KeyBlobMagicNumber.BCRYPT_ECDSA_PUBLIC_P384_MAGIC; + } + + break; + case "nistp521": + if (privatekey != null) + { + curve_magic = KeyBlobMagicNumber.BCRYPT_ECDSA_PRIVATE_P521_MAGIC; + } + else + { + curve_magic = KeyBlobMagicNumber.BCRYPT_ECDSA_PUBLIC_P521_MAGIC; + } + + break; + default: + throw new SshException("Unknown: " + curve_oid); + } + + // ECPoint as BigInteger(2) + var cord_size = (publickey.Length - 1) / 2; + var qx = new byte[cord_size]; + Buffer.BlockCopy(publickey, 1, qx, 0, qx.Length); + + var qy = new byte[cord_size]; + Buffer.BlockCopy(publickey, cord_size + 1, qy, 0, qy.Length); + + if (privatekey != null) + { + privatekey = privatekey.Pad(cord_size); + PrivateKey = privatekey; + } + + var headerSize = Marshal.SizeOf(typeof(BCRYPT_ECCKEY_BLOB)); + var blobSize = headerSize + qx.Length + qy.Length; + if (privatekey != null) + { + blobSize += privatekey.Length; + } + + var blob = new byte[blobSize]; + using (var bw = new BinaryWriter(new MemoryStream(blob))) + { + bw.Write((int)curve_magic); + bw.Write(cord_size); + bw.Write(qx); // q.x + bw.Write(qy); // q.y + if (privatekey != null) + { + bw.Write(privatekey); // d + } + } + _key = CngKey.Import(blob, privatekey is null ? CngKeyBlobFormat.EccPublicBlob : CngKeyBlobFormat.EccPrivateBlob); + + Ecdsa = new ECDsaCng(_key); +#else var curve = ECCurve.CreateFromValue(curve_oid); var parameter = new ECParameters { @@ -297,68 +402,16 @@ namespace Renci.SshNet.Security parameter.Q.Y = qy; if (privatekey != null) + { parameter.D = privatekey.TrimLeadingZeros().Pad(cord_size); + PrivateKey = parameter.D; + } Ecdsa = ECDsa.Create(parameter); -#else - var curve_magic = KeyBlobMagicNumber.BCRYPT_ECDH_PRIVATE_GENERIC_MAGIC; - switch (GetCurveName(curve_oid)) - { - case "nistp256": - if (privatekey != null) - curve_magic = KeyBlobMagicNumber.BCRYPT_ECDSA_PRIVATE_P256_MAGIC; - else - curve_magic = KeyBlobMagicNumber.BCRYPT_ECDSA_PUBLIC_P256_MAGIC; - break; - case "nistp384": - if (privatekey != null) - curve_magic = KeyBlobMagicNumber.BCRYPT_ECDSA_PRIVATE_P384_MAGIC; - else - curve_magic = KeyBlobMagicNumber.BCRYPT_ECDSA_PUBLIC_P384_MAGIC; - break; - case "nistp521": - if (privatekey != null) - curve_magic = KeyBlobMagicNumber.BCRYPT_ECDSA_PRIVATE_P521_MAGIC; - else - curve_magic = KeyBlobMagicNumber.BCRYPT_ECDSA_PUBLIC_P521_MAGIC; - break; - default: - throw new SshException("Unknown: " + curve_oid); - } - - // ECPoint as BigInteger(2) - var cord_size = (publickey.Length - 1) / 2; - var qx = new byte[cord_size]; - Buffer.BlockCopy(publickey, 1, qx, 0, qx.Length); - - var qy = new byte[cord_size]; - Buffer.BlockCopy(publickey, cord_size + 1, qy, 0, qy.Length); - - if (privatekey != null) - privatekey = privatekey.Pad(cord_size); - - int headerSize = Marshal.SizeOf(typeof(BCRYPT_ECCKEY_BLOB)); - int blobSize = headerSize + qx.Length + qy.Length; - if (privatekey != null) - blobSize += privatekey.Length; - - byte[] blob = new byte[blobSize]; - using (var bw = new BinaryWriter(new MemoryStream(blob))) - { - bw.Write((int)curve_magic); - bw.Write(cord_size); - bw.Write(qx); // q.x - bw.Write(qy); // q.y - if (privatekey != null) - bw.Write(privatekey); // d - } - key = CngKey.Import(blob, privatekey == null ? CngKeyBlobFormat.EccPublicBlob : CngKeyBlobFormat.EccPrivateBlob); - - Ecdsa = new ECDsaCng(key); #endif } - private string GetCurveOid(string curve_s) + private static string GetCurveOid(string curve_s) { switch (curve_s.ToLower()) { @@ -373,7 +426,8 @@ namespace Renci.SshNet.Security } } - private string GetCurveName(string oid) +#if NETFRAMEWORK + private static string GetCurveName(string oid) { switch (oid) { @@ -387,27 +441,29 @@ namespace Renci.SshNet.Security throw new SshException("Unexpected OID: " + oid); } } +#endif // NETFRAMEWORK - private string OidByteArrayToString(byte[] oid) + private static string OidByteArrayToString(byte[] oid) { - StringBuilder retVal = new StringBuilder(); + var retVal = new StringBuilder(); - for (int i = 0; i < oid.Length; i++) + for (var i = 0; i < oid.Length; i++) { if (i == 0) { - int b = oid[0] % 40; - int a = (oid[0] - b) / 40; - retVal.AppendFormat("{0}.{1}", a, b); + var b = oid[0] % 40; + var a = (oid[0] - b) / 40; + _ = retVal.AppendFormat("{0}.{1}", a, b); } else { if (oid[i] < 128) - retVal.AppendFormat(".{0}", oid[i]); + { + _ = retVal.AppendFormat(".{0}", oid[i]); + } else { - retVal.AppendFormat(".{0}", - ((oid[i] - 128) * 128) + oid[i + 1]); + _ = retVal.AppendFormat(".{0}", ((oid[i] - 128) * 128) + oid[i + 1]); i++; } } @@ -416,27 +472,25 @@ namespace Renci.SshNet.Security return retVal.ToString(); } - #region IDisposable Members - - private bool _isDisposed; - /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// public void Dispose() { - Dispose(true); + Dispose(disposing: true); GC.SuppressFinalize(this); } /// - /// Releases unmanaged and - optionally - managed resources + /// Releases unmanaged and - optionally - managed resources. /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected virtual void Dispose(bool disposing) { if (_isDisposed) + { return; + } if (disposing) { @@ -445,15 +499,11 @@ namespace Renci.SshNet.Security } /// - /// Releases unmanaged resources and performs other cleanup operations before the - /// is reclaimed by garbage collection. + /// Finalizes an instance of the class. /// ~EcdsaKey() { - Dispose(false); + Dispose(disposing: false); } - - #endregion } } -#endif \ No newline at end of file diff --git a/src/Renci.SshNet/Security/Cryptography/HMACMD5.cs b/src/Renci.SshNet/Security/Cryptography/HMACMD5.cs index 86b24643..cd129fe6 100644 --- a/src/Renci.SshNet/Security/Cryptography/HMACMD5.cs +++ b/src/Renci.SshNet/Security/Cryptography/HMACMD5.cs @@ -1,6 +1,4 @@ -#if FEATURE_HMAC_MD5 - -using System.Security.Cryptography; +using System.Security.Cryptography; using Renci.SshNet.Common; namespace Renci.SshNet.Security.Cryptography @@ -57,5 +55,3 @@ namespace Renci.SshNet.Security.Cryptography } } } - -#endif // FEATURE_HMAC_MD5 diff --git a/src/Renci.SshNet/Security/Cryptography/HMACSHA1.cs b/src/Renci.SshNet/Security/Cryptography/HMACSHA1.cs index d8f47af1..05a9730e 100644 --- a/src/Renci.SshNet/Security/Cryptography/HMACSHA1.cs +++ b/src/Renci.SshNet/Security/Cryptography/HMACSHA1.cs @@ -1,6 +1,5 @@ -#if FEATURE_HMAC_SHA1 +using System.Security.Cryptography; -using System.Security.Cryptography; using Renci.SshNet.Common; namespace Renci.SshNet.Security.Cryptography @@ -13,7 +12,7 @@ namespace Renci.SshNet.Security.Cryptography private readonly int _hashSize; /// - /// Initializes a with the specified key. + /// Initializes a new instance of the class with the specified key. /// /// The key. public HMACSHA1(byte[] key) @@ -23,7 +22,7 @@ namespace Renci.SshNet.Security.Cryptography } /// - /// Initializes a with the specified key and size of the computed hash code. + /// Initializes a new instance of the class with the specified key and size of the computed hash code. /// /// The key. /// The size, in bits, of the computed hash code. @@ -57,5 +56,3 @@ namespace Renci.SshNet.Security.Cryptography } } } - -#endif // FEATURE_HMAC_SHA1 diff --git a/src/Renci.SshNet/Security/Cryptography/HMACSHA256.cs b/src/Renci.SshNet/Security/Cryptography/HMACSHA256.cs index cb1c3185..2598704e 100644 --- a/src/Renci.SshNet/Security/Cryptography/HMACSHA256.cs +++ b/src/Renci.SshNet/Security/Cryptography/HMACSHA256.cs @@ -1,6 +1,4 @@ -#if FEATURE_HMAC_SHA256 - -using System.Security.Cryptography; +using System.Security.Cryptography; using Renci.SshNet.Common; namespace Renci.SshNet.Security.Cryptography @@ -58,5 +56,3 @@ namespace Renci.SshNet.Security.Cryptography } } } - -#endif // FEATURE_HMAC_SHA256 diff --git a/src/Renci.SshNet/Security/Cryptography/HMACSHA384.cs b/src/Renci.SshNet/Security/Cryptography/HMACSHA384.cs index 142e51ed..f5f0b26c 100644 --- a/src/Renci.SshNet/Security/Cryptography/HMACSHA384.cs +++ b/src/Renci.SshNet/Security/Cryptography/HMACSHA384.cs @@ -1,6 +1,4 @@ -#if FEATURE_HMAC_SHA384 - -using System.Security.Cryptography; +using System.Security.Cryptography; using Renci.SshNet.Common; namespace Renci.SshNet.Security.Cryptography @@ -57,5 +55,3 @@ namespace Renci.SshNet.Security.Cryptography } } } - -#endif // FEATURE_HMAC_SHA384 diff --git a/src/Renci.SshNet/Security/Cryptography/HMACSHA512.cs b/src/Renci.SshNet/Security/Cryptography/HMACSHA512.cs index a297ed08..72e75815 100644 --- a/src/Renci.SshNet/Security/Cryptography/HMACSHA512.cs +++ b/src/Renci.SshNet/Security/Cryptography/HMACSHA512.cs @@ -1,6 +1,4 @@ -#if FEATURE_HMAC_SHA512 - -using System.Security.Cryptography; +using System.Security.Cryptography; using Renci.SshNet.Common; namespace Renci.SshNet.Security.Cryptography @@ -57,5 +55,3 @@ namespace Renci.SshNet.Security.Cryptography } } } - -#endif // FEATURE_HMAC_SHA512 diff --git a/src/Renci.SshNet/Security/Cryptography/Key.cs b/src/Renci.SshNet/Security/Cryptography/Key.cs index c668a66c..fcc01efb 100644 --- a/src/Renci.SshNet/Security/Cryptography/Key.cs +++ b/src/Renci.SshNet/Security/Cryptography/Key.cs @@ -1,24 +1,25 @@ using System; using System.Collections.Generic; + using Renci.SshNet.Common; using Renci.SshNet.Security.Cryptography; namespace Renci.SshNet.Security { /// - /// Base class for asymmetric cipher algorithms + /// Base class for asymmetric cipher algorithms. /// public abstract class Key { /// - /// Specifies array of big integers that represent private key + /// Specifies array of big integers that represent private key. /// protected BigInteger[] _privateKey; /// - /// Gets the key specific digital signature. + /// Gets the default digital signature implementation for this key. /// - protected abstract DigitalSignature DigitalSignature { get; } + protected internal abstract DigitalSignature DigitalSignature { get; } /// /// Gets or sets the public key. @@ -36,17 +37,24 @@ namespace Renci.SshNet.Security /// public abstract int KeyLength { get; } + /// + /// Gets or sets the key comment. + /// + public string Comment { get; set; } + /// /// Initializes a new instance of the class. /// /// DER encoded private key data. protected Key(byte[] data) { - if (data == null) - throw new ArgumentNullException("data"); + if (data is null) + { + throw new ArgumentNullException(nameof(data)); + } var der = new DerData(data); - der.ReadBigInteger(); // skip version + _ = der.ReadBigInteger(); // skip version var keys = new List(); while (!der.IsEndOfData) diff --git a/src/Renci.SshNet/Security/Cryptography/RsaDigitalSignature.cs b/src/Renci.SshNet/Security/Cryptography/RsaDigitalSignature.cs index 15ac6c05..790a0da6 100644 --- a/src/Renci.SshNet/Security/Cryptography/RsaDigitalSignature.cs +++ b/src/Renci.SshNet/Security/Cryptography/RsaDigitalSignature.cs @@ -1,6 +1,5 @@ using System; using System.Security.Cryptography; -using Renci.SshNet.Abstractions; using Renci.SshNet.Common; using Renci.SshNet.Security.Cryptography.Ciphers; @@ -14,13 +13,23 @@ namespace Renci.SshNet.Security.Cryptography private HashAlgorithm _hash; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class with the SHA-1 hash algorithm. /// /// The RSA key. public RsaDigitalSignature(RsaKey rsaKey) - : base(new ObjectIdentifier(1, 3, 14, 3, 2, 26), new RsaCipher(rsaKey)) + : this(rsaKey, HashAlgorithmName.SHA1) + { } + + /// + /// Initializes a new instance of the class. + /// + /// The RSA key. + /// The hash algorithm to use in the digital signature. + public RsaDigitalSignature(RsaKey rsaKey, HashAlgorithmName hashAlgorithmName) + : base(ObjectIdentifier.FromHashAlgorithmName(hashAlgorithmName), new RsaCipher(rsaKey)) { - _hash = CryptoAbstraction.CreateSHA1(); + _hash = CryptoConfig.CreateFromName(hashAlgorithmName.Name) as HashAlgorithm + ?? throw new ArgumentException($"Could not create {nameof(HashAlgorithm)} from `{hashAlgorithmName}`.", nameof(hashAlgorithmName)); } /// @@ -44,7 +53,7 @@ namespace Renci.SshNet.Security.Cryptography /// public void Dispose() { - Dispose(true); + Dispose(disposing: true); GC.SuppressFinalize(this); } @@ -55,7 +64,9 @@ namespace Renci.SshNet.Security.Cryptography protected virtual void Dispose(bool disposing) { if (_isDisposed) + { return; + } if (disposing) { @@ -76,7 +87,7 @@ namespace Renci.SshNet.Security.Cryptography /// ~RsaDigitalSignature() { - Dispose(false); + Dispose(disposing: false); } #endregion diff --git a/src/Renci.SshNet/Security/Cryptography/RsaKey.cs b/src/Renci.SshNet/Security/Cryptography/RsaKey.cs index 6d8a5553..7b088ea6 100644 --- a/src/Renci.SshNet/Security/Cryptography/RsaKey.cs +++ b/src/Renci.SshNet/Security/Cryptography/RsaKey.cs @@ -9,6 +9,16 @@ namespace Renci.SshNet.Security /// public class RsaKey : Key, IDisposable { + private bool _isDisposed; + + /// + /// Gets the Key String. + /// + public override string ToString() + { + return "ssh-rsa"; + } + /// /// Gets the modulus. /// @@ -39,7 +49,10 @@ namespace Renci.SshNet.Security get { if (_privateKey.Length > 2) + { return _privateKey[2]; + } + return BigInteger.Zero; } } @@ -52,7 +65,11 @@ namespace Renci.SshNet.Security get { if (_privateKey.Length > 3) + { return _privateKey[3]; + } + + return BigInteger.Zero; } } @@ -65,7 +82,10 @@ namespace Renci.SshNet.Security get { if (_privateKey.Length > 4) + { return _privateKey[4]; + } + return BigInteger.Zero; } } @@ -78,7 +98,10 @@ namespace Renci.SshNet.Security get { if (_privateKey.Length > 5) + { return _privateKey[5]; + } + return BigInteger.Zero; } } @@ -91,7 +114,10 @@ namespace Renci.SshNet.Security get { if (_privateKey.Length > 6) + { return _privateKey[6]; + } + return BigInteger.Zero; } } @@ -104,7 +130,10 @@ namespace Renci.SshNet.Security get { if (_privateKey.Length > 7) + { return _privateKey[7]; + } + return BigInteger.Zero; } } @@ -124,17 +153,19 @@ namespace Renci.SshNet.Security } private RsaDigitalSignature _digitalSignature; + /// - /// Gets the digital signature. + /// /// - protected override DigitalSignature DigitalSignature + /// + /// An implementation of an RSA digital signature using the SHA-1 hash algorithm. + /// + protected internal override DigitalSignature DigitalSignature { get { - if (_digitalSignature == null) - { - _digitalSignature = new RsaDigitalSignature(this); - } + _digitalSignature ??= new RsaDigitalSignature(this); + return _digitalSignature; } } @@ -154,7 +185,9 @@ namespace Renci.SshNet.Security set { if (value.Length != 2) + { throw new InvalidOperationException("Invalid private key."); + } _privateKey = new[] { value[1], value[0] }; } @@ -165,7 +198,6 @@ namespace Renci.SshNet.Security /// public RsaKey() { - } /// @@ -176,7 +208,9 @@ namespace Renci.SshNet.Security : base(data) { if (_privateKey.Length != 8) + { throw new InvalidOperationException("Invalid private key."); + } } /// @@ -190,33 +224,31 @@ namespace Renci.SshNet.Security /// The inverse Q. public RsaKey(BigInteger modulus, BigInteger exponent, BigInteger d, BigInteger p, BigInteger q, BigInteger inverseQ) { - _privateKey = new BigInteger[8]; - _privateKey[0] = modulus; - _privateKey[1] = exponent; - _privateKey[2] = d; - _privateKey[3] = p; - _privateKey[4] = q; - _privateKey[5] = PrimeExponent(d, p); - _privateKey[6] = PrimeExponent(d, q); - _privateKey[7] = inverseQ; + _privateKey = new BigInteger[8] + { + modulus, + exponent, + d, + p, + q, + PrimeExponent(d, p), + PrimeExponent(d, q), + inverseQ + }; } private static BigInteger PrimeExponent(BigInteger privateExponent, BigInteger prime) { - BigInteger pe = prime - new BigInteger(1); + var pe = prime - new BigInteger(1); return privateExponent % pe; } - #region IDisposable Members - - private bool _isDisposed; - /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// public void Dispose() { - Dispose(true); + Dispose(disposing: true); GC.SuppressFinalize(this); } @@ -227,7 +259,9 @@ namespace Renci.SshNet.Security protected virtual void Dispose(bool disposing) { if (_isDisposed) + { return; + } if (disposing) { @@ -248,9 +282,7 @@ namespace Renci.SshNet.Security /// ~RsaKey() { - Dispose(false); + Dispose(disposing: false); } - - #endregion } } diff --git a/src/Renci.SshNet/Security/Cryptography/SymmetricCipher.cs b/src/Renci.SshNet/Security/Cryptography/SymmetricCipher.cs index a1c8a2a9..66749250 100644 --- a/src/Renci.SshNet/Security/Cryptography/SymmetricCipher.cs +++ b/src/Renci.SshNet/Security/Cryptography/SymmetricCipher.cs @@ -19,8 +19,10 @@ namespace Renci.SshNet.Security.Cryptography /// is null. protected SymmetricCipher(byte[] key) { - if (key == null) - throw new ArgumentNullException("key"); + if (key is null) + { + throw new ArgumentNullException(nameof(key)); + } Key = key; } diff --git a/src/Renci.SshNet/Security/GroupExchangeHashData.cs b/src/Renci.SshNet/Security/GroupExchangeHashData.cs index 12b5fbf1..82b889bb 100644 --- a/src/Renci.SshNet/Security/GroupExchangeHashData.cs +++ b/src/Renci.SshNet/Security/GroupExchangeHashData.cs @@ -3,7 +3,7 @@ using Renci.SshNet.Common; namespace Renci.SshNet.Security { - internal class GroupExchangeHashData : SshData + internal sealed class GroupExchangeHashData : SshData { private byte[] _serverVersion; private byte[] _clientVersion; diff --git a/src/Renci.SshNet/Security/IKeyExchange.cs b/src/Renci.SshNet/Security/IKeyExchange.cs index b6f9bb08..f12a1832 100644 --- a/src/Renci.SshNet/Security/IKeyExchange.cs +++ b/src/Renci.SshNet/Security/IKeyExchange.cs @@ -1,5 +1,6 @@ using System; using System.Security.Cryptography; + using Renci.SshNet.Common; using Renci.SshNet.Compression; using Renci.SshNet.Messages.Transport; diff --git a/src/Renci.SshNet/Security/KeyExchange.cs b/src/Renci.SshNet/Security/KeyExchange.cs index a13c272a..96a912b6 100644 --- a/src/Renci.SshNet/Security/KeyExchange.cs +++ b/src/Renci.SshNet/Security/KeyExchange.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Security.Cryptography; + using Renci.SshNet.Abstractions; using Renci.SshNet.Common; using Renci.SshNet.Compression; @@ -24,7 +25,7 @@ namespace Renci.SshNet.Security private Type _decompressionType; /// - /// Gets or sets the session. + /// Gets the session. /// /// /// The session. @@ -49,10 +50,8 @@ namespace Renci.SshNet.Security { get { - if (_exchangeHash == null) - { - _exchangeHash = CalculateHash(); - } + _exchangeHash ??= CalculateHash(); + return _exchangeHash; } } @@ -63,7 +62,7 @@ namespace Renci.SshNet.Security public event EventHandler HostKeyReceived; /// - /// Starts key exchange algorithm + /// Starts key exchange algorithm. /// /// The session. /// Key exchange init message. @@ -73,7 +72,7 @@ namespace Renci.SshNet.Security SendMessage(session.ClientInitMessage); - // Determine encryption algorithm + // Determine encryption algorithm var clientEncryptionAlgorithmName = (from b in session.ConnectionInfo.Encryptions.Keys from a in message.EncryptionAlgorithmsClientToServer where a == b @@ -86,7 +85,7 @@ namespace Renci.SshNet.Security session.ConnectionInfo.CurrentClientEncryption = clientEncryptionAlgorithmName; - // Determine encryption algorithm + // Determine encryption algorithm var serverDecryptionAlgorithmName = (from b in session.ConnectionInfo.Encryptions.Keys from a in message.EncryptionAlgorithmsServerToClient where a == b @@ -98,7 +97,7 @@ namespace Renci.SshNet.Security session.ConnectionInfo.CurrentServerEncryption = serverDecryptionAlgorithmName; - // Determine client hmac algorithm + // Determine client hmac algorithm var clientHmacAlgorithmName = (from b in session.ConnectionInfo.HmacAlgorithms.Keys from a in message.MacAlgorithmsClientToServer where a == b @@ -110,7 +109,7 @@ namespace Renci.SshNet.Security session.ConnectionInfo.CurrentClientHmacAlgorithm = clientHmacAlgorithmName; - // Determine server hmac algorithm + // Determine server hmac algorithm var serverHmacAlgorithmName = (from b in session.ConnectionInfo.HmacAlgorithms.Keys from a in message.MacAlgorithmsServerToClient where a == b @@ -122,7 +121,7 @@ namespace Renci.SshNet.Security session.ConnectionInfo.CurrentServerHmacAlgorithm = serverHmacAlgorithmName; - // Determine compression algorithm + // Determine compression algorithm var compressionAlgorithmName = (from b in session.ConnectionInfo.CompressionAlgorithms.Keys from a in message.CompressionAlgorithmsClientToServer where a == b @@ -134,7 +133,7 @@ namespace Renci.SshNet.Security session.ConnectionInfo.CurrentClientCompressionAlgorithm = compressionAlgorithmName; - // Determine decompression algorithm + // Determine decompression algorithm var decompressionAlgorithmName = (from b in session.ConnectionInfo.CompressionAlgorithms.Keys from a in message.CompressionAlgorithmsServerToClient where a == b @@ -159,15 +158,12 @@ namespace Renci.SshNet.Security /// public virtual void Finish() { - // Validate hash - if (ValidateExchangeHash()) - { - SendMessage(new NewKeysMessage()); - } - else + if (!ValidateExchangeHash()) { throw new SshConnectionException("Key exchange negotiation failed.", DisconnectReason.KeyExchangeFailed); } + + SendMessage(new NewKeysMessage()); } /// @@ -176,13 +172,13 @@ namespace Renci.SshNet.Security /// Server cipher. public Cipher CreateServerCipher() { - // Resolve Session ID + // Resolve Session ID var sessionId = Session.SessionId ?? ExchangeHash; - // Calculate server to client initial IV + // Calculate server to client initial IV var serverVector = Hash(GenerateSessionKey(SharedKey, ExchangeHash, 'B', sessionId)); - // Calculate server to client encryption + // Calculate server to client encryption var serverKey = Hash(GenerateSessionKey(SharedKey, ExchangeHash, 'D', sessionId)); serverKey = GenerateSessionKey(SharedKey, ExchangeHash, serverKey, _serverCipherInfo.KeySize / 8); @@ -193,7 +189,7 @@ namespace Renci.SshNet.Security Session.ToHex(serverKey), Session.ToHex(serverVector))); - // Create server cipher + // Create server cipher return _serverCipherInfo.Cipher(serverKey, serverVector); } @@ -203,63 +199,71 @@ namespace Renci.SshNet.Security /// Client cipher. public Cipher CreateClientCipher() { - // Resolve Session ID + // Resolve Session ID var sessionId = Session.SessionId ?? ExchangeHash; - // Calculate client to server initial IV + // Calculate client to server initial IV var clientVector = Hash(GenerateSessionKey(SharedKey, ExchangeHash, 'A', sessionId)); - // Calculate client to server encryption + // Calculate client to server encryption var clientKey = Hash(GenerateSessionKey(SharedKey, ExchangeHash, 'C', sessionId)); clientKey = GenerateSessionKey(SharedKey, ExchangeHash, clientKey, _clientCipherInfo.KeySize / 8); - // Create client cipher + // Create client cipher return _clientCipherInfo.Cipher(clientKey, clientVector); } /// /// Creates the server side hash algorithm to use. /// - /// Hash algorithm + /// + /// The server-side hash algorithm. + /// public HashAlgorithm CreateServerHash() { - // Resolve Session ID + // Resolve Session ID var sessionId = Session.SessionId ?? ExchangeHash; - var serverKey = Hash(GenerateSessionKey(SharedKey, ExchangeHash, 'F', sessionId)); + var serverKey = GenerateSessionKey(SharedKey, + ExchangeHash, + Hash(GenerateSessionKey(SharedKey, ExchangeHash, 'F', sessionId)), + _serverHashInfo.KeySize / 8); - serverKey = GenerateSessionKey(SharedKey, ExchangeHash, serverKey, _serverHashInfo.KeySize / 8); - - //return serverHMac; return _serverHashInfo.HashAlgorithm(serverKey); } /// /// Creates the client side hash algorithm to use. /// - /// Hash algorithm + /// + /// The client-side hash algorithm. + /// public HashAlgorithm CreateClientHash() { - // Resolve Session ID + // Resolve Session ID var sessionId = Session.SessionId ?? ExchangeHash; - var clientKey = Hash(GenerateSessionKey(SharedKey, ExchangeHash, 'E', sessionId)); - - clientKey = GenerateSessionKey(SharedKey, ExchangeHash, clientKey, _clientHashInfo.KeySize / 8); + var clientKey = GenerateSessionKey(SharedKey, + ExchangeHash, + Hash(GenerateSessionKey(SharedKey, ExchangeHash, 'E', sessionId)), + _clientHashInfo.KeySize / 8); - //return clientHMac; return _clientHashInfo.HashAlgorithm(clientKey); } /// /// Creates the compression algorithm to use to deflate data. /// - /// Compression method. + /// + /// The compression method. + /// public Compressor CreateCompressor() { - if (_compressionType == null) + if (_compressionType is null) + { return null; + } var compressor = _compressionType.CreateInstance(); @@ -271,11 +275,15 @@ namespace Renci.SshNet.Security /// /// Creates the compression algorithm to use to inflate data. /// - /// Compression method. + /// + /// The decompression method. + /// public Compressor CreateDecompressor() { - if (_compressionType == null) + if (_decompressionType is null) + { return null; + } var decompressor = _decompressionType.CreateInstance(); @@ -310,6 +318,28 @@ namespace Renci.SshNet.Security /// true if exchange hash is valid; otherwise false. protected abstract bool ValidateExchangeHash(); + private protected bool ValidateExchangeHash(byte[] encodedKey, byte[] encodedSignature) + { + var exchangeHash = CalculateHash(); + + var signatureData = new KeyHostAlgorithm.SignatureKeyData(); + signatureData.Load(encodedSignature); + + var keyAlgorithm = Session.ConnectionInfo.HostKeyAlgorithms[signatureData.AlgorithmName](encodedKey); + + Session.ConnectionInfo.CurrentHostKeyAlgorithm = signatureData.AlgorithmName; + + if (CanTrustHostKey(keyAlgorithm)) + { + // keyAlgorithm.VerifySignature decodes the signature data before verifying. + // But as we have already decoded the data to find the signature algorithm, + // we just verify the decoded data directly through the DigitalSignature. + return keyAlgorithm.DigitalSignature.Verify(exchangeHash, signatureData.Signature); + } + + return false; + } + /// /// Calculates key exchange hash value. /// @@ -321,12 +351,12 @@ namespace Renci.SshNet.Security /// /// The hash data. /// - /// Hashed bytes + /// The hash of the data. /// protected abstract byte[] Hash(byte[] hashData); /// - /// Sends SSH message to the server + /// Sends SSH message to the server. /// /// The message. protected void SendMessage(Message message) @@ -341,7 +371,9 @@ namespace Renci.SshNet.Security /// The exchange hash. /// The key. /// The size. - /// + /// + /// The session key. + /// private byte[] GenerateSessionKey(byte[] sharedKey, byte[] exchangeHash, byte[] key, int size) { var result = new List(key); @@ -368,7 +400,9 @@ namespace Renci.SshNet.Security /// The exchange hash. /// The p. /// The session id. - /// + /// + /// The session key. + /// private static byte[] GenerateSessionKey(byte[] sharedKey, byte[] exchangeHash, char p, byte[] sessionId) { var sessionKeyGeneration = new SessionKeyGeneration @@ -381,7 +415,7 @@ namespace Renci.SshNet.Security return sessionKeyGeneration.GetBytes(); } - private class SessionKeyGeneration : SshData + private sealed class SessionKeyGeneration : SshData { public byte[] SharedKey { get; set; } @@ -425,7 +459,7 @@ namespace Renci.SshNet.Security } } - private class SessionKeyAdjustment : SshData + private sealed class SessionKeyAdjustment : SshData { public byte[] SharedKey { get; set; } @@ -472,7 +506,7 @@ namespace Renci.SshNet.Security /// public void Dispose() { - Dispose(true); + Dispose(disposing: true); GC.SuppressFinalize(this); } @@ -490,7 +524,7 @@ namespace Renci.SshNet.Security /// ~KeyExchange() { - Dispose(false); + Dispose(disposing: false); } #endregion diff --git a/src/Renci.SshNet/Security/KeyExchangeDiffieHellman.cs b/src/Renci.SshNet/Security/KeyExchangeDiffieHellman.cs index 375c3a59..5cdba5b4 100644 --- a/src/Renci.SshNet/Security/KeyExchangeDiffieHellman.cs +++ b/src/Renci.SshNet/Security/KeyExchangeDiffieHellman.cs @@ -1,7 +1,7 @@ using System; -using System.Text; -using Renci.SshNet.Messages.Transport; + using Renci.SshNet.Common; +using Renci.SshNet.Messages.Transport; namespace Renci.SshNet.Security { @@ -21,7 +21,7 @@ namespace Renci.SshNet.Security protected BigInteger _prime; /// - /// Specifies client payload + /// Specifies client payload. /// protected byte[] _clientPayload; @@ -71,23 +71,11 @@ namespace Renci.SshNet.Security /// protected override bool ValidateExchangeHash() { - var exchangeHash = CalculateHash(); - - var length = Pack.BigEndianToUInt32(_hostKey); - var algorithmName = Encoding.UTF8.GetString(_hostKey, 4, (int)length); - var key = Session.ConnectionInfo.HostKeyAlgorithms[algorithmName](_hostKey); - - Session.ConnectionInfo.CurrentHostKeyAlgorithm = algorithmName; - - if (CanTrustHostKey(key)) - { - return key.VerifySignature(exchangeHash, _signature); - } - return false; + return ValidateExchangeHash(_hostKey, _signature); } /// - /// Starts key exchange algorithm + /// Starts key exchange algorithm. /// /// The session. /// Key exchange init message. @@ -105,10 +93,14 @@ namespace Renci.SshNet.Security protected void PopulateClientExchangeValue() { if (_group.IsZero) + { throw new ArgumentNullException("_group"); + } if (_prime.IsZero) + { throw new ArgumentNullException("_prime"); + } // generate private exponent that is twice the hash size (RFC 4419) with a minimum // of 1024 bits (whatever is less) @@ -118,11 +110,13 @@ namespace Renci.SshNet.Security do { - // create private component + // Create private component _privateExponent = BigInteger.Random(privateExponentSize); - // generate public component + + // Generate public component clientExchangeValue = BigInteger.ModPow(_group, _privateExponent, _prime); - } while (clientExchangeValue < 1 || clientExchangeValue > (_prime - 1)); + } + while (clientExchangeValue < 1 || clientExchangeValue > (_prime - 1)); _clientExchangeValue = clientExchangeValue.ToByteArray().Reverse(); } diff --git a/src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroup14Sha1.cs b/src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroup14Sha1.cs index ec7a237d..be0bfe74 100644 --- a/src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroup14Sha1.cs +++ b/src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroup14Sha1.cs @@ -5,10 +5,10 @@ namespace Renci.SshNet.Security /// /// Represents "diffie-hellman-group14-sha1" algorithm implementation. /// - internal class KeyExchangeDiffieHellmanGroup14Sha1 : KeyExchangeDiffieHellmanGroupSha1 + internal sealed class KeyExchangeDiffieHellmanGroup14Sha1 : KeyExchangeDiffieHellmanGroupSha1 { /// - /// https://tools.ietf.org/html/rfc2409#section-6.2 + /// Defined in https://tools.ietf.org/html/rfc2409#section-6.2. /// private static readonly byte[] SecondOkleyGroupReversed = { @@ -59,4 +59,4 @@ namespace Renci.SshNet.Security } } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroup14Sha256.cs b/src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroup14Sha256.cs index 276077a0..9687875e 100644 --- a/src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroup14Sha256.cs +++ b/src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroup14Sha256.cs @@ -5,10 +5,10 @@ namespace Renci.SshNet.Security /// /// Represents "diffie-hellman-group14-sha256" algorithm implementation. /// - internal class KeyExchangeDiffieHellmanGroup14Sha256 : KeyExchangeDiffieHellmanGroupSha256 + internal sealed class KeyExchangeDiffieHellmanGroup14Sha256 : KeyExchangeDiffieHellmanGroupSha256 { /// - /// https://tools.ietf.org/html/rfc2409#section-6.2 + /// Defined in https://tools.ietf.org/html/rfc2409#section-6.2. /// private static readonly byte[] SecondOkleyGroupReversed = { @@ -59,4 +59,4 @@ namespace Renci.SshNet.Security } } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroup16Sha512.cs b/src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroup16Sha512.cs index 48f7e178..ba8403fe 100644 --- a/src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroup16Sha512.cs +++ b/src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroup16Sha512.cs @@ -5,45 +5,45 @@ namespace Renci.SshNet.Security /// /// Represents "diffie-hellman-group16-sha512" algorithm implementation. /// - internal class KeyExchangeDiffieHellmanGroup16Sha512 : KeyExchangeDiffieHellmanGroupSha512 + internal sealed class KeyExchangeDiffieHellmanGroup16Sha512 : KeyExchangeDiffieHellmanGroupSha512 { /// - /// https://tools.ietf.org/html/rfc3526#section-5 + /// Defined in https://tools.ietf.org/html/rfc3526#section-5. /// private static readonly byte[] MoreModularExponentialGroup16Reversed = { - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x99,0x31,0x06,0x34,0xc9,0x35,0xf4,0x4d, - 0x8f,0xc0,0xa6,0x90,0xdc,0xb7,0xff,0x86,0xc1,0xdd,0x8f,0x8d,0x98,0xea,0xb4,0x93, - 0xa9,0x5a,0xb0,0xd5,0x27,0x91,0x06,0xd0,0x1c,0x48,0x70,0x21,0x76,0xdd,0x1b,0xb8, - 0xaf,0xd7,0xe2,0xce,0x70,0x29,0x61,0x1f,0xed,0xe7,0x5b,0x51,0x86,0xa1,0x3b,0x23, - 0xa2,0xc3,0x90,0xa0,0x4f,0x96,0xb2,0x99,0x5d,0xc0,0x6b,0x4e,0x47,0x59,0x7c,0x28, - 0xa6,0xca,0xbe,0x1f,0x14,0xfc,0x8e,0x2e,0xf9,0x8e,0xde,0x04,0xdb,0xc2,0xbb,0xdb, - 0xe8,0x4c,0xd4,0x2a,0xca,0xe9,0x83,0x25,0xda,0x0b,0x15,0xb6,0x34,0x68,0x94,0x1a, - 0x3c,0xe2,0xf4,0x6a,0x18,0x27,0xc3,0x99,0x26,0x5b,0xba,0xbd,0x10,0x9a,0x71,0x88, - 0xd7,0xe6,0x87,0xa7,0x12,0x3c,0x72,0x1a,0x01,0x08,0x21,0xa9,0x20,0xd1,0x82,0x4b, - 0x8e,0x10,0xfd,0xe0,0xfc,0x5b,0xdb,0x43,0x31,0xab,0xe5,0x74,0xa0,0x4f,0xe2,0x08, - 0xe2,0x46,0xd9,0xba,0xc0,0x88,0x09,0x77,0x6c,0x5d,0x61,0x7a,0x57,0x17,0xe1,0xbb, - 0x0c,0x20,0x7b,0x17,0x18,0x2b,0x1f,0x52,0x64,0x6a,0xc8,0x3e,0x73,0x02,0x76,0xd8, - 0x64,0x08,0x8a,0xd9,0x06,0xfa,0x2f,0xf1,0x6b,0xee,0xd2,0x1a,0x26,0xd2,0xe3,0xce, - 0x9d,0x61,0x25,0x4a,0xe0,0x94,0x8c,0x1e,0xd7,0x33,0x09,0xdb,0x8c,0xae,0xf5,0xab, - 0xc7,0xe4,0xe1,0xa6,0x85,0x0f,0x97,0xb3,0x7d,0x0c,0x06,0x5d,0x57,0x71,0xea,0x8a, - 0x0a,0xef,0xdb,0x58,0x04,0x85,0xfb,0xec,0x64,0xba,0x1c,0xdf,0xab,0x21,0x55,0xa8, - 0x33,0x7a,0x50,0x04,0x0d,0x17,0x33,0xad,0x2d,0xc4,0xaa,0x8a,0x5a,0x8e,0x72,0x15, - 0x10,0x05,0xfa,0x98,0x18,0x26,0xd2,0x15,0xe5,0x6a,0x95,0xea,0x7c,0x49,0x95,0x39, - 0x18,0x17,0x58,0x95,0xf6,0xcb,0x2b,0xde,0xc9,0x52,0x4c,0x6f,0xf0,0x5d,0xc5,0xb5, - 0x8f,0xa2,0x07,0xec,0xa2,0x83,0x27,0x9b,0x03,0x86,0x0e,0x18,0x2c,0x77,0x9e,0xe3, - 0x3b,0xce,0x36,0x2e,0x46,0x5e,0x90,0x32,0x7c,0x21,0x18,0xca,0x08,0x6c,0x74,0xf1, - 0x04,0x98,0xbc,0x4a,0x4e,0x35,0x0c,0x67,0x6d,0x96,0x96,0x70,0x07,0x29,0xd5,0x9e, - 0xbb,0x52,0x85,0x20,0x56,0xf3,0x62,0x1c,0x96,0xad,0xa3,0xdc,0x23,0x5d,0x65,0x83, - 0x5f,0xcf,0x24,0xfd,0xa8,0x3f,0x16,0x69,0x9a,0xd3,0x55,0x1c,0x36,0x48,0xda,0x98, - 0x05,0xbf,0x63,0xa1,0xb8,0x7c,0x00,0xc2,0x3d,0x5b,0xe4,0xec,0x51,0x66,0x28,0x49, - 0xe6,0x1f,0x4b,0x7c,0x11,0x24,0x9f,0xae,0xa5,0x9f,0x89,0x5a,0xfb,0x6b,0x38,0xee, - 0xed,0xb7,0x06,0xf4,0xb6,0x5c,0xff,0x0b,0x6b,0xed,0x37,0xa6,0xe9,0x42,0x4c,0xf4, - 0xc6,0x7e,0x5e,0x62,0x76,0xb5,0x85,0xe4,0x45,0xc2,0x51,0x6d,0x6d,0x35,0xe1,0x4f, - 0x37,0x14,0x5f,0xf2,0x6d,0x0a,0x2b,0x30,0x1b,0x43,0x3a,0xcd,0xb3,0x19,0x95,0xef, - 0xdd,0x04,0x34,0x8e,0x79,0x08,0x4a,0x51,0x22,0x9b,0x13,0x3b,0xa6,0xbe,0x0b,0x02, - 0x74,0xcc,0x67,0x8a,0x08,0x4e,0x02,0x29,0xd1,0x1c,0xdc,0x80,0x8b,0x62,0xc6,0xc4, - 0x34,0xc2,0x68,0x21,0xa2,0xda,0x0f,0xc9,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x99, 0x31, 0x06, 0x34, 0xc9, 0x35, 0xf4, 0x4d, + 0x8f, 0xc0, 0xa6, 0x90, 0xdc, 0xb7, 0xff, 0x86, 0xc1, 0xdd, 0x8f, 0x8d, 0x98, 0xea, 0xb4, 0x93, + 0xa9, 0x5a, 0xb0, 0xd5, 0x27, 0x91, 0x06, 0xd0, 0x1c, 0x48, 0x70, 0x21, 0x76, 0xdd, 0x1b, 0xb8, + 0xaf, 0xd7, 0xe2, 0xce, 0x70, 0x29, 0x61, 0x1f, 0xed, 0xe7, 0x5b, 0x51, 0x86, 0xa1, 0x3b, 0x23, + 0xa2, 0xc3, 0x90, 0xa0, 0x4f, 0x96, 0xb2, 0x99, 0x5d, 0xc0, 0x6b, 0x4e, 0x47, 0x59, 0x7c, 0x28, + 0xa6, 0xca, 0xbe, 0x1f, 0x14, 0xfc, 0x8e, 0x2e, 0xf9, 0x8e, 0xde, 0x04, 0xdb, 0xc2, 0xbb, 0xdb, + 0xe8, 0x4c, 0xd4, 0x2a, 0xca, 0xe9, 0x83, 0x25, 0xda, 0x0b, 0x15, 0xb6, 0x34, 0x68, 0x94, 0x1a, + 0x3c, 0xe2, 0xf4, 0x6a, 0x18, 0x27, 0xc3, 0x99, 0x26, 0x5b, 0xba, 0xbd, 0x10, 0x9a, 0x71, 0x88, + 0xd7, 0xe6, 0x87, 0xa7, 0x12, 0x3c, 0x72, 0x1a, 0x01, 0x08, 0x21, 0xa9, 0x20, 0xd1, 0x82, 0x4b, + 0x8e, 0x10, 0xfd, 0xe0, 0xfc, 0x5b, 0xdb, 0x43, 0x31, 0xab, 0xe5, 0x74, 0xa0, 0x4f, 0xe2, 0x08, + 0xe2, 0x46, 0xd9, 0xba, 0xc0, 0x88, 0x09, 0x77, 0x6c, 0x5d, 0x61, 0x7a, 0x57, 0x17, 0xe1, 0xbb, + 0x0c, 0x20, 0x7b, 0x17, 0x18, 0x2b, 0x1f, 0x52, 0x64, 0x6a, 0xc8, 0x3e, 0x73, 0x02, 0x76, 0xd8, + 0x64, 0x08, 0x8a, 0xd9, 0x06, 0xfa, 0x2f, 0xf1, 0x6b, 0xee, 0xd2, 0x1a, 0x26, 0xd2, 0xe3, 0xce, + 0x9d, 0x61, 0x25, 0x4a, 0xe0, 0x94, 0x8c, 0x1e, 0xd7, 0x33, 0x09, 0xdb, 0x8c, 0xae, 0xf5, 0xab, + 0xc7, 0xe4, 0xe1, 0xa6, 0x85, 0x0f, 0x97, 0xb3, 0x7d, 0x0c, 0x06, 0x5d, 0x57, 0x71, 0xea, 0x8a, + 0x0a, 0xef, 0xdb, 0x58, 0x04, 0x85, 0xfb, 0xec, 0x64, 0xba, 0x1c, 0xdf, 0xab, 0x21, 0x55, 0xa8, + 0x33, 0x7a, 0x50, 0x04, 0x0d, 0x17, 0x33, 0xad, 0x2d, 0xc4, 0xaa, 0x8a, 0x5a, 0x8e, 0x72, 0x15, + 0x10, 0x05, 0xfa, 0x98, 0x18, 0x26, 0xd2, 0x15, 0xe5, 0x6a, 0x95, 0xea, 0x7c, 0x49, 0x95, 0x39, + 0x18, 0x17, 0x58, 0x95, 0xf6, 0xcb, 0x2b, 0xde, 0xc9, 0x52, 0x4c, 0x6f, 0xf0, 0x5d, 0xc5, 0xb5, + 0x8f, 0xa2, 0x07, 0xec, 0xa2, 0x83, 0x27, 0x9b, 0x03, 0x86, 0x0e, 0x18, 0x2c, 0x77, 0x9e, 0xe3, + 0x3b, 0xce, 0x36, 0x2e, 0x46, 0x5e, 0x90, 0x32, 0x7c, 0x21, 0x18, 0xca, 0x08, 0x6c, 0x74, 0xf1, + 0x04, 0x98, 0xbc, 0x4a, 0x4e, 0x35, 0x0c, 0x67, 0x6d, 0x96, 0x96, 0x70, 0x07, 0x29, 0xd5, 0x9e, + 0xbb, 0x52, 0x85, 0x20, 0x56, 0xf3, 0x62, 0x1c, 0x96, 0xad, 0xa3, 0xdc, 0x23, 0x5d, 0x65, 0x83, + 0x5f, 0xcf, 0x24, 0xfd, 0xa8, 0x3f, 0x16, 0x69, 0x9a, 0xd3, 0x55, 0x1c, 0x36, 0x48, 0xda, 0x98, + 0x05, 0xbf, 0x63, 0xa1, 0xb8, 0x7c, 0x00, 0xc2, 0x3d, 0x5b, 0xe4, 0xec, 0x51, 0x66, 0x28, 0x49, + 0xe6, 0x1f, 0x4b, 0x7c, 0x11, 0x24, 0x9f, 0xae, 0xa5, 0x9f, 0x89, 0x5a, 0xfb, 0x6b, 0x38, 0xee, + 0xed, 0xb7, 0x06, 0xf4, 0xb6, 0x5c, 0xff, 0x0b, 0x6b, 0xed, 0x37, 0xa6, 0xe9, 0x42, 0x4c, 0xf4, + 0xc6, 0x7e, 0x5e, 0x62, 0x76, 0xb5, 0x85, 0xe4, 0x45, 0xc2, 0x51, 0x6d, 0x6d, 0x35, 0xe1, 0x4f, + 0x37, 0x14, 0x5f, 0xf2, 0x6d, 0x0a, 0x2b, 0x30, 0x1b, 0x43, 0x3a, 0xcd, 0xb3, 0x19, 0x95, 0xef, + 0xdd, 0x04, 0x34, 0x8e, 0x79, 0x08, 0x4a, 0x51, 0x22, 0x9b, 0x13, 0x3b, 0xa6, 0xbe, 0x0b, 0x02, + 0x74, 0xcc, 0x67, 0x8a, 0x08, 0x4e, 0x02, 0x29, 0xd1, 0x1c, 0xdc, 0x80, 0x8b, 0x62, 0xc6, 0xc4, + 0x34, 0xc2, 0x68, 0x21, 0xa2, 0xda, 0x0f, 0xc9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 }; diff --git a/src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroup1Sha1.cs b/src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroup1Sha1.cs index 3a8de7f4..d0ff2c01 100644 --- a/src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroup1Sha1.cs +++ b/src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroup1Sha1.cs @@ -5,7 +5,7 @@ namespace Renci.SshNet.Security /// /// Represents "diffie-hellman-group1-sha1" algorithm implementation. /// - internal class KeyExchangeDiffieHellmanGroup1Sha1 : KeyExchangeDiffieHellmanGroupSha1 + internal sealed class KeyExchangeDiffieHellmanGroup1Sha1 : KeyExchangeDiffieHellmanGroupSha1 { private static readonly byte[] SecondOkleyGroupReversed = { diff --git a/src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupExchangeSha1.cs b/src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupExchangeSha1.cs index a7f1b742..791e2c44 100644 --- a/src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupExchangeSha1.cs +++ b/src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupExchangeSha1.cs @@ -5,7 +5,7 @@ namespace Renci.SshNet.Security /// /// Represents "diffie-hellman-group-exchange-sha1" algorithm implementation. /// - internal class KeyExchangeDiffieHellmanGroupExchangeSha1 : KeyExchangeDiffieHellmanGroupExchangeShaBase + internal sealed class KeyExchangeDiffieHellmanGroupExchangeSha1 : KeyExchangeDiffieHellmanGroupExchangeShaBase { /// /// Gets algorithm name. @@ -31,7 +31,7 @@ namespace Renci.SshNet.Security /// /// The hash data. /// - /// Hashed bytes + /// The hash of the data. /// protected override byte[] Hash(byte[] hashData) { diff --git a/src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupExchangeSha256.cs b/src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupExchangeSha256.cs index dca2de71..2e286345 100644 --- a/src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupExchangeSha256.cs +++ b/src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupExchangeSha256.cs @@ -5,7 +5,7 @@ namespace Renci.SshNet.Security /// /// Represents "diffie-hellman-group-exchange-sha256" algorithm implementation. /// - internal class KeyExchangeDiffieHellmanGroupExchangeSha256 : KeyExchangeDiffieHellmanGroupExchangeShaBase + internal sealed class KeyExchangeDiffieHellmanGroupExchangeSha256 : KeyExchangeDiffieHellmanGroupExchangeShaBase { /// /// Gets algorithm name. @@ -29,15 +29,15 @@ namespace Renci.SshNet.Security /// /// Hashes the specified data bytes. /// - /// Data to hash. + /// Data to hash. /// - /// Hashed bytes + /// The hash of the data. /// - protected override byte[] Hash(byte[] hashBytes) + protected override byte[] Hash(byte[] hashData) { using (var sha256 = CryptoAbstraction.CreateSHA256()) { - return sha256.ComputeHash(hashBytes); + return sha256.ComputeHash(hashData); } } } diff --git a/src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupExchangeShaBase.cs b/src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupExchangeShaBase.cs index 755f77f2..93703ee8 100644 --- a/src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupExchangeShaBase.cs +++ b/src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupExchangeShaBase.cs @@ -40,7 +40,7 @@ namespace Renci.SshNet.Security } /// - /// Starts key exchange algorithm + /// Starts key exchange algorithm. /// /// The session. /// Key exchange init message. @@ -50,6 +50,7 @@ namespace Renci.SshNet.Security // Register SSH_MSG_KEX_DH_GEX_GROUP message Session.RegisterMessage("SSH_MSG_KEX_DH_GEX_GROUP"); + // Subscribe to KeyExchangeDhGroupExchangeGroupReceived events Session.KeyExchangeDhGroupExchangeGroupReceived += Session_KeyExchangeDhGroupExchangeGroupReceived; @@ -75,11 +76,13 @@ namespace Renci.SshNet.Security // Unregister SSH_MSG_KEX_DH_GEX_GROUP message once received Session.UnRegisterMessage("SSH_MSG_KEX_DH_GEX_GROUP"); + // Unsubscribe from KeyExchangeDhGroupExchangeGroupReceived events Session.KeyExchangeDhGroupExchangeGroupReceived -= Session_KeyExchangeDhGroupExchangeGroupReceived; // Register in order to be able to receive SSH_MSG_KEX_DH_GEX_REPLY message Session.RegisterMessage("SSH_MSG_KEX_DH_GEX_REPLY"); + // Subscribe to KeyExchangeDhGroupExchangeReplyReceived events Session.KeyExchangeDhGroupExchangeReplyReceived += Session_KeyExchangeDhGroupExchangeReplyReceived; @@ -99,6 +102,7 @@ namespace Renci.SshNet.Security // Unregister SSH_MSG_KEX_DH_GEX_REPLY message once received Session.UnRegisterMessage("SSH_MSG_KEX_DH_GEX_REPLY"); + // Unsubscribe from KeyExchangeDhGroupExchangeReplyReceived events Session.KeyExchangeDhGroupExchangeReplyReceived -= Session_KeyExchangeDhGroupExchangeReplyReceived; diff --git a/src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupSha1.cs b/src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupSha1.cs index 675ee2cf..4669eb8a 100644 --- a/src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupSha1.cs +++ b/src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupSha1.cs @@ -23,7 +23,7 @@ namespace Renci.SshNet.Security /// /// The hash data. /// - /// Hashed bytes + /// The hash of the data. /// protected override byte[] Hash(byte[] hashData) { diff --git a/src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupSha256.cs b/src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupSha256.cs index 6cb67460..ea29e6e2 100644 --- a/src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupSha256.cs +++ b/src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupSha256.cs @@ -23,7 +23,7 @@ namespace Renci.SshNet.Security /// /// The hash data. /// - /// Hashed bytes + /// The hash of the data. /// protected override byte[] Hash(byte[] hashData) { @@ -33,4 +33,4 @@ namespace Renci.SshNet.Security } } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupSha512.cs b/src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupSha512.cs index 54369b84..60a8e5f7 100644 --- a/src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupSha512.cs +++ b/src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupSha512.cs @@ -23,7 +23,7 @@ namespace Renci.SshNet.Security /// /// The hash data. /// - /// Hashed bytes + /// The hash of the data. /// protected override byte[] Hash(byte[] hashData) { @@ -33,4 +33,4 @@ namespace Renci.SshNet.Security } } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupShaBase.cs b/src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupShaBase.cs index 2618b63a..63c2bba4 100644 --- a/src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupShaBase.cs +++ b/src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupShaBase.cs @@ -14,30 +14,7 @@ namespace Renci.SshNet.Security public abstract BigInteger GroupPrime { get; } /// - /// Calculates key exchange hash value. - /// - /// - /// Key exchange hash. - /// - protected override byte[] CalculateHash() - { - var keyExchangeHashData = new KeyExchangeHashData - { - ClientVersion = Session.ClientVersion, - ServerVersion = Session.ServerVersion, - ClientPayload = _clientPayload, - ServerPayload = _serverPayload, - HostKey = _hostKey, - ClientExchangeValue = _clientExchangeValue, - ServerExchangeValue = _serverExchangeValue, - SharedKey = SharedKey, - }; - - return Hash(keyExchangeHashData.GetBytes()); - } - - /// - /// Starts key exchange algorithm + /// Starts key exchange algorithm. /// /// The session. /// Key exchange init message. @@ -67,16 +44,39 @@ namespace Renci.SshNet.Security Session.KeyExchangeDhReplyMessageReceived -= Session_KeyExchangeDhReplyMessageReceived; } + /// + /// Calculates key exchange hash value. + /// + /// + /// Key exchange hash. + /// + protected override byte[] CalculateHash() + { + var keyExchangeHashData = new KeyExchangeHashData + { + ClientVersion = Session.ClientVersion, + ServerVersion = Session.ServerVersion, + ClientPayload = _clientPayload, + ServerPayload = _serverPayload, + HostKey = _hostKey, + ClientExchangeValue = _clientExchangeValue, + ServerExchangeValue = _serverExchangeValue, + SharedKey = SharedKey, + }; + + return Hash(keyExchangeHashData.GetBytes()); + } + private void Session_KeyExchangeDhReplyMessageReceived(object sender, MessageEventArgs e) { var message = e.Message; - // Unregister message once received + // Unregister message once received Session.UnRegisterMessage("SSH_MSG_KEXDH_REPLY"); HandleServerDhReply(message.HostKey, message.F, message.Signature); - // When SSH_MSG_KEXDH_REPLY received key exchange is completed + // When SSH_MSG_KEXDH_REPLY received key exchange is completed Finish(); } } diff --git a/src/Renci.SshNet/Security/KeyExchangeEC.cs b/src/Renci.SshNet/Security/KeyExchangeEC.cs index 79cdf5be..f2ae22fb 100644 --- a/src/Renci.SshNet/Security/KeyExchangeEC.cs +++ b/src/Renci.SshNet/Security/KeyExchangeEC.cs @@ -1,6 +1,4 @@ -using System.Text; -using Renci.SshNet.Messages.Transport; -using Renci.SshNet.Common; +using Renci.SshNet.Messages.Transport; namespace Renci.SshNet.Security { @@ -75,23 +73,11 @@ namespace Renci.SshNet.Security /// protected override bool ValidateExchangeHash() { - var exchangeHash = CalculateHash(); - - var length = Pack.BigEndianToUInt32(_hostKey); - var algorithmName = Encoding.UTF8.GetString(_hostKey, 4, (int)length); - var key = Session.ConnectionInfo.HostKeyAlgorithms[algorithmName](_hostKey); - - Session.ConnectionInfo.CurrentHostKeyAlgorithm = algorithmName; - - if (CanTrustHostKey(key)) - { - return key.VerifySignature(exchangeHash, _signature); - } - return false; + return ValidateExchangeHash(_hostKey, _signature); } /// - /// Starts key exchange algorithm + /// Starts key exchange algorithm. /// /// The session. /// Key exchange init message. @@ -103,4 +89,4 @@ namespace Renci.SshNet.Security _clientPayload = Session.ClientInitMessage.GetBytes(); } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet/Security/KeyExchangeECCurve25519.cs b/src/Renci.SshNet/Security/KeyExchangeECCurve25519.cs index b7a318bb..18443fe7 100644 --- a/src/Renci.SshNet/Security/KeyExchangeECCurve25519.cs +++ b/src/Renci.SshNet/Security/KeyExchangeECCurve25519.cs @@ -6,7 +6,7 @@ using Renci.SshNet.Security.Chaos.NaCl.Internal.Ed25519Ref10; namespace Renci.SshNet.Security { - internal class KeyExchangeECCurve25519 : KeyExchangeEC + internal sealed class KeyExchangeECCurve25519 : KeyExchangeEC { private byte[] _privateKey; @@ -30,7 +30,7 @@ namespace Renci.SshNet.Security } /// - /// Starts key exchange algorithm + /// Starts key exchange algorithm. /// /// The session. /// Key exchange init message. @@ -68,7 +68,7 @@ namespace Renci.SshNet.Security ///
/// The hash data. /// - /// Hashed bytes + /// The hash of the data. /// protected override byte[] Hash(byte[] hashData) { @@ -82,12 +82,12 @@ namespace Renci.SshNet.Security { var message = e.Message; - // Unregister message once received + // Unregister message once received Session.UnRegisterMessage("SSH_MSG_KEX_ECDH_REPLY"); HandleServerEcdhReply(message.KS, message.QS, message.Signature); - // When SSH_MSG_KEXDH_REPLY received key exchange is completed + // When SSH_MSG_KEXDH_REPLY received key exchange is completed Finish(); } diff --git a/src/Renci.SshNet/Security/KeyExchangeECDH.cs b/src/Renci.SshNet/Security/KeyExchangeECDH.cs index f87a5c7e..c3fc7bfe 100644 --- a/src/Renci.SshNet/Security/KeyExchangeECDH.cs +++ b/src/Renci.SshNet/Security/KeyExchangeECDH.cs @@ -2,12 +2,12 @@ using Renci.SshNet.Common; using Renci.SshNet.Messages.Transport; -using Renci.SshNet.Security.Org.BouncyCastle.Crypto.Generators; -using Renci.SshNet.Security.Org.BouncyCastle.Crypto.Parameters; -using Renci.SshNet.Security.Org.BouncyCastle.Security; -using Renci.SshNet.Security.Org.BouncyCastle.Math.EC; using Renci.SshNet.Security.Org.BouncyCastle.Asn1.X9; using Renci.SshNet.Security.Org.BouncyCastle.Crypto.Agreement; +using Renci.SshNet.Security.Org.BouncyCastle.Crypto.Generators; +using Renci.SshNet.Security.Org.BouncyCastle.Crypto.Parameters; +using Renci.SshNet.Security.Org.BouncyCastle.Math.EC; +using Renci.SshNet.Security.Org.BouncyCastle.Security; namespace Renci.SshNet.Security { @@ -21,11 +21,11 @@ namespace Renci.SshNet.Security /// protected abstract X9ECParameters CurveParameter { get; } - protected ECDHCBasicAgreement KeyAgreement; - protected ECDomainParameters DomainParameters; + private ECDHCBasicAgreement _keyAgreement; + private ECDomainParameters _domainParameters; /// - /// Starts key exchange algorithm + /// Starts key exchange algorithm. /// /// The session. /// Key exchange init message. @@ -37,18 +37,18 @@ namespace Renci.SshNet.Security Session.KeyExchangeEcdhReplyMessageReceived += Session_KeyExchangeEcdhReplyMessageReceived; - DomainParameters = new ECDomainParameters(CurveParameter.Curve, + _domainParameters = new ECDomainParameters(CurveParameter.Curve, CurveParameter.G, CurveParameter.N, CurveParameter.H, CurveParameter.GetSeed()); var g = new ECKeyPairGenerator(); - g.Init(new ECKeyGenerationParameters(DomainParameters, new SecureRandom())); + g.Init(new ECKeyGenerationParameters(_domainParameters, new SecureRandom())); var aKeyPair = g.GenerateKeyPair(); - KeyAgreement = new ECDHCBasicAgreement(); - KeyAgreement.Init(aKeyPair.Private); + _keyAgreement = new ECDHCBasicAgreement(); + _keyAgreement.Init(aKeyPair.Private); _clientExchangeValue = ((ECPublicKeyParameters)aKeyPair.Public).Q.GetEncoded(); SendMessage(new KeyExchangeEcdhInitMessage(_clientExchangeValue)); @@ -68,12 +68,12 @@ namespace Renci.SshNet.Security { var message = e.Message; - // Unregister message once received + // Unregister message once received Session.UnRegisterMessage("SSH_MSG_KEX_ECDH_REPLY"); HandleServerEcdhReply(message.KS, message.QS, message.Signature); - // When SSH_MSG_KEXDH_REPLY received key exchange is completed + // When SSH_MSG_KEXDH_REPLY received key exchange is completed Finish(); } @@ -95,11 +95,11 @@ namespace Renci.SshNet.Security var y = new byte[cordSize]; Buffer.BlockCopy(serverExchangeValue, cordSize + 1, y, 0, y.Length); - var c = (FpCurve)DomainParameters.Curve; + var c = (FpCurve)_domainParameters.Curve; var q = c.CreatePoint(new Org.BouncyCastle.Math.BigInteger(1, x), new Org.BouncyCastle.Math.BigInteger(1, y)); - var publicKey = new ECPublicKeyParameters("ECDH", q, DomainParameters); + var publicKey = new ECPublicKeyParameters("ECDH", q, _domainParameters); - var k1 = KeyAgreement.CalculateAgreement(publicKey); + var k1 = _keyAgreement.CalculateAgreement(publicKey); SharedKey = k1.ToByteArray().ToBigInteger2().ToByteArray().Reverse(); } } diff --git a/src/Renci.SshNet/Security/KeyExchangeECDH256.cs b/src/Renci.SshNet/Security/KeyExchangeECDH256.cs index 8f14d9bd..b09652de 100644 --- a/src/Renci.SshNet/Security/KeyExchangeECDH256.cs +++ b/src/Renci.SshNet/Security/KeyExchangeECDH256.cs @@ -4,7 +4,7 @@ using Renci.SshNet.Security.Org.BouncyCastle.Asn1.X9; namespace Renci.SshNet.Security { - internal class KeyExchangeECDH256 : KeyExchangeECDH + internal sealed class KeyExchangeECDH256 : KeyExchangeECDH { /// /// Gets algorithm name. @@ -41,7 +41,7 @@ namespace Renci.SshNet.Security /// /// The hash data. /// - /// Hashed bytes + /// The hash of the data. /// protected override byte[] Hash(byte[] hashData) { @@ -51,4 +51,4 @@ namespace Renci.SshNet.Security } } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet/Security/KeyExchangeECDH384.cs b/src/Renci.SshNet/Security/KeyExchangeECDH384.cs index bbd7ced5..cba30430 100644 --- a/src/Renci.SshNet/Security/KeyExchangeECDH384.cs +++ b/src/Renci.SshNet/Security/KeyExchangeECDH384.cs @@ -4,7 +4,7 @@ using Renci.SshNet.Security.Org.BouncyCastle.Asn1.X9; namespace Renci.SshNet.Security { - internal class KeyExchangeECDH384 : KeyExchangeECDH + internal sealed class KeyExchangeECDH384 : KeyExchangeECDH { /// /// Gets algorithm name. @@ -41,7 +41,7 @@ namespace Renci.SshNet.Security /// /// The hash data. /// - /// Hashed bytes + /// The hash of the data. /// protected override byte[] Hash(byte[] hashData) { diff --git a/src/Renci.SshNet/Security/KeyExchangeECDH521.cs b/src/Renci.SshNet/Security/KeyExchangeECDH521.cs index 920089c0..ce5b3551 100644 --- a/src/Renci.SshNet/Security/KeyExchangeECDH521.cs +++ b/src/Renci.SshNet/Security/KeyExchangeECDH521.cs @@ -4,7 +4,7 @@ using Renci.SshNet.Security.Org.BouncyCastle.Asn1.X9; namespace Renci.SshNet.Security { - internal class KeyExchangeECDH521 : KeyExchangeECDH + internal sealed class KeyExchangeECDH521 : KeyExchangeECDH { /// /// Gets algorithm name. @@ -41,7 +41,7 @@ namespace Renci.SshNet.Security /// /// The hash data. /// - /// Hashed bytes + /// The hash of the data. /// protected override byte[] Hash(byte[] hashData) { @@ -51,4 +51,4 @@ namespace Renci.SshNet.Security } } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet/Security/KeyExchangeHash.cs b/src/Renci.SshNet/Security/KeyExchangeHash.cs index e33cb14c..f9194662 100644 --- a/src/Renci.SshNet/Security/KeyExchangeHash.cs +++ b/src/Renci.SshNet/Security/KeyExchangeHash.cs @@ -1,9 +1,10 @@ -using Renci.SshNet.Common; -using System; +using System; + +using Renci.SshNet.Common; namespace Renci.SshNet.Security { - internal class KeyExchangeHashData : SshData + internal sealed class KeyExchangeHashData : SshData { private byte[] _serverVersion; private byte[] _clientVersion; diff --git a/src/Renci.SshNet/Security/KeyHostAlgorithm.cs b/src/Renci.SshNet/Security/KeyHostAlgorithm.cs index ca94fa70..f0d2b41d 100644 --- a/src/Renci.SshNet/Security/KeyHostAlgorithm.cs +++ b/src/Renci.SshNet/Security/KeyHostAlgorithm.cs @@ -1,5 +1,9 @@ using System.Collections.Generic; +using System.Text; + using Renci.SshNet.Common; +using Renci.SshNet.Security.Chaos.NaCl; +using Renci.SshNet.Security.Cryptography; namespace Renci.SshNet.Security { @@ -9,38 +13,76 @@ namespace Renci.SshNet.Security public class KeyHostAlgorithm : HostAlgorithm { /// - /// Gets the key. + /// The key used in this host key algorithm. /// public Key Key { get; private set; } /// - /// Gets the public key data. + /// The signature implementation used in this host key algorithm. + /// + public DigitalSignature DigitalSignature { get; private set; } + + /// + /// Gets the encoded public key data. /// public override byte[] Data { get { - return new SshKeyData(Name, Key.Public).GetBytes(); + var keyFormatIdentifier = Key is RsaKey ? "ssh-rsa" : Name; + return new SshKeyData(keyFormatIdentifier, Key.Public).GetBytes(); } } /// /// Initializes a new instance of the class. /// - /// Host key name. - /// Host key. + /// The signature format identifier. + /// + /// + /// This constructor is typically passed a private key in order to create an encoded signature for later + /// verification by the host. + /// public KeyHostAlgorithm(string name, Key key) : base(name) { Key = key; + DigitalSignature = key.DigitalSignature; } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - /// Host key name. - /// Host key. + /// The signature format identifier. + /// + /// + /// + /// + /// This constructor is typically passed a private key in order to create an encoded signature for later + /// verification by the host. + /// + /// The key used by is intended to be equal to . + /// This is not verified. + /// + public KeyHostAlgorithm(string name, Key key, DigitalSignature digitalSignature) + : base(name) + { + Key = key; + DigitalSignature = digitalSignature; + } + + /// + /// Initializes a new instance of the class + /// with the given encoded public key data. The data will be decoded into . + /// + /// The signature format identifier. + /// /// Host key encoded data. + /// + /// This constructor is typically passed a new or reusable instance in + /// order to verify an encoded signature sent by the host, created by the private counterpart + /// to the host's public key, which is encoded in . + /// public KeyHostAlgorithm(string name, Key key, byte[] data) : base(name) { @@ -49,37 +91,69 @@ namespace Renci.SshNet.Security var sshKey = new SshKeyData(); sshKey.Load(data); Key.Public = sshKey.Keys; + + DigitalSignature = key.DigitalSignature; } /// - /// Signs the specified data. + /// Initializes a new instance of the class + /// with the given encoded public key data. The data will be decoded into . /// - /// The data. + /// The signature format identifier. + /// + /// Host key encoded data. + /// + /// + /// + /// This constructor is typically passed a new or reusable instance in + /// order to verify an encoded signature sent by the host, created by the private counterpart + /// to the host's public key, which is encoded in . + /// + /// The key used by is intended to be equal to . + /// This is not verified. + /// + public KeyHostAlgorithm(string name, Key key, byte[] data, DigitalSignature digitalSignature) + : base(name) + { + Key = key; + + var sshKey = new SshKeyData(); + sshKey.Load(data); + Key.Public = sshKey.Keys; + + DigitalSignature = digitalSignature; + } + + /// + /// Signs and encodes the specified data. + /// + /// The data to be signed. /// - /// Signed data. + /// The encoded signature. /// public override byte[] Sign(byte[] data) { - return new SignatureKeyData(Name, Key.Sign(data)).GetBytes(); + return new SignatureKeyData(Name, DigitalSignature.Sign(data)).GetBytes(); } /// /// Verifies the signature. /// - /// The data. - /// The signature. + /// The data to verify the signature against. + /// The encoded signature data. /// - /// True is signature was successfully verifies; otherwise false. + /// if is the result of signing + /// with the corresponding private key to . /// public override bool VerifySignature(byte[] data, byte[] signature) { var signatureData = new SignatureKeyData(); signatureData.Load(signature); - return Key.VerifySignature(data, signatureData.Signature); + return DigitalSignature.Verify(data, signatureData.Signature); } - private class SshKeyData : SshData + private sealed class SshKeyData : SshData { private byte[] _name; private List _keys; @@ -89,19 +163,28 @@ namespace Renci.SshNet.Security get { var keys = new BigInteger[_keys.Count]; + for (var i = 0; i < _keys.Count; i++) { var key = _keys[i]; keys[i] = key.ToBigInteger2(); } + return keys; } private set { _keys = new List(value.Length); + foreach (var key in value) { - _keys.Add(key.ToByteArray().Reverse()); + var keyData = key.ToByteArray().Reverse(); + if (Name == "ssh-ed25519") + { + keyData = keyData.TrimLeadingZeros().Pad(Ed25519.PublicKeySizeInBytes); + } + + _keys.Add(keyData); } } } @@ -160,18 +243,15 @@ namespace Renci.SshNet.Security } } - private class SignatureKeyData : SshData + internal sealed class SignatureKeyData : SshData { /// - /// Gets or sets the name of the algorithm as UTF-8 encoded byte array. + /// Gets or sets the signature format identifier /// - /// - /// The name of the algorithm. - /// - private byte[] AlgorithmName { get; set; } + public string AlgorithmName { get; set; } /// - /// Gets or sets the signature. + /// Gets the signature. /// /// /// The signature. @@ -190,7 +270,7 @@ namespace Renci.SshNet.Security { var capacity = base.BufferCapacity; capacity += 4; // AlgorithmName length - capacity += AlgorithmName.Length; // AlgorithmName + capacity += Encoding.UTF8.GetByteCount(AlgorithmName); // AlgorithmName capacity += 4; // Signature length capacity += Signature.Length; // Signature return capacity; @@ -203,7 +283,7 @@ namespace Renci.SshNet.Security public SignatureKeyData(string name, byte[] signature) { - AlgorithmName = Utf8.GetBytes(name); + AlgorithmName = name; Signature = signature; } @@ -212,7 +292,7 @@ namespace Renci.SshNet.Security ///
protected override void LoadData() { - AlgorithmName = ReadBinary(); + AlgorithmName = Encoding.UTF8.GetString(ReadBinary()); Signature = ReadBinary(); } @@ -221,7 +301,7 @@ namespace Renci.SshNet.Security ///
protected override void SaveData() { - WriteBinaryString(AlgorithmName); + WriteBinaryString(Encoding.UTF8.GetBytes(AlgorithmName)); WriteBinaryString(Signature); } } diff --git a/src/Renci.SshNet/ServiceFactory.cs b/src/Renci.SshNet/ServiceFactory.cs index f04e6116..16568faf 100644 --- a/src/Renci.SshNet/ServiceFactory.cs +++ b/src/Renci.SshNet/ServiceFactory.cs @@ -1,27 +1,28 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Net.Sockets; using System.Text; + +using Renci.SshNet.Abstractions; using Renci.SshNet.Common; +using Renci.SshNet.Connection; using Renci.SshNet.Messages.Transport; using Renci.SshNet.Security; using Renci.SshNet.Sftp; -using Renci.SshNet.Abstractions; -using Renci.SshNet.Connection; -using System.Net.Sockets; namespace Renci.SshNet { /// /// Basic factory for creating new services. /// - internal partial class ServiceFactory : IServiceFactory + internal sealed partial class ServiceFactory : IServiceFactory { /// /// Defines the number of times an authentication attempt with any given /// can result in before it is disregarded. /// - private static int PartialSuccessLimit = 5; + private static readonly int PartialSuccessLimit = 5; /// /// Creates a . @@ -91,10 +92,15 @@ namespace Renci.SshNet /// No key exchange algorithms are supported by both client and server. public IKeyExchange CreateKeyExchange(IDictionary clientAlgorithms, string[] serverAlgorithms) { - if (clientAlgorithms == null) - throw new ArgumentNullException("clientAlgorithms"); - if (serverAlgorithms == null) - throw new ArgumentNullException("serverAlgorithms"); + if (clientAlgorithms is null) + { + throw new ArgumentNullException(nameof(clientAlgorithms)); + } + + if (serverAlgorithms is null) + { + throw new ArgumentNullException(nameof(serverAlgorithms)); + } // find an algorithm that is supported by both client and server var keyExchangeAlgorithmType = (from c in clientAlgorithms @@ -102,7 +108,7 @@ namespace Renci.SshNet where s == c.Key select c.Value).FirstOrDefault(); - if (keyExchangeAlgorithmType == null) + if (keyExchangeAlgorithmType is null) { throw new SshConnectionException("Failed to negotiate key exchange algorithm.", DisconnectReason.KeyExchangeFailed); } @@ -116,10 +122,10 @@ namespace Renci.SshNet // Issue #292: Avoid overlapping SSH_FXP_OPEN and SSH_FXP_LSTAT requests for the same file as this // causes a performance degradation on Sun SSH - var openAsyncResult = sftpSession.BeginOpen(fileName, Flags.Read, null, null); + var openAsyncResult = sftpSession.BeginOpen(fileName, Flags.Read, callback: null, state: null); var handle = sftpSession.EndOpen(openAsyncResult); - var statAsyncResult = sftpSession.BeginLStat(fileName, null, null); + var statAsyncResult = sftpSession.BeginLStat(fileName, callback: null, state: null); long? fileSize; int maxPendingReads; @@ -157,7 +163,7 @@ namespace Renci.SshNet /// The TERM environment variable. /// The terminal width in columns. /// The terminal width in rows. - /// The terminal height in pixels. + /// The terminal width in pixels. /// The terminal height in pixels. /// The terminal mode values. /// The size of the buffer. @@ -209,10 +215,15 @@ namespace Renci.SshNet /// The value of is not supported. public IConnector CreateConnector(IConnectionInfo connectionInfo, ISocketFactory socketFactory) { - if (connectionInfo == null) - throw new ArgumentNullException("connectionInfo"); - if (socketFactory == null) - throw new ArgumentNullException("socketFactory"); + if (connectionInfo is null) + { + throw new ArgumentNullException(nameof(connectionInfo)); + } + + if (socketFactory is null) + { + throw new ArgumentNullException(nameof(socketFactory)); + } switch (connectionInfo.ProxyType) { diff --git a/src/Renci.SshNet/Session.cs b/src/Renci.SshNet/Session.cs index 0748b8da..257cf8c2 100644 --- a/src/Renci.SshNet/Session.cs +++ b/src/Renci.SshNet/Session.cs @@ -1,8 +1,13 @@ using System; +using System.Globalization; +using System.Linq; using System.Net.Sockets; using System.Security.Cryptography; using System.Text; using System.Threading; +using System.Threading.Tasks; + +using Renci.SshNet.Abstractions; using Renci.SshNet.Channels; using Renci.SshNet.Common; using Renci.SshNet.Compression; @@ -12,9 +17,6 @@ using Renci.SshNet.Messages.Authentication; using Renci.SshNet.Messages.Connection; using Renci.SshNet.Messages.Transport; using Renci.SshNet.Security; -using System.Globalization; -using System.Linq; -using Renci.SshNet.Abstractions; using Renci.SshNet.Security.Cryptography; namespace Renci.SshNet @@ -27,21 +29,13 @@ namespace Renci.SshNet internal const byte CarriageReturn = 0x0d; internal const byte LineFeed = 0x0a; - /// - /// Specifies an infinite waiting period. - /// - /// - /// The value of this field is -1 millisecond. - /// - internal static readonly TimeSpan InfiniteTimeSpan = new TimeSpan(0, 0, 0, 0, -1); - /// /// Specifies an infinite waiting period. /// /// /// The value of this field is -1. /// - internal static readonly int Infinite = -1; + internal const int Infinite = -1; /// /// Specifies maximum packet size defined by the protocol. @@ -77,105 +71,35 @@ namespace Renci.SshNet /// We currently do not enforce this limit. /// /// - private const int LocalChannelDataPacketSize = 1024*64; + private const int LocalChannelDataPacketSize = 1024 * 64; + + /// + /// Specifies an infinite waiting period. + /// + /// + /// The value of this field is -1 millisecond. + /// + internal static readonly TimeSpan InfiniteTimeSpan = new TimeSpan(0, 0, 0, 0, -1); /// /// Controls how many authentication attempts can take place at the same time. /// /// - /// Some server may restrict number to prevent authentication attacks + /// Some server may restrict number to prevent authentication attacks. /// private static readonly SemaphoreLight AuthenticationConnection = new SemaphoreLight(3); - /// - /// Holds metada about session messages - /// - private SshMessageFactory _sshMessageFactory; - - /// - /// Holds a that is signaled when the message listener loop has completed. - /// - private EventWaitHandle _messageListenerCompleted; - - /// - /// Specifies outbound packet number - /// - private volatile uint _outboundPacketSequence; - - /// - /// Specifies incoming packet number - /// - private uint _inboundPacketSequence; - - /// - /// WaitHandle to signal that last service request was accepted - /// - private EventWaitHandle _serviceAccepted = new AutoResetEvent(false); - - /// - /// WaitHandle to signal that exception was thrown by another thread. - /// - private EventWaitHandle _exceptionWaitHandle = new ManualResetEvent(false); - - /// - /// WaitHandle to signal that key exchange was completed. - /// - private EventWaitHandle _keyExchangeCompletedWaitHandle = new ManualResetEvent(false); - - /// - /// WaitHandle to signal that key exchange is in progress. - /// - private bool _keyExchangeInProgress; - - /// - /// Exception that need to be thrown by waiting thread - /// - private Exception _exception; - - /// - /// Specifies whether connection is authenticated - /// - private bool _isAuthenticated; - - /// - /// Specifies whether user issued Disconnect command or not - /// - private bool _isDisconnecting; - - private IKeyExchange _keyExchange; - - private HashAlgorithm _serverMac; - - private HashAlgorithm _clientMac; - - private Cipher _clientCipher; - - private Cipher _serverCipher; - - private Compressor _serverDecompression; - - private Compressor _clientCompression; - - private SemaphoreLight _sessionSemaphore; - /// /// Holds the factory to use for creating new services. /// private readonly IServiceFactory _serviceFactory; private readonly ISocketFactory _socketFactory; - /// - /// Holds connection socket. - /// - private Socket _socket; - -#if FEATURE_SOCKET_POLL /// /// Holds an object that is used to ensure only a single thread can read from /// at any given time. /// private readonly object _socketReadLock = new object(); -#endif // FEATURE_SOCKET_POLL /// /// Holds an object that is used to ensure only a single thread can write to @@ -197,6 +121,82 @@ namespace Renci.SshNet /// private readonly object _socketDisposeLock = new object(); + /// + /// Holds metadata about session messages. + /// + private SshMessageFactory _sshMessageFactory; + + /// + /// Holds a that is signaled when the message listener loop has completed. + /// + private ManualResetEvent _messageListenerCompleted; + + /// + /// Specifies outbound packet number. + /// + private volatile uint _outboundPacketSequence; + + /// + /// Specifies incoming packet number. + /// + private uint _inboundPacketSequence; + + /// + /// WaitHandle to signal that last service request was accepted. + /// + private EventWaitHandle _serviceAccepted = new AutoResetEvent(initialState: false); + + /// + /// WaitHandle to signal that exception was thrown by another thread. + /// + private EventWaitHandle _exceptionWaitHandle = new ManualResetEvent(initialState: false); + + /// + /// WaitHandle to signal that key exchange was completed. + /// + private EventWaitHandle _keyExchangeCompletedWaitHandle = new ManualResetEvent(initialState: false); + + /// + /// WaitHandle to signal that key exchange is in progress. + /// + private bool _keyExchangeInProgress; + + /// + /// Exception that need to be thrown by waiting thread. + /// + private Exception _exception; + + /// + /// Specifies whether connection is authenticated. + /// + private bool _isAuthenticated; + + /// + /// Specifies whether user issued Disconnect command or not. + /// + private bool _isDisconnecting; + + private IKeyExchange _keyExchange; + + private HashAlgorithm _serverMac; + + private HashAlgorithm _clientMac; + + private Cipher _clientCipher; + + private Cipher _serverCipher; + + private Compressor _serverDecompression; + + private Compressor _clientCompression; + + private SemaphoreLight _sessionSemaphore; + + /// + /// Holds connection socket. + /// + private Socket _socket; + /// /// Gets the session semaphore that controls session channels. /// @@ -207,14 +207,11 @@ namespace Renci.SshNet { get { - if (_sessionSemaphore == null) + if (_sessionSemaphore is null) { lock (this) { - if (_sessionSemaphore == null) - { - _sessionSemaphore = new SemaphoreLight(ConnectionInfo.MaxSessions); - } + _sessionSemaphore ??= new SemaphoreLight(ConnectionInfo.MaxSessions); } } @@ -278,9 +275,14 @@ namespace Renci.SshNet get { if (_disposed || _isDisconnectMessageSent || !_isAuthenticated) + { return false; - if (_messageListenerCompleted == null || _messageListenerCompleted.WaitOne(0)) + } + + if (_messageListenerCompleted is null || _messageListenerCompleted.WaitOne(0)) + { return false; + } return IsSocketConnected(); } @@ -304,44 +306,48 @@ namespace Renci.SshNet { get { - if (_clientInitMessage == null) - { - _clientInitMessage = new KeyExchangeInitMessage - { - KeyExchangeAlgorithms = ConnectionInfo.KeyExchangeAlgorithms.Keys.ToArray(), - ServerHostKeyAlgorithms = ConnectionInfo.HostKeyAlgorithms.Keys.ToArray(), - EncryptionAlgorithmsClientToServer = ConnectionInfo.Encryptions.Keys.ToArray(), - EncryptionAlgorithmsServerToClient = ConnectionInfo.Encryptions.Keys.ToArray(), - MacAlgorithmsClientToServer = ConnectionInfo.HmacAlgorithms.Keys.ToArray(), - MacAlgorithmsServerToClient = ConnectionInfo.HmacAlgorithms.Keys.ToArray(), - CompressionAlgorithmsClientToServer = ConnectionInfo.CompressionAlgorithms.Keys.ToArray(), - CompressionAlgorithmsServerToClient = ConnectionInfo.CompressionAlgorithms.Keys.ToArray(), - LanguagesClientToServer = new[] {string.Empty}, - LanguagesServerToClient = new[] {string.Empty}, - FirstKexPacketFollows = false, - Reserved = 0 - }; - } + _clientInitMessage ??= new KeyExchangeInitMessage + { + KeyExchangeAlgorithms = ConnectionInfo.KeyExchangeAlgorithms.Keys.ToArray(), + ServerHostKeyAlgorithms = ConnectionInfo.HostKeyAlgorithms.Keys.ToArray(), + EncryptionAlgorithmsClientToServer = ConnectionInfo.Encryptions.Keys.ToArray(), + EncryptionAlgorithmsServerToClient = ConnectionInfo.Encryptions.Keys.ToArray(), + MacAlgorithmsClientToServer = ConnectionInfo.HmacAlgorithms.Keys.ToArray(), + MacAlgorithmsServerToClient = ConnectionInfo.HmacAlgorithms.Keys.ToArray(), + CompressionAlgorithmsClientToServer = ConnectionInfo.CompressionAlgorithms.Keys.ToArray(), + CompressionAlgorithmsServerToClient = ConnectionInfo.CompressionAlgorithms.Keys.ToArray(), + LanguagesClientToServer = new[] { string.Empty }, + LanguagesServerToClient = new[] { string.Empty }, + FirstKexPacketFollows = false, + Reserved = 0 + }; + return _clientInitMessage; } } /// - /// Gets or sets the server version string. + /// Gets the server version string. /// - /// The server version. + /// + /// The server version. + /// public string ServerVersion { get; private set; } /// - /// Gets or sets the client version string. + /// Gets the client version string. /// - /// The client version. + /// + /// The client version. + /// public string ClientVersion { get; private set; } /// - /// Gets or sets the connection info. + /// Gets the connection info. /// - /// The connection info. + /// + /// The connection info. + /// public ConnectionInfo ConnectionInfo { get; private set; } /// @@ -389,8 +395,6 @@ namespace Renci.SshNet /// internal event EventHandler> KeyExchangeDhGroupExchangeReplyReceived; - #region Message events - /// /// Occurs when message received /// @@ -526,8 +530,6 @@ namespace Renci.SshNet /// public event EventHandler> ChannelFailureReceived; - #endregion - /// /// Initializes a new instance of the class. /// @@ -539,18 +541,26 @@ namespace Renci.SshNet /// is null. internal Session(ConnectionInfo connectionInfo, IServiceFactory serviceFactory, ISocketFactory socketFactory) { - if (connectionInfo == null) - throw new ArgumentNullException("connectionInfo"); - if (serviceFactory == null) - throw new ArgumentNullException("serviceFactory"); - if (socketFactory == null) - throw new ArgumentNullException("socketFactory"); + if (connectionInfo is null) + { + throw new ArgumentNullException(nameof(connectionInfo)); + } + + if (serviceFactory is null) + { + throw new ArgumentNullException(nameof(serviceFactory)); + } + + if (socketFactory is null) + { + throw new ArgumentNullException(nameof(socketFactory)); + } ClientVersion = "SSH-2.0-Renci.SshNet.SshClient.0.0.1"; ConnectionInfo = connectionInfo; _serviceFactory = serviceFactory; _socketFactory = socketFactory; - _messageListenerCompleted = new ManualResetEvent(true); + _messageListenerCompleted = new ManualResetEvent(initialState: true); } /// @@ -563,20 +573,26 @@ namespace Renci.SshNet public void Connect() { if (IsConnected) + { return; + } try { AuthenticationConnection.Wait(); if (IsConnected) + { return; + } lock (this) { // If connected don't connect again if (IsConnected) + { return; + } // Reset connection specific information Reset(); @@ -615,16 +631,17 @@ namespace Renci.SshNet RegisterMessage("SSH_MSG_USERAUTH_BANNER"); // Mark the message listener threads as started - _messageListenerCompleted.Reset(); + _ = _messageListenerCompleted.Reset(); // Start incoming request listener - ThreadAbstraction.ExecuteThread(() => MessageListener()); + // ToDo: Make message pump async, to not consume a thread for every session + ThreadAbstraction.ExecuteThreadLongRunning(MessageListener); // Wait for key exchange to be completed WaitOnHandle(_keyExchangeCompletedWaitHandle); // If sessionId is not set then its not connected - if (SessionId == null) + if (SessionId is null) { Disconnect(); return; @@ -665,10 +682,116 @@ namespace Renci.SshNet } finally { - AuthenticationConnection.Release(); + _ = AuthenticationConnection.Release(); } } + /// + /// Asynchronously connects to the server. + /// + /// + /// Please note this function is NOT thread safe.
+ /// The caller SHOULD limit the number of simultaneous connection attempts to a server to a single connection attempt.
+ /// The to observe. + /// A that represents the asynchronous connect operation. + /// Socket connection to the SSH server or proxy server could not be established, or an error occurred while resolving the hostname. + /// SSH session could not be established. + /// Authentication of SSH session failed. + /// Failed to establish proxy connection. + public async Task ConnectAsync(CancellationToken cancellationToken) + { + // If connected don't connect again + if (IsConnected) + { + return; + } + + // Reset connection specific information + Reset(); + + // Build list of available messages while connecting + _sshMessageFactory = new SshMessageFactory(); + + _socket = await _serviceFactory.CreateConnector(ConnectionInfo, _socketFactory) + .ConnectAsync(ConnectionInfo, cancellationToken).ConfigureAwait(false); + + var serverIdentification = await _serviceFactory.CreateProtocolVersionExchange() + .StartAsync(ClientVersion, _socket, cancellationToken).ConfigureAwait(false); + + // Set connection versions + ServerVersion = ConnectionInfo.ServerVersion = serverIdentification.ToString(); + ConnectionInfo.ClientVersion = ClientVersion; + + DiagnosticAbstraction.Log(string.Format("Server version '{0}' on '{1}'.", serverIdentification.ProtocolVersion, serverIdentification.SoftwareVersion)); + + if (!(serverIdentification.ProtocolVersion.Equals("2.0") || serverIdentification.ProtocolVersion.Equals("1.99"))) + { + throw new SshConnectionException(string.Format(CultureInfo.CurrentCulture, "Server version '{0}' is not supported.", serverIdentification.ProtocolVersion), + DisconnectReason.ProtocolVersionNotSupported); + } + + // Register Transport response messages + RegisterMessage("SSH_MSG_DISCONNECT"); + RegisterMessage("SSH_MSG_IGNORE"); + RegisterMessage("SSH_MSG_UNIMPLEMENTED"); + RegisterMessage("SSH_MSG_DEBUG"); + RegisterMessage("SSH_MSG_SERVICE_ACCEPT"); + RegisterMessage("SSH_MSG_KEXINIT"); + RegisterMessage("SSH_MSG_NEWKEYS"); + + // Some server implementations might sent this message first, prior to establishing encryption algorithm + RegisterMessage("SSH_MSG_USERAUTH_BANNER"); + + // Mark the message listener threads as started + _ = _messageListenerCompleted.Reset(); + + // Start incoming request listener + // ToDo: Make message pump async, to not consume a thread for every session + ThreadAbstraction.ExecuteThreadLongRunning(MessageListener); + + // Wait for key exchange to be completed + WaitOnHandle(_keyExchangeCompletedWaitHandle); + + // If sessionId is not set then its not connected + if (SessionId is null) + { + Disconnect(); + return; + } + + // Request user authorization service + SendMessage(new ServiceRequestMessage(ServiceName.UserAuthentication)); + + // Wait for service to be accepted + WaitOnHandle(_serviceAccepted); + + if (string.IsNullOrEmpty(ConnectionInfo.Username)) + { + throw new SshException("Username is not specified."); + } + + // Some servers send a global request immediately after successful authentication + // Avoid race condition by already enabling SSH_MSG_GLOBAL_REQUEST before authentication + RegisterMessage("SSH_MSG_GLOBAL_REQUEST"); + + ConnectionInfo.Authenticate(this, _serviceFactory); + _isAuthenticated = true; + + // Register Connection messages + RegisterMessage("SSH_MSG_REQUEST_SUCCESS"); + RegisterMessage("SSH_MSG_REQUEST_FAILURE"); + RegisterMessage("SSH_MSG_CHANNEL_OPEN_CONFIRMATION"); + RegisterMessage("SSH_MSG_CHANNEL_OPEN_FAILURE"); + RegisterMessage("SSH_MSG_CHANNEL_WINDOW_ADJUST"); + RegisterMessage("SSH_MSG_CHANNEL_EXTENDED_DATA"); + RegisterMessage("SSH_MSG_CHANNEL_REQUEST"); + RegisterMessage("SSH_MSG_CHANNEL_SUCCESS"); + RegisterMessage("SSH_MSG_CHANNEL_FAILURE"); + RegisterMessage("SSH_MSG_CHANNEL_DATA"); + RegisterMessage("SSH_MSG_CHANNEL_EOF"); + RegisterMessage("SSH_MSG_CHANNEL_CLOSE"); + } + /// /// Disconnects from the server. /// @@ -688,7 +811,7 @@ namespace Renci.SshNet // has completed if (_messageListenerCompleted != null) { - _messageListenerCompleted.WaitOne(); + _ = _messageListenerCompleted.WaitOne(); } } @@ -747,23 +870,6 @@ namespace Renci.SshNet WaitOnHandle(waitHandle, timeout); } - /// - /// Waits for the specified handle or the exception handle for the receive thread - /// to signal within the connection timeout. - /// - /// The wait handle. - /// A received package was invalid or failed the message integrity check. - /// None of the handles are signaled in time and the session is not disconnecting. - /// A socket error was signaled while receiving messages from the server. - /// - /// When neither handles are signaled in time and the session is not closing, then the - /// session is disconnected. - /// - internal void WaitOnHandle(WaitHandle waitHandle) - { - WaitOnHandle(waitHandle, ConnectionInfo.Timeout); - } - /// /// Waits for the specified to receive a signal, using a /// to specify the time interval. @@ -775,8 +881,7 @@ namespace Renci.SshNet /// WaitResult ISession.TryWait(WaitHandle waitHandle, TimeSpan timeout) { - Exception exception; - return TryWait(waitHandle, timeout, out exception); + return TryWait(waitHandle, timeout, out _); } /// @@ -806,8 +911,10 @@ namespace Renci.SshNet /// private WaitResult TryWait(WaitHandle waitHandle, TimeSpan timeout, out Exception exception) { - if (waitHandle == null) - throw new ArgumentNullException("waitHandle"); + if (waitHandle is null) + { + throw new ArgumentNullException(nameof(waitHandle)); + } var waitHandles = new[] { @@ -824,6 +931,7 @@ namespace Renci.SshNet exception = null; return WaitResult.Disconnected; } + exception = _exception; return WaitResult.Failed; case 1: @@ -840,6 +948,23 @@ namespace Renci.SshNet } } + /// + /// Waits for the specified handle or the exception handle for the receive thread + /// to signal within the connection timeout. + /// + /// The wait handle. + /// A received package was invalid or failed the message integrity check. + /// None of the handles are signaled in time and the session is not disconnecting. + /// A socket error was signaled while receiving messages from the server. + /// + /// When neither handles are signaled in time and the session is not closing, then the + /// session is disconnected. + /// + internal void WaitOnHandle(WaitHandle waitHandle) + { + WaitOnHandle(waitHandle, ConnectionInfo.Timeout); + } + /// /// Waits for the specified handle or the exception handle for the receive thread /// to signal within the specified timeout. @@ -851,8 +976,10 @@ namespace Renci.SshNet /// A socket error was signaled while receiving messages from the server. internal void WaitOnHandle(WaitHandle waitHandle, TimeSpan timeout) { - if (waitHandle == null) - throw new ArgumentNullException("waitHandle"); + if (waitHandle is null) + { + throw new ArgumentNullException(nameof(waitHandle)); + } var waitHandles = new[] { @@ -861,12 +988,17 @@ namespace Renci.SshNet waitHandle }; - switch (WaitHandle.WaitAny(waitHandles, timeout)) + var signaledElement = WaitHandle.WaitAny(waitHandles, timeout); + switch (signaledElement) { case 0: - throw _exception; + System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(_exception).Throw(); + break; case 1: throw new SshConnectionException("Client not connected."); + case 2: + // Specified waithandle was signaled + break; case WaitHandle.WaitTimeout: // when the session is disconnecting, a timeout is likely when no // network connectivity is available; depending on the configured @@ -878,7 +1010,10 @@ namespace Renci.SshNet { throw new SshOperationTimeoutException("Session operation has timed out"); } + break; + default: + throw new SshException($"Unexpected element '{signaledElement.ToString(CultureInfo.InvariantCulture)}' signaled."); } } @@ -892,17 +1027,19 @@ namespace Renci.SshNet internal void SendMessage(Message message) { if (!_socket.CanWrite()) - throw new SshConnectionException("Client not connected."); - - if (_keyExchangeInProgress && !(message is IKeyExchangedAllowed)) { - // Wait for key exchange to be completed + throw new SshConnectionException("Client not connected."); + } + + if (_keyExchangeInProgress && message is not IKeyExchangedAllowed) + { + // Wait for key exchange to be completed WaitOnHandle(_keyExchangeCompletedWaitHandle); } DiagnosticAbstraction.Log(string.Format("[{0}] Sending message '{1}' to server: '{2}'.", ToHex(SessionId), message.GetType().Name, message)); - var paddingMultiplier = _clientCipher == null ? (byte) 8 : Math.Max((byte) 8, _serverCipher.MinimumSize); + var paddingMultiplier = _clientCipher is null ? (byte) 8 : Math.Max((byte) 8, _serverCipher.MinimumSize); var packetData = message.GetPacket(paddingMultiplier, _clientCompression); // take a write lock to ensure the outbound packet sequence number is incremented @@ -916,14 +1053,15 @@ namespace Renci.SshNet { // write outbound packet sequence to start of packet data Pack.UInt32ToBigEndian(_outboundPacketSequence, packetData); - // calculate packet hash + + // calculate packet hash hash = _clientMac.ComputeHash(packetData); } // Encrypt packet data if (_clientCipher != null) { - packetData = _clientCipher.Encrypt(packetData, packetDataOffset, (packetData.Length - packetDataOffset)); + packetData = _clientCipher.Encrypt(packetData, packetDataOffset, packetData.Length - packetDataOffset); packetDataOffset = 0; } @@ -933,7 +1071,7 @@ namespace Renci.SshNet } var packetLength = packetData.Length - packetDataOffset; - if (hash == null) + if (hash is null) { SendPacket(packetData, packetDataOffset, packetLength); } @@ -948,7 +1086,7 @@ namespace Renci.SshNet // increment the packet sequence number only after we're sure the packet has // been sent; even though it's only used for the MAC, it needs to be incremented // for each package sent. - // + // // the server will use it to verify the data integrity, and as such the order in // which messages are sent must follow the outbound packet sequence number _outboundPacketSequence++; @@ -977,7 +1115,9 @@ namespace Renci.SshNet lock (_socketDisposeLock) { if (!_socket.IsConnected()) + { throw new SshConnectionException("Client not connected."); + } SocketAbstraction.Send(_socket, packet, offset, length); } @@ -1027,27 +1167,27 @@ namespace Renci.SshNet { // the length of the packet sequence field in bytes const int inboundPacketSequenceLength = 4; + // The length of the "packet length" field in bytes const int packetLengthFieldLength = 4; + // The length of the "padding length" field in bytes const int paddingLengthFieldLength = 1; // Determine the size of the first block, which is 8 or cipher block size (whichever is larger) bytes - var blockSize = _serverCipher == null ? (byte) 8 : Math.Max((byte) 8, _serverCipher.MinimumSize); + var blockSize = _serverCipher is null ? (byte) 8 : Math.Max((byte) 8, _serverCipher.MinimumSize); var serverMacLength = _serverMac != null ? _serverMac.HashSize/8 : 0; byte[] data; uint packetLength; -#if FEATURE_SOCKET_POLL // avoid reading from socket while IsSocketConnected is attempting to determine whether the // socket is still connected by invoking Socket.Poll(...) and subsequently verifying value of // Socket.Available lock (_socketReadLock) { -#endif // FEATURE_SOCKET_POLL - // Read first block - which starts with the packet length + // Read first block - which starts with the packet length var firstBlock = new byte[blockSize]; if (TrySocketRead(socket, firstBlock, 0, blockSize) == 0) { @@ -1064,9 +1204,10 @@ namespace Renci.SshNet // Test packet minimum and maximum boundaries if (packetLength < Math.Max((byte) 16, blockSize) - 4 || packetLength > MaximumSshPacketSize - 4) - throw new SshConnectionException( - string.Format(CultureInfo.CurrentCulture, "Bad packet length: {0}.", packetLength), - DisconnectReason.ProtocolError); + { + throw new SshConnectionException(string.Format(CultureInfo.CurrentCulture, "Bad packet length: {0}.", packetLength), + DisconnectReason.ProtocolError); + } // Determine the number of bytes left to read; We've already read "blockSize" bytes, but the // "packet length" field itself - which is 4 bytes - is not included in the length of the packet @@ -1074,12 +1215,12 @@ namespace Renci.SshNet // Construct buffer for holding the payload and the inbound packet sequence as we need both in order // to generate the hash. - // + // // The total length of the "data" buffer is an addition of: // - inboundPacketSequenceLength (4 bytes) // - packetLength // - serverMacLength - // + // // We include the inbound packet sequence to allow us to have the the full SSH packet in a single // byte[] for the purpose of calculating the client hash. Room for the server MAC is foreseen // to read the packet including server MAC in a single pass (except for the initial block). @@ -1094,9 +1235,7 @@ namespace Renci.SshNet return null; } } -#if FEATURE_SOCKET_POLL } -#endif // FEATURE_SOCKET_POLL if (_serverCipher != null) { @@ -1132,6 +1271,7 @@ namespace Renci.SshNet // data now only contains the decompressed payload, and as such the offset is reset to zero messagePayloadOffset = 0; + // the length of the payload is now the complete decompressed content messagePayloadLength = data.Length; } @@ -1146,14 +1286,12 @@ namespace Renci.SshNet var disconnectMessage = new DisconnectMessage(reasonCode, message); // send the disconnect message, but ignore the outcome - TrySendMessage(disconnectMessage); + _ = TrySendMessage(disconnectMessage); // mark disconnect message sent regardless of whether the send sctually succeeded _isDisconnectMessageSent = true; } - #region Handle received message events - /// /// Called when received. /// @@ -1168,15 +1306,11 @@ namespace Renci.SshNet _isDisconnecting = true; _exception = new SshConnectionException(string.Format(CultureInfo.InvariantCulture, "The connection was closed by the server: {0} ({1}).", message.Description, message.ReasonCode), message.ReasonCode); - _exceptionWaitHandle.Set(); + _ = _exceptionWaitHandle.Set(); - var disconnectReceived = DisconnectReceived; - if (disconnectReceived != null) - disconnectReceived(this, new MessageEventArgs(message)); + DisconnectReceived?.Invoke(this, new MessageEventArgs(message)); - var disconnected = Disconnected; - if (disconnected != null) - disconnected(this, new EventArgs()); + Disconnected?.Invoke(this, EventArgs.Empty); // disconnect socket, and dispose it SocketDisconnectAndDispose(); @@ -1188,9 +1322,7 @@ namespace Renci.SshNet /// message. internal void OnIgnoreReceived(IgnoreMessage message) { - var handlers = IgnoreReceived; - if (handlers != null) - handlers(this, new MessageEventArgs(message)); + IgnoreReceived?.Invoke(this, new MessageEventArgs(message)); } /// @@ -1199,9 +1331,7 @@ namespace Renci.SshNet /// message. internal void OnUnimplementedReceived(UnimplementedMessage message) { - var handlers = UnimplementedReceived; - if (handlers != null) - handlers(this, new MessageEventArgs(message)); + UnimplementedReceived?.Invoke(this, new MessageEventArgs(message)); } /// @@ -1210,9 +1340,7 @@ namespace Renci.SshNet /// message. internal void OnDebugReceived(DebugMessage message) { - var handlers = DebugReceived; - if (handlers != null) - handlers(this, new MessageEventArgs(message)); + DebugReceived?.Invoke(this, new MessageEventArgs(message)); } /// @@ -1221,9 +1349,7 @@ namespace Renci.SshNet /// message. internal void OnServiceRequestReceived(ServiceRequestMessage message) { - var handlers = ServiceRequestReceived; - if (handlers != null) - handlers(this, new MessageEventArgs(message)); + ServiceRequestReceived?.Invoke(this, new MessageEventArgs(message)); } /// @@ -1232,25 +1358,19 @@ namespace Renci.SshNet /// message. internal void OnServiceAcceptReceived(ServiceAcceptMessage message) { - var handlers = ServiceAcceptReceived; - if (handlers != null) - handlers(this, new MessageEventArgs(message)); + ServiceAcceptReceived?.Invoke(this, new MessageEventArgs(message)); - _serviceAccepted.Set(); + _ = _serviceAccepted.Set(); } internal void OnKeyExchangeDhGroupExchangeGroupReceived(KeyExchangeDhGroupExchangeGroup message) { - var handlers = KeyExchangeDhGroupExchangeGroupReceived; - if (handlers != null) - handlers(this, new MessageEventArgs(message)); + KeyExchangeDhGroupExchangeGroupReceived?.Invoke(this, new MessageEventArgs(message)); } internal void OnKeyExchangeDhGroupExchangeReplyReceived(KeyExchangeDhGroupExchangeReply message) { - var handlers = KeyExchangeDhGroupExchangeReplyReceived; - if (handlers != null) - handlers(this, new MessageEventArgs(message)); + KeyExchangeDhGroupExchangeReplyReceived?.Invoke(this, new MessageEventArgs(message)); } /// @@ -1261,7 +1381,7 @@ namespace Renci.SshNet { _keyExchangeInProgress = true; - _keyExchangeCompletedWaitHandle.Reset(); + _ = _keyExchangeCompletedWaitHandle.Reset(); // Disable messages that are not key exchange related _sshMessageFactory.DisableNonKeyExchangeMessages(); @@ -1273,26 +1393,20 @@ namespace Renci.SshNet _keyExchange.HostKeyReceived += KeyExchange_HostKeyReceived; - // Start the algorithm implementation + // Start the algorithm implementation _keyExchange.Start(this, message); - var keyExchangeInitReceived = KeyExchangeInitReceived; - if (keyExchangeInitReceived != null) - keyExchangeInitReceived(this, new MessageEventArgs(message)); + KeyExchangeInitReceived?.Invoke(this, new MessageEventArgs(message)); } internal void OnKeyExchangeDhReplyMessageReceived(KeyExchangeDhReplyMessage message) { - var handlers = KeyExchangeDhReplyMessageReceived; - if (handlers != null) - handlers(this, new MessageEventArgs(message)); + KeyExchangeDhReplyMessageReceived?.Invoke(this, new MessageEventArgs(message)); } internal void OnKeyExchangeEcdhReplyMessageReceived(KeyExchangeEcdhReplyMessage message) { - var handlers = KeyExchangeEcdhReplyMessageReceived; - if (handlers != null) - handlers(this, new MessageEventArgs(message)); + KeyExchangeEcdhReplyMessageReceived?.Invoke(this, new MessageEventArgs(message)); } /// @@ -1301,13 +1415,10 @@ namespace Renci.SshNet /// message. internal void OnNewKeysReceived(NewKeysMessage message) { - // Update sessionId - if (SessionId == null) - { - SessionId = _keyExchange.ExchangeHash; - } + // Update sessionId + SessionId ??= _keyExchange.ExchangeHash; - // Dispose of old ciphers and hash algorithms + // Dispose of old ciphers and hash algorithms if (_serverMac != null) { _serverMac.Dispose(); @@ -1320,7 +1431,7 @@ namespace Renci.SshNet _clientMac = null; } - // Update negotiated algorithms + // Update negotiated algorithms _serverCipher = _keyExchange.CreateServerCipher(); _clientCipher = _keyExchange.CreateClientCipher(); _serverMac = _keyExchange.CreateServerHash(); @@ -1328,7 +1439,7 @@ namespace Renci.SshNet _clientCompression = _keyExchange.CreateCompressor(); _serverDecompression = _keyExchange.CreateDecompressor(); - // Dispose of old KeyExchange object as it is no longer needed. + // Dispose of old KeyExchange object as it is no longer needed. if (_keyExchange != null) { _keyExchange.HostKeyReceived -= KeyExchange_HostKeyReceived; @@ -1339,12 +1450,10 @@ namespace Renci.SshNet // Enable activated messages that are not key exchange related _sshMessageFactory.EnableActivatedMessages(); - var newKeysReceived = NewKeysReceived; - if (newKeysReceived != null) - newKeysReceived(this, new MessageEventArgs(message)); + NewKeysReceived?.Invoke(this, new MessageEventArgs(message)); - // Signal that key exchange completed - _keyExchangeCompletedWaitHandle.Set(); + // Signal that key exchange completed + _ = _keyExchangeCompletedWaitHandle.Set(); _keyExchangeInProgress = false; } @@ -1363,9 +1472,7 @@ namespace Renci.SshNet /// message. internal void OnUserAuthenticationRequestReceived(RequestMessage message) { - var handlers = UserAuthenticationRequestReceived; - if (handlers != null) - handlers(this, new MessageEventArgs(message)); + UserAuthenticationRequestReceived?.Invoke(this, new MessageEventArgs(message)); } /// @@ -1374,9 +1481,7 @@ namespace Renci.SshNet /// message. internal void OnUserAuthenticationFailureReceived(FailureMessage message) { - var handlers = UserAuthenticationFailureReceived; - if (handlers != null) - handlers(this, new MessageEventArgs(message)); + UserAuthenticationFailureReceived?.Invoke(this, new MessageEventArgs(message)); } /// @@ -1385,9 +1490,7 @@ namespace Renci.SshNet /// message. internal void OnUserAuthenticationSuccessReceived(SuccessMessage message) { - var handlers = UserAuthenticationSuccessReceived; - if (handlers != null) - handlers(this, new MessageEventArgs(message)); + UserAuthenticationSuccessReceived?.Invoke(this, new MessageEventArgs(message)); } /// @@ -1396,35 +1499,26 @@ namespace Renci.SshNet /// message. internal void OnUserAuthenticationBannerReceived(BannerMessage message) { - var handlers = UserAuthenticationBannerReceived; - if (handlers != null) - handlers(this, new MessageEventArgs(message)); + UserAuthenticationBannerReceived?.Invoke(this, new MessageEventArgs(message)); } - /// /// Called when message received. /// /// message. internal void OnUserAuthenticationInformationRequestReceived(InformationRequestMessage message) { - var handlers = UserAuthenticationInformationRequestReceived; - if (handlers != null) - handlers(this, new MessageEventArgs(message)); + UserAuthenticationInformationRequestReceived?.Invoke(this, new MessageEventArgs(message)); } internal void OnUserAuthenticationPasswordChangeRequiredReceived(PasswordChangeRequiredMessage message) { - var handlers = UserAuthenticationPasswordChangeRequiredReceived; - if (handlers != null) - handlers(this, new MessageEventArgs(message)); + UserAuthenticationPasswordChangeRequiredReceived?.Invoke(this, new MessageEventArgs(message)); } internal void OnUserAuthenticationPublicKeyReceived(PublicKeyMessage message) { - var handlers = UserAuthenticationPublicKeyReceived; - if (handlers != null) - handlers(this, new MessageEventArgs(message)); + UserAuthenticationPublicKeyReceived?.Invoke(this, new MessageEventArgs(message)); } /// @@ -1433,9 +1527,7 @@ namespace Renci.SshNet /// message. internal void OnGlobalRequestReceived(GlobalRequestMessage message) { - var handlers = GlobalRequestReceived; - if (handlers != null) - handlers(this, new MessageEventArgs(message)); + GlobalRequestReceived?.Invoke(this, new MessageEventArgs(message)); } /// @@ -1444,9 +1536,7 @@ namespace Renci.SshNet /// message. internal void OnRequestSuccessReceived(RequestSuccessMessage message) { - var handlers = RequestSuccessReceived; - if (handlers != null) - handlers(this, new MessageEventArgs(message)); + RequestSuccessReceived?.Invoke(this, new MessageEventArgs(message)); } /// @@ -1455,9 +1545,7 @@ namespace Renci.SshNet /// message. internal void OnRequestFailureReceived(RequestFailureMessage message) { - var handlers = RequestFailureReceived; - if (handlers != null) - handlers(this, new MessageEventArgs(message)); + RequestFailureReceived?.Invoke(this, new MessageEventArgs(message)); } /// @@ -1466,9 +1554,7 @@ namespace Renci.SshNet /// message. internal void OnChannelOpenReceived(ChannelOpenMessage message) { - var handlers = ChannelOpenReceived; - if (handlers != null) - handlers(this, new MessageEventArgs(message)); + ChannelOpenReceived?.Invoke(this, new MessageEventArgs(message)); } /// @@ -1477,9 +1563,7 @@ namespace Renci.SshNet /// message. internal void OnChannelOpenConfirmationReceived(ChannelOpenConfirmationMessage message) { - var handlers = ChannelOpenConfirmationReceived; - if (handlers != null) - handlers(this, new MessageEventArgs(message)); + ChannelOpenConfirmationReceived?.Invoke(this, new MessageEventArgs(message)); } /// @@ -1488,9 +1572,7 @@ namespace Renci.SshNet /// message. internal void OnChannelOpenFailureReceived(ChannelOpenFailureMessage message) { - var handlers = ChannelOpenFailureReceived; - if (handlers != null) - handlers(this, new MessageEventArgs(message)); + ChannelOpenFailureReceived?.Invoke(this, new MessageEventArgs(message)); } /// @@ -1499,9 +1581,7 @@ namespace Renci.SshNet /// message. internal void OnChannelWindowAdjustReceived(ChannelWindowAdjustMessage message) { - var handlers = ChannelWindowAdjustReceived; - if (handlers != null) - handlers(this, new MessageEventArgs(message)); + ChannelWindowAdjustReceived?.Invoke(this, new MessageEventArgs(message)); } /// @@ -1510,9 +1590,7 @@ namespace Renci.SshNet /// message. internal void OnChannelDataReceived(ChannelDataMessage message) { - var handlers = ChannelDataReceived; - if (handlers != null) - handlers(this, new MessageEventArgs(message)); + ChannelDataReceived?.Invoke(this, new MessageEventArgs(message)); } /// @@ -1521,9 +1599,7 @@ namespace Renci.SshNet /// message. internal void OnChannelExtendedDataReceived(ChannelExtendedDataMessage message) { - var handlers = ChannelExtendedDataReceived; - if (handlers != null) - handlers(this, new MessageEventArgs(message)); + ChannelExtendedDataReceived?.Invoke(this, new MessageEventArgs(message)); } /// @@ -1532,9 +1608,7 @@ namespace Renci.SshNet /// message. internal void OnChannelEofReceived(ChannelEofMessage message) { - var handlers = ChannelEofReceived; - if (handlers != null) - handlers(this, new MessageEventArgs(message)); + ChannelEofReceived?.Invoke(this, new MessageEventArgs(message)); } /// @@ -1543,9 +1617,7 @@ namespace Renci.SshNet /// message. internal void OnChannelCloseReceived(ChannelCloseMessage message) { - var handlers = ChannelCloseReceived; - if (handlers != null) - handlers(this, new MessageEventArgs(message)); + ChannelCloseReceived?.Invoke(this, new MessageEventArgs(message)); } /// @@ -1554,9 +1626,7 @@ namespace Renci.SshNet /// message. internal void OnChannelRequestReceived(ChannelRequestMessage message) { - var handlers = ChannelRequestReceived; - if (handlers != null) - handlers(this, new MessageEventArgs(message)); + ChannelRequestReceived?.Invoke(this, new MessageEventArgs(message)); } /// @@ -1565,9 +1635,7 @@ namespace Renci.SshNet /// message. internal void OnChannelSuccessReceived(ChannelSuccessMessage message) { - var handlers = ChannelSuccessReceived; - if (handlers != null) - handlers(this, new MessageEventArgs(message)); + ChannelSuccessReceived?.Invoke(this, new MessageEventArgs(message)); } /// @@ -1576,22 +1644,14 @@ namespace Renci.SshNet /// message. internal void OnChannelFailureReceived(ChannelFailureMessage message) { - var handlers = ChannelFailureReceived; - if (handlers != null) - handlers(this, new MessageEventArgs(message)); + ChannelFailureReceived?.Invoke(this, new MessageEventArgs(message)); } - #endregion - private void KeyExchange_HostKeyReceived(object sender, HostKeyEventArgs e) { - var handlers = HostKeyReceived; - if (handlers != null) - handlers(this, e); + HostKeyReceived?.Invoke(this, e); } - #region Message loading functions - /// /// Registers SSH message with the session. /// @@ -1641,7 +1701,7 @@ namespace Renci.SshNet for (var i = offset; i < byteCount; i++) { var b = bytes[i]; - builder.Append(b.ToString("X2")); + _ = builder.Append(b.ToString("X2")); } return builder.ToString(); @@ -1649,15 +1709,14 @@ namespace Renci.SshNet internal static string ToHex(byte[] bytes) { - if (bytes == null) + if (bytes is null) + { return null; + } return ToHex(bytes, 0); } - #endregion - -#if FEATURE_SOCKET_POLL /// /// Gets a value indicating whether the socket is connected. /// @@ -1697,23 +1756,10 @@ namespace Renci.SshNet /// we synchronize reads from the . /// /// -#else - /// - /// Gets a value indicating whether the socket is connected. - /// - /// - /// true if the socket is connected; otherwise, false. - /// - /// - /// We verify whether is true. However, this only returns the state - /// of the socket as of the last I/O operation. - /// -#endif private bool IsSocketConnected() { lock (_socketDisposeLock) { -#if FEATURE_SOCKET_POLL if (!_socket.IsConnected()) { return false; @@ -1724,9 +1770,6 @@ namespace Renci.SshNet var connectionClosedOrDataAvailable = _socket.Poll(0, SelectMode.SelectRead); return !(connectionClosedOrDataAvailable && _socket.Available == 0); } -#else - return _socket.IsConnected(); -#endif // FEATURE_SOCKET_POLL } } @@ -1800,15 +1843,13 @@ namespace Renci.SshNet { var socket = _socket; - if (socket == null || !socket.Connected) + if (socket is null || !socket.Connected) { break; } -#if FEATURE_SOCKET_POLL || FEATURE_SOCKET_SELECT try { -#if FEATURE_SOCKET_POLL // Block until either data is available or the socket is closed var connectionClosedOrDataAvailable = socket.Poll(-1, SelectMode.SelectRead); if (connectionClosedOrDataAvailable && socket.Available == 0) @@ -1816,43 +1857,6 @@ namespace Renci.SshNet // connection with SSH server was closed or connection was reset break; } -#elif FEATURE_SOCKET_SELECT - var readSockets = new List { socket }; - - // if the socket is already disposed when Select is invoked, then a SocketException - // stating "An operation was attempted on something that is not a socket" is thrown; - // we attempt to avoid this exception by having an IsConnected() that can break the - // message loop - // - // note that there's no guarantee that the socket will not be disposed between the - // IsConnected() check and the Select invocation; we can't take a "dispose" lock - // that includes the Select invocation as we want Dispose() to be able to interrupt - // the Select - - // perform a blocking select to determine whether there's is data available to be - // read; we do not use a blocking read to allow us to use Socket.Poll to determine - // if the connection is still available (in IsSocketConnected) - - Socket.Select(readSockets, null, null, -1); - - // the Select invocation will be interrupted in one of the following conditions: - // * data is available to be read - // => the socket will not be removed from "readSockets" - // * the socket connection is closed during the Select invocation - // => the socket will be removed from "readSockets" - // * the socket is disposed during the Select invocation - // => the socket will not be removed from "readSocket" - // - // since we handle the second and third condition the same way and Socket.Connected - // allows us to check for both conditions, we use that instead of both checking for - // the removal from "readSockets" and the Connection check - if (!socket.IsConnected()) - { - // connection with SSH server was closed or socket was disposed; - // break out of the message loop - break; - } -#endif // FEATURE_SOCKET_SELECT } catch (ObjectDisposedException) { @@ -1862,13 +1866,11 @@ namespace Renci.SshNet // * a SSH_MSG_DISCONNECT received from server break; } -#endif // FEATURE_SOCKET_POLL || FEATURE_SOCKET_SELECT var message = ReceiveMessage(socket); - if (message == null) + if (message is null) { - // connection with SSH server was closed; - // break out of the message loop + // Connection with SSH server was closed, so break out of the message loop break; } @@ -1890,7 +1892,7 @@ namespace Renci.SshNet finally { // signal that the message listener thread has stopped - _messageListenerCompleted.Set(); + _ = _messageListenerCompleted.Set(); } } @@ -1906,25 +1908,26 @@ namespace Renci.SshNet if (_isDisconnecting) { - // a connection exception which is raised while isDisconnecting is normal and - // should be ignored + // a connection exception which is raised while isDisconnecting is normal and + // should be ignored if (connectionException != null) + { return; + } // any timeout while disconnecting can be caused by loss of connectivity // altogether and should be ignored - var socketException = exp as SocketException; - if (socketException != null && socketException.SocketErrorCode == SocketError.TimedOut) + if (exp is SocketException socketException && socketException.SocketErrorCode == SocketError.TimedOut) + { return; + } } // "save" exception and set exception wait handle to ensure any waits are interrupted _exception = exp; - _exceptionWaitHandle.Set(); + _ = _exceptionWaitHandle.Set(); - var errorOccured = ErrorOccured; - if (errorOccured != null) - errorOccured(this, new ExceptionEventArgs(exp)); + ErrorOccured?.Invoke(this, new ExceptionEventArgs(exp)); if (connectionException != null) { @@ -1939,12 +1942,9 @@ namespace Renci.SshNet /// private void Reset() { - if (_exceptionWaitHandle != null) - _exceptionWaitHandle.Reset(); - if (_keyExchangeCompletedWaitHandle != null) - _keyExchangeCompletedWaitHandle.Reset(); - if (_messageListenerCompleted != null) - _messageListenerCompleted.Set(); + _ = _exceptionWaitHandle?.Reset(); + _ = _keyExchangeCompletedWaitHandle?.Reset(); + _ = _messageListenerCompleted?.Set(); SessionId = null; _isDisconnectMessageSent = false; @@ -1960,8 +1960,6 @@ namespace Renci.SshNet DisconnectReason.ConnectionLost); } -#region IDisposable implementation - private bool _disposed; /// @@ -1969,18 +1967,20 @@ namespace Renci.SshNet /// public void Dispose() { - Dispose(true); + Dispose(disposing: true); GC.SuppressFinalize(this); } /// - /// Releases unmanaged and - optionally - managed resources + /// Releases unmanaged and - optionally - managed resources. /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected virtual void Dispose(bool disposing) { if (_disposed) + { return; + } if (disposing) { @@ -2043,20 +2043,15 @@ namespace Renci.SshNet } /// - /// Releases unmanaged resources and performs other cleanup operations before the - /// is reclaimed by garbage collection. + /// Finalizes an instance of the class. /// ~Session() { - Dispose(false); + Dispose(disposing: false); } -#endregion IDisposable implementation - -#region ISession implementation - /// - /// Gets or sets the connection info. + /// Gets the connection info. /// /// The connection info. IConnectionInfo ISession.ConnectionInfo @@ -2138,8 +2133,6 @@ namespace Renci.SshNet { return TrySendMessage(message); } - -#endregion ISession implementation } /// @@ -2167,4 +2160,4 @@ namespace Renci.SshNet /// Failed = 4 } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet/Sftp/ISftpFile.cs b/src/Renci.SshNet/Sftp/ISftpFile.cs new file mode 100644 index 00000000..fb0d8b22 --- /dev/null +++ b/src/Renci.SshNet/Sftp/ISftpFile.cs @@ -0,0 +1,242 @@ +using System; + +namespace Renci.SshNet.Sftp +{ + /// + /// Represents SFTP file information. + /// + public interface ISftpFile + { + /// + /// Gets the file attributes. + /// + SftpFileAttributes Attributes { get; } + + /// + /// Gets the full path of the file or directory. + /// + /// + /// The full path of the file or directory. + /// + string FullName { get; } + + /// + /// Gets the name of the file or directory. + /// + /// + /// The name of the file or directory. + /// + /// + /// For directories, this is the name of the last directory in the hierarchy if a hierarchy exists; + /// otherwise, the name of the directory. + /// + string Name { get; } + + /// + /// Gets or sets the time the current file or directory was last accessed. + /// + /// + /// The time that the current file or directory was last accessed. + /// + DateTime LastAccessTime { get; set; } + + /// + /// Gets or sets the time when the current file or directory was last written to. + /// + /// + /// The time the current file was last written. + /// + DateTime LastWriteTime { get; set; } + + /// + /// Gets or sets the time, in coordinated universal time (UTC), the current file or directory was last accessed. + /// + /// + /// The time that the current file or directory was last accessed. + /// + DateTime LastAccessTimeUtc { get; set; } + + /// + /// Gets or sets the time, in coordinated universal time (UTC), when the current file or directory was last written to. + /// + /// + /// The time the current file was last written. + /// + DateTime LastWriteTimeUtc { get; set; } + + /// + /// Gets the size, in bytes, of the current file. + /// + /// + /// The size of the current file in bytes. + /// + long Length { get; } + + /// + /// Gets or sets file user id. + /// + /// + /// File user id. + /// + int UserId { get; set; } + + /// + /// Gets or sets file group id. + /// + /// + /// File group id. + /// + int GroupId { get; set; } + + /// + /// Gets a value indicating whether file represents a socket. + /// + /// + /// true if file represents a socket; otherwise, false. + /// + bool IsSocket { get; } + + /// + /// Gets a value indicating whether file represents a symbolic link. + /// + /// + /// true if file represents a symbolic link; otherwise, false. + /// + bool IsSymbolicLink { get; } + + /// + /// Gets a value indicating whether file represents a regular file. + /// + /// + /// true if file represents a regular file; otherwise, false. + /// + bool IsRegularFile { get; } + + /// + /// Gets a value indicating whether file represents a block device. + /// + /// + /// true if file represents a block device; otherwise, false. + /// + bool IsBlockDevice { get; } + + /// + /// Gets a value indicating whether file represents a directory. + /// + /// + /// true if file represents a directory; otherwise, false. + /// + bool IsDirectory { get; } + + /// + /// Gets a value indicating whether file represents a character device. + /// + /// + /// true if file represents a character device; otherwise, false. + /// + bool IsCharacterDevice { get; } + + /// + /// Gets a value indicating whether file represents a named pipe. + /// + /// + /// true if file represents a named pipe; otherwise, false. + /// + bool IsNamedPipe { get; } + + /// + /// Gets or sets a value indicating whether the owner can read from this file. + /// + /// + /// true if owner can read from this file; otherwise, false. + /// + bool OwnerCanRead { get; set; } + + /// + /// Gets or sets a value indicating whether the owner can write into this file. + /// + /// + /// true if owner can write into this file; otherwise, false. + /// + bool OwnerCanWrite { get; set; } + + /// + /// Gets or sets a value indicating whether the owner can execute this file. + /// + /// + /// true if owner can execute this file; otherwise, false. + /// + bool OwnerCanExecute { get; set; } + + /// + /// Gets or sets a value indicating whether the group members can read from this file. + /// + /// + /// true if group members can read from this file; otherwise, false. + /// + bool GroupCanRead { get; set; } + + /// + /// Gets or sets a value indicating whether the group members can write into this file. + /// + /// + /// true if group members can write into this file; otherwise, false. + /// + bool GroupCanWrite { get; set; } + + /// + /// Gets or sets a value indicating whether the group members can execute this file. + /// + /// + /// true if group members can execute this file; otherwise, false. + /// + bool GroupCanExecute { get; set; } + + /// + /// Gets or sets a value indicating whether the others can read from this file. + /// + /// + /// true if others can read from this file; otherwise, false. + /// + bool OthersCanRead { get; set; } + + /// + /// Gets or sets a value indicating whether the others can write into this file. + /// + /// + /// true if others can write into this file; otherwise, false. + /// + bool OthersCanWrite { get; set; } + + /// + /// Gets or sets a value indicating whether the others can execute this file. + /// + /// + /// true if others can execute this file; otherwise, false. + /// + bool OthersCanExecute { get; set; } + + /// + /// Sets file permissions. + /// + /// The mode. + void SetPermissions(short mode); + + /// + /// Permanently deletes a file on remote machine. + /// + void Delete(); + + /// + /// Moves a specified file to a new location on remote machine, providing the option to specify a new file name. + /// + /// The path to move the file to, which can specify a different file name. + /// is null. + void MoveTo(string destFileName); + + /// + /// Updates file status on the server. + /// + void UpdateStatus(); + } +} diff --git a/src/Renci.SshNet/Sftp/ISftpSession.cs b/src/Renci.SshNet/Sftp/ISftpSession.cs index 3d3993bb..39b48e7f 100644 --- a/src/Renci.SshNet/Sftp/ISftpSession.cs +++ b/src/Renci.SshNet/Sftp/ISftpSession.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; using System.Threading; +using System.Threading.Tasks; + using Renci.SshNet.Sftp.Responses; namespace Renci.SshNet.Sftp @@ -38,6 +40,8 @@ namespace Renci.SshNet.Sftp /// string GetCanonicalPath(string path); + Task GetCanonicalPathAsync(string path, CancellationToken cancellationToken); + /// /// Performs SSH_FXP_FSTAT request. /// @@ -48,18 +52,20 @@ namespace Renci.SshNet.Sftp /// SftpFileAttributes RequestFStat(byte[] handle, bool nullOnError); + Task RequestFStatAsync(byte[] handle, CancellationToken cancellationToken); + /// /// Performs SSH_FXP_STAT request. /// /// The path. /// if set to true returns null instead of throwing an exception. /// - /// File attributes + /// File attributes. /// SftpFileAttributes RequestStat(string path, bool nullOnError = false); /// - /// Performs SSH_FXP_STAT request + /// Performs SSH_FXP_STAT request. /// /// The path. /// The delegate that is executed when completes. @@ -116,7 +122,7 @@ namespace Renci.SshNet.Sftp void RequestMkDir(string path); /// - /// Performs SSH_FXP_OPEN request + /// Performs SSH_FXP_OPEN request. /// /// The path. /// The flags. @@ -124,8 +130,10 @@ namespace Renci.SshNet.Sftp /// File handle. byte[] RequestOpen(string path, Flags flags, bool nullOnError = false); + Task RequestOpenAsync(string path, Flags flags, CancellationToken cancellationToken); + /// - /// Performs SSH_FXP_OPEN request + /// Performs SSH_FXP_OPEN request. /// /// The path. /// The flags. @@ -151,13 +159,15 @@ namespace Renci.SshNet.Sftp byte[] EndOpen(SftpOpenAsyncResult asyncResult); /// - /// Performs SSH_FXP_OPENDIR request + /// Performs SSH_FXP_OPENDIR request. /// /// The path. /// if set to true returns null instead of throwing an exception. /// File handle. byte[] RequestOpenDir(string path, bool nullOnError = false); + Task RequestOpenDirAsync(string path, CancellationToken cancellationToken); + /// /// Performs posix-rename@openssh.com extended request. /// @@ -201,13 +211,17 @@ namespace Renci.SshNet.Sftp /// is null. byte[] EndRead(SftpReadAsyncResult asyncResult); + Task RequestReadAsync(byte[] handle, ulong offset, uint length, CancellationToken cancellationToken); + /// - /// Performs SSH_FXP_READDIR request + /// Performs SSH_FXP_READDIR request. /// /// The handle. /// KeyValuePair[] RequestReadDir(byte[] handle); + Task[]> RequestReadDirAsync(byte[] handle, CancellationToken cancellationToken); + /// /// Performs SSH_FXP_REALPATH request. /// @@ -235,6 +249,8 @@ namespace Renci.SshNet.Sftp /// The path. void RequestRemove(string path); + Task RequestRemoveAsync(string path, CancellationToken cancellationToken); + /// /// Performs SSH_FXP_RENAME request. /// @@ -242,6 +258,8 @@ namespace Renci.SshNet.Sftp /// The new path. void RequestRename(string oldPath, string newPath); + Task RequestRenameAsync(string oldPath, string newPath, CancellationToken cancellationToken); + /// /// Performs SSH_FXP_RMDIR request. /// @@ -263,6 +281,8 @@ namespace Renci.SshNet.Sftp /// SftpFileSytemInformation RequestStatVfs(string path, bool nullOnError = false); + Task RequestStatVfsAsync(string path, CancellationToken cancellationToken); + /// /// Performs SSH_FXP_SYMLINK request. /// @@ -295,12 +315,16 @@ namespace Renci.SshNet.Sftp AutoResetEvent wait, Action writeCompleted = null); + Task RequestWriteAsync(byte[] handle, ulong serverOffset, byte[] data, int offset, int length, CancellationToken cancellationToken); + /// /// Performs SSH_FXP_CLOSE request. /// /// The handle. void RequestClose(byte[] handle); + Task RequestCloseAsync(byte[] handle, CancellationToken cancellationToken); + /// /// Performs SSH_FXP_CLOSE request. /// diff --git a/src/Renci.SshNet/Sftp/Requests/ExtendedRequests/FStatVfsRequest.cs b/src/Renci.SshNet/Sftp/Requests/ExtendedRequests/FStatVfsRequest.cs index 5b5b02ec..685c0369 100644 --- a/src/Renci.SshNet/Sftp/Requests/ExtendedRequests/FStatVfsRequest.cs +++ b/src/Renci.SshNet/Sftp/Requests/ExtendedRequests/FStatVfsRequest.cs @@ -1,9 +1,10 @@ using System; + using Renci.SshNet.Sftp.Responses; namespace Renci.SshNet.Sftp.Requests { - internal class FStatVfsRequest : SftpExtendedRequest + internal sealed class FStatVfsRequest : SftpExtendedRequest { private readonly Action _extendedReplyAction; @@ -42,8 +43,7 @@ namespace Renci.SshNet.Sftp.Requests public override void Complete(SftpResponse response) { - var extendedReplyResponse = response as SftpExtendedReplyResponse; - if (extendedReplyResponse != null) + if (response is SftpExtendedReplyResponse extendedReplyResponse) { _extendedReplyAction(extendedReplyResponse); } diff --git a/src/Renci.SshNet/Sftp/Requests/ExtendedRequests/HardLinkRequest.cs b/src/Renci.SshNet/Sftp/Requests/ExtendedRequests/HardLinkRequest.cs index de7e06db..cd744f0e 100644 --- a/src/Renci.SshNet/Sftp/Requests/ExtendedRequests/HardLinkRequest.cs +++ b/src/Renci.SshNet/Sftp/Requests/ExtendedRequests/HardLinkRequest.cs @@ -1,9 +1,10 @@ using System; + using Renci.SshNet.Sftp.Responses; namespace Renci.SshNet.Sftp.Requests { - internal class HardLinkRequest : SftpExtendedRequest + internal sealed class HardLinkRequest : SftpExtendedRequest { private byte[] _oldPath; private byte[] _newPath; @@ -53,4 +54,4 @@ namespace Renci.SshNet.Sftp.Requests WriteBinaryString(_newPath); } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet/Sftp/Requests/ExtendedRequests/PosixRenameRequest.cs b/src/Renci.SshNet/Sftp/Requests/ExtendedRequests/PosixRenameRequest.cs index d0b530f0..5d996723 100644 --- a/src/Renci.SshNet/Sftp/Requests/ExtendedRequests/PosixRenameRequest.cs +++ b/src/Renci.SshNet/Sftp/Requests/ExtendedRequests/PosixRenameRequest.cs @@ -1,10 +1,11 @@ using System; -using Renci.SshNet.Sftp.Responses; using System.Text; +using Renci.SshNet.Sftp.Responses; + namespace Renci.SshNet.Sftp.Requests { - internal class PosixRenameRequest : SftpExtendedRequest + internal sealed class PosixRenameRequest : SftpExtendedRequest { private byte[] _oldPath; private byte[] _newPath; @@ -21,7 +22,7 @@ namespace Renci.SshNet.Sftp.Requests private set { _newPath = Encoding.GetBytes(value); } } - public Encoding Encoding { get; private set; } + public Encoding Encoding { get; } /// /// Gets the size of the message in bytes. @@ -53,8 +54,9 @@ namespace Renci.SshNet.Sftp.Requests protected override void SaveData() { base.SaveData(); + WriteBinaryString(_oldPath); WriteBinaryString(_newPath); } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet/Sftp/Requests/ExtendedRequests/StatVfsRequest.cs b/src/Renci.SshNet/Sftp/Requests/ExtendedRequests/StatVfsRequest.cs index b1bda3c0..fe437325 100644 --- a/src/Renci.SshNet/Sftp/Requests/ExtendedRequests/StatVfsRequest.cs +++ b/src/Renci.SshNet/Sftp/Requests/ExtendedRequests/StatVfsRequest.cs @@ -1,13 +1,14 @@ using System; -using Renci.SshNet.Sftp.Responses; using System.Text; +using Renci.SshNet.Sftp.Responses; + namespace Renci.SshNet.Sftp.Requests { - internal class StatVfsRequest : SftpExtendedRequest + internal sealed class StatVfsRequest : SftpExtendedRequest { - private byte[] _path; private readonly Action _extendedReplyAction; + private byte[] _path; public string Path { @@ -51,8 +52,7 @@ namespace Renci.SshNet.Sftp.Requests public override void Complete(SftpResponse response) { - var extendedReplyResponse = response as SftpExtendedReplyResponse; - if (extendedReplyResponse != null) + if (response is SftpExtendedReplyResponse extendedReplyResponse) { _extendedReplyAction(extendedReplyResponse); } diff --git a/src/Renci.SshNet/Sftp/Requests/SftpBlockRequest.cs b/src/Renci.SshNet/Sftp/Requests/SftpBlockRequest.cs index 9414150c..9c2ccd6c 100644 --- a/src/Renci.SshNet/Sftp/Requests/SftpBlockRequest.cs +++ b/src/Renci.SshNet/Sftp/Requests/SftpBlockRequest.cs @@ -1,9 +1,10 @@ using System; + using Renci.SshNet.Sftp.Responses; namespace Renci.SshNet.Sftp.Requests { - internal class SftpBlockRequest : SftpRequest + internal sealed class SftpBlockRequest : SftpRequest { public override SftpMessageTypes SftpMessageType { @@ -38,7 +39,7 @@ namespace Renci.SshNet.Sftp.Requests } } - public SftpBlockRequest(uint protocolVersion, uint requestId, byte[] handle, UInt64 offset, UInt64 length, UInt32 lockMask, Action statusAction) + public SftpBlockRequest(uint protocolVersion, uint requestId, byte[] handle, ulong offset, ulong length, uint lockMask, Action statusAction) : base(protocolVersion, requestId, statusAction) { Handle = handle; @@ -50,6 +51,7 @@ namespace Renci.SshNet.Sftp.Requests protected override void LoadData() { base.LoadData(); + Handle = ReadBinary(); Offset = ReadUInt64(); Length = ReadUInt64(); @@ -59,6 +61,7 @@ namespace Renci.SshNet.Sftp.Requests protected override void SaveData() { base.SaveData(); + WriteBinaryString(Handle); Write(Offset); Write(Length); diff --git a/src/Renci.SshNet/Sftp/Requests/SftpCloseRequest.cs b/src/Renci.SshNet/Sftp/Requests/SftpCloseRequest.cs index dd2dace1..f17673f9 100644 --- a/src/Renci.SshNet/Sftp/Requests/SftpCloseRequest.cs +++ b/src/Renci.SshNet/Sftp/Requests/SftpCloseRequest.cs @@ -3,7 +3,7 @@ using Renci.SshNet.Sftp.Responses; namespace Renci.SshNet.Sftp.Requests { - internal class SftpCloseRequest : SftpRequest + internal sealed class SftpCloseRequest : SftpRequest { public override SftpMessageTypes SftpMessageType { diff --git a/src/Renci.SshNet/Sftp/Requests/SftpFSetStatRequest.cs b/src/Renci.SshNet/Sftp/Requests/SftpFSetStatRequest.cs index 5de45c5a..d24f757b 100644 --- a/src/Renci.SshNet/Sftp/Requests/SftpFSetStatRequest.cs +++ b/src/Renci.SshNet/Sftp/Requests/SftpFSetStatRequest.cs @@ -3,7 +3,7 @@ using Renci.SshNet.Sftp.Responses; namespace Renci.SshNet.Sftp.Requests { - internal class SftpFSetStatRequest : SftpRequest + internal sealed class SftpFSetStatRequest : SftpRequest { private byte[] _attributesBytes; @@ -20,10 +20,7 @@ namespace Renci.SshNet.Sftp.Requests { get { - if (_attributesBytes == null) - { - _attributesBytes = Attributes.GetBytes(); - } + _attributesBytes ??= Attributes.GetBytes(); return _attributesBytes; } } @@ -56,6 +53,7 @@ namespace Renci.SshNet.Sftp.Requests protected override void LoadData() { base.LoadData(); + Handle = ReadBinary(); Attributes = ReadAttributes(); } @@ -63,6 +61,7 @@ namespace Renci.SshNet.Sftp.Requests protected override void SaveData() { base.SaveData(); + WriteBinaryString(Handle); Write(AttributesBytes); } diff --git a/src/Renci.SshNet/Sftp/Requests/SftpFStatRequest.cs b/src/Renci.SshNet/Sftp/Requests/SftpFStatRequest.cs index da71a62e..6fa4576f 100644 --- a/src/Renci.SshNet/Sftp/Requests/SftpFStatRequest.cs +++ b/src/Renci.SshNet/Sftp/Requests/SftpFStatRequest.cs @@ -1,9 +1,10 @@ using System; + using Renci.SshNet.Sftp.Responses; namespace Renci.SshNet.Sftp.Requests { - internal class SftpFStatRequest : SftpRequest + internal sealed class SftpFStatRequest : SftpRequest { private readonly Action _attrsAction; @@ -52,8 +53,7 @@ namespace Renci.SshNet.Sftp.Requests public override void Complete(SftpResponse response) { - var attrsResponse = response as SftpAttrsResponse; - if (attrsResponse != null) + if (response is SftpAttrsResponse attrsResponse) { _attrsAction(attrsResponse); } diff --git a/src/Renci.SshNet/Sftp/Requests/SftpInitRequest.cs b/src/Renci.SshNet/Sftp/Requests/SftpInitRequest.cs index cb95c7c0..25e21dab 100644 --- a/src/Renci.SshNet/Sftp/Requests/SftpInitRequest.cs +++ b/src/Renci.SshNet/Sftp/Requests/SftpInitRequest.cs @@ -1,6 +1,6 @@ namespace Renci.SshNet.Sftp.Requests { - internal class SftpInitRequest : SftpMessage + internal sealed class SftpInitRequest : SftpMessage { public override SftpMessageTypes SftpMessageType { diff --git a/src/Renci.SshNet/Sftp/Requests/SftpLStatRequest.cs b/src/Renci.SshNet/Sftp/Requests/SftpLStatRequest.cs index 1edb9c69..950ef8f4 100644 --- a/src/Renci.SshNet/Sftp/Requests/SftpLStatRequest.cs +++ b/src/Renci.SshNet/Sftp/Requests/SftpLStatRequest.cs @@ -4,10 +4,10 @@ using Renci.SshNet.Sftp.Responses; namespace Renci.SshNet.Sftp.Requests { - internal class SftpLStatRequest : SftpRequest + internal sealed class SftpLStatRequest : SftpRequest { - private byte[] _path; private readonly Action _attrsAction; + private byte[] _path; public override SftpMessageTypes SftpMessageType { @@ -61,8 +61,7 @@ namespace Renci.SshNet.Sftp.Requests public override void Complete(SftpResponse response) { - var attrsResponse = response as SftpAttrsResponse; - if (attrsResponse != null) + if (response is SftpAttrsResponse attrsResponse) { _attrsAction(attrsResponse); } diff --git a/src/Renci.SshNet/Sftp/Requests/SftpLinkRequest.cs b/src/Renci.SshNet/Sftp/Requests/SftpLinkRequest.cs index 04470b36..063ad3b2 100644 --- a/src/Renci.SshNet/Sftp/Requests/SftpLinkRequest.cs +++ b/src/Renci.SshNet/Sftp/Requests/SftpLinkRequest.cs @@ -1,9 +1,10 @@ -using Renci.SshNet.Sftp.Responses; -using System; +using System; + +using Renci.SshNet.Sftp.Responses; namespace Renci.SshNet.Sftp.Requests { - internal class SftpLinkRequest : SftpRequest + internal sealed class SftpLinkRequest : SftpRequest { private byte[] _newLinkPath; private byte[] _existingPath; @@ -67,6 +68,7 @@ namespace Renci.SshNet.Sftp.Requests protected override void LoadData() { base.LoadData(); + _newLinkPath = ReadBinary(); _existingPath = ReadBinary(); IsSymLink = ReadBoolean(); @@ -75,6 +77,7 @@ namespace Renci.SshNet.Sftp.Requests protected override void SaveData() { base.SaveData(); + WriteBinaryString(_newLinkPath); WriteBinaryString(_existingPath); Write(IsSymLink); diff --git a/src/Renci.SshNet/Sftp/Requests/SftpMkDirRequest.cs b/src/Renci.SshNet/Sftp/Requests/SftpMkDirRequest.cs index 9dc60edc..126925b9 100644 --- a/src/Renci.SshNet/Sftp/Requests/SftpMkDirRequest.cs +++ b/src/Renci.SshNet/Sftp/Requests/SftpMkDirRequest.cs @@ -4,7 +4,7 @@ using Renci.SshNet.Sftp.Responses; namespace Renci.SshNet.Sftp.Requests { - internal class SftpMkDirRequest : SftpRequest + internal sealed class SftpMkDirRequest : SftpRequest { private byte[] _path; private byte[] _attributesBytes; @@ -28,10 +28,8 @@ namespace Renci.SshNet.Sftp.Requests { get { - if (_attributesBytes == null) - { - _attributesBytes = Attributes.GetBytes(); - } + _attributesBytes ??= Attributes.GetBytes(); + return _attributesBytes; } } diff --git a/src/Renci.SshNet/Sftp/Requests/SftpOpenDirRequest.cs b/src/Renci.SshNet/Sftp/Requests/SftpOpenDirRequest.cs index 81a54b62..cde5a7af 100644 --- a/src/Renci.SshNet/Sftp/Requests/SftpOpenDirRequest.cs +++ b/src/Renci.SshNet/Sftp/Requests/SftpOpenDirRequest.cs @@ -1,13 +1,14 @@ using System; using System.Text; + using Renci.SshNet.Sftp.Responses; namespace Renci.SshNet.Sftp.Requests { - internal class SftpOpenDirRequest : SftpRequest + internal sealed class SftpOpenDirRequest : SftpRequest { - private byte[] _path; private readonly Action _handleAction; + private byte[] _path; public override SftpMessageTypes SftpMessageType { @@ -64,8 +65,7 @@ namespace Renci.SshNet.Sftp.Requests public override void Complete(SftpResponse response) { - var handleResponse = response as SftpHandleResponse; - if (handleResponse != null) + if (response is SftpHandleResponse handleResponse) { _handleAction(handleResponse); } diff --git a/src/Renci.SshNet/Sftp/Requests/SftpOpenRequest.cs b/src/Renci.SshNet/Sftp/Requests/SftpOpenRequest.cs index da7a7a09..780552fb 100644 --- a/src/Renci.SshNet/Sftp/Requests/SftpOpenRequest.cs +++ b/src/Renci.SshNet/Sftp/Requests/SftpOpenRequest.cs @@ -4,11 +4,11 @@ using Renci.SshNet.Sftp.Responses; namespace Renci.SshNet.Sftp.Requests { - internal class SftpOpenRequest : SftpRequest + internal sealed class SftpOpenRequest : SftpRequest { + private readonly Action _handleAction; private byte[] _fileName; private byte[] _attributes; - private readonly Action _handleAction; public override SftpMessageTypes SftpMessageType { @@ -21,7 +21,7 @@ namespace Renci.SshNet.Sftp.Requests private set { _fileName = Encoding.GetBytes(value); } } - public Flags Flags { get; private set; } + public Flags Flags { get; } public SftpFileAttributes Attributes { @@ -29,7 +29,7 @@ namespace Renci.SshNet.Sftp.Requests private set { _attributes = value.GetBytes(); } } - public Encoding Encoding { get; private set; } + public Encoding Encoding { get; } /// /// Gets the size of the message in bytes. @@ -83,8 +83,7 @@ namespace Renci.SshNet.Sftp.Requests public override void Complete(SftpResponse response) { - var handleResponse = response as SftpHandleResponse; - if (handleResponse != null) + if (response is SftpHandleResponse handleResponse) { _handleAction(handleResponse); } diff --git a/src/Renci.SshNet/Sftp/Requests/SftpReadDirRequest.cs b/src/Renci.SshNet/Sftp/Requests/SftpReadDirRequest.cs index 9d46c69b..9eae8325 100644 --- a/src/Renci.SshNet/Sftp/Requests/SftpReadDirRequest.cs +++ b/src/Renci.SshNet/Sftp/Requests/SftpReadDirRequest.cs @@ -1,9 +1,10 @@ using System; + using Renci.SshNet.Sftp.Responses; namespace Renci.SshNet.Sftp.Requests { - internal class SftpReadDirRequest : SftpRequest + internal sealed class SftpReadDirRequest : SftpRequest { private readonly Action _nameAction; @@ -53,8 +54,7 @@ namespace Renci.SshNet.Sftp.Requests public override void Complete(SftpResponse response) { - var nameResponse = response as SftpNameResponse; - if (nameResponse != null) + if (response is SftpNameResponse nameResponse) { _nameAction(nameResponse); } diff --git a/src/Renci.SshNet/Sftp/Requests/SftpReadLinkRequest.cs b/src/Renci.SshNet/Sftp/Requests/SftpReadLinkRequest.cs index 90e36b69..969eca56 100644 --- a/src/Renci.SshNet/Sftp/Requests/SftpReadLinkRequest.cs +++ b/src/Renci.SshNet/Sftp/Requests/SftpReadLinkRequest.cs @@ -1,13 +1,14 @@ using System; using System.Text; + using Renci.SshNet.Sftp.Responses; namespace Renci.SshNet.Sftp.Requests { - internal class SftpReadLinkRequest : SftpRequest + internal sealed class SftpReadLinkRequest : SftpRequest { - private byte[] _path; private readonly Action _nameAction; + private byte[] _path; public override SftpMessageTypes SftpMessageType { @@ -20,7 +21,7 @@ namespace Renci.SshNet.Sftp.Requests private set { _path = Encoding.GetBytes(value); } } - public Encoding Encoding { get; private set; } + public Encoding Encoding { get; } /// /// Gets the size of the message in bytes. @@ -64,8 +65,7 @@ namespace Renci.SshNet.Sftp.Requests public override void Complete(SftpResponse response) { - var nameResponse = response as SftpNameResponse; - if (nameResponse != null) + if (response is SftpNameResponse nameResponse) { _nameAction(nameResponse); } diff --git a/src/Renci.SshNet/Sftp/Requests/SftpReadRequest.cs b/src/Renci.SshNet/Sftp/Requests/SftpReadRequest.cs index 72dc9287..b413b4a3 100644 --- a/src/Renci.SshNet/Sftp/Requests/SftpReadRequest.cs +++ b/src/Renci.SshNet/Sftp/Requests/SftpReadRequest.cs @@ -1,9 +1,10 @@ using System; + using Renci.SshNet.Sftp.Responses; namespace Renci.SshNet.Sftp.Requests { - internal class SftpReadRequest : SftpRequest + internal sealed class SftpReadRequest : SftpRequest { private readonly Action _dataAction; @@ -37,7 +38,7 @@ namespace Renci.SshNet.Sftp.Requests } } - public SftpReadRequest(uint protocolVersion, uint requestId, byte[] handle, UInt64 offset, UInt32 length, Action dataAction, Action statusAction) + public SftpReadRequest(uint protocolVersion, uint requestId, byte[] handle, ulong offset, uint length, Action dataAction, Action statusAction) : base(protocolVersion, requestId, statusAction) { Handle = handle; @@ -49,6 +50,7 @@ namespace Renci.SshNet.Sftp.Requests protected override void LoadData() { base.LoadData(); + Handle = ReadBinary(); Offset = ReadUInt64(); Length = ReadUInt32(); @@ -57,6 +59,7 @@ namespace Renci.SshNet.Sftp.Requests protected override void SaveData() { base.SaveData(); + WriteBinaryString(Handle); Write(Offset); Write(Length); @@ -64,8 +67,7 @@ namespace Renci.SshNet.Sftp.Requests public override void Complete(SftpResponse response) { - var dataResponse = response as SftpDataResponse; - if (dataResponse != null) + if (response is SftpDataResponse dataResponse) { _dataAction(dataResponse); } diff --git a/src/Renci.SshNet/Sftp/Requests/SftpRealPathRequest.cs b/src/Renci.SshNet/Sftp/Requests/SftpRealPathRequest.cs index 44b11e90..d19497f0 100644 --- a/src/Renci.SshNet/Sftp/Requests/SftpRealPathRequest.cs +++ b/src/Renci.SshNet/Sftp/Requests/SftpRealPathRequest.cs @@ -1,13 +1,14 @@ using System; using System.Text; + using Renci.SshNet.Sftp.Responses; namespace Renci.SshNet.Sftp.Requests { - internal class SftpRealPathRequest : SftpRequest + internal sealed class SftpRealPathRequest : SftpRequest { - private byte[] _path; private readonly Action _nameAction; + private byte[] _path; public override SftpMessageTypes SftpMessageType { @@ -20,7 +21,7 @@ namespace Renci.SshNet.Sftp.Requests private set { _path = Encoding.GetBytes(value); } } - public Encoding Encoding { get; private set; } + public Encoding Encoding { get; } /// /// Gets the size of the message in bytes. @@ -42,12 +43,13 @@ namespace Renci.SshNet.Sftp.Requests public SftpRealPathRequest(uint protocolVersion, uint requestId, string path, Encoding encoding, Action nameAction, Action statusAction) : base(protocolVersion, requestId, statusAction) { - if (nameAction == null) - throw new ArgumentNullException("nameAction"); + if (nameAction is null) + { + throw new ArgumentNullException(nameof(nameAction)); + } Encoding = encoding; Path = path; - _nameAction = nameAction; } @@ -59,8 +61,7 @@ namespace Renci.SshNet.Sftp.Requests public override void Complete(SftpResponse response) { - var nameResponse = response as SftpNameResponse; - if (nameResponse != null) + if (response is SftpNameResponse nameResponse) { _nameAction(nameResponse); } diff --git a/src/Renci.SshNet/Sftp/Requests/SftpRemoveRequest.cs b/src/Renci.SshNet/Sftp/Requests/SftpRemoveRequest.cs index a527165c..99d072f5 100644 --- a/src/Renci.SshNet/Sftp/Requests/SftpRemoveRequest.cs +++ b/src/Renci.SshNet/Sftp/Requests/SftpRemoveRequest.cs @@ -4,7 +4,7 @@ using Renci.SshNet.Sftp.Responses; namespace Renci.SshNet.Sftp.Requests { - internal class SftpRemoveRequest : SftpRequest + internal sealed class SftpRemoveRequest : SftpRequest { private byte[] _fileName; diff --git a/src/Renci.SshNet/Sftp/Requests/SftpRenameRequest.cs b/src/Renci.SshNet/Sftp/Requests/SftpRenameRequest.cs index c6cf98f1..8d7fc91a 100644 --- a/src/Renci.SshNet/Sftp/Requests/SftpRenameRequest.cs +++ b/src/Renci.SshNet/Sftp/Requests/SftpRenameRequest.cs @@ -4,7 +4,7 @@ using Renci.SshNet.Sftp.Responses; namespace Renci.SshNet.Sftp.Requests { - internal class SftpRenameRequest : SftpRequest + internal sealed class SftpRenameRequest : SftpRequest { private byte[] _oldPath; private byte[] _newPath; diff --git a/src/Renci.SshNet/Sftp/Requests/SftpRequest.cs b/src/Renci.SshNet/Sftp/Requests/SftpRequest.cs index 88458e5f..a3e4d059 100644 --- a/src/Renci.SshNet/Sftp/Requests/SftpRequest.cs +++ b/src/Renci.SshNet/Sftp/Requests/SftpRequest.cs @@ -7,9 +7,9 @@ namespace Renci.SshNet.Sftp.Requests { private readonly Action _statusAction; - public uint RequestId { get; private set; } - - public uint ProtocolVersion { get; private set; } + public uint RequestId { get; } + + public uint ProtocolVersion { get; } /// /// Gets the size of the message in bytes. @@ -36,8 +36,7 @@ namespace Renci.SshNet.Sftp.Requests public virtual void Complete(SftpResponse response) { - var statusResponse = response as SftpStatusResponse; - if (statusResponse != null) + if (response is SftpStatusResponse statusResponse) { _statusAction(statusResponse); } @@ -55,6 +54,7 @@ namespace Renci.SshNet.Sftp.Requests protected override void SaveData() { base.SaveData(); + Write(RequestId); } } diff --git a/src/Renci.SshNet/Sftp/Requests/SftpRmDirRequest.cs b/src/Renci.SshNet/Sftp/Requests/SftpRmDirRequest.cs index 87c0de3d..003198af 100644 --- a/src/Renci.SshNet/Sftp/Requests/SftpRmDirRequest.cs +++ b/src/Renci.SshNet/Sftp/Requests/SftpRmDirRequest.cs @@ -4,7 +4,7 @@ using Renci.SshNet.Sftp.Responses; namespace Renci.SshNet.Sftp.Requests { - internal class SftpRmDirRequest : SftpRequest + internal sealed class SftpRmDirRequest : SftpRequest { private byte[] _path; diff --git a/src/Renci.SshNet/Sftp/Requests/SftpSetStatRequest.cs b/src/Renci.SshNet/Sftp/Requests/SftpSetStatRequest.cs index 3443968a..8731b9fc 100644 --- a/src/Renci.SshNet/Sftp/Requests/SftpSetStatRequest.cs +++ b/src/Renci.SshNet/Sftp/Requests/SftpSetStatRequest.cs @@ -4,7 +4,7 @@ using Renci.SshNet.Sftp.Responses; namespace Renci.SshNet.Sftp.Requests { - internal class SftpSetStatRequest : SftpRequest + internal sealed class SftpSetStatRequest : SftpRequest { private byte[] _path; private byte[] _attributesBytes; @@ -28,10 +28,8 @@ namespace Renci.SshNet.Sftp.Requests { get { - if (_attributesBytes == null) - { - _attributesBytes = Attributes.GetBytes(); - } + _attributesBytes ??= Attributes.GetBytes(); + return _attributesBytes; } } diff --git a/src/Renci.SshNet/Sftp/Requests/SftpStatRequest.cs b/src/Renci.SshNet/Sftp/Requests/SftpStatRequest.cs index 91e8823d..09657bff 100644 --- a/src/Renci.SshNet/Sftp/Requests/SftpStatRequest.cs +++ b/src/Renci.SshNet/Sftp/Requests/SftpStatRequest.cs @@ -4,10 +4,10 @@ using Renci.SshNet.Sftp.Responses; namespace Renci.SshNet.Sftp.Requests { - internal class SftpStatRequest : SftpRequest + internal sealed class SftpStatRequest : SftpRequest { - private byte[] _path; private readonly Action _attrsAction; + private byte[] _path; public override SftpMessageTypes SftpMessageType { @@ -20,7 +20,7 @@ namespace Renci.SshNet.Sftp.Requests private set { _path = Encoding.GetBytes(value); } } - public Encoding Encoding { get; private set; } + public Encoding Encoding { get; } /// /// Gets the size of the message in bytes. @@ -50,19 +50,20 @@ namespace Renci.SshNet.Sftp.Requests protected override void LoadData() { base.LoadData(); + _path = ReadBinary(); } protected override void SaveData() { base.SaveData(); + WriteBinaryString(_path); } public override void Complete(SftpResponse response) { - var attrsResponse = response as SftpAttrsResponse; - if (attrsResponse != null) + if (response is SftpAttrsResponse attrsResponse) { _attrsAction(attrsResponse); } diff --git a/src/Renci.SshNet/Sftp/Requests/SftpSymLinkRequest.cs b/src/Renci.SshNet/Sftp/Requests/SftpSymLinkRequest.cs index b338923e..63127cdd 100644 --- a/src/Renci.SshNet/Sftp/Requests/SftpSymLinkRequest.cs +++ b/src/Renci.SshNet/Sftp/Requests/SftpSymLinkRequest.cs @@ -4,7 +4,7 @@ using Renci.SshNet.Sftp.Responses; namespace Renci.SshNet.Sftp.Requests { - internal class SftpSymLinkRequest : SftpRequest + internal sealed class SftpSymLinkRequest : SftpRequest { private byte[] _newLinkPath; private byte[] _existingPath; diff --git a/src/Renci.SshNet/Sftp/Requests/SftpUnblockRequest.cs b/src/Renci.SshNet/Sftp/Requests/SftpUnblockRequest.cs index 7f4a2147..6dff3836 100644 --- a/src/Renci.SshNet/Sftp/Requests/SftpUnblockRequest.cs +++ b/src/Renci.SshNet/Sftp/Requests/SftpUnblockRequest.cs @@ -3,7 +3,7 @@ using Renci.SshNet.Sftp.Responses; namespace Renci.SshNet.Sftp.Requests { - internal class SftpUnblockRequest : SftpRequest + internal sealed class SftpUnblockRequest : SftpRequest { public override SftpMessageTypes SftpMessageType { @@ -35,7 +35,7 @@ namespace Renci.SshNet.Sftp.Requests } } - public SftpUnblockRequest(uint protocolVersion, uint requestId, byte[] handle, UInt64 offset, UInt64 length, Action statusAction) + public SftpUnblockRequest(uint protocolVersion, uint requestId, byte[] handle, ulong offset, ulong length, Action statusAction) : base(protocolVersion, requestId, statusAction) { Handle = handle; @@ -46,6 +46,7 @@ namespace Renci.SshNet.Sftp.Requests protected override void LoadData() { base.LoadData(); + Handle = ReadBinary(); Offset = ReadUInt64(); Length = ReadUInt64(); @@ -54,6 +55,7 @@ namespace Renci.SshNet.Sftp.Requests protected override void SaveData() { base.SaveData(); + WriteBinaryString(Handle); Write(Offset); Write(Length); diff --git a/src/Renci.SshNet/Sftp/Requests/SftpWriteRequest.cs b/src/Renci.SshNet/Sftp/Requests/SftpWriteRequest.cs index 6e8ca316..be7f6da9 100644 --- a/src/Renci.SshNet/Sftp/Requests/SftpWriteRequest.cs +++ b/src/Renci.SshNet/Sftp/Requests/SftpWriteRequest.cs @@ -3,7 +3,7 @@ using Renci.SshNet.Sftp.Responses; namespace Renci.SshNet.Sftp.Requests { - internal class SftpWriteRequest : SftpRequest + internal sealed class SftpWriteRequest : SftpRequest { public override SftpMessageTypes SftpMessageType { @@ -81,6 +81,7 @@ namespace Renci.SshNet.Sftp.Requests protected override void LoadData() { base.LoadData(); + Handle = ReadBinary(); ServerFileOffset = ReadUInt64(); Data = ReadBinary(); @@ -91,6 +92,7 @@ namespace Renci.SshNet.Sftp.Requests protected override void SaveData() { base.SaveData(); + WriteBinaryString(Handle); Write(ServerFileOffset); WriteBinary(Data, Offset, Length); diff --git a/src/Renci.SshNet/Sftp/Responses/ExtendedReplies/StatVfsReplyInfo.cs b/src/Renci.SshNet/Sftp/Responses/ExtendedReplies/StatVfsReplyInfo.cs index 71c45dc1..76876441 100644 --- a/src/Renci.SshNet/Sftp/Responses/ExtendedReplies/StatVfsReplyInfo.cs +++ b/src/Renci.SshNet/Sftp/Responses/ExtendedReplies/StatVfsReplyInfo.cs @@ -2,7 +2,7 @@ namespace Renci.SshNet.Sftp.Responses { - internal class StatVfsReplyInfo : ExtendedReplyInfo + internal sealed class StatVfsReplyInfo : ExtendedReplyInfo { public SftpFileSytemInformation Information { get; private set; } @@ -22,4 +22,4 @@ namespace Renci.SshNet.Sftp.Responses ); } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet/Sftp/Responses/SftpAttrsResponse.cs b/src/Renci.SshNet/Sftp/Responses/SftpAttrsResponse.cs index fbf3250e..f26aa5d3 100644 --- a/src/Renci.SshNet/Sftp/Responses/SftpAttrsResponse.cs +++ b/src/Renci.SshNet/Sftp/Responses/SftpAttrsResponse.cs @@ -1,6 +1,6 @@ namespace Renci.SshNet.Sftp.Responses { - internal class SftpAttrsResponse : SftpResponse + internal sealed class SftpAttrsResponse : SftpResponse { public override SftpMessageTypes SftpMessageType { @@ -17,6 +17,7 @@ protected override void LoadData() { base.LoadData(); + Attributes = ReadAttributes(); } } diff --git a/src/Renci.SshNet/Sftp/Responses/SftpDataResponse.cs b/src/Renci.SshNet/Sftp/Responses/SftpDataResponse.cs index f15a8dcc..ce0d414c 100644 --- a/src/Renci.SshNet/Sftp/Responses/SftpDataResponse.cs +++ b/src/Renci.SshNet/Sftp/Responses/SftpDataResponse.cs @@ -1,6 +1,6 @@ namespace Renci.SshNet.Sftp.Responses { - internal class SftpDataResponse : SftpResponse + internal sealed class SftpDataResponse : SftpResponse { public override SftpMessageTypes SftpMessageType { diff --git a/src/Renci.SshNet/Sftp/Responses/SftpExtendedReplyResponse.cs b/src/Renci.SshNet/Sftp/Responses/SftpExtendedReplyResponse.cs index 1a7048ec..bbd41680 100644 --- a/src/Renci.SshNet/Sftp/Responses/SftpExtendedReplyResponse.cs +++ b/src/Renci.SshNet/Sftp/Responses/SftpExtendedReplyResponse.cs @@ -1,6 +1,6 @@ namespace Renci.SshNet.Sftp.Responses { - internal class SftpExtendedReplyResponse : SftpResponse + internal sealed class SftpExtendedReplyResponse : SftpResponse { public override SftpMessageTypes SftpMessageType { diff --git a/src/Renci.SshNet/Sftp/Responses/SftpHandleResponse.cs b/src/Renci.SshNet/Sftp/Responses/SftpHandleResponse.cs index 233e3e37..31da2e93 100644 --- a/src/Renci.SshNet/Sftp/Responses/SftpHandleResponse.cs +++ b/src/Renci.SshNet/Sftp/Responses/SftpHandleResponse.cs @@ -1,6 +1,6 @@ namespace Renci.SshNet.Sftp.Responses { - internal class SftpHandleResponse : SftpResponse + internal sealed class SftpHandleResponse : SftpResponse { public override SftpMessageTypes SftpMessageType { diff --git a/src/Renci.SshNet/Sftp/Responses/SftpNameResponse.cs b/src/Renci.SshNet/Sftp/Responses/SftpNameResponse.cs index 3750119d..2bdf6789 100644 --- a/src/Renci.SshNet/Sftp/Responses/SftpNameResponse.cs +++ b/src/Renci.SshNet/Sftp/Responses/SftpNameResponse.cs @@ -1,10 +1,10 @@ -using Renci.SshNet.Common; +using System; using System.Collections.Generic; using System.Text; namespace Renci.SshNet.Sftp.Responses { - internal class SftpNameResponse : SftpResponse + internal sealed class SftpNameResponse : SftpResponse { public override SftpMessageTypes SftpMessageType { @@ -20,14 +20,14 @@ namespace Renci.SshNet.Sftp.Responses public SftpNameResponse(uint protocolVersion, Encoding encoding) : base(protocolVersion) { - Files = Array>.Empty; + Files = Array.Empty>(); Encoding = encoding; } protected override void LoadData() { base.LoadData(); - + Count = ReadUInt32(); Files = new KeyValuePair[Count]; @@ -36,8 +36,9 @@ namespace Renci.SshNet.Sftp.Responses var fileName = ReadString(Encoding); if (SupportsLongName(ProtocolVersion)) { - ReadString(Encoding); // skip longname + _ = ReadString(Encoding); // skip longname } + Files[i] = new KeyValuePair(fileName, ReadAttributes()); } } diff --git a/src/Renci.SshNet/Sftp/Responses/SftpStatusResponse.cs b/src/Renci.SshNet/Sftp/Responses/SftpStatusResponse.cs index f41692f0..2f43cdc3 100644 --- a/src/Renci.SshNet/Sftp/Responses/SftpStatusResponse.cs +++ b/src/Renci.SshNet/Sftp/Responses/SftpStatusResponse.cs @@ -1,6 +1,6 @@ namespace Renci.SshNet.Sftp.Responses { - internal class SftpStatusResponse : SftpResponse + internal sealed class SftpStatusResponse : SftpResponse { public override SftpMessageTypes SftpMessageType { diff --git a/src/Renci.SshNet/Sftp/Responses/SftpVersionResponse.cs b/src/Renci.SshNet/Sftp/Responses/SftpVersionResponse.cs index 87c9f13f..1ae8e6d6 100644 --- a/src/Renci.SshNet/Sftp/Responses/SftpVersionResponse.cs +++ b/src/Renci.SshNet/Sftp/Responses/SftpVersionResponse.cs @@ -2,7 +2,7 @@ namespace Renci.SshNet.Sftp.Responses { - internal class SftpVersionResponse : SftpMessage + internal sealed class SftpVersionResponse : SftpMessage { public override SftpMessageTypes SftpMessageType { @@ -16,6 +16,7 @@ namespace Renci.SshNet.Sftp.Responses protected override void LoadData() { base.LoadData(); + Version = ReadUInt32(); Extentions = ReadExtensionPair(); } @@ -25,8 +26,11 @@ namespace Renci.SshNet.Sftp.Responses base.SaveData(); Write(Version); + if (Extentions != null) + { Write(Extentions); + } } } } diff --git a/src/Renci.SshNet/Sftp/SFtpStatAsyncResult.cs b/src/Renci.SshNet/Sftp/SFtpStatAsyncResult.cs index d3776ce2..41d91549 100644 --- a/src/Renci.SshNet/Sftp/SFtpStatAsyncResult.cs +++ b/src/Renci.SshNet/Sftp/SFtpStatAsyncResult.cs @@ -1,11 +1,13 @@ -using Renci.SshNet.Common; -using System; +using System; + +using Renci.SshNet.Common; namespace Renci.SshNet.Sftp { - internal class SFtpStatAsyncResult : AsyncResult + internal sealed class SFtpStatAsyncResult : AsyncResult { - public SFtpStatAsyncResult(AsyncCallback asyncCallback, object state) : base(asyncCallback, state) + public SFtpStatAsyncResult(AsyncCallback asyncCallback, object state) + : base(asyncCallback, state) { } } diff --git a/src/Renci.SshNet/Sftp/SftpCloseAsyncResult.cs b/src/Renci.SshNet/Sftp/SftpCloseAsyncResult.cs index 6349371f..d4148e5c 100644 --- a/src/Renci.SshNet/Sftp/SftpCloseAsyncResult.cs +++ b/src/Renci.SshNet/Sftp/SftpCloseAsyncResult.cs @@ -1,11 +1,13 @@ -using Renci.SshNet.Common; -using System; +using System; + +using Renci.SshNet.Common; namespace Renci.SshNet.Sftp { - internal class SftpCloseAsyncResult : AsyncResult + internal sealed class SftpCloseAsyncResult : AsyncResult { - public SftpCloseAsyncResult(AsyncCallback asyncCallback, object state) : base(asyncCallback, state) + public SftpCloseAsyncResult(AsyncCallback asyncCallback, object state) + : base(asyncCallback, state) { } } diff --git a/src/Renci.SshNet/Sftp/SftpFile.cs b/src/Renci.SshNet/Sftp/SftpFile.cs index cfcc37a2..b5fe3af3 100644 --- a/src/Renci.SshNet/Sftp/SftpFile.cs +++ b/src/Renci.SshNet/Sftp/SftpFile.cs @@ -5,9 +5,9 @@ using Renci.SshNet.Common; namespace Renci.SshNet.Sftp { /// - /// Represents SFTP file information + /// Represents SFTP file information. /// - public class SftpFile + public sealed class SftpFile : ISftpFile { private readonly ISftpSession _sftpSession; @@ -25,32 +25,45 @@ namespace Renci.SshNet.Sftp /// or is null. internal SftpFile(ISftpSession sftpSession, string fullName, SftpFileAttributes attributes) { - if (sftpSession == null) + if (sftpSession is null) + { throw new SshConnectionException("Client not connected."); + } - if (attributes == null) - throw new ArgumentNullException("attributes"); + if (attributes is null) + { + throw new ArgumentNullException(nameof(attributes)); + } - if (fullName == null) - throw new ArgumentNullException("fullName"); + if (fullName is null) + { + throw new ArgumentNullException(nameof(fullName)); + } _sftpSession = sftpSession; Attributes = attributes; - Name = fullName.Substring(fullName.LastIndexOf('/') + 1); - FullName = fullName; } /// - /// Gets the full path of the directory or file. + /// Gets the full path of the file or directory. /// + /// + /// The full path of the file or directory. + /// public string FullName { get; private set; } /// - /// For files, gets the name of the file. For directories, gets the name of the last directory in the hierarchy if a hierarchy exists. - /// Otherwise, the Name property gets the name of the directory. + /// Gets the name of the file or directory. /// + /// + /// The name of the file or directory. + /// + /// + /// For directories, this is the name of the last directory in the hierarchy if a hierarchy exists; + /// otherwise, the name of the directory. + /// public string Name { get; private set; } /// @@ -126,7 +139,7 @@ namespace Renci.SshNet.Sftp } /// - /// Gets or sets the size, in bytes, of the current file. + /// Gets the size, in bytes, of the current file. /// /// /// The size of the current file in bytes. @@ -179,7 +192,7 @@ namespace Renci.SshNet.Sftp /// Gets a value indicating whether file represents a socket. /// /// - /// true if file represents a socket; otherwise, false. + /// true if file represents a socket; otherwise, false. /// public bool IsSocket { @@ -193,7 +206,7 @@ namespace Renci.SshNet.Sftp /// Gets a value indicating whether file represents a symbolic link. /// /// - /// true if file represents a symbolic link; otherwise, false. + /// true if file represents a symbolic link; otherwise, false. /// public bool IsSymbolicLink { @@ -207,7 +220,7 @@ namespace Renci.SshNet.Sftp /// Gets a value indicating whether file represents a regular file. /// /// - /// true if file represents a regular file; otherwise, false. + /// true if file represents a regular file; otherwise, false. /// public bool IsRegularFile { @@ -221,7 +234,7 @@ namespace Renci.SshNet.Sftp /// Gets a value indicating whether file represents a block device. /// /// - /// true if file represents a block device; otherwise, false. + /// true if file represents a block device; otherwise, false. /// public bool IsBlockDevice { @@ -235,7 +248,7 @@ namespace Renci.SshNet.Sftp /// Gets a value indicating whether file represents a directory. /// /// - /// true if file represents a directory; otherwise, false. + /// true if file represents a directory; otherwise, false. /// public bool IsDirectory { @@ -249,7 +262,7 @@ namespace Renci.SshNet.Sftp /// Gets a value indicating whether file represents a character device. /// /// - /// true if file represents a character device; otherwise, false. + /// true if file represents a character device; otherwise, false. /// public bool IsCharacterDevice { @@ -263,7 +276,7 @@ namespace Renci.SshNet.Sftp /// Gets a value indicating whether file represents a named pipe. /// /// - /// true if file represents a named pipe; otherwise, false. + /// true if file represents a named pipe; otherwise, false. /// public bool IsNamedPipe { @@ -277,7 +290,7 @@ namespace Renci.SshNet.Sftp /// Gets or sets a value indicating whether the owner can read from this file. /// /// - /// true if owner can read from this file; otherwise, false. + /// true if owner can read from this file; otherwise, false. /// public bool OwnerCanRead { @@ -295,7 +308,7 @@ namespace Renci.SshNet.Sftp /// Gets or sets a value indicating whether the owner can write into this file. /// /// - /// true if owner can write into this file; otherwise, false. + /// true if owner can write into this file; otherwise, false. /// public bool OwnerCanWrite { @@ -313,7 +326,7 @@ namespace Renci.SshNet.Sftp /// Gets or sets a value indicating whether the owner can execute this file. /// /// - /// true if owner can execute this file; otherwise, false. + /// true if owner can execute this file; otherwise, false. /// public bool OwnerCanExecute { @@ -331,7 +344,7 @@ namespace Renci.SshNet.Sftp /// Gets or sets a value indicating whether the group members can read from this file. /// /// - /// true if group members can read from this file; otherwise, false. + /// true if group members can read from this file; otherwise, false. /// public bool GroupCanRead { @@ -349,7 +362,7 @@ namespace Renci.SshNet.Sftp /// Gets or sets a value indicating whether the group members can write into this file. /// /// - /// true if group members can write into this file; otherwise, false. + /// true if group members can write into this file; otherwise, false. /// public bool GroupCanWrite { @@ -367,7 +380,7 @@ namespace Renci.SshNet.Sftp /// Gets or sets a value indicating whether the group members can execute this file. /// /// - /// true if group members can execute this file; otherwise, false. + /// true if group members can execute this file; otherwise, false. /// public bool GroupCanExecute { @@ -385,7 +398,7 @@ namespace Renci.SshNet.Sftp /// Gets or sets a value indicating whether the others can read from this file. /// /// - /// true if others can read from this file; otherwise, false. + /// true if others can read from this file; otherwise, false. /// public bool OthersCanRead { @@ -403,7 +416,7 @@ namespace Renci.SshNet.Sftp /// Gets or sets a value indicating whether the others can write into this file. /// /// - /// true if others can write into this file; otherwise, false. + /// true if others can write into this file; otherwise, false. /// public bool OthersCanWrite { @@ -421,7 +434,7 @@ namespace Renci.SshNet.Sftp /// Gets or sets a value indicating whether the others can execute this file. /// /// - /// true if others can execute this file; otherwise, false. + /// true if others can execute this file; otherwise, false. /// public bool OthersCanExecute { @@ -436,7 +449,7 @@ namespace Renci.SshNet.Sftp } /// - /// Sets file permissions. + /// Sets file permissions. /// /// The mode. public void SetPermissions(short mode) @@ -468,8 +481,11 @@ namespace Renci.SshNet.Sftp /// is null. public void MoveTo(string destFileName) { - if (destFileName == null) - throw new ArgumentNullException("destFileName"); + if (destFileName is null) + { + throw new ArgumentNullException(nameof(destFileName)); + } + _sftpSession.RequestRename(FullName, destFileName); var fullPath = _sftpSession.GetCanonicalPath(destFileName); @@ -488,14 +504,21 @@ namespace Renci.SshNet.Sftp } /// - /// Returns a that represents this instance. + /// Returns a that represents this instance. /// /// - /// A that represents this instance. + /// A that represents this instance. /// public override string ToString() { - return string.Format(CultureInfo.CurrentCulture, "Name {0}, Length {1}, User ID {2}, Group ID {3}, Accessed {4}, Modified {5}", Name, Length, UserId, GroupId, LastAccessTime, LastWriteTime); + return string.Format(CultureInfo.CurrentCulture, + "Name {0}, Length {1}, User ID {2}, Group ID {3}, Accessed {4}, Modified {5}", + Name, + Length, + UserId, + GroupId, + LastAccessTime, + LastWriteTime); } } } diff --git a/src/Renci.SshNet/Sftp/SftpFileAttributes.cs b/src/Renci.SshNet/Sftp/SftpFileAttributes.cs index 573deb92..e0cea9db 100644 --- a/src/Renci.SshNet/Sftp/SftpFileAttributes.cs +++ b/src/Renci.SshNet/Sftp/SftpFileAttributes.cs @@ -1,9 +1,9 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Globalization; +using System.Linq; + using Renci.SshNet.Common; -using System.Diagnostics; namespace Renci.SshNet.Sftp { @@ -12,54 +12,28 @@ namespace Renci.SshNet.Sftp /// public class SftpFileAttributes { - #region Bitmask constants - - private const uint S_IFMT = 0xF000; // bitmask for the file type bitfields - - private const uint S_IFSOCK = 0xC000; // socket - - private const uint S_IFLNK = 0xA000; // symbolic link - - private const uint S_IFREG = 0x8000; // regular file - - private const uint S_IFBLK = 0x6000; // block device - - private const uint S_IFDIR = 0x4000; // directory - - private const uint S_IFCHR = 0x2000; // character device - - private const uint S_IFIFO = 0x1000; // FIFO - - private const uint S_ISUID = 0x0800; // set UID bit - - private const uint S_ISGID = 0x0400; // set-group-ID bit (see below) - - private const uint S_ISVTX = 0x0200; // sticky bit (see below) - - private const uint S_IRUSR = 0x0100; // owner has read permission - - private const uint S_IWUSR = 0x0080; // owner has write permission - - private const uint S_IXUSR = 0x0040; // owner has execute permission - - private const uint S_IRGRP = 0x0020; // group has read permission - - private const uint S_IWGRP = 0x0010; // group has write permission - - private const uint S_IXGRP = 0x0008; // group has execute permission - - private const uint S_IROTH = 0x0004; // others have read permission - - private const uint S_IWOTH = 0x0002; // others have write permission - - private const uint S_IXOTH = 0x0001; // others have execute permission - - #endregion - - private bool _isBitFiledsBitSet; - private bool _isUIDBitSet; - private bool _isGroupIDBitSet; - private bool _isStickyBitSet; +#pragma warning disable IDE1006 // Naming Styles + private const uint S_IFMT = 0xF000; // bitmask for the file type bitfields + private const uint S_IFSOCK = 0xC000; // socket + private const uint S_IFLNK = 0xA000; // symbolic link + private const uint S_IFREG = 0x8000; // regular file + private const uint S_IFBLK = 0x6000; // block device + private const uint S_IFDIR = 0x4000; // directory + private const uint S_IFCHR = 0x2000; // character device + private const uint S_IFIFO = 0x1000; // FIFO + private const uint S_ISUID = 0x0800; // set UID bit + private const uint S_ISGID = 0x0400; // set-group-ID bit (see below) + private const uint S_ISVTX = 0x0200; // sticky bit (see below) + private const uint S_IRUSR = 0x0100; // owner has read permission + private const uint S_IWUSR = 0x0080; // owner has write permission + private const uint S_IXUSR = 0x0040; // owner has execute permission + private const uint S_IRGRP = 0x0020; // group has read permission + private const uint S_IWGRP = 0x0010; // group has write permission + private const uint S_IXGRP = 0x0008; // group has execute permission + private const uint S_IROTH = 0x0004; // others have read permission + private const uint S_IWOTH = 0x0002; // others have write permission + private const uint S_IXOTH = 0x0001; // others have execute permission +#pragma warning restore IDE1006 // Naming Styles private readonly DateTime _originalLastAccessTimeUtc; private readonly DateTime _originalLastWriteTimeUtc; @@ -69,6 +43,11 @@ namespace Renci.SshNet.Sftp private readonly uint _originalPermissions; private readonly IDictionary _originalExtensions; + private bool _isBitFiledsBitSet; + private bool _isUIDBitSet; + private bool _isGroupIDBitSet; + private bool _isStickyBitSet; + internal bool IsLastAccessTimeChanged { get { return _originalLastAccessTimeUtc != LastAccessTimeUtc; } @@ -114,12 +93,12 @@ namespace Renci.SshNet.Sftp { get { - return ToLocalTime(this.LastAccessTimeUtc); + return ToLocalTime(LastAccessTimeUtc); } set { - this.LastAccessTimeUtc = ToUniversalTime(value); + LastAccessTimeUtc = ToUniversalTime(value); } } @@ -133,12 +112,12 @@ namespace Renci.SshNet.Sftp { get { - return ToLocalTime(this.LastWriteTimeUtc); + return ToLocalTime(LastWriteTimeUtc); } set { - this.LastWriteTimeUtc = ToUniversalTime(value); + LastWriteTimeUtc = ToUniversalTime(value); } } @@ -194,7 +173,7 @@ namespace Renci.SshNet.Sftp /// Gets a value indicating whether file represents a symbolic link. /// /// - /// true if file represents a symbolic link; otherwise, false. + /// true if file represents a symbolic link; otherwise, false. /// public bool IsSymbolicLink { get; private set; } @@ -202,7 +181,7 @@ namespace Renci.SshNet.Sftp /// Gets a value indicating whether file represents a regular file. /// /// - /// true if file represents a regular file; otherwise, false. + /// true if file represents a regular file; otherwise, false. /// public bool IsRegularFile { get; private set; } @@ -210,7 +189,7 @@ namespace Renci.SshNet.Sftp /// Gets a value indicating whether file represents a block device. /// /// - /// true if file represents a block device; otherwise, false. + /// true if file represents a block device; otherwise, false. /// public bool IsBlockDevice { get; private set; } @@ -218,7 +197,7 @@ namespace Renci.SshNet.Sftp /// Gets a value indicating whether file represents a directory. /// /// - /// true if file represents a directory; otherwise, false. + /// true if file represents a directory; otherwise, false. /// public bool IsDirectory { get; private set; } @@ -226,7 +205,7 @@ namespace Renci.SshNet.Sftp /// Gets a value indicating whether file represents a character device. /// /// - /// true if file represents a character device; otherwise, false. + /// true if file represents a character device; otherwise, false. /// public bool IsCharacterDevice { get; private set; } @@ -234,84 +213,84 @@ namespace Renci.SshNet.Sftp /// Gets a value indicating whether file represents a named pipe. /// /// - /// true if file represents a named pipe; otherwise, false. + /// true if file represents a named pipe; otherwise, false. /// public bool IsNamedPipe { get; private set; } /// - /// Gets a value indicating whether the owner can read from this file. + /// Gets or sets a value indicating whether the owner can read from this file. /// /// - /// true if owner can read from this file; otherwise, false. + /// true if owner can read from this file; otherwise, false. /// public bool OwnerCanRead { get; set; } /// - /// Gets a value indicating whether the owner can write into this file. + /// Gets or sets a value indicating whether the owner can write into this file. /// /// - /// true if owner can write into this file; otherwise, false. + /// true if owner can write into this file; otherwise, false. /// public bool OwnerCanWrite { get; set; } /// - /// Gets a value indicating whether the owner can execute this file. + /// Gets or sets a value indicating whether the owner can execute this file. /// /// - /// true if owner can execute this file; otherwise, false. + /// true if owner can execute this file; otherwise, false. /// public bool OwnerCanExecute { get; set; } /// - /// Gets a value indicating whether the group members can read from this file. + /// Gets or sets a value indicating whether the group members can read from this file. /// /// - /// true if group members can read from this file; otherwise, false. + /// true if group members can read from this file; otherwise, false. /// public bool GroupCanRead { get; set; } /// - /// Gets a value indicating whether the group members can write into this file. + /// Gets or sets a value indicating whether the group members can write into this file. /// /// - /// true if group members can write into this file; otherwise, false. + /// true if group members can write into this file; otherwise, false. /// public bool GroupCanWrite { get; set; } /// - /// Gets a value indicating whether the group members can execute this file. + /// Gets or sets a value indicating whether the group members can execute this file. /// /// - /// true if group members can execute this file; otherwise, false. + /// true if group members can execute this file; otherwise, false. /// public bool GroupCanExecute { get; set; } /// - /// Gets a value indicating whether the others can read from this file. + /// Gets or sets a value indicating whether the others can read from this file. /// /// - /// true if others can read from this file; otherwise, false. + /// true if others can read from this file; otherwise, false. /// public bool OthersCanRead { get; set; } /// - /// Gets a value indicating whether the others can write into this file. + /// Gets or sets a value indicating whether the others can write into this file. /// /// - /// true if others can write into this file; otherwise, false. + /// true if others can write into this file; otherwise, false. /// public bool OthersCanWrite { get; set; } /// - /// Gets a value indicating whether the others can execute this file. + /// Gets or sets a value indicating whether the others can execute this file. /// /// - /// true if others can execute this file; otherwise, false. + /// true if others can execute this file; otherwise, false. /// public bool OthersCanExecute { get; set; } /// - /// Gets or sets the extensions. + /// Gets the extensions. /// /// /// The extensions. @@ -325,108 +304,148 @@ namespace Renci.SshNet.Sftp uint permission = 0; if (_isBitFiledsBitSet) - permission = permission | S_IFMT; + { + permission |= S_IFMT; + } if (IsSocket) - permission = permission | S_IFSOCK; + { + permission |= S_IFSOCK; + } if (IsSymbolicLink) - permission = permission | S_IFLNK; + { + permission |= S_IFLNK; + } if (IsRegularFile) - permission = permission | S_IFREG; + { + permission |= S_IFREG; + } if (IsBlockDevice) - permission = permission | S_IFBLK; + { + permission |= S_IFBLK; + } if (IsDirectory) - permission = permission | S_IFDIR; + { + permission |= S_IFDIR; + } if (IsCharacterDevice) - permission = permission | S_IFCHR; + { + permission |= S_IFCHR; + } if (IsNamedPipe) - permission = permission | S_IFIFO; + { + permission |= S_IFIFO; + } if (_isUIDBitSet) - permission = permission | S_ISUID; + { + permission |= S_ISUID; + } if (_isGroupIDBitSet) - permission = permission | S_ISGID; + { + permission |= S_ISGID; + } if (_isStickyBitSet) - permission = permission | S_ISVTX; + { + permission |= S_ISVTX; + } if (OwnerCanRead) - permission = permission | S_IRUSR; + { + permission |= S_IRUSR; + } if (OwnerCanWrite) - permission = permission | S_IWUSR; + { + permission |= S_IWUSR; + } if (OwnerCanExecute) - permission = permission | S_IXUSR; + { + permission |= S_IXUSR; + } if (GroupCanRead) - permission = permission | S_IRGRP; + { + permission |= S_IRGRP; + } if (GroupCanWrite) - permission = permission | S_IWGRP; + { + permission |= S_IWGRP; + } if (GroupCanExecute) - permission = permission | S_IXGRP; + { + permission |= S_IXGRP; + } if (OthersCanRead) - permission = permission | S_IROTH; + { + permission |= S_IROTH; + } if (OthersCanWrite) - permission = permission | S_IWOTH; + { + permission |= S_IWOTH; + } if (OthersCanExecute) - permission = permission | S_IXOTH; + { + permission |= S_IXOTH; + } return permission; } private set { - _isBitFiledsBitSet = ((value & S_IFMT) == S_IFMT); + _isBitFiledsBitSet = (value & S_IFMT) == S_IFMT; - IsSocket = ((value & S_IFSOCK) == S_IFSOCK); + IsSocket = (value & S_IFSOCK) == S_IFSOCK; - IsSymbolicLink = ((value & S_IFLNK) == S_IFLNK); + IsSymbolicLink = (value & S_IFLNK) == S_IFLNK; - IsRegularFile = ((value & S_IFREG) == S_IFREG); + IsRegularFile = (value & S_IFREG) == S_IFREG; - IsBlockDevice = ((value & S_IFBLK) == S_IFBLK); + IsBlockDevice = (value & S_IFBLK) == S_IFBLK; - IsDirectory = ((value & S_IFDIR) == S_IFDIR); + IsDirectory = (value & S_IFDIR) == S_IFDIR; - IsCharacterDevice = ((value & S_IFCHR) == S_IFCHR); + IsCharacterDevice = (value & S_IFCHR) == S_IFCHR; - IsNamedPipe = ((value & S_IFIFO) == S_IFIFO); + IsNamedPipe = (value & S_IFIFO) == S_IFIFO; - _isUIDBitSet = ((value & S_ISUID) == S_ISUID); + _isUIDBitSet = (value & S_ISUID) == S_ISUID; - _isGroupIDBitSet = ((value & S_ISGID) == S_ISGID); + _isGroupIDBitSet = (value & S_ISGID) == S_ISGID; - _isStickyBitSet = ((value & S_ISVTX) == S_ISVTX); + _isStickyBitSet = (value & S_ISVTX) == S_ISVTX; - OwnerCanRead = ((value & S_IRUSR) == S_IRUSR); + OwnerCanRead = (value & S_IRUSR) == S_IRUSR; - OwnerCanWrite = ((value & S_IWUSR) == S_IWUSR); + OwnerCanWrite = (value & S_IWUSR) == S_IWUSR; - OwnerCanExecute = ((value & S_IXUSR) == S_IXUSR); + OwnerCanExecute = (value & S_IXUSR) == S_IXUSR; - GroupCanRead = ((value & S_IRGRP) == S_IRGRP); + GroupCanRead = (value & S_IRGRP) == S_IRGRP; - GroupCanWrite = ((value & S_IWGRP) == S_IWGRP); + GroupCanWrite = (value & S_IWGRP) == S_IWGRP; - GroupCanExecute = ((value & S_IXGRP) == S_IXGRP); + GroupCanExecute = (value & S_IXGRP) == S_IXGRP; - OthersCanRead = ((value & S_IROTH) == S_IROTH); + OthersCanRead = (value & S_IROTH) == S_IROTH; - OthersCanWrite = ((value & S_IWOTH) == S_IWOTH); + OthersCanWrite = (value & S_IWOTH) == S_IWOTH; - OthersCanExecute = ((value & S_IXOTH) == S_IXOTH); + OthersCanExecute = (value & S_IXOTH) == S_IXOTH; } } @@ -451,9 +470,9 @@ namespace Renci.SshNet.Sftp /// The mode. public void SetPermissions(short mode) { - if (mode < 0 || mode > 999) + if (mode is < 0 or > 999) { - throw new ArgumentOutOfRangeException("mode"); + throw new ArgumentOutOfRangeException(nameof(mode)); } var modeBytes = mode.ToString(CultureInfo.InvariantCulture).PadLeft(3, '0').ToCharArray(); @@ -562,26 +581,26 @@ namespace Renci.SshNet.Sftp uint permissions = 0; DateTime accessTime; DateTime modifyTime; - IDictionary extensions = null; + Dictionary extensions = null; - if ((flag & 0x00000001) == 0x00000001) // SSH_FILEXFER_ATTR_SIZE + if ((flag & 0x00000001) == 0x00000001) // SSH_FILEXFER_ATTR_SIZE { size = (long) stream.ReadUInt64(); } - if ((flag & 0x00000002) == 0x00000002) // SSH_FILEXFER_ATTR_UIDGID + if ((flag & 0x00000002) == 0x00000002) // SSH_FILEXFER_ATTR_UIDGID { userId = (int) stream.ReadUInt32(); groupId = (int) stream.ReadUInt32(); } - if ((flag & 0x00000004) == 0x00000004) // SSH_FILEXFER_ATTR_PERMISSIONS + if ((flag & 0x00000004) == 0x00000004) // SSH_FILEXFER_ATTR_PERMISSIONS { permissions = stream.ReadUInt32(); } - if ((flag & 0x00000008) == 0x00000008) // SSH_FILEXFER_ATTR_ACMODTIME + if ((flag & 0x00000008) == 0x00000008) // SSH_FILEXFER_ATTR_ACMODTIME { // The incoming times are "Unix times", so they're already in UTC. We need to preserve that // to avoid losing information in a local time conversion during the "fall back" hour in DST. @@ -596,7 +615,7 @@ namespace Renci.SshNet.Sftp modifyTime = DateTime.SpecifyKind(DateTime.MinValue, DateTimeKind.Utc); } - if ((flag & 0x80000000) == 0x80000000) // SSH_FILEXFER_ATTR_EXTENDED + if ((flag & 0x80000000) == 0x80000000) // SSH_FILEXFER_ATTR_EXTENDED { var extendedCount = (int) stream.ReadUInt32(); extensions = new Dictionary(extendedCount); diff --git a/src/Renci.SshNet/Sftp/SftpFileReader.cs b/src/Renci.SshNet/Sftp/SftpFileReader.cs index 3aed4f53..c28dd8ac 100644 --- a/src/Renci.SshNet/Sftp/SftpFileReader.cs +++ b/src/Renci.SshNet/Sftp/SftpFileReader.cs @@ -1,55 +1,58 @@ -using Renci.SshNet.Abstractions; -using Renci.SshNet.Common; -using System; +using System; using System.Collections.Generic; using System.Globalization; +using System.Runtime.ExceptionServices; using System.Threading; +using Renci.SshNet.Abstractions; +using Renci.SshNet.Common; + namespace Renci.SshNet.Sftp { - internal class SftpFileReader : ISftpFileReader + internal sealed class SftpFileReader : ISftpFileReader { private const int ReadAheadWaitTimeoutInMilliseconds = 1000; private readonly byte[] _handle; private readonly ISftpSession _sftpSession; private readonly uint _chunkSize; - private ulong _offset; + private readonly SemaphoreLight _semaphore; + private readonly object _readLock; + private readonly ManualResetEvent _disposingWaitHandle; + private readonly ManualResetEvent _readAheadCompleted; + private readonly Dictionary _queue; + private readonly WaitHandle[] _waitHandles; /// /// Holds the size of the file, when available. /// private readonly long? _fileSize; - private readonly Dictionary _queue; - private readonly WaitHandle[] _waitHandles; + private ulong _offset; private int _readAheadChunkIndex; private ulong _readAheadOffset; - private readonly ManualResetEvent _readAheadCompleted; private int _nextChunkIndex; /// /// Holds a value indicating whether EOF has already been signaled by the SSH server. /// private bool _endOfFileReceived; + /// /// Holds a value indicating whether the client has read up to the end of the file. /// private bool _isEndOfFileRead; - private readonly SemaphoreLight _semaphore; - private readonly object _readLock; - private readonly ManualResetEvent _disposingWaitHandle; private bool _disposingOrDisposed; private Exception _exception; /// - /// Initializes a new instance with the specified handle, + /// Initializes a new instance of the class with the specified handle, /// and the maximum number of pending reads. /// - /// - /// + /// The file handle. + /// The SFT session. /// The size of a individual read-ahead chunk. /// The maximum number of pending reads. /// The size of the file, if known; otherwise, null. @@ -62,8 +65,8 @@ namespace Renci.SshNet.Sftp _semaphore = new SemaphoreLight(maxPendingReads); _queue = new Dictionary(maxPendingReads); _readLock = new object(); - _readAheadCompleted = new ManualResetEvent(false); - _disposingWaitHandle = new ManualResetEvent(false); + _readAheadCompleted = new ManualResetEvent(initialState: false); + _disposingWaitHandle = new ManualResetEvent(initialState: false); _waitHandles = _sftpSession.CreateWaitHandleArray(_disposingWaitHandle, _semaphore.AvailableWaitHandle); StartReadAhead(); @@ -72,26 +75,36 @@ namespace Renci.SshNet.Sftp public byte[] Read() { if (_disposingOrDisposed) + { throw new ObjectDisposedException(GetType().FullName); - if (_exception != null) - throw _exception; + } + + if (_exception is not null) + { + ExceptionDispatchInfo.Capture(_exception).Throw(); + } + if (_isEndOfFileRead) + { throw new SshException("Attempting to read beyond the end of the file."); + } BufferedRead nextChunk; lock (_readLock) { - // wait until either the next chunk is avalable, an exception has occurred or the current + // wait until either the next chunk is available, an exception has occurred or the current // instance is already disposed - while (!_queue.TryGetValue(_nextChunkIndex, out nextChunk) && _exception == null) + while (!_queue.TryGetValue(_nextChunkIndex, out nextChunk) && _exception is null) { - Monitor.Wait(_readLock); + _ =Monitor.Wait(_readLock); } // throw when exception occured in read-ahead, or the current instance is already disposed if (_exception != null) - throw _exception; + { + ExceptionDispatchInfo.Capture(_exception).Throw(); + } var data = nextChunk.Data; @@ -101,51 +114,57 @@ namespace Renci.SshNet.Sftp if (data.Length == 0) { // PERF: we do not bother updating all of the internal state when we've reached EOF - _isEndOfFileRead = true; } else { // remove processed chunk - _queue.Remove(_nextChunkIndex); + _ = _queue.Remove(_nextChunkIndex); + // update offset _offset += (ulong) data.Length; + // move to next chunk _nextChunkIndex++; } + // unblock wait in read-ahead - _semaphore.Release(); + _ = _semaphore.Release(); return data; } - // when we received an EOF for the next chunk and the size of the file is known, then - // we only complete the current chunk if we haven't already read up to the file size; - // this way we save an extra round-trip to the server + // When we received an EOF for the next chunk and the size of the file is known, then + // we only complete the current chunk if we haven't already read up to the file size. + // This way we save an extra round-trip to the server. if (data.Length == 0 && _fileSize.HasValue && _offset == (ulong) _fileSize.Value) { // avoid future reads _isEndOfFileRead = true; + // unblock wait in read-ahead - _semaphore.Release(); + _ = _semaphore.Release(); + // signal EOF to caller return nextChunk.Data; } } - // When the server returned less bytes than requested (for the previous chunk) - // we'll synchronously request the remaining data. - // - // Due to the optimization above, we'll only get here in one of the following cases: - // - an EOF situation for files for which we were unable to obtain the file size - // - fewer bytes that requested were returned - // - // According to the SSH specification, this last case should never happen for normal - // disk files (but can happen for device files). In practice, OpenSSH - for example - - // returns less bytes than requested when requesting more than 64 KB. - // - // Important: - // To avoid a deadlock, this read must be done outside of the read lock + /* + * When the server returned less bytes than requested (for the previous chunk) + * we'll synchronously request the remaining data. + * + * Due to the optimization above, we'll only get here in one of the following cases: + * - an EOF situation for files for which we were unable to obtain the file size + * - fewer bytes that requested were returned + * + * According to the SSH specification, this last case should never happen for normal + * disk files (but can happen for device files). In practice, OpenSSH - for example - + * returns less bytes than requested when requesting more than 64 KB. + * + * Important: + * To avoid a deadlock, this read must be done outside of the read lock. + */ var bytesToCatchUp = nextChunk.Offset - _offset; @@ -162,24 +181,28 @@ namespace Renci.SshNet.Sftp if (nextChunk.Data.Length == 0) { _isEndOfFileRead = true; + // ensure we've not yet disposed the current instance if (!_disposingOrDisposed) { // unblock wait in read-ahead - _semaphore.Release(); + _ = _semaphore.Release(); } + // signal EOF to caller return read; } // move reader to error state _exception = new SshException("Unexpectedly reached end of file."); + // ensure we've not yet disposed the current instance if (!_disposingOrDisposed) { // unblock wait in read-ahead - _semaphore.Release(); + _ = _semaphore.Release(); } + // notify caller of error throw _exception; } @@ -192,23 +215,25 @@ namespace Renci.SshNet.Sftp ~SftpFileReader() { - Dispose(false); + Dispose(disposing: false); } public void Dispose() { - Dispose(true); + Dispose(disposing: true); GC.SuppressFinalize(this); } /// - /// Releases unmanaged and - optionally - managed resources + /// Releases unmanaged and - optionally - managed resources. /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. - protected void Dispose(bool disposing) + private void Dispose(bool disposing) { if (_disposingOrDisposed) + { return; + } // transition to disposing state _disposingOrDisposed = true; @@ -219,10 +244,10 @@ namespace Renci.SshNet.Sftp _exception = new ObjectDisposedException(GetType().FullName); // signal that we're disposing to interrupt wait in read-ahead - _disposingWaitHandle.Set(); + _ = _disposingWaitHandle.Set(); // wait until the read-ahead thread has completed - _readAheadCompleted.WaitOne(); + _ = _readAheadCompleted.WaitOne(); // unblock the Read() lock (_readLock) @@ -230,6 +255,7 @@ namespace Renci.SshNet.Sftp // dispose semaphore in read lock to ensure we don't run into an ObjectDisposedException // in Read() _semaphore.Dispose(); + // awake Read Monitor.PulseAll(_readLock); } @@ -241,7 +267,7 @@ namespace Renci.SshNet.Sftp { try { - var closeAsyncResult = _sftpSession.BeginClose(_handle, null, null); + var closeAsyncResult = _sftpSession.BeginClose(_handle, callback: null, state: null); _sftpSession.EndClose(closeAsyncResult); } catch (Exception ex) @@ -256,7 +282,7 @@ namespace Renci.SshNet.Sftp { ThreadAbstraction.ExecuteThread(() => { - while (!_endOfFileReceived && _exception == null) + while (!_endOfFileReceived && _exception is null) { // check if we should continue with the read-ahead loop // note that the EOF and exception check are not included @@ -269,6 +295,7 @@ namespace Renci.SshNet.Sftp { Monitor.PulseAll(_readLock); } + // break the read-ahead loop break; } @@ -285,7 +312,9 @@ namespace Renci.SshNet.Sftp // don't bother reading any more chunks if we received EOF, an exception has occurred // or the current instance is disposed if (_endOfFileReceived || _exception != null) + { break; + } // start reading next chunk var bufferedRead = new BufferedRead(_readAheadChunkIndex, _readAheadOffset); @@ -301,14 +330,13 @@ namespace Renci.SshNet.Sftp // mode to avoid having multiple read-aheads that read beyond EOF if (_fileSize != null && (long) _readAheadOffset > _fileSize.Value) { - var asyncResult = _sftpSession.BeginRead(_handle, _readAheadOffset, _chunkSize, null, - bufferedRead); + var asyncResult = _sftpSession.BeginRead(_handle, _readAheadOffset, _chunkSize, callback: null, bufferedRead); var data = _sftpSession.EndRead(asyncResult); ReadCompletedCore(bufferedRead, data); } else { - _sftpSession.BeginRead(_handle, _readAheadOffset, _chunkSize, ReadCompleted, bufferedRead); + _ = _sftpSession.BeginRead(_handle, _readAheadOffset, _chunkSize, ReadCompleted, bufferedRead); } } catch (Exception ex) @@ -319,11 +347,12 @@ namespace Renci.SshNet.Sftp // advance read-ahead offset _readAheadOffset += _chunkSize; + // increment index of read-ahead chunk _readAheadChunkIndex++; } - _readAheadCompleted.Set(); + _ = _readAheadCompleted.Set(); }); } @@ -350,7 +379,7 @@ namespace Renci.SshNet.Sftp } catch (Exception ex) { - Interlocked.CompareExchange(ref _exception, ex, null); + _ = Interlocked.CompareExchange(ref _exception, ex, comparand: null); return false; } } @@ -392,8 +421,9 @@ namespace Renci.SshNet.Sftp { // add item to queue _queue.Add(bufferedRead.ChunkIndex, bufferedRead); - // signal that a chunk has been read or EOF has been reached; - // in both cases, Read() will eventually also unblock the "read-ahead" thread + + // Signal that a chunk has been read or EOF has been reached. + // In both cases, Read() will eventually also unblock the "read-ahead" thread. Monitor.PulseAll(_readLock); } @@ -407,10 +437,10 @@ namespace Renci.SshNet.Sftp private void HandleFailure(Exception cause) { - Interlocked.CompareExchange(ref _exception, cause, null); + _ = Interlocked.CompareExchange(ref _exception, cause, comparand: null); // unblock read-ahead - _semaphore.Release(); + _ = _semaphore.Release(); // unblock Read() lock (_readLock) @@ -419,13 +449,13 @@ namespace Renci.SshNet.Sftp } } - internal class BufferedRead + internal sealed class BufferedRead { - public int ChunkIndex { get; private set; } + public int ChunkIndex { get; } public byte[] Data { get; private set; } - public ulong Offset { get; private set; } + public ulong Offset { get; } public BufferedRead(int chunkIndex, ulong offset) { diff --git a/src/Renci.SshNet/Sftp/SftpFileStream.cs b/src/Renci.SshNet/Sftp/SftpFileStream.cs index 8f51250c..a17d5b8e 100644 --- a/src/Renci.SshNet/Sftp/SftpFileStream.cs +++ b/src/Renci.SshNet/Sftp/SftpFileStream.cs @@ -1,7 +1,9 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Threading; -using System.Diagnostics.CodeAnalysis; +using System.Threading.Tasks; + using Renci.SshNet.Common; namespace Renci.SshNet.Sftp @@ -9,17 +11,19 @@ namespace Renci.SshNet.Sftp /// /// Exposes a around a remote SFTP file, supporting both synchronous and asynchronous read and write operations. /// + /// public class SftpFileStream : Stream { - // TODO: Add security method to set userid, groupid and other permission settings + private readonly object _lock = new object(); + private readonly int _readBufferSize; + private readonly int _writeBufferSize; + // Internal state. private byte[] _handle; private ISftpSession _session; // Buffer information. - private readonly int _readBufferSize; private byte[] _readBuffer; - private readonly int _writeBufferSize; private byte[] _writeBuffer; private int _bufferPosition; private int _bufferLen; @@ -29,8 +33,6 @@ namespace Renci.SshNet.Sftp private bool _canSeek; private bool _canWrite; - private readonly object _lock = new object(); - /// /// Gets a value indicating whether the current stream supports reading. /// @@ -65,7 +67,7 @@ namespace Renci.SshNet.Sftp } /// - /// Indicates whether timeout properties are usable for . + /// Gets a value indicating whether timeout properties are usable for . /// /// /// true in all cases. @@ -93,7 +95,9 @@ namespace Renci.SshNet.Sftp CheckSessionIsOpen(); if (!CanSeek) + { throw new NotSupportedException("Seek operation is not supported."); + } // Flush the write buffer, because it may // affect the length of the stream. @@ -103,11 +107,12 @@ namespace Renci.SshNet.Sftp } // obtain file attributes - var attributes = _session.RequestFStat(_handle, true); + var attributes = _session.RequestFStat(_handle, nullOnError: true); if (attributes != null) { return attributes.Size; } + throw new IOException("Seek operation failed."); } } @@ -125,13 +130,17 @@ namespace Renci.SshNet.Sftp get { CheckSessionIsOpen(); + if (!CanSeek) + { throw new NotSupportedException("Seek operation not supported."); + } + return _position; } set { - Seek(value, SeekOrigin.Begin); + _ = Seek(value, SeekOrigin.Begin); } } @@ -166,14 +175,46 @@ namespace Renci.SshNet.Sftp /// public TimeSpan Timeout { get; set; } + private SftpFileStream(ISftpSession session, string path, FileAccess access, int bufferSize, byte[] handle, long position) + { + Timeout = TimeSpan.FromSeconds(30); + Name = path; + + _session = session; + _canRead = (access & FileAccess.Read) != 0; + _canSeek = true; + _canWrite = (access & FileAccess.Write) != 0; + + _handle = handle; + + /* + * Instead of using the specified buffer size as is, we use it to calculate a buffer size + * that ensures we always receive or send the max. number of bytes in a single SSH_FXP_READ + * or SSH_FXP_WRITE message. + */ + + _readBufferSize = (int) session.CalculateOptimalReadLength((uint) bufferSize); + _writeBufferSize = (int) session.CalculateOptimalWriteLength((uint) bufferSize, _handle); + + _position = position; + } + internal SftpFileStream(ISftpSession session, string path, FileMode mode, FileAccess access, int bufferSize) { - if (session == null) + if (session is null) + { throw new SshConnectionException("Client not connected."); - if (path == null) - throw new ArgumentNullException("path"); + } + + if (path is null) + { + throw new ArgumentNullException(nameof(path)); + } + if (bufferSize <= 0) - throw new ArgumentOutOfRangeException("bufferSize"); + { + throw new ArgumentOutOfRangeException(nameof(bufferSize), "Cannot be less than or equal to zero."); + } Timeout = TimeSpan.FromSeconds(30); Name = path; @@ -199,7 +240,7 @@ namespace Renci.SshNet.Sftp flags |= Flags.Write; break; default: - throw new ArgumentOutOfRangeException("access"); + throw new ArgumentOutOfRangeException(nameof(access)); } if ((access & FileAccess.Read) != 0 && mode == FileMode.Append) @@ -209,13 +250,13 @@ namespace Renci.SshNet.Sftp if ((access & FileAccess.Write) == 0) { - if (mode == FileMode.Create || mode == FileMode.CreateNew || mode == FileMode.Truncate || mode == FileMode.Append) + if (mode is FileMode.Create or FileMode.CreateNew or FileMode.Truncate or FileMode.Append) { throw new ArgumentException(string.Format("Combining {0}: {1} with {2}: {3} is invalid.", - typeof(FileMode).Name, - mode, - typeof(FileAccess).Name, - access)); + nameof(FileMode), + mode, + nameof(FileAccess), + access)); } } @@ -225,8 +266,8 @@ namespace Renci.SshNet.Sftp flags |= Flags.Append | Flags.CreateNewOrOpen; break; case FileMode.Create: - _handle = _session.RequestOpen(path, flags | Flags.Truncate, true); - if (_handle == null) + _handle = _session.RequestOpen(path, flags | Flags.Truncate, nullOnError: true); + if (_handle is null) { flags |= Flags.CreateNew; } @@ -234,6 +275,7 @@ namespace Renci.SshNet.Sftp { flags |= Flags.Truncate; } + break; case FileMode.CreateNew: flags |= Flags.CreateNew; @@ -247,33 +289,136 @@ namespace Renci.SshNet.Sftp flags |= Flags.Truncate; break; default: - throw new ArgumentOutOfRangeException("mode"); + throw new ArgumentOutOfRangeException(nameof(mode)); } - if (_handle == null) - _handle = _session.RequestOpen(path, flags); + _handle ??= _session.RequestOpen(path, flags); - // instead of using the specified buffer size as is, we use it to calculate a buffer size - // that ensures we always receive or send the max. number of bytes in a single SSH_FXP_READ - // or SSH_FXP_WRITE message + /* + * Instead of using the specified buffer size as is, we use it to calculate a buffer size + * that ensures we always receive or send the max. number of bytes in a single SSH_FXP_READ + * or SSH_FXP_WRITE message. + */ _readBufferSize = (int) session.CalculateOptimalReadLength((uint) bufferSize); _writeBufferSize = (int) session.CalculateOptimalWriteLength((uint) bufferSize, _handle); if (mode == FileMode.Append) { - var attributes = _session.RequestFStat(_handle, false); + var attributes = _session.RequestFStat(_handle, nullOnError: false); _position = attributes.Size; } } + internal static async Task OpenAsync(ISftpSession session, string path, FileMode mode, FileAccess access, int bufferSize, CancellationToken cancellationToken) + { + if (session is null) + { + throw new SshConnectionException("Client not connected."); + } + + if (path is null) + { + throw new ArgumentNullException(nameof(path)); + } + + if (bufferSize <= 0) + { + throw new ArgumentOutOfRangeException(nameof(bufferSize), "Cannot be less than or equal to zero."); + } + + var flags = Flags.None; + + switch (access) + { + case FileAccess.Read: + flags |= Flags.Read; + break; + case FileAccess.Write: + flags |= Flags.Write; + break; + case FileAccess.ReadWrite: + flags |= Flags.Read; + flags |= Flags.Write; + break; + default: + throw new ArgumentOutOfRangeException(nameof(access)); + } + + if ((access & FileAccess.Read) != 0 && mode == FileMode.Append) + { + throw new ArgumentException(string.Format("{0} mode can be requested only when combined with write-only access.", mode.ToString("G"))); + } + + if ((access & FileAccess.Write) == 0) + { + if (mode is FileMode.Create or FileMode.CreateNew or FileMode.Truncate or FileMode.Append) + { + throw new ArgumentException(string.Format("Combining {0}: {1} with {2}: {3} is invalid.", + nameof(FileMode), + mode, + nameof(FileAccess), + access)); + } + } + + switch (mode) + { + case FileMode.Append: + flags |= Flags.Append | Flags.CreateNewOrOpen; + break; + case FileMode.Create: + flags |= Flags.CreateNewOrOpen | Flags.Truncate; + break; + case FileMode.CreateNew: + flags |= Flags.CreateNew; + break; + case FileMode.Open: + break; + case FileMode.OpenOrCreate: + flags |= Flags.CreateNewOrOpen; + break; + case FileMode.Truncate: + flags |= Flags.Truncate; + break; + default: + throw new ArgumentOutOfRangeException(nameof(mode)); + } + + var handle = await session.RequestOpenAsync(path, flags, cancellationToken).ConfigureAwait(false); + + long position = 0; + if (mode == FileMode.Append) + { + try + { + var attributes = await session.RequestFStatAsync(handle, cancellationToken).ConfigureAwait(false); + position = attributes.Size; + } + catch + { + try + { + await session.RequestCloseAsync(handle, cancellationToken).ConfigureAwait(false); + } + catch + { + // The original exception is presumably more informative, so we just ignore this one. + } + + throw; + } + } + + return new SftpFileStream(session, path, access, bufferSize, handle, position); + } + /// - /// Releases unmanaged resources and performs other cleanup operations before the - /// is reclaimed by garbage collection. + /// Finalizes an instance of the class. /// ~SftpFileStream() { - Dispose(false); + Dispose(disposing: false); } /// @@ -298,6 +443,27 @@ namespace Renci.SshNet.Sftp } } + /// + /// Asynchronously clears all buffers for this stream and causes any buffered data to be written to the file. + /// + /// The to observe. + /// A that represents the asynchronous flush operation. + /// An I/O error occurs. + /// Stream is closed. + public override Task FlushAsync(CancellationToken cancellationToken) + { + CheckSessionIsOpen(); + + if (_bufferOwnedByWrite) + { + return FlushWriteBufferAsync(cancellationToken); + } + + FlushReadBuffer(); + + return Task.CompletedTask; + } + /// /// Reads a sequence of bytes from the current stream and advances the position within the stream by the /// number of bytes read. @@ -335,14 +501,25 @@ namespace Renci.SshNet.Sftp { var readLen = 0; - if (buffer == null) - throw new ArgumentNullException("buffer"); + if (buffer is null) + { + throw new ArgumentNullException(nameof(buffer)); + } + if (offset < 0) - throw new ArgumentOutOfRangeException("offset"); + { + throw new ArgumentOutOfRangeException(nameof(offset)); + } + if (count < 0) - throw new ArgumentOutOfRangeException("count"); + { + throw new ArgumentOutOfRangeException(nameof(count)); + } + if ((buffer.Length - offset) < count) + { throw new ArgumentException("Invalid array range."); + } // Lock down the file stream while we do this. lock (_lock) @@ -374,6 +551,7 @@ namespace Renci.SshNet.Sftp { // write all data read to caller-provided buffer bytesToWriteToCallerBuffer = data.Length; + // reset buffer since we will skip buffering _bufferPosition = 0; _bufferLen = 0; @@ -382,18 +560,23 @@ namespace Renci.SshNet.Sftp { // determine number of bytes that we should write into read buffer var bytesToWriteToReadBuffer = data.Length - bytesToWriteToCallerBuffer; + // write remaining bytes to read buffer Buffer.BlockCopy(data, count, GetOrCreateReadBuffer(), 0, bytesToWriteToReadBuffer); + // update position in read buffer _bufferPosition = 0; + // update number of bytes in read buffer _bufferLen = bytesToWriteToReadBuffer; } // write bytes to caller-provided buffer Buffer.BlockCopy(data, 0, buffer, offset, bytesToWriteToCallerBuffer); + // update stream position _position += bytesToWriteToCallerBuffer; + // record total number of bytes read into caller-provided buffer readLen += bytesToWriteToCallerBuffer; @@ -409,6 +592,7 @@ namespace Renci.SshNet.Sftp // advance offset to start writing bytes into caller-provided buffer offset += bytesToWriteToCallerBuffer; + // update number of bytes left to read into caller-provided buffer count -= bytesToWriteToCallerBuffer; } @@ -416,18 +600,25 @@ namespace Renci.SshNet.Sftp { // limit the number of bytes to use from read buffer to the caller-request number of bytes if (bytesAvailableInBuffer > count) + { bytesAvailableInBuffer = count; + } // copy data from read buffer to the caller-provided buffer Buffer.BlockCopy(GetOrCreateReadBuffer(), _bufferPosition, buffer, offset, bytesAvailableInBuffer); + // update position in read buffer _bufferPosition += bytesAvailableInBuffer; + // update stream position _position += bytesAvailableInBuffer; + // record total number of bytes read into caller-provided buffer readLen += bytesAvailableInBuffer; + // advance offset to start writing bytes into caller-provided buffer offset += bytesAvailableInBuffer; + // update number of bytes left to read count -= bytesAvailableInBuffer; } @@ -438,6 +629,143 @@ namespace Renci.SshNet.Sftp return readLen; } + /// + /// Asynchronously reads a sequence of bytes from the current stream and advances the position within the stream by the + /// number of bytes read. + /// + /// An array of bytes. When this method returns, the buffer contains the specified byte array with the values between and ( + - 1) replaced by the bytes read from the current source. + /// The zero-based byte offset in at which to begin storing the data read from the current stream. + /// The maximum number of bytes to be read from the current stream. + /// The to observe. + /// A that represents the asynchronous read operation. + public override async Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + var readLen = 0; + + if (buffer is null) + { + throw new ArgumentNullException(nameof(buffer)); + } + + if (offset < 0) + { + throw new ArgumentOutOfRangeException(nameof(offset)); + } + + if (count < 0) + { + throw new ArgumentOutOfRangeException(nameof(count)); + } + + if ((buffer.Length - offset) < count) + { + throw new ArgumentException("Invalid array range."); + } + + CheckSessionIsOpen(); + + // Set up for the read operation. + SetupRead(); + + // Read data into the caller's buffer. + while (count > 0) + { + // How much data do we have available in the buffer? + var bytesAvailableInBuffer = _bufferLen - _bufferPosition; + if (bytesAvailableInBuffer <= 0) + { + var data = await _session.RequestReadAsync(_handle, (ulong)_position, (uint)_readBufferSize, cancellationToken).ConfigureAwait(false); + + if (data.Length == 0) + { + _bufferPosition = 0; + _bufferLen = 0; + + break; + } + + var bytesToWriteToCallerBuffer = count; + if (bytesToWriteToCallerBuffer >= data.Length) + { + // write all data read to caller-provided buffer + bytesToWriteToCallerBuffer = data.Length; + + // reset buffer since we will skip buffering + _bufferPosition = 0; + _bufferLen = 0; + } + else + { + // determine number of bytes that we should write into read buffer + var bytesToWriteToReadBuffer = data.Length - bytesToWriteToCallerBuffer; + + // write remaining bytes to read buffer + Buffer.BlockCopy(data, count, GetOrCreateReadBuffer(), 0, bytesToWriteToReadBuffer); + + // update position in read buffer + _bufferPosition = 0; + + // update number of bytes in read buffer + _bufferLen = bytesToWriteToReadBuffer; + } + + // write bytes to caller-provided buffer + Buffer.BlockCopy(data, 0, buffer, offset, bytesToWriteToCallerBuffer); + + // update stream position + _position += bytesToWriteToCallerBuffer; + + // record total number of bytes read into caller-provided buffer + readLen += bytesToWriteToCallerBuffer; + + // break out of the read loop when the server returned less than the request number of bytes + // as that *may* indicate that we've reached EOF + // + // doing this avoids reading from server twice to determine EOF: once in the read loop, and + // once upon the next Read or ReadByte invocation by the caller + if (data.Length < _readBufferSize) + { + break; + } + + // advance offset to start writing bytes into caller-provided buffer + offset += bytesToWriteToCallerBuffer; + + // update number of bytes left to read into caller-provided buffer + count -= bytesToWriteToCallerBuffer; + } + else + { + // limit the number of bytes to use from read buffer to the caller-request number of bytes + if (bytesAvailableInBuffer > count) + { + bytesAvailableInBuffer = count; + } + + // copy data from read buffer to the caller-provided buffer + Buffer.BlockCopy(GetOrCreateReadBuffer(), _bufferPosition, buffer, offset, bytesAvailableInBuffer); + + // update position in read buffer + _bufferPosition += bytesAvailableInBuffer; + + // update stream position + _position += bytesAvailableInBuffer; + + // record total number of bytes read into caller-provided buffer + readLen += bytesAvailableInBuffer; + + // advance offset to start writing bytes into caller-provided buffer + offset += bytesAvailableInBuffer; + + // update number of bytes left to read + count -= bytesAvailableInBuffer; + } + } + + // return the number of bytes that were read to the caller. + return readLen; + } + /// /// Reads a byte from the stream and advances the position within the stream by one byte, or returns -1 if at the end of the stream. /// @@ -482,6 +810,7 @@ namespace Renci.SshNet.Sftp // Extract the next byte from the buffer. ++_position; + return readBuffer[_bufferPosition++]; } } @@ -499,7 +828,7 @@ namespace Renci.SshNet.Sftp /// Methods were called after the stream was closed. public override long Seek(long offset, SeekOrigin origin) { - long newPosn = -1; + long newPosn; // Lock down the file stream while we do this. lock (_lock) @@ -507,13 +836,16 @@ namespace Renci.SshNet.Sftp CheckSessionIsOpen(); if (!CanSeek) + { throw new NotSupportedException("Seek is not supported."); + } // Don't do anything if the position won't be moving. if (origin == SeekOrigin.Begin && offset == _position) { return offset; } + if (origin == SeekOrigin.Current && offset == 0) { return _position; @@ -524,26 +856,6 @@ namespace Renci.SshNet.Sftp { // Flush the write buffer and then seek. FlushWriteBuffer(); - - switch (origin) - { - case SeekOrigin.Begin: - newPosn = offset; - break; - case SeekOrigin.Current: - newPosn = _position + offset; - break; - case SeekOrigin.End: - var attributes = _session.RequestFStat(_handle, false); - newPosn = attributes.Size - offset; - break; - } - - if (newPosn == -1) - { - throw new EndOfStreamException("End of stream."); - } - _position = newPosn; } else { @@ -574,29 +886,31 @@ namespace Renci.SshNet.Sftp // Abandon the read buffer. _bufferPosition = 0; _bufferLen = 0; - - // Seek to the new position. - switch (origin) - { - case SeekOrigin.Begin: - newPosn = offset; - break; - case SeekOrigin.Current: - newPosn = _position + offset; - break; - case SeekOrigin.End: - var attributes = _session.RequestFStat(_handle, false); - newPosn = attributes.Size - offset; - break; - } - - if (newPosn < 0) - { - throw new EndOfStreamException(); - } - - _position = newPosn; } + + // Seek to the new position. + switch (origin) + { + case SeekOrigin.Begin: + newPosn = offset; + break; + case SeekOrigin.Current: + newPosn = _position + offset; + break; + case SeekOrigin.End: + var attributes = _session.RequestFStat(_handle, nullOnError: false); + newPosn = attributes.Size + offset; + break; + default: + throw new ArgumentException("Invalid seek origin.", nameof(origin)); + } + + if (newPosn < 0) + { + throw new EndOfStreamException(); + } + + _position = newPosn; return _position; } } @@ -625,7 +939,9 @@ namespace Renci.SshNet.Sftp public override void SetLength(long value) { if (value < 0) - throw new ArgumentOutOfRangeException("value"); + { + throw new ArgumentOutOfRangeException(nameof(value)); + } // Lock down the file stream while we do this. lock (_lock) @@ -633,7 +949,9 @@ namespace Renci.SshNet.Sftp CheckSessionIsOpen(); if (!CanSeek) + { throw new NotSupportedException("Seek is not supported."); + } if (_bufferOwnedByWrite) { @@ -644,7 +962,7 @@ namespace Renci.SshNet.Sftp SetupWrite(); } - var attributes = _session.RequestFStat(_handle, false); + var attributes = _session.RequestFStat(_handle, nullOnError: false); attributes.Size = value; _session.RequestFSetStat(_handle, attributes); @@ -669,14 +987,25 @@ namespace Renci.SshNet.Sftp /// Methods were called after the stream was closed. public override void Write(byte[] buffer, int offset, int count) { - if (buffer == null) - throw new ArgumentNullException("buffer"); + if (buffer is null) + { + throw new ArgumentNullException(nameof(buffer)); + } + if (offset < 0) - throw new ArgumentOutOfRangeException("offset"); + { + throw new ArgumentOutOfRangeException(nameof(offset)); + } + if (count < 0) - throw new ArgumentOutOfRangeException("count"); + { + throw new ArgumentOutOfRangeException(nameof(count)); + } + if ((buffer.Length - offset) < count) + { throw new ArgumentException("Invalid array range."); + } // Lock down the file stream while we do this. lock (_lock) @@ -695,6 +1024,7 @@ namespace Renci.SshNet.Sftp { // flush write buffer, and mark it empty FlushWriteBuffer(); + // we can now write or buffer the full buffer size tempLen = _writeBufferSize; } @@ -708,7 +1038,7 @@ namespace Renci.SshNet.Sftp // Can we short-cut the internal buffer? if (_bufferPosition == 0 && tempLen == _writeBufferSize) { - using (var wait = new AutoResetEvent(false)) + using (var wait = new AutoResetEvent(initialState: false)) { _session.RequestWrite(_handle, (ulong) _position, buffer, offset, tempLen, wait); } @@ -730,7 +1060,7 @@ namespace Renci.SshNet.Sftp // rather than waiting for the next call to this method. if (_bufferPosition >= _writeBufferSize) { - using (var wait = new AutoResetEvent(false)) + using (var wait = new AutoResetEvent(initialState: false)) { _session.RequestWrite(_handle, (ulong) (_position - _bufferPosition), GetOrCreateWriteBuffer(), 0, _bufferPosition, wait); } @@ -740,6 +1070,94 @@ namespace Renci.SshNet.Sftp } } + /// + /// Asynchronously writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written. + /// + /// An array of bytes. This method copies bytes from to the current stream. + /// The zero-based byte offset in at which to begin copying bytes to the current stream. + /// The number of bytes to be written to the current stream. + /// The to observe. + /// A that represents the asynchronous write operation. + /// The sum of and is greater than the buffer length. + /// is null. + /// or is negative. + /// An I/O error occurs. + /// The stream does not support writing. + /// Methods were called after the stream was closed. + public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + if (buffer is null) + { + throw new ArgumentNullException(nameof(buffer)); + } + + if (offset < 0) + { + throw new ArgumentOutOfRangeException(nameof(offset)); + } + + if (count < 0) + { + throw new ArgumentOutOfRangeException(nameof(count)); + } + + if ((buffer.Length - offset) < count) + { + throw new ArgumentException("Invalid array range."); + } + + CheckSessionIsOpen(); + + // Setup this object for writing. + SetupWrite(); + + // Write data to the file stream. + while (count > 0) + { + // Determine how many bytes we can write to the buffer. + var tempLen = _writeBufferSize - _bufferPosition; + if (tempLen <= 0) + { + // flush write buffer, and mark it empty + await FlushWriteBufferAsync(cancellationToken).ConfigureAwait(false); + + // we can now write or buffer the full buffer size + tempLen = _writeBufferSize; + } + + // limit the number of bytes to write to the actual number of bytes requested + if (tempLen > count) + { + tempLen = count; + } + + // Can we short-cut the internal buffer? + if (_bufferPosition == 0 && tempLen == _writeBufferSize) + { + await _session.RequestWriteAsync(_handle, (ulong)_position, buffer, offset, tempLen, cancellationToken).ConfigureAwait(false); + } + else + { + // No: copy the data to the write buffer first. + Buffer.BlockCopy(buffer, offset, GetOrCreateWriteBuffer(), _bufferPosition, tempLen); + _bufferPosition += tempLen; + } + + // Advance the buffer and stream positions. + _position += tempLen; + offset += tempLen; + count -= tempLen; + } + + // If the buffer is full, then do a speculative flush now, + // rather than waiting for the next call to this method. + if (_bufferPosition >= _writeBufferSize) + { + await _session.RequestWriteAsync(_handle, (ulong)(_position - _bufferPosition), GetOrCreateWriteBuffer(), 0, _bufferPosition, cancellationToken).ConfigureAwait(false); + _bufferPosition = 0; + } + } + /// /// Writes a byte to the current position in the stream and advances the position within the stream by one byte. /// @@ -762,7 +1180,7 @@ namespace Renci.SshNet.Sftp // Flush the current buffer if it is full. if (_bufferPosition >= _writeBufferSize) { - using (var wait = new AutoResetEvent(false)) + using (var wait = new AutoResetEvent(initialState: false)) { _session.RequestWrite(_handle, (ulong) (_position - _bufferPosition), writeBuffer, 0, _bufferPosition, wait); } @@ -820,15 +1238,13 @@ namespace Renci.SshNet.Sftp private byte[] GetOrCreateReadBuffer() { - if (_readBuffer == null) - _readBuffer = new byte[_readBufferSize]; + _readBuffer ??= new byte[_readBufferSize]; return _readBuffer; } private byte[] GetOrCreateWriteBuffer() { - if (_writeBuffer == null) - _writeBuffer = new byte[_writeBufferSize]; + _writeBuffer ??= new byte[_writeBufferSize]; return _writeBuffer; } @@ -848,7 +1264,7 @@ namespace Renci.SshNet.Sftp { if (_bufferPosition > 0) { - using (var wait = new AutoResetEvent(false)) + using (var wait = new AutoResetEvent(initialState: false)) { _session.RequestWrite(_handle, (ulong) (_position - _bufferPosition), _writeBuffer, 0, _bufferPosition, wait); } @@ -857,13 +1273,24 @@ namespace Renci.SshNet.Sftp } } + private async Task FlushWriteBufferAsync(CancellationToken cancellationToken) + { + if (_bufferPosition > 0) + { + await _session.RequestWriteAsync(_handle, (ulong)(_position - _bufferPosition), _writeBuffer, 0, _bufferPosition, cancellationToken).ConfigureAwait(false); + _bufferPosition = 0; + } + } + /// /// Setups the read. /// private void SetupRead() { if (!CanRead) + { throw new NotSupportedException("Read not supported."); + } if (_bufferOwnedByWrite) { @@ -877,8 +1304,10 @@ namespace Renci.SshNet.Sftp /// private void SetupWrite() { - if ((!CanWrite)) + if (!CanWrite) + { throw new NotSupportedException("Write not supported."); + } if (!_bufferOwnedByWrite) { @@ -889,10 +1318,15 @@ namespace Renci.SshNet.Sftp private void CheckSessionIsOpen() { - if (_session == null) + if (_session is null) + { throw new ObjectDisposedException(GetType().FullName); + } + if (!_session.IsOpen) + { throw new ObjectDisposedException(GetType().FullName, "Cannot access a closed SFTP session."); + } } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet/Sftp/SftpListDirectoryAsyncResult.cs b/src/Renci.SshNet/Sftp/SftpListDirectoryAsyncResult.cs index cb7b6013..b69c05b2 100644 --- a/src/Renci.SshNet/Sftp/SftpListDirectoryAsyncResult.cs +++ b/src/Renci.SshNet/Sftp/SftpListDirectoryAsyncResult.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; + using Renci.SshNet.Common; namespace Renci.SshNet.Sftp @@ -7,22 +8,21 @@ namespace Renci.SshNet.Sftp /// /// Encapsulates the results of an asynchronous directory list operation. /// - public class SftpListDirectoryAsyncResult : AsyncResult> + public class SftpListDirectoryAsyncResult : AsyncResult> { /// /// Gets the number of files read so far. /// public int FilesRead { get; private set; } - + /// /// Initializes a new instance of the class. /// /// The async callback. /// The state. - public SftpListDirectoryAsyncResult(AsyncCallback asyncCallback, Object state) + public SftpListDirectoryAsyncResult(AsyncCallback asyncCallback, object state) : base(asyncCallback, state) { - } /// diff --git a/src/Renci.SshNet/Sftp/SftpMessage.cs b/src/Renci.SshNet/Sftp/SftpMessage.cs index 8f131fa7..5ecb69bc 100644 --- a/src/Renci.SshNet/Sftp/SftpMessage.cs +++ b/src/Renci.SshNet/Sftp/SftpMessage.cs @@ -1,6 +1,7 @@ -using System.IO; +using System.Globalization; +using System.IO; + using Renci.SshNet.Common; -using System.Globalization; namespace Renci.SshNet.Sftp { @@ -44,7 +45,7 @@ namespace Renci.SshNet.Sftp var startPosition = stream.Position; // skip 4 bytes for the length of the SFTP message data - stream.Seek(sizeOfDataLengthBytes, SeekOrigin.Current); + _ = stream.Seek(sizeOfDataLengthBytes, SeekOrigin.Current); // write the SFTP message data to the stream base.WriteBytes(stream); diff --git a/src/Renci.SshNet/Sftp/SftpMessageTypes.cs b/src/Renci.SshNet/Sftp/SftpMessageTypes.cs index d74fc6cc..25ea00ea 100644 --- a/src/Renci.SshNet/Sftp/SftpMessageTypes.cs +++ b/src/Renci.SshNet/Sftp/SftpMessageTypes.cs @@ -1,130 +1,155 @@ - -namespace Renci.SshNet.Sftp +namespace Renci.SshNet.Sftp { internal enum SftpMessageTypes : byte { /// - /// SSH_FXP_INIT + /// SSH_FXP_INIT. /// Init = 1, + /// - /// SSH_FXP_VERSION + /// SSH_FXP_VERSION. /// Version = 2, + /// - /// SSH_FXP_OPEN + /// SSH_FXP_OPEN. /// Open = 3, + /// - /// SSH_FXP_CLOSE + /// SSH_FXP_CLOSE. /// Close = 4, + /// - /// SSH_FXP_READ + /// SSH_FXP_READ. /// Read = 5, + /// - /// SSH_FXP_WRITE + /// SSH_FXP_WRITE. /// Write = 6, + /// - /// SSH_FXP_LSTAT + /// SSH_FXP_LSTAT. /// LStat = 7, + /// - /// SSH_FXP_FSTAT + /// SSH_FXP_FSTAT. /// FStat = 8, + /// - /// SSH_FXP_SETSTAT + /// SSH_FXP_SETSTAT. /// SetStat = 9, + /// - /// SSH_FXP_FSETSTAT + /// SSH_FXP_FSETSTAT. /// FSetStat = 10, + /// - /// SSH_FXP_OPENDIR + /// SSH_FXP_OPENDIR. /// OpenDir = 11, + /// - /// SSH_FXP_READDIR + /// SSH_FXP_READDIR. /// ReadDir = 12, + /// - /// SSH_FXP_REMOVE + /// SSH_FXP_REMOVE. /// Remove = 13, + /// - /// SSH_FXP_MKDIR + /// SSH_FXP_MKDIR. /// MkDir = 14, + /// - /// SSH_FXP_RMDIR + /// SSH_FXP_RMDIR. /// RmDir = 15, + /// - /// SSH_FXP_REALPATH + /// SSH_FXP_REALPATH. /// RealPath = 16, + /// - /// SSH_FXP_STAT + /// SSH_FXP_STAT. /// Stat = 17, + /// - /// SSH_FXP_RENAME + /// SSH_FXP_RENAME. /// Rename = 18, + /// - /// SSH_FXP_READLINK + /// SSH_FXP_READLINK. /// ReadLink = 19, + /// - /// SSH_FXP_SYMLINK + /// SSH_FXP_SYMLINK. /// SymLink = 20, + /// - /// SSH_FXP_LINK + /// SSH_FXP_LINK. /// Link = 21, + /// - /// SSH_FXP_BLOCK + /// SSH_FXP_BLOCK. /// Block = 22, + /// - /// SSH_FXP_UNBLOCK + /// SSH_FXP_UNBLOCK. /// Unblock = 23, /// - /// SSH_FXP_STATUS + /// SSH_FXP_STATUS. /// Status = 101, + /// - /// SSH_FXP_HANDLE + /// SSH_FXP_HANDLE. /// Handle = 102, + /// - /// SSH_FXP_DATA + /// SSH_FXP_DATA. /// Data = 103, + /// - /// SSH_FXP_NAME + /// SSH_FXP_NAME. /// Name = 104, + /// - /// SSH_FXP_ATTRS + /// SSH_FXP_ATTRS. /// Attrs = 105, /// - /// SSH_FXP_EXTENDED + /// SSH_FXP_EXTENDED. /// Extended = 200, + /// - /// SSH_FXP_EXTENDED_REPLY + /// SSH_FXP_EXTENDED_REPLY. /// ExtendedReply = 201 - } } diff --git a/src/Renci.SshNet/Sftp/SftpOpenAsyncResult.cs b/src/Renci.SshNet/Sftp/SftpOpenAsyncResult.cs index b19de7e7..a239172e 100644 --- a/src/Renci.SshNet/Sftp/SftpOpenAsyncResult.cs +++ b/src/Renci.SshNet/Sftp/SftpOpenAsyncResult.cs @@ -1,11 +1,13 @@ -using Renci.SshNet.Common; -using System; +using System; + +using Renci.SshNet.Common; namespace Renci.SshNet.Sftp { - internal class SftpOpenAsyncResult : AsyncResult + internal sealed class SftpOpenAsyncResult : AsyncResult { - public SftpOpenAsyncResult(AsyncCallback asyncCallback, object state) : base(asyncCallback, state) + public SftpOpenAsyncResult(AsyncCallback asyncCallback, object state) + : base(asyncCallback, state) { } } diff --git a/src/Renci.SshNet/Sftp/SftpReadAsyncResult.cs b/src/Renci.SshNet/Sftp/SftpReadAsyncResult.cs index 65911d22..9814f0e8 100644 --- a/src/Renci.SshNet/Sftp/SftpReadAsyncResult.cs +++ b/src/Renci.SshNet/Sftp/SftpReadAsyncResult.cs @@ -1,11 +1,13 @@ -using Renci.SshNet.Common; -using System; +using System; + +using Renci.SshNet.Common; namespace Renci.SshNet.Sftp { - internal class SftpReadAsyncResult : AsyncResult + internal sealed class SftpReadAsyncResult : AsyncResult { - public SftpReadAsyncResult(AsyncCallback asyncCallback, object state) : base(asyncCallback, state) + public SftpReadAsyncResult(AsyncCallback asyncCallback, object state) + : base(asyncCallback, state) { } } diff --git a/src/Renci.SshNet/Sftp/SftpRealPathAsyncResult.cs b/src/Renci.SshNet/Sftp/SftpRealPathAsyncResult.cs index e1f1fbf9..ab860032 100644 --- a/src/Renci.SshNet/Sftp/SftpRealPathAsyncResult.cs +++ b/src/Renci.SshNet/Sftp/SftpRealPathAsyncResult.cs @@ -1,11 +1,13 @@ -using Renci.SshNet.Common; -using System; +using System; + +using Renci.SshNet.Common; namespace Renci.SshNet.Sftp { - internal class SftpRealPathAsyncResult : AsyncResult + internal sealed class SftpRealPathAsyncResult : AsyncResult { - public SftpRealPathAsyncResult(AsyncCallback asyncCallback, object state) : base(asyncCallback, state) + public SftpRealPathAsyncResult(AsyncCallback asyncCallback, object state) + : base(asyncCallback, state) { } } diff --git a/src/Renci.SshNet/Sftp/SftpResponseFactory.cs b/src/Renci.SshNet/Sftp/SftpResponseFactory.cs index ad6b2291..1d5b4ad5 100644 --- a/src/Renci.SshNet/Sftp/SftpResponseFactory.cs +++ b/src/Renci.SshNet/Sftp/SftpResponseFactory.cs @@ -1,7 +1,8 @@ using System; -using System.Text; -using Renci.SshNet.Sftp.Responses; using System.Globalization; +using System.Text; + +using Renci.SshNet.Sftp.Responses; namespace Renci.SshNet.Sftp { @@ -13,6 +14,7 @@ namespace Renci.SshNet.Sftp SftpMessage message; +#pragma warning disable IDE0010 // Add missing cases switch (sftpMessageType) { case SftpMessageTypes.Version: @@ -39,6 +41,7 @@ namespace Renci.SshNet.Sftp default: throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Message type '{0}' is not supported.", sftpMessageType)); } +#pragma warning restore IDE0010 // Add missing cases return message; } diff --git a/src/Renci.SshNet/Sftp/SftpSession.cs b/src/Renci.SshNet/Sftp/SftpSession.cs index 283ff617..d797378d 100644 --- a/src/Renci.SshNet/Sftp/SftpSession.cs +++ b/src/Renci.SshNet/Sftp/SftpSession.cs @@ -1,31 +1,28 @@ using System; -using System.Text; -using System.Threading; -using Renci.SshNet.Common; using System.Collections.Generic; using System.Globalization; -using Renci.SshNet.Sftp.Responses; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +using Renci.SshNet.Common; using Renci.SshNet.Sftp.Requests; +using Renci.SshNet.Sftp.Responses; namespace Renci.SshNet.Sftp { - internal class SftpSession : SubsystemSession, ISftpSession + internal sealed class SftpSession : SubsystemSession, ISftpSession { internal const int MaximumSupportedVersion = 3; private const int MinimumSupportedVersion = 0; private readonly Dictionary _requests = new Dictionary(); private readonly ISftpResponseFactory _sftpResponseFactory; - //FIXME: obtain from SftpClient! private readonly List _data = new List(32 * 1024); - private EventWaitHandle _sftpVersionConfirmed = new AutoResetEvent(false); + private readonly Encoding _encoding; + private EventWaitHandle _sftpVersionConfirmed = new AutoResetEvent(initialState: false); private IDictionary _supportedExtensions; - /// - /// Gets the character encoding to use. - /// - protected Encoding Encoding { get; private set; } - /// /// Gets the remote working directory. /// @@ -55,10 +52,17 @@ namespace Renci.SshNet.Sftp } } + /// + /// Initializes a new instance of the class. + /// + /// The SSH session. + /// The operation timeout. + /// The character encoding to use. + /// The factory to create SFTP responses. public SftpSession(ISession session, int operationTimeout, Encoding encoding, ISftpResponseFactory sftpResponseFactory) : base(session, "sftp", operationTimeout) { - Encoding = encoding; + _encoding = encoding; _sftpResponseFactory = sftpResponseFactory; } @@ -94,7 +98,7 @@ namespace Renci.SshNet.Sftp var canonizedPath = string.Empty; - var realPathFiles = RequestRealPath(fullPath, true); + var realPathFiles = RequestRealPath(fullPath, nullOnError: true); if (realPathFiles != null) { @@ -102,23 +106,29 @@ namespace Renci.SshNet.Sftp } if (!string.IsNullOrEmpty(canonizedPath)) + { return canonizedPath; + } - // Check for special cases + // Check for special cases if (fullPath.EndsWith("/.", StringComparison.OrdinalIgnoreCase) || fullPath.EndsWith("/..", StringComparison.OrdinalIgnoreCase) || fullPath.Equals("/", StringComparison.OrdinalIgnoreCase) || fullPath.IndexOf('/') < 0) + { return fullPath; + } var pathParts = fullPath.Split('/'); var partialFullPath = string.Join("/", pathParts, 0, pathParts.Length - 1); if (string.IsNullOrEmpty(partialFullPath)) + { partialFullPath = "/"; + } - realPathFiles = RequestRealPath(partialFullPath, true); + realPathFiles = RequestRealPath(partialFullPath, nullOnError: true); if (realPathFiles != null) { @@ -132,10 +142,68 @@ namespace Renci.SshNet.Sftp var slash = string.Empty; if (canonizedPath[canonizedPath.Length - 1] != '/') + { slash = "/"; + } + return string.Format(CultureInfo.InvariantCulture, "{0}{1}{2}", canonizedPath, slash, pathParts[pathParts.Length - 1]); } + public async Task GetCanonicalPathAsync(string path, CancellationToken cancellationToken) + { + var fullPath = GetFullRemotePath(path); + + var canonizedPath = string.Empty; + var realPathFiles = await RequestRealPathAsync(fullPath, nullOnError: true, cancellationToken).ConfigureAwait(false); + if (realPathFiles != null) + { + canonizedPath = realPathFiles[0].Key; + } + + if (!string.IsNullOrEmpty(canonizedPath)) + { + return canonizedPath; + } + + // Check for special cases + if (fullPath.EndsWith("/.", StringComparison.Ordinal) || + fullPath.EndsWith("/..", StringComparison.Ordinal) || + fullPath.Equals("/", StringComparison.Ordinal) || + fullPath.IndexOf('/') < 0) + { + return fullPath; + } + + var pathParts = fullPath.Split('/'); + + var partialFullPath = string.Join("/", pathParts, 0, pathParts.Length - 1); + + if (string.IsNullOrEmpty(partialFullPath)) + { + partialFullPath = "/"; + } + + realPathFiles = await RequestRealPathAsync(partialFullPath, nullOnError: true, cancellationToken).ConfigureAwait(false); + + if (realPathFiles != null) + { + canonizedPath = realPathFiles[0].Key; + } + + if (string.IsNullOrEmpty(canonizedPath)) + { + return fullPath; + } + + var slash = string.Empty; + if (canonizedPath[canonizedPath.Length - 1] != '/') + { + slash = "/"; + } + + return canonizedPath + slash + pathParts[pathParts.Length - 1]; + } + public ISftpFileReader CreateFileReader(byte[] handle, ISftpSession sftpSession, uint chunkSize, int maxPendingReads, long? fileSize) { return new SftpFileReader(handle, sftpSession, chunkSize, maxPendingReads, fileSize); @@ -149,11 +217,11 @@ namespace Renci.SshNet.Sftp { if (WorkingDirectory[WorkingDirectory.Length - 1] == '/') { - fullPath = string.Format(CultureInfo.InvariantCulture, "{0}{1}", WorkingDirectory, path); + fullPath = WorkingDirectory + path; } else { - fullPath = string.Format(CultureInfo.InvariantCulture, "{0}/{1}", WorkingDirectory, path); + fullPath = WorkingDirectory + '/' + path; } } return fullPath; @@ -165,12 +233,12 @@ namespace Renci.SshNet.Sftp WaitOnHandle(_sftpVersionConfirmed, OperationTimeout); - if (ProtocolVersion > MaximumSupportedVersion || ProtocolVersion < MinimumSupportedVersion) + if (ProtocolVersion is > MaximumSupportedVersion or < MinimumSupportedVersion) { throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Server SFTP version {0} is not supported.", ProtocolVersion)); } - // Resolve current directory + // Resolve current directory WorkingDirectory = RequestRealPath(".")[0].Key; } @@ -291,19 +359,19 @@ namespace Renci.SshNet.Sftp private bool TryLoadSftpMessage(byte[] packetData, int offset, int count) { // Create SFTP message - var response = _sftpResponseFactory.Create(ProtocolVersion, packetData[offset], Encoding); + var response = _sftpResponseFactory.Create(ProtocolVersion, packetData[offset], _encoding); + // Load message data into it response.Load(packetData, offset + 1, count - 1); try { - var versionResponse = response as SftpVersionResponse; - if (versionResponse != null) + if (response is SftpVersionResponse versionResponse) { ProtocolVersion = versionResponse.Version; _supportedExtensions = versionResponse.Extentions; - _sftpVersionConfirmed.Set(); + _ = _sftpVersionConfirmed.Set(); } else { @@ -344,10 +412,8 @@ namespace Renci.SshNet.Sftp SendMessage(request); } - #region SFTP API functions - /// - /// Performs SSH_FXP_OPEN request + /// Performs SSH_FXP_OPEN request. /// /// The path. /// The flags. @@ -358,19 +424,23 @@ namespace Renci.SshNet.Sftp byte[] handle = null; SshException exception = null; - using (var wait = new AutoResetEvent(false)) + using (var wait = new AutoResetEvent(initialState: false)) { - var request = new SftpOpenRequest(ProtocolVersion, NextRequestId, path, Encoding, flags, - response => - { - handle = response.Handle; - wait.Set(); - }, - response => - { - exception = GetSftpException(response); - wait.Set(); - }); + var request = new SftpOpenRequest(ProtocolVersion, + NextRequestId, + path, + _encoding, + flags, + response => + { + handle = response.Handle; + _ = wait.Set(); + }, + response => + { + exception = GetSftpException(response); + _ = wait.Set(); + }); SendRequest(request); @@ -385,8 +455,28 @@ namespace Renci.SshNet.Sftp return handle; } + public async Task RequestOpenAsync(string path, Flags flags, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + + using (cancellationToken.Register((s) => ((TaskCompletionSource) s).TrySetCanceled(), tcs, useSynchronizationContext: false)) + { + SendRequest(new SftpOpenRequest(ProtocolVersion, + NextRequestId, + path, + _encoding, + flags, + response => tcs.TrySetResult(response.Handle), + response => tcs.TrySetException(GetSftpException(response)))); + + return await tcs.Task.ConfigureAwait(false); + } + } + /// - /// Performs SSH_FXP_OPEN request + /// Performs SSH_FXP_OPEN request. /// /// The path. /// The flags. @@ -399,15 +489,19 @@ namespace Renci.SshNet.Sftp { var asyncResult = new SftpOpenAsyncResult(callback, state); - var request = new SftpOpenRequest(ProtocolVersion, NextRequestId, path, Encoding, flags, - response => - { - asyncResult.SetAsCompleted(response.Handle, false); - }, - response => - { - asyncResult.SetAsCompleted(GetSftpException(response), false); - }); + var request = new SftpOpenRequest(ProtocolVersion, + NextRequestId, + path, + _encoding, + flags, + response => + { + asyncResult.SetAsCompleted(response.Handle, completedSynchronously: false); + }, + response => + { + asyncResult.SetAsCompleted(GetSftpException(response), completedSynchronously: false); + }); SendRequest(request); @@ -428,14 +522,20 @@ namespace Renci.SshNet.Sftp /// is null. public byte[] EndOpen(SftpOpenAsyncResult asyncResult) { - if (asyncResult == null) - throw new ArgumentNullException("asyncResult"); + if (asyncResult is null) + { + throw new ArgumentNullException(nameof(asyncResult)); + } if (asyncResult.EndInvokeCalled) + { throw new InvalidOperationException("EndOpen has already been called."); + } if (asyncResult.IsCompleted) + { return asyncResult.EndInvoke(); + } using (var waitHandle = asyncResult.AsyncWaitHandle) { @@ -452,14 +552,16 @@ namespace Renci.SshNet.Sftp { SshException exception = null; - using (var wait = new AutoResetEvent(false)) + using (var wait = new AutoResetEvent(initialState: false)) { - var request = new SftpCloseRequest(ProtocolVersion, NextRequestId, handle, - response => - { - exception = GetSftpException(response); - wait.Set(); - }); + var request = new SftpCloseRequest(ProtocolVersion, + NextRequestId, + handle, + response => + { + exception = GetSftpException(response); + _ = wait.Set(); + }); SendRequest(request); @@ -472,6 +574,34 @@ namespace Renci.SshNet.Sftp } } + public async Task RequestCloseAsync(byte[] handle, CancellationToken cancellationToken) + { + var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + + SendRequest(new SftpCloseRequest(ProtocolVersion, + NextRequestId, + handle, + response => + { + if (response.StatusCode == StatusCodes.Ok) + { + _ = tcs.TrySetResult(true); + } + else + { + _ = tcs.TrySetException(GetSftpException(response)); + } + })); + + // Only check for cancellation after the SftpCloseRequest was sent + cancellationToken.ThrowIfCancellationRequested(); + + using (cancellationToken.Register((s) => ((TaskCompletionSource) s).TrySetCanceled(), tcs, useSynchronizationContext: false)) + { + _ = await tcs.Task.ConfigureAwait(false); + } + } + /// /// Performs SSH_FXP_CLOSE request. /// @@ -485,11 +615,13 @@ namespace Renci.SshNet.Sftp { var asyncResult = new SftpCloseAsyncResult(callback, state); - var request = new SftpCloseRequest(ProtocolVersion, NextRequestId, handle, - response => - { - asyncResult.SetAsCompleted(GetSftpException(response), false); - }); + var request = new SftpCloseRequest(ProtocolVersion, + NextRequestId, + handle, + response => + { + asyncResult.SetAsCompleted(GetSftpException(response), completedSynchronously: false); + }); SendRequest(request); return asyncResult; @@ -502,11 +634,15 @@ namespace Renci.SshNet.Sftp /// is null. public void EndClose(SftpCloseAsyncResult asyncResult) { - if (asyncResult == null) - throw new ArgumentNullException("asyncResult"); + if (asyncResult is null) + { + throw new ArgumentNullException(nameof(asyncResult)); + } if (asyncResult.EndInvokeCalled) + { throw new InvalidOperationException("EndClose has already been called."); + } if (asyncResult.IsCompleted) { @@ -537,22 +673,26 @@ namespace Renci.SshNet.Sftp { var asyncResult = new SftpReadAsyncResult(callback, state); - var request = new SftpReadRequest(ProtocolVersion, NextRequestId, handle, offset, length, - response => - { - asyncResult.SetAsCompleted(response.Data, false); - }, - response => - { - if (response.StatusCode != StatusCodes.Eof) - { - asyncResult.SetAsCompleted(GetSftpException(response), false); - } - else - { - asyncResult.SetAsCompleted(Array.Empty, false); - } - }); + var request = new SftpReadRequest(ProtocolVersion, + NextRequestId, + handle, + offset, + length, + response => + { + asyncResult.SetAsCompleted(response.Data, completedSynchronously: false); + }, + response => + { + if (response.StatusCode != StatusCodes.Eof) + { + asyncResult.SetAsCompleted(GetSftpException(response), completedSynchronously: false); + } + else + { + asyncResult.SetAsCompleted(Array.Empty(), completedSynchronously: false); + } + }); SendRequest(request); return asyncResult; @@ -572,14 +712,20 @@ namespace Renci.SshNet.Sftp /// is null. public byte[] EndRead(SftpReadAsyncResult asyncResult) { - if (asyncResult == null) - throw new ArgumentNullException("asyncResult"); + if (asyncResult is null) + { + throw new ArgumentNullException(nameof(asyncResult)); + } if (asyncResult.EndInvokeCalled) + { throw new InvalidOperationException("EndRead has already been called."); + } if (asyncResult.IsCompleted) + { return asyncResult.EndInvoke(); + } using (var waitHandle = asyncResult.AsyncWaitHandle) { @@ -601,26 +747,31 @@ namespace Renci.SshNet.Sftp byte[] data = null; - using (var wait = new AutoResetEvent(false)) + using (var wait = new AutoResetEvent(initialState: false)) { - var request = new SftpReadRequest(ProtocolVersion, NextRequestId, handle, offset, length, - response => - { - data = response.Data; - wait.Set(); - }, - response => - { - if (response.StatusCode != StatusCodes.Eof) - { - exception = GetSftpException(response); - } - else - { - data = Array.Empty; - } - wait.Set(); - }); + var request = new SftpReadRequest(ProtocolVersion, + NextRequestId, + handle, + offset, + length, + response => + { + data = response.Data; + _ = wait.Set(); + }, + response => + { + if (response.StatusCode != StatusCodes.Eof) + { + exception = GetSftpException(response); + } + else + { + data = Array.Empty(); + } + + _ = wait.Set(); + }); SendRequest(request); @@ -635,6 +786,36 @@ namespace Renci.SshNet.Sftp return data; } + public async Task RequestReadAsync(byte[] handle, ulong offset, uint length, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + + using (cancellationToken.Register((s) => ((TaskCompletionSource) s).TrySetCanceled(), tcs, useSynchronizationContext: false)) + { + SendRequest(new SftpReadRequest(ProtocolVersion, + NextRequestId, + handle, + offset, + length, + response => tcs.TrySetResult(response.Data), + response => + { + if (response.StatusCode == StatusCodes.Eof) + { + _ = tcs.TrySetResult(Array.Empty()); + } + else + { + _ = tcs.TrySetException(GetSftpException(response)); + } + })); + + return await tcs.Task.ConfigureAwait(false); + } + } + /// /// Performs SSH_FXP_WRITE request. /// @@ -655,23 +836,30 @@ namespace Renci.SshNet.Sftp { SshException exception = null; - var request = new SftpWriteRequest(ProtocolVersion, NextRequestId, handle, serverOffset, data, offset, - length, response => - { - if (writeCompleted != null) - { - writeCompleted(response); - } + var request = new SftpWriteRequest(ProtocolVersion, + NextRequestId, + handle, + serverOffset, + data, + offset, + length, + response => + { + writeCompleted?.Invoke(response); - exception = GetSftpException(response); - if (wait != null) - wait.Set(); - }); + exception = GetSftpException(response); + if (wait != null) + { + _ = wait.Set(); + } + }); SendRequest(request); if (wait != null) + { WaitOnHandle(wait, OperationTimeout); + } if (exception != null) { @@ -679,31 +867,65 @@ namespace Renci.SshNet.Sftp } } + public async Task RequestWriteAsync(byte[] handle, ulong serverOffset, byte[] data, int offset, int length, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + + using (cancellationToken.Register((s) => ((TaskCompletionSource) s).TrySetCanceled(), tcs, useSynchronizationContext: false)) + { + SendRequest(new SftpWriteRequest(ProtocolVersion, + NextRequestId, + handle, + serverOffset, + data, + offset, + length, + response => + { + if (response.StatusCode == StatusCodes.Ok) + { + _ = tcs.TrySetResult(true); + } + else + { + _ = tcs.TrySetException(GetSftpException(response)); + } + })); + + _ = await tcs.Task.ConfigureAwait(false); + } + } + /// /// Performs SSH_FXP_LSTAT request. /// /// The path. /// - /// File attributes + /// File attributes. /// public SftpFileAttributes RequestLStat(string path) { SshException exception = null; SftpFileAttributes attributes = null; - using (var wait = new AutoResetEvent(false)) + using (var wait = new AutoResetEvent(initialState: false)) { - var request = new SftpLStatRequest(ProtocolVersion, NextRequestId, path, Encoding, - response => - { - attributes = response.Attributes; - wait.Set(); - }, - response => - { - exception = GetSftpException(response); - wait.Set(); - }); + var request = new SftpLStatRequest(ProtocolVersion, + NextRequestId, + path, + _encoding, + response => + { + attributes = response.Attributes; + _ = wait.Set(); + }, + response => + { + exception = GetSftpException(response); + _ = wait.Set(); + }); SendRequest(request); @@ -731,15 +953,18 @@ namespace Renci.SshNet.Sftp { var asyncResult = new SFtpStatAsyncResult(callback, state); - var request = new SftpLStatRequest(ProtocolVersion, NextRequestId, path, Encoding, - response => - { - asyncResult.SetAsCompleted(response.Attributes, false); - }, - response => - { - asyncResult.SetAsCompleted(GetSftpException(response), false); - }); + var request = new SftpLStatRequest(ProtocolVersion, + NextRequestId, + path, + _encoding, + response => + { + asyncResult.SetAsCompleted(response.Attributes, completedSynchronously: false); + }, + response => + { + asyncResult.SetAsCompleted(GetSftpException(response), completedSynchronously: false); + }); SendRequest(request); return asyncResult; @@ -755,14 +980,20 @@ namespace Renci.SshNet.Sftp /// is null. public SftpFileAttributes EndLStat(SFtpStatAsyncResult asyncResult) { - if (asyncResult == null) - throw new ArgumentNullException("asyncResult"); + if (asyncResult is null) + { + throw new ArgumentNullException(nameof(asyncResult)); + } if (asyncResult.EndInvokeCalled) + { throw new InvalidOperationException("EndLStat has already been called."); + } if (asyncResult.IsCompleted) + { return asyncResult.EndInvoke(); + } using (var waitHandle = asyncResult.AsyncWaitHandle) { @@ -777,26 +1008,28 @@ namespace Renci.SshNet.Sftp /// The handle. /// if set to true returns null instead of throwing an exception. /// - /// File attributes + /// File attributes. /// public SftpFileAttributes RequestFStat(byte[] handle, bool nullOnError) { SshException exception = null; SftpFileAttributes attributes = null; - using (var wait = new AutoResetEvent(false)) + using (var wait = new AutoResetEvent(initialState: false)) { - var request = new SftpFStatRequest(ProtocolVersion, NextRequestId, handle, - response => - { - attributes = response.Attributes; - wait.Set(); - }, - response => - { - exception = GetSftpException(response); - wait.Set(); - }); + var request = new SftpFStatRequest(ProtocolVersion, + NextRequestId, + handle, + response => + { + attributes = response.Attributes; + _ = wait.Set(); + }, + response => + { + exception = GetSftpException(response); + _ = wait.Set(); + }); SendRequest(request); @@ -811,6 +1044,24 @@ namespace Renci.SshNet.Sftp return attributes; } + public async Task RequestFStatAsync(byte[] handle, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + + using (cancellationToken.Register((s) => ((TaskCompletionSource) s).TrySetCanceled(), tcs, useSynchronizationContext: false)) + { + SendRequest(new SftpFStatRequest(ProtocolVersion, + NextRequestId, + handle, + response => tcs.TrySetResult(response.Attributes), + response => tcs.TrySetException(GetSftpException(response)))); + + return await tcs.Task.ConfigureAwait(false); + } + } + /// /// Performs SSH_FXP_SETSTAT request. /// @@ -820,14 +1071,18 @@ namespace Renci.SshNet.Sftp { SshException exception = null; - using (var wait = new AutoResetEvent(false)) + using (var wait = new AutoResetEvent(initialState: false)) { - var request = new SftpSetStatRequest(ProtocolVersion, NextRequestId, path, Encoding, attributes, - response => - { - exception = GetSftpException(response); - wait.Set(); - }); + var request = new SftpSetStatRequest(ProtocolVersion, + NextRequestId, + path, + _encoding, + attributes, + response => + { + exception = GetSftpException(response); + _ = wait.Set(); + }); SendRequest(request); @@ -849,14 +1104,17 @@ namespace Renci.SshNet.Sftp { SshException exception = null; - using (var wait = new AutoResetEvent(false)) + using (var wait = new AutoResetEvent(initialState: false)) { - var request = new SftpFSetStatRequest(ProtocolVersion, NextRequestId, handle, attributes, - response => - { - exception = GetSftpException(response); - wait.Set(); - }); + var request = new SftpFSetStatRequest(ProtocolVersion, + NextRequestId, + handle, + attributes, + response => + { + exception = GetSftpException(response); + _ = wait.Set(); + }); SendRequest(request); @@ -870,7 +1128,7 @@ namespace Renci.SshNet.Sftp } /// - /// Performs SSH_FXP_OPENDIR request + /// Performs SSH_FXP_OPENDIR request. /// /// The path. /// if set to true returns null instead of throwing an exception. @@ -881,19 +1139,22 @@ namespace Renci.SshNet.Sftp byte[] handle = null; - using (var wait = new AutoResetEvent(false)) + using (var wait = new AutoResetEvent(initialState: false)) { - var request = new SftpOpenDirRequest(ProtocolVersion, NextRequestId, path, Encoding, - response => - { - handle = response.Handle; - wait.Set(); - }, - response => - { - exception = GetSftpException(response); - wait.Set(); - }); + var request = new SftpOpenDirRequest(ProtocolVersion, + NextRequestId, + path, + _encoding, + response => + { + handle = response.Handle; + _ = wait.Set(); + }, + response => + { + exception = GetSftpException(response); + _ = wait.Set(); + }); SendRequest(request); @@ -908,8 +1169,27 @@ namespace Renci.SshNet.Sftp return handle; } + public async Task RequestOpenDirAsync(string path, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + + using (cancellationToken.Register((s) => ((TaskCompletionSource) s).TrySetCanceled(), tcs, useSynchronizationContext: false)) + { + SendRequest(new SftpOpenDirRequest(ProtocolVersion, + NextRequestId, + path, + _encoding, + response => tcs.TrySetResult(response.Handle), + response => tcs.TrySetException(GetSftpException(response)))); + + return await tcs.Task.ConfigureAwait(false); + } + } + /// - /// Performs SSH_FXP_READDIR request + /// Performs SSH_FXP_READDIR request. /// /// The handle. /// @@ -919,22 +1199,25 @@ namespace Renci.SshNet.Sftp KeyValuePair[] result = null; - using (var wait = new AutoResetEvent(false)) + using (var wait = new AutoResetEvent(initialState: false)) { - var request = new SftpReadDirRequest(ProtocolVersion, NextRequestId, handle, - response => - { - result = response.Files; - wait.Set(); - }, - response => - { - if (response.StatusCode != StatusCodes.Eof) - { - exception = GetSftpException(response); - } - wait.Set(); - }); + var request = new SftpReadDirRequest(ProtocolVersion, + NextRequestId, + handle, + response => + { + result = response.Files; + _ = wait.Set(); + }, + response => + { + if (response.StatusCode != StatusCodes.Eof) + { + exception = GetSftpException(response); + } + + _ = wait.Set(); + }); SendRequest(request); @@ -949,6 +1232,34 @@ namespace Renci.SshNet.Sftp return result; } + public async Task[]> RequestReadDirAsync(byte[] handle, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + var tcs = new TaskCompletionSource[]>(TaskCreationOptions.RunContinuationsAsynchronously); + + using (cancellationToken.Register((s) => ((TaskCompletionSource[]>) s).TrySetCanceled(), tcs, useSynchronizationContext: false)) + { + SendRequest(new SftpReadDirRequest(ProtocolVersion, + NextRequestId, + handle, + response => tcs.TrySetResult(response.Files), + response => + { + if (response.StatusCode == StatusCodes.Eof) + { + _ = tcs.TrySetResult(null); + } + else + { + _ = tcs.TrySetException(GetSftpException(response)); + } + })); + + return await tcs.Task.ConfigureAwait(false); + } + } + /// /// Performs SSH_FXP_REMOVE request. /// @@ -957,14 +1268,17 @@ namespace Renci.SshNet.Sftp { SshException exception = null; - using (var wait = new AutoResetEvent(false)) + using (var wait = new AutoResetEvent(initialState: false)) { - var request = new SftpRemoveRequest(ProtocolVersion, NextRequestId, path, Encoding, - response => - { - exception = GetSftpException(response); - wait.Set(); - }); + var request = new SftpRemoveRequest(ProtocolVersion, + NextRequestId, + path, + _encoding, + response => + { + exception = GetSftpException(response); + _ = wait.Set(); + }); SendRequest(request); @@ -977,6 +1291,34 @@ namespace Renci.SshNet.Sftp } } + public async Task RequestRemoveAsync(string path, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + + using (cancellationToken.Register((s) => ((TaskCompletionSource) s).TrySetCanceled(), tcs, useSynchronizationContext: false)) + { + SendRequest(new SftpRemoveRequest(ProtocolVersion, + NextRequestId, + path, + _encoding, + response => + { + if (response.StatusCode == StatusCodes.Ok) + { + _ = tcs.TrySetResult(true); + } + else + { + _ = tcs.TrySetException(GetSftpException(response)); + } + })); + + _ = await tcs.Task.ConfigureAwait(false); + } + } + /// /// Performs SSH_FXP_MKDIR request. /// @@ -985,14 +1327,17 @@ namespace Renci.SshNet.Sftp { SshException exception = null; - using (var wait = new AutoResetEvent(false)) + using (var wait = new AutoResetEvent(initialState: false)) { - var request = new SftpMkDirRequest(ProtocolVersion, NextRequestId, path, Encoding, - response => - { - exception = GetSftpException(response); - wait.Set(); - }); + var request = new SftpMkDirRequest(ProtocolVersion, + NextRequestId, + path, + _encoding, + response => + { + exception = GetSftpException(response); + _ = wait.Set(); + }); SendRequest(request); @@ -1013,14 +1358,17 @@ namespace Renci.SshNet.Sftp { SshException exception = null; - using (var wait = new AutoResetEvent(false)) + using (var wait = new AutoResetEvent(initialState: false)) { - var request = new SftpRmDirRequest(ProtocolVersion, NextRequestId, path, Encoding, - response => - { - exception = GetSftpException(response); - wait.Set(); - }); + var request = new SftpRmDirRequest(ProtocolVersion, + NextRequestId, + path, + _encoding, + response => + { + exception = GetSftpException(response); + _ = wait.Set(); + }); SendRequest(request); @@ -1034,7 +1382,7 @@ namespace Renci.SshNet.Sftp } /// - /// Performs SSH_FXP_REALPATH request + /// Performs SSH_FXP_REALPATH request. /// /// The path. /// if set to true returns null instead of throwing an exception. @@ -1047,19 +1395,22 @@ namespace Renci.SshNet.Sftp KeyValuePair[] result = null; - using (var wait = new AutoResetEvent(false)) + using (var wait = new AutoResetEvent(initialState: false)) { - var request = new SftpRealPathRequest(ProtocolVersion, NextRequestId, path, Encoding, - response => - { - result = response.Files; - wait.Set(); - }, - response => - { - exception = GetSftpException(response); - wait.Set(); - }); + var request = new SftpRealPathRequest(ProtocolVersion, + NextRequestId, + path, + _encoding, + response => + { + result = response.Files; + _ = wait.Set(); + }, + response => + { + exception = GetSftpException(response); + _ = wait.Set(); + }); SendRequest(request); @@ -1070,10 +1421,39 @@ namespace Renci.SshNet.Sftp { throw exception; } - + return result; } + internal async Task[]> RequestRealPathAsync(string path, bool nullOnError, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + var tcs = new TaskCompletionSource[]>(TaskCreationOptions.RunContinuationsAsynchronously); + + using (cancellationToken.Register((s) => ((TaskCompletionSource[]>) s).TrySetCanceled(), tcs, useSynchronizationContext: false)) + { + SendRequest(new SftpRealPathRequest(ProtocolVersion, + NextRequestId, + path, + _encoding, + response => tcs.TrySetResult(response.Files), + response => + { + if (nullOnError) + { + _ = tcs.TrySetResult(null); + } + else + { + _ = tcs.TrySetException(GetSftpException(response)); + } + })); + + return await tcs.Task.ConfigureAwait(false); + } + } + /// /// Performs SSH_FXP_REALPATH request. /// @@ -1087,15 +1467,12 @@ namespace Renci.SshNet.Sftp { var asyncResult = new SftpRealPathAsyncResult(callback, state); - var request = new SftpRealPathRequest(ProtocolVersion, NextRequestId, path, Encoding, - response => - { - asyncResult.SetAsCompleted(response.Files[0].Key, false); - }, - response => - { - asyncResult.SetAsCompleted(GetSftpException(response), false); - }); + var request = new SftpRealPathRequest(ProtocolVersion, + NextRequestId, + path, + _encoding, + response => asyncResult.SetAsCompleted(response.Files[0].Key, completedSynchronously: false), + response => asyncResult.SetAsCompleted(GetSftpException(response), completedSynchronously: false)); SendRequest(request); return asyncResult; @@ -1111,14 +1488,20 @@ namespace Renci.SshNet.Sftp /// is null. public string EndRealPath(SftpRealPathAsyncResult asyncResult) { - if (asyncResult == null) - throw new ArgumentNullException("asyncResult"); + if (asyncResult is null) + { + throw new ArgumentNullException(nameof(asyncResult)); + } if (asyncResult.EndInvokeCalled) + { throw new InvalidOperationException("EndRealPath has already been called."); + } if (asyncResult.IsCompleted) + { return asyncResult.EndInvoke(); + } using (var waitHandle = asyncResult.AsyncWaitHandle) { @@ -1133,7 +1516,7 @@ namespace Renci.SshNet.Sftp /// The path. /// if set to true returns null instead of throwing an exception. /// - /// File attributes + /// File attributes. /// public SftpFileAttributes RequestStat(string path, bool nullOnError = false) { @@ -1141,19 +1524,22 @@ namespace Renci.SshNet.Sftp SftpFileAttributes attributes = null; - using (var wait = new AutoResetEvent(false)) + using (var wait = new AutoResetEvent(initialState: false)) { - var request = new SftpStatRequest(ProtocolVersion, NextRequestId, path, Encoding, - response => - { - attributes = response.Attributes; - wait.Set(); - }, - response => - { - exception = GetSftpException(response); - wait.Set(); - }); + var request = new SftpStatRequest(ProtocolVersion, + NextRequestId, + path, + _encoding, + response => + { + attributes = response.Attributes; + _ = wait.Set(); + }, + response => + { + exception = GetSftpException(response); + _ = wait.Set(); + }); SendRequest(request); @@ -1169,7 +1555,7 @@ namespace Renci.SshNet.Sftp } /// - /// Performs SSH_FXP_STAT request + /// Performs SSH_FXP_STAT request. /// /// The path. /// The delegate that is executed when completes. @@ -1181,15 +1567,12 @@ namespace Renci.SshNet.Sftp { var asyncResult = new SFtpStatAsyncResult(callback, state); - var request = new SftpStatRequest(ProtocolVersion, NextRequestId, path, Encoding, - response => - { - asyncResult.SetAsCompleted(response.Attributes, false); - }, - response => - { - asyncResult.SetAsCompleted(GetSftpException(response), false); - }); + var request = new SftpStatRequest(ProtocolVersion, + NextRequestId, + path, + _encoding, + response => asyncResult.SetAsCompleted(response.Attributes, completedSynchronously: false), + response => asyncResult.SetAsCompleted(GetSftpException(response), completedSynchronously: false)); SendRequest(request); return asyncResult; @@ -1205,14 +1588,20 @@ namespace Renci.SshNet.Sftp /// is null. public SftpFileAttributes EndStat(SFtpStatAsyncResult asyncResult) { - if (asyncResult == null) - throw new ArgumentNullException("asyncResult"); + if (asyncResult is null) + { + throw new ArgumentNullException(nameof(asyncResult)); + } if (asyncResult.EndInvokeCalled) + { throw new InvalidOperationException("EndStat has already been called."); + } if (asyncResult.IsCompleted) + { return asyncResult.EndInvoke(); + } using (var waitHandle = asyncResult.AsyncWaitHandle) { @@ -1235,14 +1624,18 @@ namespace Renci.SshNet.Sftp SshException exception = null; - using (var wait = new AutoResetEvent(false)) + using (var wait = new AutoResetEvent(initialState: false)) { - var request = new SftpRenameRequest(ProtocolVersion, NextRequestId, oldPath, newPath, Encoding, - response => - { - exception = GetSftpException(response); - wait.Set(); - }); + var request = new SftpRenameRequest(ProtocolVersion, + NextRequestId, + oldPath, + newPath, + _encoding, + response => + { + exception = GetSftpException(response); + _ = wait.Set(); + }); SendRequest(request); @@ -1255,6 +1648,35 @@ namespace Renci.SshNet.Sftp } } + public async Task RequestRenameAsync(string oldPath, string newPath, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + + using (cancellationToken.Register((s) => ((TaskCompletionSource) s).TrySetCanceled(), tcs, useSynchronizationContext: false)) + { + SendRequest(new SftpRenameRequest(ProtocolVersion, + NextRequestId, + oldPath, + newPath, + _encoding, + response => + { + if (response.StatusCode == StatusCodes.Ok) + { + _ = tcs.TrySetResult(true); + } + else + { + _ = tcs.TrySetException(GetSftpException(response)); + } + })); + + _ = await tcs.Task.ConfigureAwait(false); + } + } + /// /// Performs SSH_FXP_READLINK request. /// @@ -1272,19 +1694,22 @@ namespace Renci.SshNet.Sftp KeyValuePair[] result = null; - using (var wait = new AutoResetEvent(false)) + using (var wait = new AutoResetEvent(initialState: false)) { - var request = new SftpReadLinkRequest(ProtocolVersion, NextRequestId, path, Encoding, - response => - { - result = response.Files; - wait.Set(); - }, - response => - { - exception = GetSftpException(response); - wait.Set(); - }); + var request = new SftpReadLinkRequest(ProtocolVersion, + NextRequestId, + path, + _encoding, + response => + { + result = response.Files; + _ = wait.Set(); + }, + response => + { + exception = GetSftpException(response); + _ = wait.Set(); + }); SendRequest(request); @@ -1313,14 +1738,18 @@ namespace Renci.SshNet.Sftp SshException exception = null; - using (var wait = new AutoResetEvent(false)) + using (var wait = new AutoResetEvent(initialState: false)) { - var request = new SftpSymLinkRequest(ProtocolVersion, NextRequestId, linkpath, targetpath, Encoding, - response => - { - exception = GetSftpException(response); - wait.Set(); - }); + var request = new SftpSymLinkRequest(ProtocolVersion, + NextRequestId, + linkpath, + targetpath, + _encoding, + response => + { + exception = GetSftpException(response); + _ = wait.Set(); + }); SendRequest(request); @@ -1333,10 +1762,6 @@ namespace Renci.SshNet.Sftp } } - #endregion - - #region SFTP Extended API functions - /// /// Performs posix-rename@openssh.com extended request. /// @@ -1351,17 +1776,23 @@ namespace Renci.SshNet.Sftp SshException exception = null; - using (var wait = new AutoResetEvent(false)) + using (var wait = new AutoResetEvent(initialState: false)) { - var request = new PosixRenameRequest(ProtocolVersion, NextRequestId, oldPath, newPath, Encoding, - response => - { - exception = GetSftpException(response); - wait.Set(); - }); + var request = new PosixRenameRequest(ProtocolVersion, + NextRequestId, + oldPath, + newPath, + _encoding, + response => + { + exception = GetSftpException(response); + _ = wait.Set(); + }); if (!_supportedExtensions.ContainsKey(request.Name)) + { throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Extension method {0} currently not supported by the server.", request.Name)); + } SendRequest(request); @@ -1379,7 +1810,9 @@ namespace Renci.SshNet.Sftp /// /// The path. /// if set to true [null on error]. - /// + /// + /// A for the specified path. + /// public SftpFileSytemInformation RequestStatVfs(string path, bool nullOnError = false) { if (ProtocolVersion < 3) @@ -1391,22 +1824,27 @@ namespace Renci.SshNet.Sftp SftpFileSytemInformation information = null; - using (var wait = new AutoResetEvent(false)) + using (var wait = new AutoResetEvent(initialState: false)) { - var request = new StatVfsRequest(ProtocolVersion, NextRequestId, path, Encoding, - response => - { - information = response.GetReply().Information; - wait.Set(); - }, - response => - { - exception = GetSftpException(response); - wait.Set(); - }); + var request = new StatVfsRequest(ProtocolVersion, + NextRequestId, + path, + _encoding, + response => + { + information = response.GetReply().Information; + _ = wait.Set(); + }, + response => + { + exception = GetSftpException(response); + _ = wait.Set(); + }); if (!_supportedExtensions.ContainsKey(request.Name)) + { throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Extension method {0} currently not supported by the server.", request.Name)); + } SendRequest(request); @@ -1421,13 +1859,39 @@ namespace Renci.SshNet.Sftp return information; } + public async Task RequestStatVfsAsync(string path, CancellationToken cancellationToken) + { + if (ProtocolVersion < 3) + { + throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "SSH_FXP_EXTENDED operation is not supported in {0} version that server operates in.", ProtocolVersion)); + } + + cancellationToken.ThrowIfCancellationRequested(); + + var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + + using (cancellationToken.Register((s) => ((TaskCompletionSource) s).TrySetCanceled(), tcs, useSynchronizationContext: false)) + { + SendRequest(new StatVfsRequest(ProtocolVersion, + NextRequestId, + path, + _encoding, + response => tcs.TrySetResult(response.GetReply().Information), + response => tcs.TrySetException(GetSftpException(response)))); + + return await tcs.Task.ConfigureAwait(false); + } + } + /// /// Performs fstatvfs@openssh.com extended request. /// /// The file handle. /// if set to true [null on error]. - /// - /// + /// + /// A for the specified path. + /// + /// This operation is not supported for the current SFTP protocol version. internal SftpFileSytemInformation RequestFStatVfs(byte[] handle, bool nullOnError = false) { if (ProtocolVersion < 3) @@ -1439,28 +1903,32 @@ namespace Renci.SshNet.Sftp SftpFileSytemInformation information = null; - using (var wait = new AutoResetEvent(false)) + using (var wait = new AutoResetEvent(initialState: false)) { - var request = new FStatVfsRequest(ProtocolVersion, NextRequestId, handle, - response => - { - information = response.GetReply().Information; - wait.Set(); - }, - response => - { - exception = GetSftpException(response); - wait.Set(); - }); + var request = new FStatVfsRequest(ProtocolVersion, + NextRequestId, + handle, + response => + { + information = response.GetReply().Information; + _ = wait.Set(); + }, + response => + { + exception = GetSftpException(response); + _ = wait.Set(); + }); if (!_supportedExtensions.ContainsKey(request.Name)) + { throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Extension method {0} currently not supported by the server.", request.Name)); + } SendRequest(request); WaitOnHandle(wait, OperationTimeout); } - + if (!nullOnError && exception != null) { throw exception; @@ -1483,17 +1951,22 @@ namespace Renci.SshNet.Sftp SshException exception = null; - using (var wait = new AutoResetEvent(false)) + using (var wait = new AutoResetEvent(initialState: false)) { - var request = new HardLinkRequest(ProtocolVersion, NextRequestId, oldPath, newPath, - response => - { - exception = GetSftpException(response); - wait.Set(); - }); + var request = new HardLinkRequest(ProtocolVersion, + NextRequestId, + oldPath, + newPath, + response => + { + exception = GetSftpException(response); + _ = wait.Set(); + }); if (!_supportedExtensions.ContainsKey(request.Name)) + { throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Extension method {0} currently not supported by the server.", request.Name)); + } SendRequest(request); @@ -1506,8 +1979,6 @@ namespace Renci.SshNet.Sftp } } - #endregion - /// /// Calculates the optimal size of the buffer to read data from the channel. /// @@ -1521,7 +1992,7 @@ namespace Renci.SshNet.Sftp // bytes 1 to 4: packet length // byte 5: message type // bytes 6 to 9: response id - // bytes 10 to 13: length of payload‏ + // bytes 10 to 13: length of payload // // WinSCP uses a payload length of 32755 bytes // @@ -1557,8 +2028,10 @@ namespace Renci.SshNet.Sftp // 14-21: offset // 22-25: data length - // Putty uses data length of 4096 bytes - // WinSCP uses data length of 32739 bytes (total 32768 bytes; 32739 + 25 + 4 bytes for handle) + /* + * Putty uses data length of 4096 bytes + * WinSCP uses data length of 32739 bytes (total 32768 bytes; 32739 + 25 + 4 bytes for handle) + */ var lengthOfNonDataProtocolFields = 25u + (uint)handle.Length; var maximumPacketSize = Channel.RemotePacketSize; @@ -1567,6 +2040,7 @@ namespace Renci.SshNet.Sftp private static SshException GetSftpException(SftpStatusResponse response) { +#pragma warning disable IDE0010 // Add missing cases switch (response.StatusCode) { case StatusCodes.Ok: @@ -1578,6 +2052,7 @@ namespace Renci.SshNet.Sftp default: return new SshException(response.ErrorMessage); } +#pragma warning restore IDE0010 // Add missing cases } private void HandleResponse(SftpResponse response) @@ -1585,15 +2060,17 @@ namespace Renci.SshNet.Sftp SftpRequest request; lock (_requests) { - _requests.TryGetValue(response.ResponseId, out request); + _ = _requests.TryGetValue(response.ResponseId, out request); if (request != null) { - _requests.Remove(response.ResponseId); + _ = _requests.Remove(response.ResponseId); } } - if (request == null) + if (request is null) + { throw new InvalidOperationException("Invalid response."); + } request.Complete(response); } diff --git a/src/Renci.SshNet/Sftp/SftpSynchronizeDirectoriesAsyncResult.cs b/src/Renci.SshNet/Sftp/SftpSynchronizeDirectoriesAsyncResult.cs index 0d2644de..35aacd20 100644 --- a/src/Renci.SshNet/Sftp/SftpSynchronizeDirectoriesAsyncResult.cs +++ b/src/Renci.SshNet/Sftp/SftpSynchronizeDirectoriesAsyncResult.cs @@ -1,8 +1,9 @@ using System; using System.Collections.Generic; -using Renci.SshNet.Common; using System.IO; +using Renci.SshNet.Common; + namespace Renci.SshNet.Sftp { /// @@ -16,11 +17,11 @@ namespace Renci.SshNet.Sftp public int FilesRead { get; private set; } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The async callback. /// The state. - public SftpSynchronizeDirectoriesAsyncResult(AsyncCallback asyncCallback, Object state) + public SftpSynchronizeDirectoriesAsyncResult(AsyncCallback asyncCallback, object state) : base(asyncCallback, state) { } diff --git a/src/Renci.SshNet/Sftp/SftpUploadAsyncResult.cs b/src/Renci.SshNet/Sftp/SftpUploadAsyncResult.cs index 5f586daf..10069b25 100644 --- a/src/Renci.SshNet/Sftp/SftpUploadAsyncResult.cs +++ b/src/Renci.SshNet/Sftp/SftpUploadAsyncResult.cs @@ -1,4 +1,5 @@ using System; + using Renci.SshNet.Common; namespace Renci.SshNet.Sftp @@ -9,7 +10,7 @@ namespace Renci.SshNet.Sftp public class SftpUploadAsyncResult : AsyncResult { /// - /// Gets or sets a value indicating whether to cancel asynchronous upload operation + /// Gets or sets a value indicating whether to cancel asynchronous upload operation. /// /// /// true if upload operation to be canceled; otherwise, false. @@ -29,7 +30,7 @@ namespace Renci.SshNet.Sftp /// /// The async callback. /// The state. - public SftpUploadAsyncResult(AsyncCallback asyncCallback, Object state) + public SftpUploadAsyncResult(AsyncCallback asyncCallback, object state) : base(asyncCallback, state) { } diff --git a/src/Renci.SshNet/SftpClient.cs b/src/Renci.SshNet/SftpClient.cs index 3c22290d..877fed09 100644 --- a/src/Renci.SshNet/SftpClient.cs +++ b/src/Renci.SshNet/SftpClient.cs @@ -3,13 +3,16 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Globalization; -using System.Linq; using System.Net; using System.Text; using System.Threading; using Renci.SshNet.Abstractions; using Renci.SshNet.Common; using Renci.SshNet.Sftp; +using System.Threading.Tasks; +#if FEATURE_ASYNC_ENUMERABLE +using System.Runtime.CompilerServices; +#endif namespace Renci.SshNet { @@ -18,7 +21,7 @@ namespace Renci.SshNet /// public class SftpClient : BaseClient, ISftpClient { - private static readonly Encoding Utf8NoBOM = new UTF8Encoding(false, true); + private static readonly Encoding Utf8NoBOM = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true); /// /// Holds the instance that is used to communicate to the @@ -44,7 +47,7 @@ namespace Renci.SshNet /// one (-1) milliseconds, which indicates an infinite timeout period. /// /// The method was called after the client was disposed. - /// represents a value that is less than -1 or greater than milliseconds. + /// represents a value that is less than -1 or greater than milliseconds. public TimeSpan OperationTimeout { get @@ -58,8 +61,10 @@ namespace Renci.SshNet CheckDisposed(); var timeoutInMilliseconds = value.TotalMilliseconds; - if (timeoutInMilliseconds < -1d || timeoutInMilliseconds > int.MaxValue) - throw new ArgumentOutOfRangeException("value", "The timeout must represent a value between -1 and Int32.MaxValue, inclusive."); + if (timeoutInMilliseconds is < -1d or > int.MaxValue) + { + throw new ArgumentOutOfRangeException(nameof(value), "The timeout must represent a value between -1 and Int32.MaxValue, inclusive."); + } _operationTimeout = (int) timeoutInMilliseconds; } @@ -90,7 +95,7 @@ namespace Renci.SshNet /// SSH_FXP_DATA protocol fields. /// /// - /// The size of the each indivual SSH_FXP_DATA message is limited to the + /// The size of the each individual SSH_FXP_DATA message is limited to the /// local maximum packet size of the channel, which is set to 64 KB /// for SSH.NET. However, the peer can limit this even further. /// @@ -120,8 +125,12 @@ namespace Renci.SshNet get { CheckDisposed(); - if (_sftpSession == null) + + if (_sftpSession is null) + { throw new SshConnectionException("Client not connected."); + } + return _sftpSession.WorkingDirectory; } } @@ -136,8 +145,12 @@ namespace Renci.SshNet get { CheckDisposed(); - if (_sftpSession == null) + + if (_sftpSession is null) + { throw new SshConnectionException("Client not connected."); + } + return (int) _sftpSession.ProtocolVersion; } } @@ -161,7 +174,7 @@ namespace Renci.SshNet /// The connection info. /// is null. public SftpClient(ConnectionInfo connectionInfo) - : this(connectionInfo, false) + : this(connectionInfo, ownsConnectionInfo: false) { } @@ -177,7 +190,7 @@ namespace Renci.SshNet /// is not within and . [SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope", Justification = "Disposed in Dispose(bool) method.")] public SftpClient(string host, int port, string username, string password) - : this(new PasswordConnectionInfo(host, port, username, password), true) + : this(new PasswordConnectionInfo(host, port, username, password), ownsConnectionInfo: true) { } @@ -205,8 +218,8 @@ namespace Renci.SshNet /// is invalid. -or- is nunullll or contains only whitespace characters. /// is not within and . [SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope", Justification = "Disposed in Dispose(bool) method.")] - public SftpClient(string host, int port, string username, params PrivateKeyFile[] keyFiles) - : this(new PrivateKeyConnectionInfo(host, port, username, keyFiles), true) + public SftpClient(string host, int port, string username, params IPrivateKeySource[] keyFiles) + : this(new PrivateKeyConnectionInfo(host, port, username, keyFiles), ownsConnectionInfo: true) { } @@ -218,7 +231,7 @@ namespace Renci.SshNet /// Authentication private key file(s) . /// is null. /// is invalid. -or- is null or contains only whitespace characters. - public SftpClient(string host, string username, params PrivateKeyFile[] keyFiles) + public SftpClient(string host, string username, params IPrivateKeySource[] keyFiles) : this(host, ConnectionInfo.DefaultPort, username, keyFiles) { } @@ -273,11 +286,15 @@ namespace Renci.SshNet { CheckDisposed(); - if (path == null) - throw new ArgumentNullException("path"); + if (path is null) + { + throw new ArgumentNullException(nameof(path)); + } - if (_sftpSession == null) + if (_sftpSession is null) + { throw new SshConnectionException("Client not connected."); + } _sftpSession.ChangeDirectory(path); } @@ -312,11 +329,15 @@ namespace Renci.SshNet { CheckDisposed(); - if (path.IsNullOrWhiteSpace()) + if (string.IsNullOrWhiteSpace(path)) + { throw new ArgumentException(path); + } - if (_sftpSession == null) + if (_sftpSession is null) + { throw new SshConnectionException("Client not connected."); + } var fullPath = _sftpSession.GetCanonicalPath(path); @@ -337,11 +358,15 @@ namespace Renci.SshNet { CheckDisposed(); - if (path.IsNullOrWhiteSpace()) + if (string.IsNullOrWhiteSpace(path)) + { throw new ArgumentException("path"); + } - if (_sftpSession == null) + if (_sftpSession is null) + { throw new SshConnectionException("Client not connected."); + } var fullPath = _sftpSession.GetCanonicalPath(path); @@ -362,17 +387,53 @@ namespace Renci.SshNet { CheckDisposed(); - if (path.IsNullOrWhiteSpace()) + if (string.IsNullOrWhiteSpace(path)) + { throw new ArgumentException("path"); + } - if (_sftpSession == null) + if (_sftpSession is null) + { throw new SshConnectionException("Client not connected."); + } var fullPath = _sftpSession.GetCanonicalPath(path); _sftpSession.RequestRemove(fullPath); } + /// + /// Asynchronously deletes remote file specified by path. + /// + /// File to be deleted path. + /// The to observe. + /// A that represents the asynchronous delete operation. + /// is null or contains only whitespace characters. + /// Client is not connected. + /// was not found on the remote host. + /// Permission to delete the file was denied by the remote host. -or- A SSH command was denied by the server. + /// A SSH error where is the message from the remote host. + /// The method was called after the client was disposed. + public async Task DeleteFileAsync(string path, CancellationToken cancellationToken) + { + CheckDisposed(); + + if (string.IsNullOrWhiteSpace(path)) + { + throw new ArgumentException("path"); + } + + if (_sftpSession is null) + { + throw new SshConnectionException("Client not connected."); + } + + cancellationToken.ThrowIfCancellationRequested(); + + var fullPath = await _sftpSession.GetCanonicalPathAsync(path, cancellationToken).ConfigureAwait(false); + await _sftpSession.RequestRemoveAsync(fullPath, cancellationToken).ConfigureAwait(false); + } + /// /// Renames remote file from old path to new path. /// @@ -385,7 +446,45 @@ namespace Renci.SshNet /// The method was called after the client was disposed. public void RenameFile(string oldPath, string newPath) { - RenameFile(oldPath, newPath, false); + RenameFile(oldPath, newPath, isPosix: false); + } + + /// + /// Asynchronously renames remote file from old path to new path. + /// + /// Path to the old file location. + /// Path to the new file location. + /// The to observe. + /// A that represents the asynchronous rename operation. + /// is null. -or- or is null. + /// Client is not connected. + /// Permission to rename the file was denied by the remote host. -or- A SSH command was denied by the server. + /// A SSH error where is the message from the remote host. + /// The method was called after the client was disposed. + public async Task RenameFileAsync(string oldPath, string newPath, CancellationToken cancellationToken) + { + CheckDisposed(); + + if (oldPath is null) + { + throw new ArgumentNullException(nameof(oldPath)); + } + + if (newPath is null) + { + throw new ArgumentNullException(nameof(newPath)); + } + + if (_sftpSession is null) + { + throw new SshConnectionException("Client not connected."); + } + + cancellationToken.ThrowIfCancellationRequested(); + + var oldFullPath = await _sftpSession.GetCanonicalPathAsync(oldPath, cancellationToken).ConfigureAwait(false); + var newFullPath = await _sftpSession.GetCanonicalPathAsync(newPath, cancellationToken).ConfigureAwait(false); + await _sftpSession.RequestRenameAsync(oldFullPath, newFullPath, cancellationToken).ConfigureAwait(false); } /// @@ -403,14 +502,20 @@ namespace Renci.SshNet { CheckDisposed(); - if (oldPath == null) - throw new ArgumentNullException("oldPath"); + if (oldPath is null) + { + throw new ArgumentNullException(nameof(oldPath)); + } - if (newPath == null) - throw new ArgumentNullException("newPath"); + if (newPath is null) + { + throw new ArgumentNullException(nameof(newPath)); + } - if (_sftpSession == null) + if (_sftpSession is null) + { throw new SshConnectionException("Client not connected."); + } var oldFullPath = _sftpSession.GetCanonicalPath(oldPath); @@ -440,14 +545,20 @@ namespace Renci.SshNet { CheckDisposed(); - if (path.IsNullOrWhiteSpace()) + if (string.IsNullOrWhiteSpace(path)) + { throw new ArgumentException("path"); + } - if (linkPath.IsNullOrWhiteSpace()) + if (string.IsNullOrWhiteSpace(linkPath)) + { throw new ArgumentException("linkPath"); + } - if (_sftpSession == null) + if (_sftpSession is null) + { throw new SshConnectionException("Client not connected."); + } var fullPath = _sftpSession.GetCanonicalPath(path); @@ -469,13 +580,74 @@ namespace Renci.SshNet /// Permission to list the contents of the directory was denied by the remote host. -or- A SSH command was denied by the server. /// A SSH error where is the message from the remote host. /// The method was called after the client was disposed. - public IEnumerable ListDirectory(string path, Action listCallback = null) + public IEnumerable ListDirectory(string path, Action listCallback = null) { CheckDisposed(); return InternalListDirectory(path, listCallback); } +#if FEATURE_ASYNC_ENUMERABLE + /// + /// Asynchronously enumerates the files in remote directory. + /// + /// The path. + /// The to observe. + /// + /// An of that represents the asynchronous enumeration operation. + /// The enumeration contains an async stream of for the files in the directory specified by . + /// + /// is null. + /// Client is not connected. + /// Permission to list the contents of the directory was denied by the remote host. -or- A SSH command was denied by the server. + /// A SSH error where is the message from the remote host. + /// The method was called after the client was disposed. + public async IAsyncEnumerable ListDirectoryAsync(string path, [EnumeratorCancellation] CancellationToken cancellationToken) + { + CheckDisposed(); + + if (path is null) + { + throw new ArgumentNullException(nameof(path)); + } + + if (_sftpSession is null) + { + throw new SshConnectionException("Client not connected."); + } + + cancellationToken.ThrowIfCancellationRequested(); + + var fullPath = await _sftpSession.GetCanonicalPathAsync(path, cancellationToken).ConfigureAwait(false); + + var handle = await _sftpSession.RequestOpenDirAsync(fullPath, cancellationToken).ConfigureAwait(false); + try + { + var basePath = (fullPath[fullPath.Length - 1] == '/') ? + fullPath : + fullPath + '/'; + + while (true) + { + var files = await _sftpSession.RequestReadDirAsync(handle, cancellationToken).ConfigureAwait(false); + if (files is null) + { + break; + } + + foreach (var file in files) + { + yield return new SftpFile(_sftpSession, basePath + file.Key, file.Value); + } + } + } + finally + { + await _sftpSession.RequestCloseAsync(handle, cancellationToken).ConfigureAwait(false); + } + } +#endif //FEATURE_ASYNC_ENUMERABLE + /// /// Begins an asynchronous operation of retrieving list of files in remote directory. /// @@ -501,17 +673,14 @@ namespace Renci.SshNet { asyncResult.Update(count); - if (listCallback != null) - { - listCallback(count); - } + listCallback?.Invoke(count); }); - asyncResult.SetAsCompleted(result, false); + asyncResult.SetAsCompleted(result, completedSynchronously: false); } catch (Exception exp) { - asyncResult.SetAsCompleted(exp, false); + asyncResult.SetAsCompleted(exp, completedSynchronously: false); } }); @@ -526,12 +695,12 @@ namespace Renci.SshNet /// A list of files. /// /// The object did not come from the corresponding async method on this type.-or- was called multiple times with the same . - public IEnumerable EndListDirectory(IAsyncResult asyncResult) + public IEnumerable EndListDirectory(IAsyncResult asyncResult) { - var ar = asyncResult as SftpListDirectoryAsyncResult; - - if (ar == null || ar.EndInvokeCalled) + if (asyncResult is not SftpListDirectoryAsyncResult ar || ar.EndInvokeCalled) + { throw new ArgumentException("Either the IAsyncResult object did not come from the corresponding async method on this type, or EndExecute was called multiple times with the same IAsyncResult."); + } // Wait for operation to complete, then return result or throw exception return ar.EndInvoke(); @@ -542,21 +711,25 @@ namespace Renci.SshNet /// /// The path. /// - /// A reference to file object. + /// A reference to file object. /// /// Client is not connected. /// was not found on the remote host. /// is null. /// The method was called after the client was disposed. - public SftpFile Get(string path) + public ISftpFile Get(string path) { CheckDisposed(); - if (path == null) - throw new ArgumentNullException("path"); + if (path is null) + { + throw new ArgumentNullException(nameof(path)); + } - if (_sftpSession == null) + if (_sftpSession is null) + { throw new SshConnectionException("Client not connected."); + } var fullPath = _sftpSession.GetCanonicalPath(path); @@ -581,24 +754,28 @@ namespace Renci.SshNet { CheckDisposed(); - if (path.IsNullOrWhiteSpace()) + if (string.IsNullOrWhiteSpace(path)) + { throw new ArgumentException("path"); + } - if (_sftpSession == null) + if (_sftpSession is null) + { throw new SshConnectionException("Client not connected."); + } var fullPath = _sftpSession.GetCanonicalPath(path); // using SSH_FXP_REALPATH is not an alternative as the SFTP specification has not always // been clear on how the server should respond when the specified path is not present on // the server: - // + // // SSH 1 to 4: // No mention of how the server should respond if the path is not present on the server. // // SSH 5: // The server SHOULD fail the request if the path is not present on the server. - // + // // SSH 6: // Draft 06: The server SHOULD fail the request if the path is not present on the server. // Draft 07 to 13: The server MUST NOT fail the request if the path does not exist. @@ -608,7 +785,7 @@ namespace Renci.SshNet try { - _sftpSession.RequestLStat(fullPath); + _ = _sftpSession.RequestLStat(fullPath); return true; } catch (SftpPathNotFoundException) @@ -627,7 +804,7 @@ namespace Renci.SshNet /// is null or contains only whitespace characters. /// Client is not connected. /// Permission to perform the operation was denied by the remote host. -or- A SSH command was denied by the server. - /// was not found on the remote host./// + /// was not found on the remote host./// /// A SSH error where is the message from the remote host. /// The method was called after the client was disposed. /// @@ -637,7 +814,7 @@ namespace Renci.SshNet { CheckDisposed(); - InternalDownloadFile(path, output, null, downloadCallback); + InternalDownloadFile(path, output, asyncResult: null, downloadCallback); } /// @@ -659,7 +836,7 @@ namespace Renci.SshNet /// public IAsyncResult BeginDownloadFile(string path, Stream output) { - return BeginDownloadFile(path, output, null, null); + return BeginDownloadFile(path, output, asyncCallback: null, state: null); } /// @@ -682,7 +859,7 @@ namespace Renci.SshNet /// public IAsyncResult BeginDownloadFile(string path, Stream output, AsyncCallback asyncCallback) { - return BeginDownloadFile(path, output, asyncCallback, null); + return BeginDownloadFile(path, output, asyncCallback, state: null); } /// @@ -706,11 +883,15 @@ namespace Renci.SshNet { CheckDisposed(); - if (path.IsNullOrWhiteSpace()) + if (string.IsNullOrWhiteSpace(path)) + { throw new ArgumentException("path"); + } - if (output == null) - throw new ArgumentNullException("output"); + if (output is null) + { + throw new ArgumentNullException(nameof(output)); + } var asyncResult = new SftpDownloadAsyncResult(asyncCallback, state); @@ -722,17 +903,14 @@ namespace Renci.SshNet { asyncResult.Update(offset); - if (downloadCallback != null) - { - downloadCallback(offset); - } + downloadCallback?.Invoke(offset); }); - asyncResult.SetAsCompleted(null, false); + asyncResult.SetAsCompleted(exception: null, completedSynchronously: false); } catch (Exception exp) { - asyncResult.SetAsCompleted(exp, false); + asyncResult.SetAsCompleted(exp, completedSynchronously: false); } }); @@ -750,10 +928,10 @@ namespace Renci.SshNet /// A SSH error where is the message from the remote host. public void EndDownloadFile(IAsyncResult asyncResult) { - var ar = asyncResult as SftpDownloadAsyncResult; - - if (ar == null || ar.EndInvokeCalled) + if (asyncResult is not SftpDownloadAsyncResult ar || ar.EndInvokeCalled) + { throw new ArgumentException("Either the IAsyncResult object did not come from the corresponding async method on this type, or EndExecute was called multiple times with the same IAsyncResult."); + } // Wait for operation to complete, then return result or throw exception ar.EndInvoke(); @@ -776,7 +954,7 @@ namespace Renci.SshNet /// public void UploadFile(Stream input, string path, Action uploadCallback = null) { - UploadFile(input, path, true, uploadCallback); + UploadFile(input, path, canOverride: true, uploadCallback); } /// @@ -802,11 +980,15 @@ namespace Renci.SshNet var flags = Flags.Write | Flags.Truncate; if (canOverride) + { flags |= Flags.CreateNewOrOpen; + } else + { flags |= Flags.CreateNew; + } - InternalUploadFile(input, path, flags, null, uploadCallback); + InternalUploadFile(input, path, flags, asyncResult: null, uploadCallback); } /// @@ -833,7 +1015,7 @@ namespace Renci.SshNet /// public IAsyncResult BeginUploadFile(Stream input, string path) { - return BeginUploadFile(input, path, true, null, null); + return BeginUploadFile(input, path, canOverride: true, asyncCallback: null, state: null); } /// @@ -861,7 +1043,7 @@ namespace Renci.SshNet /// public IAsyncResult BeginUploadFile(Stream input, string path, AsyncCallback asyncCallback) { - return BeginUploadFile(input, path, true, asyncCallback, null); + return BeginUploadFile(input, path, canOverride: true, asyncCallback, state: null); } /// @@ -891,7 +1073,7 @@ namespace Renci.SshNet /// public IAsyncResult BeginUploadFile(Stream input, string path, AsyncCallback asyncCallback, object state, Action uploadCallback = null) { - return BeginUploadFile(input, path, true, asyncCallback, state, uploadCallback); + return BeginUploadFile(input, path, canOverride: true, asyncCallback, state, uploadCallback); } /// @@ -923,18 +1105,26 @@ namespace Renci.SshNet { CheckDisposed(); - if (input == null) - throw new ArgumentNullException("input"); + if (input is null) + { + throw new ArgumentNullException(nameof(input)); + } - if (path.IsNullOrWhiteSpace()) + if (string.IsNullOrWhiteSpace(path)) + { throw new ArgumentException("path"); + } var flags = Flags.Write | Flags.Truncate; if (canOverride) + { flags |= Flags.CreateNewOrOpen; + } else + { flags |= Flags.CreateNew; + } var asyncResult = new SftpUploadAsyncResult(asyncCallback, state); @@ -943,21 +1133,16 @@ namespace Renci.SshNet try { InternalUploadFile(input, path, flags, asyncResult, offset => - { - asyncResult.Update(offset); - - if (uploadCallback != null) { - uploadCallback(offset); - } + asyncResult.Update(offset); + uploadCallback?.Invoke(offset); + }); - }); - - asyncResult.SetAsCompleted(null, false); + asyncResult.SetAsCompleted(exception: null, completedSynchronously: false); } catch (Exception exp) { - asyncResult.SetAsCompleted(exp, false); + asyncResult.SetAsCompleted(exception: exp, completedSynchronously: false); } }); @@ -975,10 +1160,10 @@ namespace Renci.SshNet /// A SSH error where is the message from the remote host. public void EndUploadFile(IAsyncResult asyncResult) { - var ar = asyncResult as SftpUploadAsyncResult; - - if (ar == null || ar.EndInvokeCalled) + if (asyncResult is not SftpUploadAsyncResult ar || ar.EndInvokeCalled) + { throw new ArgumentException("Either the IAsyncResult object did not come from the corresponding async method on this type, or EndExecute was called multiple times with the same IAsyncResult."); + } // Wait for operation to complete, then return result or throw exception ar.EndInvoke(); @@ -998,17 +1183,53 @@ namespace Renci.SshNet { CheckDisposed(); - if (path == null) - throw new ArgumentNullException("path"); + if (path is null) + { + throw new ArgumentNullException(nameof(path)); + } - if (_sftpSession == null) + if (_sftpSession is null) + { throw new SshConnectionException("Client not connected."); + } var fullPath = _sftpSession.GetCanonicalPath(path); return _sftpSession.RequestStatVfs(fullPath); } + /// + /// Asynchronously gets status using statvfs@openssh.com request. + /// + /// The path. + /// The to observe. + /// + /// A that represents the status operation. + /// The task result contains the instance that contains file status information. + /// + /// Client is not connected. + /// is null. + /// The method was called after the client was disposed. + public async Task GetStatusAsync(string path, CancellationToken cancellationToken) + { + CheckDisposed(); + + if (path is null) + { + throw new ArgumentNullException(nameof(path)); + } + + if (_sftpSession is null) + { + throw new SshConnectionException("Client not connected."); + } + + cancellationToken.ThrowIfCancellationRequested(); + + var fullPath = await _sftpSession.GetCanonicalPathAsync(path, cancellationToken).ConfigureAwait(false); + return await _sftpSession.RequestStatVfsAsync(fullPath, cancellationToken).ConfigureAwait(false); + } + #region File Methods /// @@ -1027,8 +1248,10 @@ namespace Renci.SshNet { CheckDisposed(); - if (contents == null) - throw new ArgumentNullException("contents"); + if (contents is null) + { + throw new ArgumentNullException(nameof(contents)); + } using (var stream = AppendText(path)) { @@ -1053,8 +1276,10 @@ namespace Renci.SshNet { CheckDisposed(); - if (contents == null) - throw new ArgumentNullException("contents"); + if (contents is null) + { + throw new ArgumentNullException(nameof(contents)); + } using (var stream = AppendText(path, encoding)) { @@ -1138,8 +1363,10 @@ namespace Renci.SshNet { CheckDisposed(); - if (encoding == null) - throw new ArgumentNullException("encoding"); + if (encoding is null) + { + throw new ArgumentNullException(nameof(encoding)); + } return new StreamWriter(new SftpFileStream(_sftpSession, path, FileMode.Append, FileAccess.Write, (int) _bufferSize), encoding); } @@ -1356,6 +1583,39 @@ namespace Renci.SshNet return new SftpFileStream(_sftpSession, path, mode, access, (int) _bufferSize); } + /// + /// Asynchronously opens a on the specified path, with the specified mode and access. + /// + /// The file to open. + /// A value that specifies whether a file is created if one does not exist, and determines whether the contents of existing files are retained or overwritten. + /// A value that specifies the operations that can be performed on the file. + /// The to observe. + /// + /// A that represents the asynchronous open operation. + /// The task result contains the that provides access to the specified file, with the specified mode and access. + /// + /// is null. + /// Client is not connected. + /// The method was called after the client was disposed. + public Task OpenAsync(string path, FileMode mode, FileAccess access, CancellationToken cancellationToken) + { + CheckDisposed(); + + if (path is null) + { + throw new ArgumentNullException(nameof(path)); + } + + if (_sftpSession is null) + { + throw new SshConnectionException("Client not connected."); + } + + cancellationToken.ThrowIfCancellationRequested(); + + return SftpFileStream.OpenAsync(_sftpSession, path, mode, access, (int)_bufferSize, cancellationToken); + } + /// /// Opens an existing file for reading. /// @@ -1421,7 +1681,7 @@ namespace Renci.SshNet using (var stream = OpenRead(path)) { var buffer = new byte[stream.Length]; - stream.Read(buffer, 0, buffer.Length); + _ = stream.Read(buffer, 0, buffer.Length); return buffer; } } @@ -1541,10 +1801,11 @@ namespace Renci.SshNet /// /// The file for which to set the access date and time information. /// A containing the value to set for the last access date and time of path. This value is expressed in local time. - [Obsolete("Note: This method currently throws NotImplementedException because it has not yet been implemented.")] public void SetLastAccessTime(string path, DateTime lastAccessTime) { - throw new NotImplementedException(); + var attributes = GetAttributes(path); + attributes.LastAccessTime = lastAccessTime; + SetAttributes(path, attributes); } /// @@ -1552,10 +1813,11 @@ namespace Renci.SshNet /// /// The file for which to set the access date and time information. /// A containing the value to set for the last access date and time of path. This value is expressed in UTC time. - [Obsolete("Note: This method currently throws NotImplementedException because it has not yet been implemented.")] public void SetLastAccessTimeUtc(string path, DateTime lastAccessTimeUtc) { - throw new NotImplementedException(); + var attributes = GetAttributes(path); + attributes.LastAccessTimeUtc = lastAccessTimeUtc; + SetAttributes(path, attributes); } /// @@ -1563,10 +1825,11 @@ namespace Renci.SshNet /// /// The file for which to set the date and time information. /// A containing the value to set for the last write date and time of path. This value is expressed in local time. - [Obsolete("Note: This method currently throws NotImplementedException because it has not yet been implemented.")] public void SetLastWriteTime(string path, DateTime lastWriteTime) { - throw new NotImplementedException(); + var attributes = GetAttributes(path); + attributes.LastWriteTime = lastWriteTime; + SetAttributes(path, attributes); } /// @@ -1574,10 +1837,11 @@ namespace Renci.SshNet /// /// The file for which to set the date and time information. /// A containing the value to set for the last write date and time of path. This value is expressed in UTC time. - [Obsolete("Note: This method currently throws NotImplementedException because it has not yet been implemented.")] public void SetLastWriteTimeUtc(string path, DateTime lastWriteTimeUtc) { - throw new NotImplementedException(); + var attributes = GetAttributes(path); + attributes.LastWriteTimeUtc = lastWriteTimeUtc; + SetAttributes(path, attributes); } /// @@ -1782,8 +2046,10 @@ namespace Renci.SshNet { CheckDisposed(); - if (_sftpSession == null) + if (_sftpSession is null) + { throw new SshConnectionException("Client not connected."); + } var fullPath = _sftpSession.GetCanonicalPath(path); @@ -1802,8 +2068,10 @@ namespace Renci.SshNet { CheckDisposed(); - if (_sftpSession == null) + if (_sftpSession is null) + { throw new SshConnectionException("Client not connected."); + } var fullPath = _sftpSession.GetCanonicalPath(path); @@ -1835,14 +2103,20 @@ namespace Renci.SshNet /// is null. /// is null or contains only whitespace. /// was not found on the remote host. + /// If a problem occurs while copying the file public IEnumerable SynchronizeDirectories(string sourcePath, string destinationPath, string searchPattern) { - if (sourcePath == null) - throw new ArgumentNullException("sourcePath"); - if (destinationPath.IsNullOrWhiteSpace()) - throw new ArgumentException("destinationPath"); + if (sourcePath is null) + { + throw new ArgumentNullException(nameof(sourcePath)); + } - return InternalSynchronizeDirectories(sourcePath, destinationPath, searchPattern, null); + if (string.IsNullOrWhiteSpace(destinationPath)) + { + throw new ArgumentException("destinationPath"); + } + + return InternalSynchronizeDirectories(sourcePath, destinationPath, searchPattern, asynchResult: null); } /// @@ -1858,28 +2132,34 @@ namespace Renci.SshNet /// /// is null. /// is null or contains only whitespace. + /// If a problem occurs while copying the file public IAsyncResult BeginSynchronizeDirectories(string sourcePath, string destinationPath, string searchPattern, AsyncCallback asyncCallback, object state) { - if (sourcePath == null) - throw new ArgumentNullException("sourcePath"); - if (destinationPath.IsNullOrWhiteSpace()) + if (sourcePath is null) + { + throw new ArgumentNullException(nameof(sourcePath)); + } + + if (string.IsNullOrWhiteSpace(destinationPath)) + { throw new ArgumentException("destDir"); + } var asyncResult = new SftpSynchronizeDirectoriesAsyncResult(asyncCallback, state); ThreadAbstraction.ExecuteThread(() => - { - try { - var result = InternalSynchronizeDirectories(sourcePath, destinationPath, searchPattern, asyncResult); + try + { + var result = InternalSynchronizeDirectories(sourcePath, destinationPath, searchPattern, asyncResult); - asyncResult.SetAsCompleted(result, false); - } - catch (Exception exp) - { - asyncResult.SetAsCompleted(exp, false); - } - }); + asyncResult.SetAsCompleted(result, completedSynchronously: false); + } + catch (Exception exp) + { + asyncResult.SetAsCompleted(exp, completedSynchronously: false); + } + }); return asyncResult; } @@ -1895,10 +2175,10 @@ namespace Renci.SshNet /// The destination path was not found on the remote host. public IEnumerable EndSynchronizeDirectories(IAsyncResult asyncResult) { - var ar = asyncResult as SftpSynchronizeDirectoriesAsyncResult; - - if (ar == null || ar.EndInvokeCalled) + if (asyncResult is not SftpSynchronizeDirectoriesAsyncResult ar || ar.EndInvokeCalled) + { throw new ArgumentException("Either the IAsyncResult object did not come from the corresponding async method on this type, or EndExecute was called multiple times with the same IAsyncResult."); + } // Wait for operation to complete, then return result or throw exception return ar.EndInvoke(); @@ -1907,66 +2187,77 @@ namespace Renci.SshNet private IEnumerable InternalSynchronizeDirectories(string sourcePath, string destinationPath, string searchPattern, SftpSynchronizeDirectoriesAsyncResult asynchResult) { if (!Directory.Exists(sourcePath)) + { throw new FileNotFoundException(string.Format("Source directory not found: {0}", sourcePath)); + } var uploadedFiles = new List(); var sourceDirectory = new DirectoryInfo(sourcePath); - var sourceFiles = FileSystemAbstraction.EnumerateFiles(sourceDirectory, searchPattern).ToList(); - if (sourceFiles.Count == 0) - return uploadedFiles; - - #region Existing Files at The Destination - - var destFiles = InternalListDirectory(destinationPath, null); - var destDict = new Dictionary(); - foreach (var destFile in destFiles) + using (var sourceFiles = sourceDirectory.EnumerateFiles(searchPattern).GetEnumerator()) { - if (destFile.IsDirectory) - continue; - destDict.Add(destFile.Name, destFile); - } - - #endregion - - #region Upload the difference - - const Flags uploadFlag = Flags.Write | Flags.Truncate | Flags.CreateNewOrOpen; - foreach (var localFile in sourceFiles) - { - var isDifferent = !destDict.ContainsKey(localFile.Name); - - if (!isDifferent) + if (!sourceFiles.MoveNext()) { - var temp = destDict[localFile.Name]; - // TODO: Use md5 to detect a difference - //ltang: File exists at the destination => Using filesize to detect the difference - isDifferent = localFile.Length != temp.Length; + return uploadedFiles; } - if (isDifferent) + #region Existing Files at The Destination + + var destFiles = InternalListDirectory(destinationPath, listCallback: null); + var destDict = new Dictionary(); + foreach (var destFile in destFiles) { - var remoteFileName = string.Format(CultureInfo.InvariantCulture, @"{0}/{1}", destinationPath, localFile.Name); - try + if (destFile.IsDirectory) { - using (var file = File.OpenRead(localFile.FullName)) - { - InternalUploadFile(file, remoteFileName, uploadFlag, null, null); - } - - uploadedFiles.Add(localFile); - - if (asynchResult != null) - { - asynchResult.Update(uploadedFiles.Count); - } + continue; } - catch (Exception ex) + + destDict.Add(destFile.Name, destFile); + } + + #endregion + + #region Upload the difference + + const Flags uploadFlag = Flags.Write | Flags.Truncate | Flags.CreateNewOrOpen; + do + { + var localFile = sourceFiles.Current; + if (localFile is null) { - throw new Exception(string.Format("Failed to upload {0} to {1}", localFile.FullName, remoteFileName), ex); + continue; + } + + var isDifferent = true; + if (destDict.TryGetValue(localFile.Name, out var remoteFile)) + { + // TODO: Use md5 to detect a difference + //ltang: File exists at the destination => Using filesize to detect the difference + isDifferent = localFile.Length != remoteFile.Length; + } + + if (isDifferent) + { + var remoteFileName = string.Format(CultureInfo.InvariantCulture, @"{0}/{1}", destinationPath, localFile.Name); + try + { + using (var file = File.OpenRead(localFile.FullName)) + { + InternalUploadFile(file, remoteFileName, uploadFlag, asyncResult: null, uploadCallback: null); + } + + uploadedFiles.Add(localFile); + + asynchResult?.Update(uploadedFiles.Count); + } + catch (Exception ex) + { + throw new SshException($"Failed to upload {localFile.FullName} to {remoteFileName}", ex); + } } } + while (sourceFiles.MoveNext()); } #endregion @@ -1986,13 +2277,17 @@ namespace Renci.SshNet /// /// is null. /// Client not connected. - private IEnumerable InternalListDirectory(string path, Action listCallback) + private IEnumerable InternalListDirectory(string path, Action listCallback) { - if (path == null) - throw new ArgumentNullException("path"); + if (path is null) + { + throw new ArgumentNullException(nameof(path)); + } - if (_sftpSession == null) + if (_sftpSession is null) + { throw new SshConnectionException("Client not connected."); + } var fullPath = _sftpSession.GetCanonicalPath(path); @@ -2001,19 +2296,25 @@ namespace Renci.SshNet var basePath = fullPath; if (!basePath.EndsWith("/")) + { basePath = string.Format("{0}/", fullPath); + } - var result = new List(); + var result = new List(); var files = _sftpSession.RequestReadDir(handle); - while (files != null) + while (files is not null) { - result.AddRange(from f in files - select new SftpFile(_sftpSession, string.Format(CultureInfo.InvariantCulture, "{0}{1}", basePath, f.Key), f.Value)); + foreach (var f in files) + { + result.Add(new SftpFile(_sftpSession, + string.Format(CultureInfo.InvariantCulture, "{0}{1}", basePath, f.Key), + f.Value)); + } // Call callback to report number of files read - if (listCallback != null) + if (listCallback is not null) { // Execute callback on different thread ThreadAbstraction.ExecuteThread(() => listCallback(result.Count)); @@ -2039,14 +2340,20 @@ namespace Renci.SshNet /// Client not connected. private void InternalDownloadFile(string path, Stream output, SftpDownloadAsyncResult asyncResult, Action downloadCallback) { - if (output == null) - throw new ArgumentNullException("output"); + if (output is null) + { + throw new ArgumentNullException(nameof(output)); + } - if (path.IsNullOrWhiteSpace()) + if (string.IsNullOrWhiteSpace(path)) + { throw new ArgumentException("path"); + } - if (_sftpSession == null) + if (_sftpSession is null) + { throw new SshConnectionException("Client not connected."); + } var fullPath = _sftpSession.GetCanonicalPath(path); @@ -2056,19 +2363,23 @@ namespace Renci.SshNet while (true) { - // Cancel download - if (asyncResult != null && asyncResult.IsDownloadCanceled) + // Cancel download + if (asyncResult is not null && asyncResult.IsDownloadCanceled) + { break; + } var data = fileReader.Read(); if (data.Length == 0) + { break; + } output.Write(data, 0, data.Length); totalBytesRead += (ulong) data.Length; - if (downloadCallback != null) + if (downloadCallback is not null) { // copy offset to ensure it's not modified between now and execution of callback var downloadOffset = totalBytesRead; @@ -2093,14 +2404,20 @@ namespace Renci.SshNet /// Client not connected. private void InternalUploadFile(Stream input, string path, Flags flags, SftpUploadAsyncResult asyncResult, Action uploadCallback) { - if (input == null) - throw new ArgumentNullException("input"); + if (input is null) + { + throw new ArgumentNullException(nameof(input)); + } - if (path.IsNullOrWhiteSpace()) + if (string.IsNullOrWhiteSpace(path)) + { throw new ArgumentException("path"); + } - if (_sftpSession == null) + if (_sftpSession is null) + { throw new SshConnectionException("Client not connected."); + } var fullPath = _sftpSession.GetCanonicalPath(path); @@ -2113,34 +2430,37 @@ namespace Renci.SshNet var bytesRead = input.Read(buffer, 0, buffer.Length); var expectedResponses = 0; - var responseReceivedWaitHandle = new AutoResetEvent(false); + var responseReceivedWaitHandle = new AutoResetEvent(initialState: false); do { - // Cancel upload - if (asyncResult != null && asyncResult.IsUploadCanceled) + // Cancel upload + if (asyncResult is not null && asyncResult.IsUploadCanceled) + { break; + } if (bytesRead > 0) { var writtenBytes = offset + (ulong) bytesRead; - _sftpSession.RequestWrite(handle, offset, buffer, 0, bytesRead, null, s => + _sftpSession.RequestWrite(handle, offset, buffer, offset: 0, bytesRead, wait: null, s => { if (s.StatusCode == StatusCodes.Ok) { - Interlocked.Decrement(ref expectedResponses); - responseReceivedWaitHandle.Set(); + _ = Interlocked.Decrement(ref expectedResponses); + _ = responseReceivedWaitHandle.Set(); // Call callback to report number of bytes written - if (uploadCallback != null) + if (uploadCallback is not null) { // Execute callback on different thread ThreadAbstraction.ExecuteThread(() => uploadCallback(writtenBytes)); } } }); - Interlocked.Increment(ref expectedResponses); + + _ = Interlocked.Increment(ref expectedResponses); offset += (ulong) bytesRead; @@ -2151,7 +2471,8 @@ namespace Renci.SshNet // Wait for expectedResponses to change _sftpSession.WaitOnHandle(responseReceivedWaitHandle, _operationTimeout); } - } while (expectedResponses > 0 || bytesRead > 0); + } + while (expectedResponses > 0 || bytesRead > 0); _sftpSession.RequestClose(handle); } @@ -2176,7 +2497,7 @@ namespace Renci.SshNet // disconnect, dispose and dereference the SFTP session since we create a new SFTP session // on each connect var sftpSession = _sftpSession; - if (sftpSession != null) + if (sftpSession is not null) { _sftpSession = null; sftpSession.Dispose(); @@ -2194,7 +2515,7 @@ namespace Renci.SshNet if (disposing) { var sftpSession = _sftpSession; - if (sftpSession != null) + if (sftpSession is not null) { _sftpSession = null; sftpSession.Dispose(); diff --git a/src/Renci.SshNet/Shell.cs b/src/Renci.SshNet/Shell.cs index c508be89..0990ae81 100644 --- a/src/Renci.SshNet/Shell.cs +++ b/src/Renci.SshNet/Shell.cs @@ -1,32 +1,33 @@ using System; +using System.Collections.Generic; using System.IO; using System.Threading; + +using Renci.SshNet.Abstractions; using Renci.SshNet.Channels; using Renci.SshNet.Common; -using System.Collections.Generic; -using Renci.SshNet.Abstractions; namespace Renci.SshNet { /// - /// Represents instance of the SSH shell object + /// Represents instance of the SSH shell object. /// public class Shell : IDisposable { private readonly ISession _session; - private IChannelSession _channel; - private EventWaitHandle _channelClosedWaitHandle; - private Stream _input; private readonly string _terminalName; private readonly uint _columns; private readonly uint _rows; private readonly uint _width; private readonly uint _height; private readonly IDictionary _terminalModes; - private EventWaitHandle _dataReaderTaskCompleted; private readonly Stream _outputStream; private readonly Stream _extendedOutputStream; private readonly int _bufferSize; + private EventWaitHandle _dataReaderTaskCompleted; + private IChannelSession _channel; + private EventWaitHandle _channelClosedWaitHandle; + private Stream _input; /// /// Gets a value indicating whether this shell is started. @@ -101,10 +102,7 @@ namespace Renci.SshNet throw new SshException("Shell is started."); } - if (Starting != null) - { - Starting(this, new EventArgs()); - } + Starting?.Invoke(this, EventArgs.Empty); _channel = _session.CreateChannelSession(); _channel.DataReceived += Channel_DataReceived; @@ -114,13 +112,13 @@ namespace Renci.SshNet _session.ErrorOccured += Session_ErrorOccured; _channel.Open(); - _channel.SendPseudoTerminalRequest(_terminalName, _columns, _rows, _width, _height, _terminalModes); - _channel.SendShellRequest(); + _ = _channel.SendPseudoTerminalRequest(_terminalName, _columns, _rows, _width, _height, _terminalModes); + _ = _channel.SendShellRequest(); - _channelClosedWaitHandle = new AutoResetEvent(false); + _channelClosedWaitHandle = new AutoResetEvent(initialState: false); - // Start input stream listener - _dataReaderTaskCompleted = new ManualResetEvent(false); + // Start input stream listener + _dataReaderTaskCompleted = new ManualResetEvent(initialState: false); ThreadAbstraction.ExecuteThread(() => { try @@ -129,7 +127,6 @@ namespace Renci.SshNet while (_channel.IsOpen) { -#if FEATURE_STREAM_TAP var readTask = _input.ReadAsync(buffer, 0, buffer.Length); var readWaitHandle = ((IAsyncResult) readTask).AsyncWaitHandle; @@ -139,24 +136,7 @@ namespace Renci.SshNet _channel.SendData(buffer, 0, read); continue; } -#elif FEATURE_STREAM_APM - var asyncResult = _input.BeginRead(buffer, 0, buffer.Length, result => - { - // If input stream is closed and disposed already don't finish reading the stream - if (_input == null) - return; - var read = _input.EndRead(result); - _channel.SendData(buffer, 0, read); - }, null); - - WaitHandle.WaitAny(new[] { asyncResult.AsyncWaitHandle, _channelClosedWaitHandle }); - - if (asyncResult.IsCompleted) - continue; -#else - #error Async receive is not implemented. -#endif break; } } @@ -166,16 +146,13 @@ namespace Renci.SshNet } finally { - _dataReaderTaskCompleted.Set(); + _ = _dataReaderTaskCompleted.Set(); } }); IsStarted = true; - if (Started != null) - { - Started(this, new EventArgs()); - } + Started?.Invoke(this, EventArgs.Empty); } /// @@ -189,10 +166,7 @@ namespace Renci.SshNet throw new SshException("Shell is not started."); } - if (_channel != null) - { - _channel.Dispose(); - } + _channel?.Dispose(); } private void Session_ErrorOccured(object sender, ExceptionEventArgs e) @@ -202,11 +176,7 @@ namespace Renci.SshNet private void RaiseError(ExceptionEventArgs e) { - var handler = ErrorOccurred; - if (handler != null) - { - handler(this, e); - } + ErrorOccurred?.Invoke(this, e); } private void Session_Disconnected(object sender, EventArgs e) @@ -216,35 +186,29 @@ namespace Renci.SshNet private void Channel_ExtendedDataReceived(object sender, ChannelExtendedDataEventArgs e) { - if (_extendedOutputStream != null) - { - _extendedOutputStream.Write(e.Data, 0, e.Data.Length); - } + _extendedOutputStream?.Write(e.Data, 0, e.Data.Length); } private void Channel_DataReceived(object sender, ChannelDataEventArgs e) { - if (_outputStream != null) - { - _outputStream.Write(e.Data, 0, e.Data.Length); - } + _outputStream?.Write(e.Data, 0, e.Data.Length); } private void Channel_Closed(object sender, ChannelEventArgs e) { - if (Stopping != null) + if (Stopping is not null) { - // Handle event on different thread - ThreadAbstraction.ExecuteThread(() => Stopping(this, new EventArgs())); + // Handle event on different thread + ThreadAbstraction.ExecuteThread(() => Stopping(this, EventArgs.Empty)); } _channel.Dispose(); - _channelClosedWaitHandle.Set(); + _ = _channelClosedWaitHandle.Set(); _input.Dispose(); _input = null; - _dataReaderTaskCompleted.WaitOne(_session.ConnectionInfo.Timeout); + _ = _dataReaderTaskCompleted.WaitOne(_session.ConnectionInfo.Timeout); _dataReaderTaskCompleted.Dispose(); _dataReaderTaskCompleted = null; @@ -256,8 +220,8 @@ namespace Renci.SshNet if (Stopped != null) { - // Handle event on different thread - ThreadAbstraction.ExecuteThread(() => Stopped(this, new EventArgs())); + // Handle event on different thread + ThreadAbstraction.ExecuteThread(() => Stopped(this, EventArgs.Empty)); } _channel = null; @@ -272,15 +236,15 @@ namespace Renci.SshNet /// private void UnsubscribeFromSessionEvents(ISession session) { - if (session == null) + if (session is null) + { return; + } session.Disconnected -= Session_Disconnected; session.ErrorOccured -= Session_ErrorOccured; } - #region IDisposable Members - private bool _disposed; /// @@ -288,39 +252,41 @@ namespace Renci.SshNet /// public void Dispose() { - Dispose(true); + Dispose(disposing: true); GC.SuppressFinalize(this); } /// - /// Releases unmanaged and - optionally - managed resources + /// Releases unmanaged and - optionally - managed resources. /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected virtual void Dispose(bool disposing) { if (_disposed) + { return; + } if (disposing) { UnsubscribeFromSessionEvents(_session); var channelClosedWaitHandle = _channelClosedWaitHandle; - if (channelClosedWaitHandle != null) + if (channelClosedWaitHandle is not null) { channelClosedWaitHandle.Dispose(); _channelClosedWaitHandle = null; } var channel = _channel; - if (channel != null) + if (channel is not null) { channel.Dispose(); _channel = null; } var dataReaderTaskCompleted = _dataReaderTaskCompleted; - if (dataReaderTaskCompleted != null) + if (dataReaderTaskCompleted is not null) { dataReaderTaskCompleted.Dispose(); _dataReaderTaskCompleted = null; @@ -331,15 +297,11 @@ namespace Renci.SshNet } /// - /// Releases unmanaged resources and performs other cleanup operations before the - /// is reclaimed by garbage collection. + /// Finalizes an instance of the class. /// ~Shell() { - Dispose(false); + Dispose(disposing: false); } - - #endregion - } } diff --git a/src/Renci.SshNet/ShellStream.cs b/src/Renci.SshNet/ShellStream.cs index 3274fe19..37c3ac4d 100644 --- a/src/Renci.SshNet/ShellStream.cs +++ b/src/Renci.SshNet/ShellStream.cs @@ -1,12 +1,13 @@ using System; using System.Collections.Generic; -using System.Text; using System.IO; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading; + +using Renci.SshNet.Abstractions; using Renci.SshNet.Channels; using Renci.SshNet.Common; -using System.Threading; -using System.Text.RegularExpressions; -using Renci.SshNet.Abstractions; namespace Renci.SshNet { @@ -23,7 +24,7 @@ namespace Renci.SshNet private readonly Queue _incoming; private readonly Queue _outgoing; private IChannelSession _channel; - private AutoResetEvent _dataReceived = new AutoResetEvent(false); + private AutoResetEvent _dataReceived = new AutoResetEvent(initialState: false); private bool _isDisposed; /// @@ -37,7 +38,7 @@ namespace Renci.SshNet public event EventHandler ErrorOccurred; /// - /// Gets a value that indicates whether data is available on the to be read. + /// Gets a value indicating whether data is available on the to be read. /// /// /// true if data is available to be read; otherwise, false. @@ -65,13 +66,13 @@ namespace Renci.SshNet } /// - /// Initializes a new instance. + /// Initializes a new instance of the class. /// /// The SSH session. /// The TERM environment variable. /// The terminal width in columns. /// The terminal width in rows. - /// The terminal height in pixels. + /// The terminal width in pixels. /// The terminal height in pixels. /// The terminal mode values. /// The size of the buffer. @@ -95,10 +96,12 @@ namespace Renci.SshNet try { _channel.Open(); + if (!_channel.SendPseudoTerminalRequest(terminalName, columns, rows, width, height, terminalModeValues)) { throw new SshException("The pseudo-terminal request was not accepted by the server. Consult the server log for more information."); } + if (!_channel.SendShellRequest()) { throw new SshException("The request to start a shell was not accepted by the server. Consult the server log for more information."); @@ -112,8 +115,6 @@ namespace Renci.SshNet } } - #region Stream overide methods - /// /// Gets a value indicating whether the current stream supports reading. /// @@ -150,11 +151,11 @@ namespace Renci.SshNet /// /// Clears all buffers for this stream and causes any buffered data to be written to the underlying device. /// - /// An I/O error occurs. + /// An I/O error occurs. /// Methods were called after the stream was closed. public override void Flush() { - if (_channel == null) + if (_channel is null) { throw new ObjectDisposedException("ShellStream"); } @@ -189,9 +190,9 @@ namespace Renci.SshNet /// /// The current position within the stream. /// - /// An I/O error occurs. - /// The stream does not support seeking. - /// Methods were called after the stream was closed. + /// An I/O error occurs. + /// The stream does not support seeking. + /// Methods were called after the stream was closed. public override long Position { get { return 0; } @@ -207,12 +208,12 @@ namespace Renci.SshNet /// /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached. /// - /// The sum of and is larger than the buffer length. - /// is null. - /// or is negative. - /// An I/O error occurs. - /// The stream does not support reading. - /// Methods were called after the stream was closed. + /// The sum of and is larger than the buffer length. + /// is null. + /// or is negative. + /// An I/O error occurs. + /// The stream does not support reading. + /// Methods were called after the stream was closed. public override int Read(byte[] buffer, int offset, int count) { var i = 0; @@ -232,13 +233,13 @@ namespace Renci.SshNet /// This method is not supported. /// /// A byte offset relative to the parameter. - /// A value of type indicating the reference point used to obtain the new position. + /// A value of type indicating the reference point used to obtain the new position. /// /// The new position within the current stream. /// - /// An I/O error occurs. - /// The stream does not support seeking, such as if the stream is constructed from a pipe or console output. - /// Methods were called after the stream was closed. + /// An I/O error occurs. + /// The stream does not support seeking, such as if the stream is constructed from a pipe or console output. + /// Methods were called after the stream was closed. public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); @@ -248,9 +249,9 @@ namespace Renci.SshNet /// This method is not supported. /// /// The desired length of the current stream in bytes. - /// An I/O error occurs. - /// The stream does not support both writing and seeking, such as if the stream is constructed from a pipe or console output. - /// Methods were called after the stream was closed. + /// An I/O error occurs. + /// The stream does not support both writing and seeking, such as if the stream is constructed from a pipe or console output. + /// Methods were called after the stream was closed. public override void SetLength(long value) { throw new NotSupportedException(); @@ -262,12 +263,12 @@ namespace Renci.SshNet /// An array of bytes. This method copies bytes from to the current stream. /// The zero-based byte offset in at which to begin copying bytes to the current stream. /// The number of bytes to be written to the current stream. - /// The sum of and is greater than the buffer length. - /// is null. - /// or is negative. - /// An I/O error occurs. - /// The stream does not support writing. - /// Methods were called after the stream was closed. + /// The sum of and is greater than the buffer length. + /// is null. + /// or is negative. + /// An I/O error occurs. + /// The stream does not support writing. + /// Methods were called after the stream was closed. public override void Write(byte[] buffer, int offset, int count) { foreach (var b in buffer.Take(offset, count)) @@ -281,8 +282,6 @@ namespace Renci.SshNet } } - #endregion - /// /// Expects the specified expression and performs action when one is found. /// @@ -323,8 +322,8 @@ namespace Renci.SshNet for (var i = 0; i < match.Index + match.Length && _incoming.Count > 0; i++) { - // Remove processed items from the queue - _incoming.Dequeue(); + // Remove processed items from the queue + _ = _incoming.Dequeue(); } expectAction.Action(result); @@ -345,7 +344,7 @@ namespace Renci.SshNet } else { - _dataReceived.WaitOne(); + _ = _dataReceived.WaitOne(); } } } @@ -361,7 +360,7 @@ namespace Renci.SshNet /// public IAsyncResult BeginExpect(params ExpectAction[] expectActions) { - return BeginExpect(TimeSpan.Zero, null, null, expectActions); + return BeginExpect(TimeSpan.Zero, callback: null, state: null, expectActions); } /// @@ -374,7 +373,7 @@ namespace Renci.SshNet /// public IAsyncResult BeginExpect(AsyncCallback callback, params ExpectAction[] expectActions) { - return BeginExpect(TimeSpan.Zero, callback, null, expectActions); + return BeginExpect(TimeSpan.Zero, callback, state: null, expectActions); } /// @@ -405,21 +404,19 @@ namespace Renci.SshNet { var text = string.Empty; - // Create new AsyncResult object + // Create new AsyncResult object var asyncResult = new ExpectAsyncResult(callback, state); - // Execute callback on different thread + // Execute callback on different thread ThreadAbstraction.ExecuteThread(() => { string expectActionResult = null; try { - do { lock (_incoming) { - if (_incoming.Count > 0) { text = _encoding.GetString(_incoming.ToArray(), 0, _incoming.Count); @@ -437,16 +434,12 @@ namespace Renci.SshNet for (var i = 0; i < match.Index + match.Length && _incoming.Count > 0; i++) { - // Remove processed items from the queue - _incoming.Dequeue(); + // Remove processed items from the queue + _ = _incoming.Dequeue(); } expectAction.Action(result); - - if (callback != null) - { - callback(asyncResult); - } + callback?.Invoke(asyncResult); expectActionResult = result; } } @@ -454,30 +447,30 @@ namespace Renci.SshNet } if (expectActionResult != null) + { break; + } if (timeout.Ticks > 0) { if (!_dataReceived.WaitOne(timeout)) { - if (callback != null) - { - callback(asyncResult); - } + callback?.Invoke(asyncResult); break; } } else { - _dataReceived.WaitOne(); + _ = _dataReceived.WaitOne(); } - } while (true); + } + while (true); - asyncResult.SetAsCompleted(expectActionResult, true); + asyncResult.SetAsCompleted(expectActionResult, completedSynchronously: true); } catch (Exception exp) { - asyncResult.SetAsCompleted(exp, true); + asyncResult.SetAsCompleted(exp, completedSynchronously: true); } }); @@ -491,10 +484,10 @@ namespace Renci.SshNet /// Either the IAsyncResult object did not come from the corresponding async method on this type, or EndExecute was called multiple times with the same IAsyncResult. public string EndExpect(IAsyncResult asyncResult) { - var ar = asyncResult as ExpectAsyncResult; - - if (ar == null || ar.EndInvokeCalled) + if (asyncResult is not ExpectAsyncResult ar || ar.EndInvokeCalled) + { throw new ArgumentException("Either the IAsyncResult object did not come from the corresponding async method on this type, or EndExecute was called multiple times with the same IAsyncResult."); + } // Wait for operation to complete, then return result or throw exception return ar.EndInvoke(); @@ -563,11 +556,12 @@ namespace Renci.SshNet if (match.Success) { - // Remove processed items from the queue + // Remove processed items from the queue for (var i = 0; i < match.Index + match.Length && _incoming.Count > 0; i++) { - _incoming.Dequeue(); + _ = _incoming.Dequeue(); } + break; } } @@ -581,9 +575,8 @@ namespace Renci.SshNet } else { - _dataReceived.WaitOne(); + _ = _dataReceived.WaitOne(); } - } return text; @@ -631,7 +624,9 @@ namespace Renci.SshNet // remove processed bytes from the queue for (var i = 0; i < bytesProcessed; i++) - _incoming.Dequeue(); + { + _ = _incoming.Dequeue(); + } break; } @@ -646,9 +641,8 @@ namespace Renci.SshNet } else { - _dataReceived.WaitOne(); + _ = _dataReceived.WaitOne(); } - } return text; @@ -682,10 +676,12 @@ namespace Renci.SshNet /// public void Write(string text) { - if (text == null) + if (text is null) + { return; + } - if (_channel == null) + if (_channel is null) { throw new ObjectDisposedException("ShellStream"); } @@ -715,7 +711,9 @@ namespace Renci.SshNet base.Dispose(disposing); if (_isDisposed) + { return; + } if (disposing) { @@ -752,8 +750,10 @@ namespace Renci.SshNet /// private void UnsubscribeFromSessionEvents(ISession session) { - if (session == null) + if (session is null) + { return; + } session.Disconnected -= Session_Disconnected; session.ErrorOccured -= Session_ErrorOccured; @@ -766,13 +766,12 @@ namespace Renci.SshNet private void Session_Disconnected(object sender, EventArgs e) { - if (_channel != null) - _channel.Dispose(); + _channel?.Dispose(); } private void Channel_Closed(object sender, ChannelEventArgs e) { - // TODO: Do we need to call dispose here ?? + // TODO: Do we need to call dispose here ?? Dispose(); } @@ -781,31 +780,27 @@ namespace Renci.SshNet lock (_incoming) { foreach (var b in e.Data) + { _incoming.Enqueue(b); + } } if (_dataReceived != null) - _dataReceived.Set(); + { + _ = _dataReceived.Set(); + } OnDataReceived(e.Data); } private void OnRaiseError(ExceptionEventArgs e) { - var handler = ErrorOccurred; - if (handler != null) - { - handler(this, e); - } + ErrorOccurred?.Invoke(this, e); } private void OnDataReceived(byte[] data) { - var handler = DataReceived; - if (handler != null) - { - handler(this, new ShellDataEventArgs(data)); - } + DataReceived?.Invoke(this, new ShellDataEventArgs(data)); } } } diff --git a/src/Renci.SshNet/SshClient.cs b/src/Renci.SshNet/SshClient.cs index 49c9ff84..fb8bb37f 100644 --- a/src/Renci.SshNet/SshClient.cs +++ b/src/Renci.SshNet/SshClient.cs @@ -1,9 +1,10 @@ using System; using System.Collections.Generic; -using System.IO; -using System.Text; using System.Diagnostics.CodeAnalysis; +using System.IO; using System.Net; +using System.Text; + using Renci.SshNet.Common; namespace Renci.SshNet @@ -14,7 +15,7 @@ namespace Renci.SshNet public class SshClient : BaseClient { /// - /// Holds the list of forwarded ports + /// Holds the list of forwarded ports. /// private readonly List _forwardedPorts; @@ -39,8 +40,6 @@ namespace Renci.SshNet } } - #region Constructors - /// /// Initializes a new instance of the class. /// @@ -53,7 +52,7 @@ namespace Renci.SshNet /// /// is null. public SshClient(ConnectionInfo connectionInfo) - : this(connectionInfo, false) + : this(connectionInfo, ownsConnectionInfo: false) { } @@ -69,7 +68,7 @@ namespace Renci.SshNet /// is not within and . [SuppressMessage("Microsoft.Reliability", "C2A000:DisposeObjectsBeforeLosingScope", Justification = "Disposed in Dispose(bool) method.")] public SshClient(string host, int port, string username, string password) - : this(new PasswordConnectionInfo(host, port, username, password), true) + : this(new PasswordConnectionInfo(host, port, username, password), ownsConnectionInfo: true) { } @@ -104,8 +103,8 @@ namespace Renci.SshNet /// is invalid, -or- is null or contains only whitespace characters. /// is not within and . [SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope", Justification = "Disposed in Dispose(bool) method.")] - public SshClient(string host, int port, string username, params PrivateKeyFile[] keyFiles) - : this(new PrivateKeyConnectionInfo(host, port, username, keyFiles), true) + public SshClient(string host, int port, string username, params IPrivateKeySource[] keyFiles) + : this(new PrivateKeyConnectionInfo(host, port, username, keyFiles), ownsConnectionInfo: true) { } @@ -121,7 +120,7 @@ namespace Renci.SshNet /// /// is null. /// is invalid, -or- is null or contains only whitespace characters. - public SshClient(string host, string username, params PrivateKeyFile[] keyFiles) + public SshClient(string host, string username, params IPrivateKeySource[] keyFiles) : this(host, ConnectionInfo.DefaultPort, username, keyFiles) { } @@ -159,8 +158,6 @@ namespace Renci.SshNet _forwardedPorts = new List(); } - #endregion - /// /// Called when client is disconnecting from the server. /// @@ -187,8 +184,11 @@ namespace Renci.SshNet /// Client is not connected. public void AddForwardedPort(ForwardedPort port) { - if (port == null) - throw new ArgumentNullException("port"); + if (port is null) + { + throw new ArgumentNullException(nameof(port)); + } + EnsureSessionIsOpen(); AttachForwardedPort(port); @@ -202,20 +202,24 @@ namespace Renci.SshNet /// is null. public void RemoveForwardedPort(ForwardedPort port) { - if (port == null) - throw new ArgumentNullException("port"); + if (port is null) + { + throw new ArgumentNullException(nameof(port)); + } - // Stop port forwarding before removing it + // Stop port forwarding before removing it port.Stop(); DetachForwardedPort(port); - _forwardedPorts.Remove(port); + _ = _forwardedPorts.Remove(port); } private void AttachForwardedPort(ForwardedPort port) { if (port.Session != null && port.Session != Session) + { throw new InvalidOperationException("Forwarded port is already added to a different client."); + } port.Session = Session; } @@ -264,14 +268,14 @@ namespace Renci.SshNet /// /// /// CommandText property is empty. - /// Invalid Operation - An existing channel was used to execute this command. + /// Invalid Operation - An existing channel was used to execute this command. /// Asynchronous operation is already in progress. /// Client is not connected. /// is null. public SshCommand RunCommand(string commandText) { var cmd = CreateCommand(commandText); - cmd.Execute(); + _ = cmd.Execute(); return cmd; } @@ -332,7 +336,7 @@ namespace Renci.SshNet /// Client is not connected. public Shell CreateShell(Stream input, Stream output, Stream extendedOutput) { - return CreateShell(input, output, extendedOutput, string.Empty, 0, 0, 0, 0, null, 1024); + return CreateShell(input, output, extendedOutput, string.Empty, 0, 0, 0, 0, terminalModes: null, 1024); } /// @@ -361,7 +365,7 @@ namespace Renci.SshNet var writer = new StreamWriter(_inputStream, encoding); writer.Write(input); writer.Flush(); - _inputStream.Seek(0, SeekOrigin.Begin); + _ = _inputStream.Seek(0, SeekOrigin.Begin); return CreateShell(_inputStream, output, extendedOutput, terminalName, columns, rows, width, height, terminalModes, bufferSize); } @@ -401,7 +405,7 @@ namespace Renci.SshNet /// Client is not connected. public Shell CreateShell(Encoding encoding, string input, Stream output, Stream extendedOutput) { - return CreateShell(encoding, input, output, extendedOutput, string.Empty, 0, 0, 0, 0, null, 1024); + return CreateShell(encoding, input, output, extendedOutput, string.Empty, 0, 0, 0, 0, terminalModes: null, 1024); } /// @@ -410,7 +414,7 @@ namespace Renci.SshNet /// The TERM environment variable. /// The terminal width in columns. /// The terminal width in rows. - /// The terminal height in pixels. + /// The terminal width in pixels. /// The terminal height in pixels. /// The size of the buffer. /// @@ -429,7 +433,7 @@ namespace Renci.SshNet /// public ShellStream CreateShellStream(string terminalName, uint columns, uint rows, uint width, uint height, int bufferSize) { - return CreateShellStream(terminalName, columns, rows, width, height, bufferSize, null); + return CreateShellStream(terminalName, columns, rows, width, height, bufferSize, terminalModeValues: null); } /// @@ -438,7 +442,7 @@ namespace Renci.SshNet /// The TERM environment variable. /// The terminal width in columns. /// The terminal width in rows. - /// The terminal height in pixels. + /// The terminal width in pixels. /// The terminal height in pixels. /// The size of the buffer. /// The terminal mode values. @@ -479,7 +483,7 @@ namespace Renci.SshNet } /// - /// Releases unmanaged and - optionally - managed resources + /// Releases unmanaged and - optionally - managed resources. /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected override void Dispose(bool disposing) @@ -487,7 +491,9 @@ namespace Renci.SshNet base.Dispose(disposing); if (_isDisposed) + { return; + } if (disposing) { @@ -503,8 +509,10 @@ namespace Renci.SshNet private void EnsureSessionIsOpen() { - if (Session == null) + if (Session is null) + { throw new SshConnectionException("Client not connected."); + } } } } diff --git a/src/Renci.SshNet/SshCommand.cs b/src/Renci.SshNet/SshCommand.cs index 37e91da0..a4b861cd 100644 --- a/src/Renci.SshNet/SshCommand.cs +++ b/src/Renci.SshNet/SshCommand.cs @@ -1,13 +1,15 @@ using System; +using System.Globalization; using System.IO; +using System.Runtime.ExceptionServices; using System.Text; using System.Threading; + +using Renci.SshNet.Abstractions; using Renci.SshNet.Channels; using Renci.SshNet.Common; using Renci.SshNet.Messages.Connection; using Renci.SshNet.Messages.Transport; -using System.Globalization; -using Renci.SshNet.Abstractions; namespace Renci.SshNet { @@ -16,15 +18,19 @@ namespace Renci.SshNet /// public class SshCommand : IDisposable { - private ISession _session; private readonly Encoding _encoding; + private readonly object _endExecuteLock = new object(); + + private ISession _session; private IChannelSession _channel; private CommandAsyncResult _asyncResult; private AsyncCallback _callback; private EventWaitHandle _sessionErrorOccuredWaitHandle; private Exception _exception; + private StringBuilder _result; + private StringBuilder _error; private bool _hasError; - private readonly object _endExecuteLock = new object(); + private bool _isDisposed; /// /// Gets the command text. @@ -66,7 +72,6 @@ namespace Renci.SshNet /// public Stream ExtendedOutputStream { get; private set; } - private StringBuilder _result; /// /// Gets the command execution result. /// @@ -77,23 +82,19 @@ namespace Renci.SshNet { get { - if (_result == null) - { - _result = new StringBuilder(); - } + _result ??= new StringBuilder(); if (OutputStream != null && OutputStream.Length > 0) { // do not dispose the StreamReader, as it would also dispose the stream var sr = new StreamReader(OutputStream, _encoding); - _result.Append(sr.ReadToEnd()); + _ = _result.Append(sr.ReadToEnd()); } return _result.ToString(); } } - private StringBuilder _error; /// /// Gets the command execution error. /// @@ -106,20 +107,18 @@ namespace Renci.SshNet { if (_hasError) { - if (_error == null) - { - _error = new StringBuilder(); - } + _error ??= new StringBuilder(); if (ExtendedOutputStream != null && ExtendedOutputStream.Length > 0) { // do not dispose the StreamReader, as it would also dispose the stream var sr = new StreamReader(ExtendedOutputStream, _encoding); - _error.Append(sr.ReadToEnd()); + _ = _error.Append(sr.ReadToEnd()); } return _error.ToString(); } + return string.Empty; } } @@ -133,18 +132,26 @@ namespace Renci.SshNet /// Either , is null. internal SshCommand(ISession session, string commandText, Encoding encoding) { - if (session == null) - throw new ArgumentNullException("session"); - if (commandText == null) - throw new ArgumentNullException("commandText"); - if (encoding == null) - throw new ArgumentNullException("encoding"); + if (session is null) + { + throw new ArgumentNullException(nameof(session)); + } + + if (commandText is null) + { + throw new ArgumentNullException(nameof(commandText)); + } + + if (encoding is null) + { + throw new ArgumentNullException(nameof(encoding)); + } _session = session; CommandText = commandText; _encoding = encoding; CommandTimeout = Session.InfiniteTimeSpan; - _sessionErrorOccuredWaitHandle = new AutoResetEvent(false); + _sessionErrorOccuredWaitHandle = new AutoResetEvent(initialState: false); _session.Disconnected += Session_Disconnected; _session.ErrorOccured += Session_ErrorOccured; @@ -154,7 +161,7 @@ namespace Renci.SshNet /// Begins an asynchronous command execution. /// /// - /// An that represents the asynchronous command execution, which could still be pending. + /// An that represents the asynchronous command execution, which could still be pending. /// /// /// @@ -164,11 +171,9 @@ namespace Renci.SshNet /// CommandText property is empty. /// Client is not connected. /// Operation has timed out. - /// Asynchronous operation is already in progress. - /// CommandText property is empty. public IAsyncResult BeginExecute() { - return BeginExecute(null, null); + return BeginExecute(callback: null, state: null); } /// @@ -176,18 +181,16 @@ namespace Renci.SshNet /// /// An optional asynchronous callback, to be called when the command execution is complete. /// - /// An that represents the asynchronous command execution, which could still be pending. + /// An that represents the asynchronous command execution, which could still be pending. /// /// Asynchronous operation is already in progress. /// Invalid operation. /// CommandText property is empty. /// Client is not connected. /// Operation has timed out. - /// Asynchronous operation is already in progress. - /// CommandText property is empty. public IAsyncResult BeginExecute(AsyncCallback callback) { - return BeginExecute(callback, null); + return BeginExecute(callback, state: null); } /// @@ -203,48 +206,48 @@ namespace Renci.SshNet /// CommandText property is empty. /// Client is not connected. /// Operation has timed out. - /// Asynchronous operation is already in progress. - /// CommandText property is empty. public IAsyncResult BeginExecute(AsyncCallback callback, object state) { - // Prevent from executing BeginExecute before calling EndExecute + // Prevent from executing BeginExecute before calling EndExecute if (_asyncResult != null && !_asyncResult.EndCalled) { throw new InvalidOperationException("Asynchronous operation is already in progress."); } - // Create new AsyncResult object + // Create new AsyncResult object _asyncResult = new CommandAsyncResult { - AsyncWaitHandle = new ManualResetEvent(false), + AsyncWaitHandle = new ManualResetEvent(initialState: false), IsCompleted = false, AsyncState = state, }; - // When command re-executed again, create a new channel - if (_channel != null) + // When command re-executed again, create a new channel + if (_channel is not null) { throw new SshException("Invalid operation."); } if (string.IsNullOrEmpty(CommandText)) + { throw new ArgumentException("CommandText property is empty."); + } var outputStream = OutputStream; - if (outputStream != null) + if (outputStream is not null) { outputStream.Dispose(); OutputStream = null; } var extendedOutputStream = ExtendedOutputStream; - if (extendedOutputStream != null) + if (extendedOutputStream is not null) { extendedOutputStream.Dispose(); ExtendedOutputStream = null; } - // Initialize output streams + // Initialize output streams OutputStream = new PipeStream(); ExtendedOutputStream = new PipeStream(); @@ -254,7 +257,7 @@ namespace Renci.SshNet _channel = CreateChannel(); _channel.Open(); - _channel.SendExecRequest(CommandText); + _ = _channel.SendExecRequest(CommandText); return _asyncResult; } @@ -266,10 +269,10 @@ namespace Renci.SshNet /// An optional asynchronous callback, to be called when the command execution is complete. /// A user-provided object that distinguishes this particular asynchronous read request from other requests. /// - /// An that represents the asynchronous command execution, which could still be pending. + /// An that represents the asynchronous command execution, which could still be pending. /// - /// Client is not connected. - /// Operation has timed out. + /// Client is not connected. + /// Operation has timed out. public IAsyncResult BeginExecute(string commandText, AsyncCallback callback, object state) { CommandText = commandText; @@ -289,15 +292,14 @@ namespace Renci.SshNet /// is null. public string EndExecute(IAsyncResult asyncResult) { - if (asyncResult == null) + if (asyncResult is null) { - throw new ArgumentNullException("asyncResult"); + throw new ArgumentNullException(nameof(asyncResult)); } - var commandAsyncResult = asyncResult as CommandAsyncResult; - if (commandAsyncResult == null || _asyncResult != commandAsyncResult) + if (asyncResult is not CommandAsyncResult commandAsyncResult || _asyncResult != commandAsyncResult) { - throw new ArgumentException(string.Format("The {0} object was not returned from the corresponding asynchronous method on this class.", typeof(IAsyncResult).Name)); + throw new ArgumentException(string.Format("The {0} object was not returned from the corresponding asynchronous method on this class.", nameof(IAsyncResult))); } lock (_endExecuteLock) @@ -307,7 +309,7 @@ namespace Renci.SshNet throw new ArgumentException("EndExecute can only be called once for each asynchronous operation."); } - // wait for operation to complete (or time out) + // wait for operation to complete (or time out) WaitOnHandle(_asyncResult.AsyncWaitHandle); UnsubscribeFromEventsAndDisposeChannel(_channel); @@ -328,19 +330,19 @@ namespace Renci.SshNet /// /// /// - /// Client is not connected. - /// Operation has timed out. + /// Client is not connected. + /// Operation has timed out. public string Execute() { - return EndExecute(BeginExecute(null, null)); + return EndExecute(BeginExecute(callback: null, state: null)); } /// - /// Cancels command execution in asynchronous scenarios. + /// Cancels command execution in asynchronous scenarios. /// public void CancelAsync() { - if (_channel != null && _channel.IsOpen && _asyncResult != null) + if (_channel is not null && _channel.IsOpen && _asyncResult is not null) { // TODO: check with Oleg if we shouldn't dispose the channel and uninitialize it ? _channel.Dispose(); @@ -351,9 +353,11 @@ namespace Renci.SshNet /// Executes the specified command text. /// /// The command text. - /// Command execution result - /// Client is not connected. - /// Operation has timed out. + /// + /// The result of the command execution. + /// + /// Client is not connected. + /// Operation has timed out. public string Execute(string commandText) { CommandText = commandText; @@ -373,54 +377,49 @@ namespace Renci.SshNet private void Session_Disconnected(object sender, EventArgs e) { - // If objected is disposed or being disposed don't handle this event + // If objected is disposed or being disposed don't handle this event if (_isDisposed) + { return; + } _exception = new SshConnectionException("An established connection was aborted by the software in your host machine.", DisconnectReason.ConnectionLost); - _sessionErrorOccuredWaitHandle.Set(); + _ = _sessionErrorOccuredWaitHandle.Set(); } private void Session_ErrorOccured(object sender, ExceptionEventArgs e) { - // If objected is disposed or being disposed don't handle this event + // If objected is disposed or being disposed don't handle this event if (_isDisposed) + { return; + } _exception = e.Exception; - _sessionErrorOccuredWaitHandle.Set(); + _ = _sessionErrorOccuredWaitHandle.Set(); } private void Channel_Closed(object sender, ChannelEventArgs e) { - var outputStream = OutputStream; - if (outputStream != null) - { - outputStream.Flush(); - } - - var extendedOutputStream = ExtendedOutputStream; - if (extendedOutputStream != null) - { - extendedOutputStream.Flush(); - } + OutputStream?.Flush(); + ExtendedOutputStream?.Flush(); _asyncResult.IsCompleted = true; - if (_callback != null) + if (_callback is not null) { - // Execute callback on different thread + // Execute callback on different thread ThreadAbstraction.ExecuteThread(() => _callback(_asyncResult)); } - ((EventWaitHandle) _asyncResult.AsyncWaitHandle).Set(); + + _ = ((EventWaitHandle) _asyncResult.AsyncWaitHandle).Set(); } private void Channel_RequestReceived(object sender, ChannelRequestEventArgs e) { - var exitStatusInfo = e.Info as ExitStatusRequestInfo; - if (exitStatusInfo != null) + if (e.Info is ExitStatusRequestInfo exitStatusInfo) { ExitStatus = (int) exitStatusInfo.ExitStatus; @@ -481,12 +480,20 @@ namespace Renci.SshNet waitHandle }; - switch (WaitHandle.WaitAny(waitHandles, CommandTimeout)) + var signaledElement = WaitHandle.WaitAny(waitHandles, CommandTimeout); + switch (signaledElement) { case 0: - throw _exception; + ExceptionDispatchInfo.Capture(_exception).Throw(); + break; + case 1: + // Specified waithandle was signaled + break; case WaitHandle.WaitTimeout: throw new SshOperationTimeoutException(string.Format(CultureInfo.CurrentCulture, "Command '{0}' has timed out.", CommandText)); + default: + throw new SshException($"Unexpected element '{signaledElement.ToString(CultureInfo.InvariantCulture)}' signaled."); + } } @@ -500,8 +507,10 @@ namespace Renci.SshNet /// private void UnsubscribeFromEventsAndDisposeChannel(IChannel channel) { - if (channel == null) + if (channel is null) + { return; + } // unsubscribe from events as we do not want to be signaled should these get fired // during the dispose of the channel @@ -514,27 +523,25 @@ namespace Renci.SshNet channel.Dispose(); } - #region IDisposable Members - - private bool _isDisposed; - /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// public void Dispose() { - Dispose(true); + Dispose(disposing: true); GC.SuppressFinalize(this); } /// - /// Releases unmanaged and - optionally - managed resources + /// Releases unmanaged and - optionally - managed resources. /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected virtual void Dispose(bool disposing) { if (_isDisposed) + { return; + } if (disposing) { @@ -583,14 +590,13 @@ namespace Renci.SshNet } /// + /// Finalizes an instance of the class. /// Releases unmanaged resources and performs other cleanup operations before the /// is reclaimed by garbage collection. /// ~SshCommand() { - Dispose(false); + Dispose(disposing: false); } - - #endregion } } diff --git a/src/Renci.SshNet/SshMessageFactory.cs b/src/Renci.SshNet/SshMessageFactory.cs index 153024db..5941c120 100644 --- a/src/Renci.SshNet/SshMessageFactory.cs +++ b/src/Renci.SshNet/SshMessageFactory.cs @@ -9,13 +9,13 @@ using Renci.SshNet.Messages.Transport; namespace Renci.SshNet { - internal class SshMessageFactory + internal sealed class SshMessageFactory { private readonly MessageMetadata[] _enabledMessagesByNumber; private readonly bool[] _activatedMessagesById; internal static readonly MessageMetadata[] AllMessages; - private static readonly IDictionary MessagesByName; + private static readonly Dictionary MessagesByName; /// /// Defines the highest message number that is currently supported. @@ -30,40 +30,40 @@ namespace Renci.SshNet static SshMessageFactory() { AllMessages = new MessageMetadata[] - { - new MessageMetadata(0, "SSH_MSG_KEXINIT", 20), - new MessageMetadata (1, "SSH_MSG_NEWKEYS", 21), - new MessageMetadata (2, "SSH_MSG_REQUEST_FAILURE", 82), - new MessageMetadata (3, "SSH_MSG_CHANNEL_OPEN_FAILURE", 92), - new MessageMetadata (4, "SSH_MSG_CHANNEL_FAILURE", 100), - new MessageMetadata (5, "SSH_MSG_CHANNEL_EXTENDED_DATA", 95), - new MessageMetadata (6, "SSH_MSG_CHANNEL_DATA", 94), - new MessageMetadata (7, "SSH_MSG_CHANNEL_REQUEST", 98), - new MessageMetadata (8, "SSH_MSG_USERAUTH_BANNER", 53), - new MessageMetadata (9, "SSH_MSG_USERAUTH_INFO_RESPONSE", 61), - new MessageMetadata (10, "SSH_MSG_USERAUTH_FAILURE", 51), - new MessageMetadata (11, "SSH_MSG_DEBUG", 4), - new MessageMetadata (12, "SSH_MSG_GLOBAL_REQUEST", 80), - new MessageMetadata (13, "SSH_MSG_CHANNEL_OPEN", 90), - new MessageMetadata (14, "SSH_MSG_CHANNEL_OPEN_CONFIRMATION", 91), - new MessageMetadata (15, "SSH_MSG_USERAUTH_INFO_REQUEST", 60), - new MessageMetadata (16, "SSH_MSG_UNIMPLEMENTED", 3), - new MessageMetadata (17, "SSH_MSG_REQUEST_SUCCESS", 81), - new MessageMetadata (18, "SSH_MSG_CHANNEL_SUCCESS", 99), - new MessageMetadata (19, "SSH_MSG_USERAUTH_PASSWD_CHANGEREQ", 60), - new MessageMetadata (20, "SSH_MSG_DISCONNECT", 1), - new MessageMetadata (21, "SSH_MSG_USERAUTH_SUCCESS", 52), - new MessageMetadata (22, "SSH_MSG_USERAUTH_PK_OK", 60), - new MessageMetadata (23, "SSH_MSG_IGNORE", 2), - new MessageMetadata (24, "SSH_MSG_CHANNEL_WINDOW_ADJUST", 93), - new MessageMetadata (25, "SSH_MSG_CHANNEL_EOF", 96), - new MessageMetadata (26, "SSH_MSG_CHANNEL_CLOSE", 97), - new MessageMetadata (27, "SSH_MSG_SERVICE_ACCEPT", 6), - new MessageMetadata (28, "SSH_MSG_KEX_DH_GEX_GROUP", 31), - new MessageMetadata (29, "SSH_MSG_KEXDH_REPLY", 31), - new MessageMetadata (30, "SSH_MSG_KEX_DH_GEX_REPLY", 33), - new MessageMetadata (31, "SSH_MSG_KEX_ECDH_REPLY", 31) - }; + { + new MessageMetadata(0, "SSH_MSG_KEXINIT", 20), + new MessageMetadata(1, "SSH_MSG_NEWKEYS", 21), + new MessageMetadata(2, "SSH_MSG_REQUEST_FAILURE", 82), + new MessageMetadata(3, "SSH_MSG_CHANNEL_OPEN_FAILURE", 92), + new MessageMetadata(4, "SSH_MSG_CHANNEL_FAILURE", 100), + new MessageMetadata(5, "SSH_MSG_CHANNEL_EXTENDED_DATA", 95), + new MessageMetadata(6, "SSH_MSG_CHANNEL_DATA", 94), + new MessageMetadata(7, "SSH_MSG_CHANNEL_REQUEST", 98), + new MessageMetadata(8, "SSH_MSG_USERAUTH_BANNER", 53), + new MessageMetadata(9, "SSH_MSG_USERAUTH_INFO_RESPONSE", 61), + new MessageMetadata(10, "SSH_MSG_USERAUTH_FAILURE", 51), + new MessageMetadata(11, "SSH_MSG_DEBUG", 4), + new MessageMetadata(12, "SSH_MSG_GLOBAL_REQUEST", 80), + new MessageMetadata(13, "SSH_MSG_CHANNEL_OPEN", 90), + new MessageMetadata(14, "SSH_MSG_CHANNEL_OPEN_CONFIRMATION", 91), + new MessageMetadata(15, "SSH_MSG_USERAUTH_INFO_REQUEST", 60), + new MessageMetadata(16, "SSH_MSG_UNIMPLEMENTED", 3), + new MessageMetadata(17, "SSH_MSG_REQUEST_SUCCESS", 81), + new MessageMetadata(18, "SSH_MSG_CHANNEL_SUCCESS", 99), + new MessageMetadata(19, "SSH_MSG_USERAUTH_PASSWD_CHANGEREQ", 60), + new MessageMetadata(20, "SSH_MSG_DISCONNECT", 1), + new MessageMetadata(21, "SSH_MSG_USERAUTH_SUCCESS", 52), + new MessageMetadata(22, "SSH_MSG_USERAUTH_PK_OK", 60), + new MessageMetadata(23, "SSH_MSG_IGNORE", 2), + new MessageMetadata(24, "SSH_MSG_CHANNEL_WINDOW_ADJUST", 93), + new MessageMetadata(25, "SSH_MSG_CHANNEL_EOF", 96), + new MessageMetadata(26, "SSH_MSG_CHANNEL_CLOSE", 97), + new MessageMetadata(27, "SSH_MSG_SERVICE_ACCEPT", 6), + new MessageMetadata(28, "SSH_MSG_KEX_DH_GEX_GROUP", 31), + new MessageMetadata(29, "SSH_MSG_KEXDH_REPLY", 31), + new MessageMetadata(30, "SSH_MSG_KEX_DH_GEX_REPLY", 33), + new MessageMetadata(31, "SSH_MSG_KEX_ECDH_REPLY", 31) + }; MessagesByName = new Dictionary(AllMessages.Length); for (var i = 0; i < AllMessages.Length; i++) @@ -73,6 +73,9 @@ namespace Renci.SshNet } } + /// + /// Initializes a new instance of the class. + /// public SshMessageFactory() { _activatedMessagesById = new bool[TotalMessageCount]; @@ -96,7 +99,7 @@ namespace Renci.SshNet } var enabledMessageMetadata = _enabledMessagesByNumber[messageNumber]; - if (enabledMessageMetadata == null) + if (enabledMessageMetadata is null) { MessageMetadata definedMessageMetadata = null; @@ -111,7 +114,7 @@ namespace Renci.SshNet } } - if (definedMessageMetadata == null) + if (definedMessageMetadata is null) { throw CreateMessageTypeNotSupportedException(messageNumber); } @@ -129,7 +132,7 @@ namespace Renci.SshNet var messageMetadata = AllMessages[i]; var messageNumber = messageMetadata.Number; - if ((messageNumber > 2 && messageNumber < 20) || messageNumber > 30) + if (messageNumber is (> 2 and < 20) or > 30) { _enabledMessagesByNumber[messageNumber] = null; } @@ -143,29 +146,32 @@ namespace Renci.SshNet var messageMetadata = AllMessages[i]; if (!_activatedMessagesById[messageMetadata.Id]) + { continue; + } var enabledMessageMetadata = _enabledMessagesByNumber[messageMetadata.Number]; if (enabledMessageMetadata != null && enabledMessageMetadata != messageMetadata) { throw CreateMessageTypeAlreadyEnabledForOtherMessageException(messageMetadata.Number, - messageMetadata.Name, - enabledMessageMetadata.Name); + messageMetadata.Name, + enabledMessageMetadata.Name); } + _enabledMessagesByNumber[messageMetadata.Number] = messageMetadata; } } public void EnableAndActivateMessage(string messageName) { - if (messageName == null) - throw new ArgumentNullException("messageName"); + if (messageName is null) + { + throw new ArgumentNullException(nameof(messageName)); + } lock (this) { - MessageMetadata messageMetadata; - - if (!MessagesByName.TryGetValue(messageName, out messageMetadata)) + if (!MessagesByName.TryGetValue(messageName, out var messageMetadata)) { throw CreateMessageNotSupportedException(messageName); } @@ -185,14 +191,14 @@ namespace Renci.SshNet public void DisableAndDeactivateMessage(string messageName) { - if (messageName == null) - throw new ArgumentNullException("messageName"); + if (messageName is null) + { + throw new ArgumentNullException(nameof(messageName)); + } lock (this) { - MessageMetadata messageMetadata; - - if (!MessagesByName.TryGetValue(messageName, out messageMetadata)) + if (!MessagesByName.TryGetValue(messageName, out var messageMetadata)) { throw CreateMessageNotSupportedException(messageName); } @@ -201,8 +207,8 @@ namespace Renci.SshNet if (enabledMessageMetadata != null && enabledMessageMetadata != messageMetadata) { throw CreateMessageTypeAlreadyEnabledForOtherMessageException(messageMetadata.Number, - messageMetadata.Name, - enabledMessageMetadata.Name); + messageMetadata.Name, + enabledMessageMetadata.Name); } _activatedMessagesById[messageMetadata.Id] = false; @@ -245,7 +251,8 @@ namespace Renci.SshNet public abstract Message Create(); } - internal class MessageMetadata : MessageMetadata where T : Message, new() + internal sealed class MessageMetadata : MessageMetadata + where T : Message, new() { public MessageMetadata(byte id, string name, byte number) : base(id, name, number) diff --git a/src/Renci.SshNet/SubsystemSession.cs b/src/Renci.SshNet/SubsystemSession.cs index 943a2db9..86a081bb 100644 --- a/src/Renci.SshNet/SubsystemSession.cs +++ b/src/Renci.SshNet/SubsystemSession.cs @@ -1,6 +1,8 @@ using System; using System.Globalization; +using System.Runtime.ExceptionServices; using System.Threading; + using Renci.SshNet.Abstractions; using Renci.SshNet.Channels; using Renci.SshNet.Common; @@ -8,7 +10,7 @@ using Renci.SshNet.Common; namespace Renci.SshNet { /// - /// Base class for SSH subsystem implementations + /// Base class for SSH subsystem implementations. /// internal abstract class SubsystemSession : ISubsystemSession { @@ -18,13 +20,14 @@ namespace Renci.SshNet /// private const int SystemWaitHandleCount = 3; - private ISession _session; private readonly string _subsystemName; + private ISession _session; private IChannelSession _channel; private Exception _exception; - private EventWaitHandle _errorOccuredWaitHandle = new ManualResetEvent(false); - private EventWaitHandle _sessionDisconnectedWaitHandle = new ManualResetEvent(false); - private EventWaitHandle _channelClosedWaitHandle = new ManualResetEvent(false); + private EventWaitHandle _errorOccuredWaitHandle = new ManualResetEvent(initialState: false); + private EventWaitHandle _sessionDisconnectedWaitHandle = new ManualResetEvent(initialState: false); + private EventWaitHandle _channelClosedWaitHandle = new ManualResetEvent(initialState: false); + private bool _isDisposed; /// /// Gets or set the number of seconds to wait for an operation to complete. @@ -68,11 +71,11 @@ namespace Renci.SshNet /// public bool IsOpen { - get { return _channel != null && _channel.IsOpen; } + get { return _channel is not null && _channel.IsOpen; } } /// - /// Initializes a new instance of the SubsystemSession class. + /// Initializes a new instance of the class. /// /// The session. /// Name of the subsystem. @@ -80,10 +83,15 @@ namespace Renci.SshNet /// or is null. protected SubsystemSession(ISession session, string subsystemName, int operationTimeout) { - if (session == null) - throw new ArgumentNullException("session"); - if (subsystemName == null) - throw new ArgumentNullException("subsystemName"); + if (session is null) + { + throw new ArgumentNullException(nameof(session)); + } + + if (subsystemName is null) + { + throw new ArgumentNullException(nameof(subsystemName)); + } _session = session; _subsystemName = subsystemName; @@ -101,13 +109,15 @@ namespace Renci.SshNet EnsureNotDisposed(); if (IsOpen) + { throw new InvalidOperationException("The session is already connected."); + } // reset waithandles in case we're reconnecting - _errorOccuredWaitHandle.Reset(); - _sessionDisconnectedWaitHandle.Reset(); - _sessionDisconnectedWaitHandle.Reset(); - _channelClosedWaitHandle.Reset(); + _ = _errorOccuredWaitHandle.Reset(); + _ = _sessionDisconnectedWaitHandle.Reset(); + _ = _sessionDisconnectedWaitHandle.Reset(); + _ = _channelClosedWaitHandle.Reset(); _session.ErrorOccured += Session_ErrorOccured; _session.Disconnected += Session_Disconnected; @@ -122,6 +132,7 @@ namespace Renci.SshNet { // close channel session Disconnect(); + // signal subsystem failure throw new SshException(string.Format(CultureInfo.InvariantCulture, "Subsystem '{0}' could not be executed.", @@ -139,7 +150,7 @@ namespace Renci.SshNet UnsubscribeFromSessionEvents(_session); var channel = _channel; - if (channel != null) + if (channel is not null) { _channel = null; channel.DataReceived -= Channel_DataReceived; @@ -182,9 +193,7 @@ namespace Renci.SshNet DiagnosticAbstraction.Log("Raised exception: " + error); - var errorOccuredWaitHandle = _errorOccuredWaitHandle; - if (errorOccuredWaitHandle != null) - errorOccuredWaitHandle.Set(); + _ = _errorOccuredWaitHandle?.Set(); SignalErrorOccurred(error); } @@ -208,9 +217,7 @@ namespace Renci.SshNet private void Channel_Closed(object sender, ChannelEventArgs e) { - var channelClosedWaitHandle = _channelClosedWaitHandle; - if (channelClosedWaitHandle != null) - channelClosedWaitHandle.Set(); + _ = _channelClosedWaitHandle?.Set(); } /// @@ -235,7 +242,8 @@ namespace Renci.SshNet switch (result) { case 0: - throw _exception; + ExceptionDispatchInfo.Capture(_exception).Throw(); + break; case 1: throw new SshException("Connection was closed by the server."); case 2: @@ -280,7 +288,8 @@ namespace Renci.SshNet switch (result) { case 0: - throw _exception; + ExceptionDispatchInfo.Capture(_exception).Throw(); + return false; // unreached case 1: throw new SshException("Connection was closed by the server."); case 2: @@ -334,7 +343,8 @@ namespace Renci.SshNet switch (result) { case 0: - throw _exception; + ExceptionDispatchInfo.Capture(_exception).Throw(); + return -1; // unreached case 1: throw new SshException("Connection was closed by the server."); case 2: @@ -371,7 +381,8 @@ namespace Renci.SshNet switch (result) { case 0: - throw _exception; + ExceptionDispatchInfo.Capture(_exception).Throw(); + return -1; // unreached case 1: throw new SshException("Connection was closed by the server."); case 2: @@ -429,9 +440,7 @@ namespace Renci.SshNet private void Session_Disconnected(object sender, EventArgs e) { - var sessionDisconnectedWaitHandle = _sessionDisconnectedWaitHandle; - if (sessionDisconnectedWaitHandle != null) - sessionDisconnectedWaitHandle.Set(); + _ = _sessionDisconnectedWaitHandle?.Set(); SignalDisconnected(); } @@ -443,26 +452,20 @@ namespace Renci.SshNet private void SignalErrorOccurred(Exception error) { - var errorOccurred = ErrorOccurred; - if (errorOccurred != null) - { - errorOccurred(this, new ExceptionEventArgs(error)); - } + ErrorOccurred?.Invoke(this, new ExceptionEventArgs(error)); } private void SignalDisconnected() { - var disconnected = Disconnected; - if (disconnected != null) - { - disconnected(this, new EventArgs()); - } + Disconnected?.Invoke(this, EventArgs.Empty); } private void EnsureSessionIsOpen() { if (!IsOpen) + { throw new InvalidOperationException("The session is not open."); + } } /// @@ -474,34 +477,34 @@ namespace Renci.SshNet /// private void UnsubscribeFromSessionEvents(ISession session) { - if (session == null) + if (session is null) + { return; + } session.Disconnected -= Session_Disconnected; session.ErrorOccured -= Session_ErrorOccured; } - #region IDisposable Members - - private bool _isDisposed; - /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// public void Dispose() { - Dispose(true); + Dispose(disposing: true); GC.SuppressFinalize(this); } /// - /// Releases unmanaged and - optionally - managed resources + /// Releases unmanaged and - optionally - managed resources. /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected virtual void Dispose(bool disposing) { if (_isDisposed) + { return; + } if (disposing) { @@ -539,15 +542,15 @@ namespace Renci.SshNet /// ~SubsystemSession() { - Dispose(false); + Dispose(disposing: false); } private void EnsureNotDisposed() { if (_isDisposed) + { throw new ObjectDisposedException(GetType().FullName); + } } - - #endregion } } diff --git a/stylecop.json b/stylecop.json new file mode 100644 index 00000000..ff7c9dbf --- /dev/null +++ b/stylecop.json @@ -0,0 +1,25 @@ +{ + "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", + "settings": { + "documentationRules": { + "xmlHeader": false, + "documentInternalElements": false + }, + "layoutRules": { + "newlineAtEndOfFile": "require" + }, + "indentation": { + "indentationSize": 4, + "tabSize": 4, + "useTabs": false + }, + "namingRules": { + "allowCommonHungarianPrefixes": false + }, + "orderingRules": { + "systemUsingDirectivesFirst": true, + "usingDirectivesPlacement": "outsideNamespace", + "blankLinesBetweenUsingGroups": "require" + } + } +} diff --git a/test/.editorconfig b/test/.editorconfig new file mode 100644 index 00000000..ff0eebcf --- /dev/null +++ b/test/.editorconfig @@ -0,0 +1,111 @@ +[*.cs] + +# Sonar rules + +# S1854: Unused assignments should be removed +# https://rules.sonarsource.com/csharp/RSPEC-1854 +# +# We sometimes increment the value of a variable on each use to make the code future-proof. +# +# For example: +# int idSequence = 0; +# var train1 = new Train { Id = ++idSequence }; +# var train2 = new Train { Id = ++idSequence }; +# +# The increment of 'idSequence' in the last line will cause this diagnostic to be reported. We prefer to keep the increment to make +# sure the value of the variable will remain correct when we introduce a 'train3'. +# +# For unit tests, we do not care about this diagnostic. +dotnet_diagnostic.S1854.severity = none + +#### StyleCop rules #### + +# SA1202: Elements must be ordered by access +dotnet_diagnostic.SA1202.severity = none + +# SA1600: Elements must be documented +# +# For unit test projects, we do not care about documentation. +dotnet_diagnostic.SA1600.severity = none + +# SA1601: Partial elements should be documented +# +# For unit test projects, we do not care about documentation. +dotnet_diagnostic.SA1601.severity = none + +# SA1602: Enumeration items must be documented +# +# For unit test projects, we do not care about documentation. +dotnet_diagnostic.SA1602.severity = none + +# SA1604: Element documentation should have summary +# +# TODO: Remove this when code has been updated! +dotnet_diagnostic.SA1604.severity = none + +# SA1606: Element documentation should have summary text +# +# TODO: Remove this when code has been updated! +dotnet_diagnostic.SA1606.severity = none + +# SA1607: Partial element documentation should have summary text +# +# For unit test projects, we do not care about documentation. +dotnet_diagnostic.SA1607.severity = none + +# SA1611: Element parameters must be documented +# +# For unit test projects, we do not care about documentation. +dotnet_diagnostic.SA1611.severity = none + +# SA1614: Element parameter documentation must have text +# +# TODO: Remove this when code has been updated! +dotnet_diagnostic.SA1614.severity = none + +# SA1615: Element return value must be documented +# +# For unit test projects, we do not care about documentation. +dotnet_diagnostic.SA1615.severity = none + +# SA1616: Element return value documentation should have text +# +# TODO: Remove this when code has been updated! +dotnet_diagnostic.SA1616.severity = none + +# SA1623: Property summary documentation must match accessors +# +# TODO: Remove this when code has been updated! +dotnet_diagnostic.SA1623.severity = none + +# SA1629: Documentation text must end with a period +# +# For unit test projects, we do not care about documentation. +dotnet_diagnostic.SA1629.severity = none + +#### .NET Compiler Platform analysers rules #### + +# CA1001: Types that own disposable fields should be disposable +# +# We do not care about this for unit tests. +dotnet_diagnostic.CA1001.severity = none + +# CA1707: Identifiers should not contain underscores +# +# We frequently use underscores in test classes and test methods. +dotnet_diagnostic.CA1707.severity = none + +# CA1711: Identifiers should not have incorrect suffix +# +# We frequently define test classes and test method with a suffix that refers to a type. +dotnet_diagnostic.CA1711.severity = none + +# CA1720: Identifiers should not contain type names +# +# We do not care about this for unit tests. +dotnet_diagnostic.CA1720.severity = none + +# CA5394: Do not use insecure randomness +# +# We do not care about this for unit tests. +dotnet_diagnostic.CA5394.severity = none diff --git a/test/Directory.Build.props b/test/Directory.Build.props new file mode 100644 index 00000000..1e96f3c4 --- /dev/null +++ b/test/Directory.Build.props @@ -0,0 +1,16 @@ + + + + + + $(NoWarn);CS1591 + + diff --git a/test/Renci.SshNet.WindowsPhone8.Tests/App.xaml b/test/Renci.SshNet.WindowsPhone8.Tests/App.xaml deleted file mode 100644 index 0ce73407..00000000 --- a/test/Renci.SshNet.WindowsPhone8.Tests/App.xaml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/test/Renci.SshNet.WindowsPhone8.Tests/App.xaml.cs b/test/Renci.SshNet.WindowsPhone8.Tests/App.xaml.cs deleted file mode 100644 index 771bdf25..00000000 --- a/test/Renci.SshNet.WindowsPhone8.Tests/App.xaml.cs +++ /dev/null @@ -1,222 +0,0 @@ -using System; -using System.Diagnostics; -using System.Windows; -using System.Windows.Markup; -using System.Windows.Navigation; -using Microsoft.Phone.Controls; -using Microsoft.Phone.Shell; -using Renci.SshNet.Tests.Resources; - -namespace Renci.SshNet.Tests -{ - public partial class App : Application - { - /// - /// Provides easy access to the root frame of the Phone Application. - /// - /// The root frame of the Phone Application. - public static PhoneApplicationFrame RootFrame { get; private set; } - - /// - /// Constructor for the Application object. - /// - public App() - { - // Global handler for uncaught exceptions. - UnhandledException += Application_UnhandledException; - - // Standard XAML initialization - InitializeComponent(); - - // Phone-specific initialization - InitializePhoneApplication(); - - // Language display initialization - InitializeLanguage(); - - // Show graphics profiling information while debugging. - if (Debugger.IsAttached) - { - // Display the current frame rate counters. - Application.Current.Host.Settings.EnableFrameRateCounter = true; - - // Show the areas of the app that are being redrawn in each frame. - //Application.Current.Host.Settings.EnableRedrawRegions = true; - - // Enable non-production analysis visualization mode, - // which shows areas of a page that are handed off to GPU with a colored overlay. - //Application.Current.Host.Settings.EnableCacheVisualization = true; - - // Prevent the screen from turning off while under the debugger by disabling - // the application's idle detection. - // Caution:- Use this under debug mode only. Application that disables user idle detection will continue to run - // and consume battery power when the user is not using the phone. - PhoneApplicationService.Current.UserIdleDetectionMode = IdleDetectionMode.Disabled; - } - - } - - // Code to execute when the application is launching (eg, from Start) - // This code will not execute when the application is reactivated - private void Application_Launching(object sender, LaunchingEventArgs e) - { - } - - // Code to execute when the application is activated (brought to foreground) - // This code will not execute when the application is first launched - private void Application_Activated(object sender, ActivatedEventArgs e) - { - } - - // Code to execute when the application is deactivated (sent to background) - // This code will not execute when the application is closing - private void Application_Deactivated(object sender, DeactivatedEventArgs e) - { - } - - // Code to execute when the application is closing (eg, user hit Back) - // This code will not execute when the application is deactivated - private void Application_Closing(object sender, ClosingEventArgs e) - { - } - - // Code to execute if a navigation fails - private void RootFrame_NavigationFailed(object sender, NavigationFailedEventArgs e) - { - if (Debugger.IsAttached) - { - // A navigation has failed; break into the debugger - Debugger.Break(); - } - } - - // Code to execute on Unhandled Exceptions - private void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e) - { - if (Debugger.IsAttached) - { - // An unhandled exception has occurred; break into the debugger - Debugger.Break(); - } - } - - #region Phone application initialization - - // Avoid double-initialization - private bool phoneApplicationInitialized = false; - - // Do not add any additional code to this method - private void InitializePhoneApplication() - { - if (phoneApplicationInitialized) - return; - - // Create the frame but don't set it as RootVisual yet; this allows the splash - // screen to remain active until the application is ready to render. - RootFrame = new PhoneApplicationFrame(); - RootFrame.Navigated += CompleteInitializePhoneApplication; - - // Handle navigation failures - RootFrame.NavigationFailed += RootFrame_NavigationFailed; - - // Handle reset requests for clearing the backstack - RootFrame.Navigated += CheckForResetNavigation; - - // Ensure we don't initialize again - phoneApplicationInitialized = true; - } - - // Do not add any additional code to this method - private void CompleteInitializePhoneApplication(object sender, NavigationEventArgs e) - { - // Set the root visual to allow the application to render - if (RootVisual != RootFrame) - RootVisual = RootFrame; - - // Remove this handler since it is no longer needed - RootFrame.Navigated -= CompleteInitializePhoneApplication; - } - - private void CheckForResetNavigation(object sender, NavigationEventArgs e) - { - // If the app has received a 'reset' navigation, then we need to check - // on the next navigation to see if the page stack should be reset - if (e.NavigationMode == NavigationMode.Reset) - RootFrame.Navigated += ClearBackStackAfterReset; - } - - private void ClearBackStackAfterReset(object sender, NavigationEventArgs e) - { - // Unregister the event so it doesn't get called again - RootFrame.Navigated -= ClearBackStackAfterReset; - - // Only clear the stack for 'new' (forward) and 'refresh' navigations - if (e.NavigationMode != NavigationMode.New && e.NavigationMode != NavigationMode.Refresh) - return; - - // For UI consistency, clear the entire page stack - while (RootFrame.RemoveBackEntry() != null) - { - ; // do nothing - } - } - - #endregion - - // Initialize the app's font and flow direction as defined in its localized resource strings. - // - // To ensure that the font of your application is aligned with its supported languages and that the - // FlowDirection for each of those languages follows its traditional direction, ResourceLanguage - // and ResourceFlowDirection should be initialized in each resx file to match these values with that - // file's culture. For example: - // - // AppResources.es-ES.resx - // ResourceLanguage's value should be "es-ES" - // ResourceFlowDirection's value should be "LeftToRight" - // - // AppResources.ar-SA.resx - // ResourceLanguage's value should be "ar-SA" - // ResourceFlowDirection's value should be "RightToLeft" - // - // For more info on localizing Windows Phone apps see http://go.microsoft.com/fwlink/?LinkId=262072. - // - private void InitializeLanguage() - { - try - { - // Set the font to match the display language defined by the - // ResourceLanguage resource string for each supported language. - // - // Fall back to the font of the neutral language if the Display - // language of the phone is not supported. - // - // If a compiler error is hit then ResourceLanguage is missing from - // the resource file. - RootFrame.Language = XmlLanguage.GetLanguage(AppResources.ResourceLanguage); - - // Set the FlowDirection of all elements under the root frame based - // on the ResourceFlowDirection resource string for each - // supported language. - // - // If a compiler error is hit then ResourceFlowDirection is missing from - // the resource file. - FlowDirection flow = (FlowDirection)Enum.Parse(typeof(FlowDirection), AppResources.ResourceFlowDirection); - RootFrame.FlowDirection = flow; - } - catch - { - // If an exception is caught here it is most likely due to either - // ResourceLangauge not being correctly set to a supported language - // code or ResourceFlowDirection is set to a value other than LeftToRight - // or RightToLeft. - - if (Debugger.IsAttached) - { - Debugger.Break(); - } - - throw; - } - } - } -} \ No newline at end of file diff --git a/test/Renci.SshNet.WindowsPhone8.Tests/Assets/AlignmentGrid.png b/test/Renci.SshNet.WindowsPhone8.Tests/Assets/AlignmentGrid.png deleted file mode 100644 index f7d2e978..00000000 Binary files a/test/Renci.SshNet.WindowsPhone8.Tests/Assets/AlignmentGrid.png and /dev/null differ diff --git a/test/Renci.SshNet.WindowsPhone8.Tests/Assets/ApplicationIcon.png b/test/Renci.SshNet.WindowsPhone8.Tests/Assets/ApplicationIcon.png deleted file mode 100644 index 7d95d4e0..00000000 Binary files a/test/Renci.SshNet.WindowsPhone8.Tests/Assets/ApplicationIcon.png and /dev/null differ diff --git a/test/Renci.SshNet.WindowsPhone8.Tests/Assets/Tiles/FlipCycleTileLarge.png b/test/Renci.SshNet.WindowsPhone8.Tests/Assets/Tiles/FlipCycleTileLarge.png deleted file mode 100644 index e0c59ac0..00000000 Binary files a/test/Renci.SshNet.WindowsPhone8.Tests/Assets/Tiles/FlipCycleTileLarge.png and /dev/null differ diff --git a/test/Renci.SshNet.WindowsPhone8.Tests/Assets/Tiles/FlipCycleTileMedium.png b/test/Renci.SshNet.WindowsPhone8.Tests/Assets/Tiles/FlipCycleTileMedium.png deleted file mode 100644 index e93b89d6..00000000 Binary files a/test/Renci.SshNet.WindowsPhone8.Tests/Assets/Tiles/FlipCycleTileMedium.png and /dev/null differ diff --git a/test/Renci.SshNet.WindowsPhone8.Tests/Assets/Tiles/FlipCycleTileSmall.png b/test/Renci.SshNet.WindowsPhone8.Tests/Assets/Tiles/FlipCycleTileSmall.png deleted file mode 100644 index 550b1b5e..00000000 Binary files a/test/Renci.SshNet.WindowsPhone8.Tests/Assets/Tiles/FlipCycleTileSmall.png and /dev/null differ diff --git a/test/Renci.SshNet.WindowsPhone8.Tests/LocalizedStrings.cs b/test/Renci.SshNet.WindowsPhone8.Tests/LocalizedStrings.cs deleted file mode 100644 index 233fed37..00000000 --- a/test/Renci.SshNet.WindowsPhone8.Tests/LocalizedStrings.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Renci.SshNet.Tests.Resources; - -namespace Renci.SshNet.Tests -{ - /// - /// Provides access to string resources. - /// - public class LocalizedStrings - { - private static readonly AppResources _localizedResources = new AppResources(); - - public AppResources LocalizedResources { get { return _localizedResources; } } - } -} \ No newline at end of file diff --git a/test/Renci.SshNet.WindowsPhone8.Tests/MainPage.xaml b/test/Renci.SshNet.WindowsPhone8.Tests/MainPage.xaml deleted file mode 100644 index 4da3c91e..00000000 --- a/test/Renci.SshNet.WindowsPhone8.Tests/MainPage.xaml +++ /dev/null @@ -1,62 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/test/Renci.SshNet.WindowsPhone8.Tests/MainPage.xaml.cs b/test/Renci.SshNet.WindowsPhone8.Tests/MainPage.xaml.cs deleted file mode 100644 index ce08790e..00000000 --- a/test/Renci.SshNet.WindowsPhone8.Tests/MainPage.xaml.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Threading; -using Microsoft.Phone.Controls; -using Microsoft.VisualStudio.TestPlatform.Core; -using Microsoft.VisualStudio.TestPlatform.TestExecutor; -using vstest_executionengine_platformbridge; - -namespace Renci.SshNet.Tests -{ - public partial class MainPage : PhoneApplicationPage - { - // Constructor - public MainPage() - { - InitializeComponent(); - - var wrapper = new TestExecutorServiceWrapper(); - new Thread(new ServiceMain((param0, param1) => wrapper.SendMessage((ContractName)param0, param1)).Run).Start(); - - } - } -} \ No newline at end of file diff --git a/test/Renci.SshNet.WindowsPhone8.Tests/Properties/AppManifest.xml b/test/Renci.SshNet.WindowsPhone8.Tests/Properties/AppManifest.xml deleted file mode 100644 index 6712a117..00000000 --- a/test/Renci.SshNet.WindowsPhone8.Tests/Properties/AppManifest.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - diff --git a/test/Renci.SshNet.WindowsPhone8.Tests/Properties/AssemblyInfo.cs b/test/Renci.SshNet.WindowsPhone8.Tests/Properties/AssemblyInfo.cs deleted file mode 100644 index c9bbc102..00000000 --- a/test/Renci.SshNet.WindowsPhone8.Tests/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Resources; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Renci.SshNet.WindowsPhone8.Tests")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("Renci.SshNet.WindowsPhone8.Tests")] -[assembly: AssemblyCopyright("Copyright © 2016")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("911d635c-6e0e-46e8-9e8b-99bfb1790991")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Revision and Build Numbers -// by using the '*' as shown below: -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] -[assembly: NeutralResourcesLanguageAttribute("en-US")] diff --git a/test/Renci.SshNet.WindowsPhone8.Tests/Properties/WMAppManifest.xml b/test/Renci.SshNet.WindowsPhone8.Tests/Properties/WMAppManifest.xml deleted file mode 100644 index 1590bfd8..00000000 --- a/test/Renci.SshNet.WindowsPhone8.Tests/Properties/WMAppManifest.xml +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - Assets\ApplicationIcon.png - - - - - - - - - - - - - - Assets\Tiles\FlipCycleTileSmall.png - 0 - Assets\Tiles\FlipCycleTileMedium.png - Renci.SshNet.WindowsPhone8.Tests - - - - - - - - - - - vstest_executionengine_platformbridge.dll - - - - - - - - - - - - \ No newline at end of file diff --git a/test/Renci.SshNet.WindowsPhone8.Tests/Renci.SshNet.WindowsPhone8.Tests.csproj b/test/Renci.SshNet.WindowsPhone8.Tests/Renci.SshNet.WindowsPhone8.Tests.csproj deleted file mode 100644 index 212e2840..00000000 --- a/test/Renci.SshNet.WindowsPhone8.Tests/Renci.SshNet.WindowsPhone8.Tests.csproj +++ /dev/null @@ -1,148 +0,0 @@ - - - - Debug - x86 - 10.0.20506 - 2.0 - {26F0D644-B3EF-47DF-8040-E9E4B2E63884} - {C089C8C0-30E0-4E22-80C0-CE093F111A43};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} - Library - Properties - Renci.SshNet.Tests - Renci.SshNet.Tests - WindowsPhone - v8.0 - $(TargetFrameworkVersion) - true - true - - - true - true - Renci.SshNet.WindowsPhone8.Tests_$(Configuration)_$(Platform).xap - Properties\AppManifest.xml - Renci.SshNet.WindowsPhone8.Tests.App - false - 11.0 - true - - - true - full - false - Bin\x86\Debug - TRACE;DEBUG;SILVERLIGHT - true - true - prompt - 4 - - - pdbonly - true - Bin\x86\Release - TRACE;SILVERLIGHT;WINDOWS_PHONE - true - true - prompt - 4 - - - true - full - false - Bin\ARM\Debug - DEBUG;TRACE;SILVERLIGHT;WINDOWS_PHONE - true - true - prompt - 4 - - - pdbonly - true - Bin\ARM\Release - TRACE;SILVERLIGHT;WINDOWS_PHONE - true - true - prompt - 4 - - - - App.xaml - - - - MainPage.xaml - - - - True - True - AppResources.resx - - - - - Designer - MSBuild:Compile - - - Designer - MSBuild:Compile - - - - - - Designer - - - - - - - - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - - - PublicResXFileCodeGenerator - AppResources.Designer.cs - - - - - - - - {4a6ca785-1c8a-47fe-98c0-30c675a9328b} - Renci.SshNet.WindowsPhone8 - - - - - - - - \ No newline at end of file diff --git a/test/Renci.SshNet.WindowsPhone8.Tests/Resources/AppResources.Designer.cs b/test/Renci.SshNet.WindowsPhone8.Tests/Resources/AppResources.Designer.cs deleted file mode 100644 index d4a8eaf7..00000000 --- a/test/Renci.SshNet.WindowsPhone8.Tests/Resources/AppResources.Designer.cs +++ /dev/null @@ -1,108 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace Renci.SshNet.Tests.Resources { - using System; - - - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - public class AppResources { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal AppResources() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - public static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Renci.SshNet.Tests.Resources.AppResources", typeof(AppResources).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - public static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - /// - /// Looks up a localized string similar to add. - /// - public static string AppBarButtonText { - get { - return ResourceManager.GetString("AppBarButtonText", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Menu Item. - /// - public static string AppBarMenuItemText { - get { - return ResourceManager.GetString("AppBarMenuItemText", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to MY APPLICATION. - /// - public static string ApplicationTitle { - get { - return ResourceManager.GetString("ApplicationTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to LeftToRight. - /// - public static string ResourceFlowDirection { - get { - return ResourceManager.GetString("ResourceFlowDirection", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to en-US. - /// - public static string ResourceLanguage { - get { - return ResourceManager.GetString("ResourceLanguage", resourceCulture); - } - } - } -} diff --git a/test/Renci.SshNet.WindowsPhone8.Tests/Resources/AppResources.resx b/test/Renci.SshNet.WindowsPhone8.Tests/Resources/AppResources.resx deleted file mode 100644 index 529a1943..00000000 --- a/test/Renci.SshNet.WindowsPhone8.Tests/Resources/AppResources.resx +++ /dev/null @@ -1,137 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - LeftToRight - Controls the FlowDirection for all elements in the RootFrame. Set to the traditional direction of this resource file's language - - - en-US - Controls the Language and ensures that the font for all elements in the RootFrame aligns with the app's language. Set to the language code of this resource file's language. - - - MY APPLICATION - - - add - - - Menu Item - - \ No newline at end of file