mirror of
https://github.com/netchx/netch.git
synced 2026-03-18 18:13:21 +08:00
Refactor SS,SSR Controller Generate Argument
This commit is contained in:
49
Netch/Models/Arguments.cs
Normal file
49
Netch/Models/Arguments.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Netch.Utils;
|
||||
|
||||
namespace Netch.Models
|
||||
{
|
||||
public static class Arguments
|
||||
{
|
||||
public static string Format(IEnumerable<object?> a)
|
||||
{
|
||||
var arguments = a.ToList();
|
||||
if (arguments.Count % 2 != 0)
|
||||
throw new FormatException("missing last argument value");
|
||||
|
||||
var tokens = new List<string>();
|
||||
|
||||
for (var i = 0; i < arguments.Count; i += 2)
|
||||
{
|
||||
var keyObj = arguments[i];
|
||||
var valueObj = arguments[i + 1];
|
||||
|
||||
if (keyObj is not string key)
|
||||
throw new FormatException($"argument key at array index {i} is not string");
|
||||
|
||||
switch (valueObj)
|
||||
{
|
||||
case SpecialArgument.Flag:
|
||||
tokens.Add(key);
|
||||
break;
|
||||
case null:
|
||||
case string value when value.IsNullOrWhiteSpace():
|
||||
continue;
|
||||
default:
|
||||
tokens.Add(key);
|
||||
tokens.Add(valueObj.ToString()!);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return string.Join(' ', tokens);
|
||||
}
|
||||
}
|
||||
|
||||
public enum SpecialArgument
|
||||
{
|
||||
Flag
|
||||
}
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Netch.Models
|
||||
{
|
||||
public abstract class ParameterBase
|
||||
{
|
||||
// null value par
|
||||
|
||||
private readonly bool _full;
|
||||
|
||||
protected readonly string ParametersSeparate = " ";
|
||||
protected readonly string Separate = " ";
|
||||
protected readonly string VerbPrefix = "-";
|
||||
protected readonly string FullPrefix = "--";
|
||||
|
||||
protected ParameterBase()
|
||||
{
|
||||
_full = !GetType().IsDefined(typeof(VerbAttribute));
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var parameters = GetType().GetProperties().Select(PropToParameter).Where(s => s != null).Cast<string>();
|
||||
return string.Join(ParametersSeparate, parameters).Trim();
|
||||
}
|
||||
|
||||
private string? PropToParameter(PropertyInfo p)
|
||||
{
|
||||
// prefix
|
||||
bool full;
|
||||
if (p.IsDefined(typeof(VerbAttribute)))
|
||||
full = false;
|
||||
else if (p.IsDefined(typeof(FullAttribute)))
|
||||
full = true;
|
||||
else
|
||||
full = _full;
|
||||
|
||||
var prefix = full ? FullPrefix : VerbPrefix;
|
||||
// key
|
||||
var key = p.GetCustomAttribute<RealNameAttribute>()?.Name ?? p.Name;
|
||||
|
||||
// build
|
||||
var value = p.GetValue(this);
|
||||
switch (value)
|
||||
{
|
||||
case bool b:
|
||||
return b ? $"{prefix}{key}" : null;
|
||||
default:
|
||||
if (string.IsNullOrWhiteSpace(value?.ToString()))
|
||||
return p.IsDefined(typeof(OptionalAttribute)) ? null : throw new RequiredArgumentValueInvalidException(p.Name, this, null);
|
||||
|
||||
if (p.IsDefined(typeof(QuoteAttribute)))
|
||||
value = $"\"{value}\"";
|
||||
|
||||
return $"{prefix}{key}{Separate}{value}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Class)]
|
||||
public class VerbAttribute : Attribute
|
||||
{
|
||||
// Don't use verb and full both on one class or property
|
||||
// if you did, will take verb
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Class)]
|
||||
public class FullAttribute : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
public class OptionalAttribute : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
public class QuoteAttribute : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
public class RealNameAttribute : Attribute
|
||||
{
|
||||
public string Name { get; }
|
||||
|
||||
public RealNameAttribute(string name)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class RequiredArgumentValueInvalidException : Exception
|
||||
{
|
||||
public string? ArgumentName { get; }
|
||||
|
||||
public object? ArgumentObject { get; }
|
||||
|
||||
private readonly string? _message;
|
||||
|
||||
private const string DefaultMessage = "{0}'s Argument \"{1}\" value invalid. A required argument's value can't be null or empty.";
|
||||
|
||||
public override string Message => _message ?? string.Format(DefaultMessage, ArgumentObject!.GetType(), ArgumentName);
|
||||
|
||||
public RequiredArgumentValueInvalidException()
|
||||
{
|
||||
_message = "Some Argument value invalid. A required argument value's can't be null or empty.";
|
||||
}
|
||||
|
||||
public RequiredArgumentValueInvalidException(string argumentName, object argumentObject, string? message)
|
||||
{
|
||||
ArgumentName = argumentName;
|
||||
ArgumentObject = argumentObject;
|
||||
_message = message;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,48 +27,21 @@ namespace Netch.Servers.Shadowsocks
|
||||
{
|
||||
var server = (Shadowsocks)s;
|
||||
|
||||
var command = new SSParameter
|
||||
var arguments = new object?[]
|
||||
{
|
||||
s = await server.AutoResolveHostnameAsync(),
|
||||
p = server.Port,
|
||||
b = this.LocalAddress(),
|
||||
l = this.Socks5LocalPort(),
|
||||
m = server.EncryptMethod,
|
||||
k = server.Password,
|
||||
u = true,
|
||||
plugin = server.Plugin,
|
||||
plugin_opts = server.PluginOption
|
||||
"-s", await server.AutoResolveHostnameAsync(),
|
||||
"-p", server.Port,
|
||||
"-b", this.LocalAddress(),
|
||||
"-l", this.Socks5LocalPort(),
|
||||
"-m", server.EncryptMethod,
|
||||
"-k", server.Password,
|
||||
"-u", SpecialArgument.Flag,
|
||||
"--plugin", server.Plugin,
|
||||
"--plugin-opts", server.PluginOption
|
||||
};
|
||||
|
||||
await StartGuardAsync(command.ToString());
|
||||
await StartGuardAsync(Arguments.Format(arguments));
|
||||
return new Socks5Bridge(IPAddress.Loopback.ToString(), this.Socks5LocalPort(), server.Hostname);
|
||||
}
|
||||
|
||||
[Verb]
|
||||
private class SSParameter : ParameterBase
|
||||
{
|
||||
public string? s { get; set; }
|
||||
|
||||
public ushort? p { get; set; }
|
||||
|
||||
public string? b { get; set; }
|
||||
|
||||
public ushort? l { get; set; }
|
||||
|
||||
public string? m { get; set; }
|
||||
|
||||
public string? k { get; set; }
|
||||
|
||||
public bool u { get; set; }
|
||||
|
||||
[Full] [Optional] public string? plugin { get; set; }
|
||||
|
||||
[Full]
|
||||
[Optional]
|
||||
[RealName("plugin-opts")]
|
||||
public string? plugin_opts { get; set; }
|
||||
|
||||
[Full] [Quote] [Optional] public string? acl { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
@@ -27,62 +28,24 @@ namespace Netch.Servers.ShadowsocksR
|
||||
{
|
||||
var server = (ShadowsocksR)s;
|
||||
|
||||
var command = new SSRParameter
|
||||
var arguments = new object?[]
|
||||
{
|
||||
s = await server.AutoResolveHostnameAsync(),
|
||||
p = server.Port,
|
||||
k = server.Password,
|
||||
m = server.EncryptMethod,
|
||||
t = "120",
|
||||
O = server.Protocol,
|
||||
G = server.ProtocolParam,
|
||||
o = server.OBFS,
|
||||
g = server.OBFSParam,
|
||||
b = this.LocalAddress(),
|
||||
l = this.Socks5LocalPort(),
|
||||
u = true
|
||||
"-s", await server.AutoResolveHostnameAsync(),
|
||||
"-p", server.Port,
|
||||
"-k", server.Password,
|
||||
"-m", server.EncryptMethod,
|
||||
"-t", 120,
|
||||
"-O", server.Protocol,
|
||||
"-G", server.ProtocolParam,
|
||||
"-o", server.OBFS,
|
||||
"-g", server.OBFSParam,
|
||||
"-b", this.LocalAddress(),
|
||||
"-l", this.Socks5LocalPort(),
|
||||
"-u", SpecialArgument.Flag
|
||||
};
|
||||
|
||||
await StartGuardAsync(command.ToString());
|
||||
return new Socks5Bridge(IPAddress.Loopback.ToString(), this.Socks5LocalPort(),server.Hostname);
|
||||
}
|
||||
|
||||
[Verb]
|
||||
class SSRParameter : ParameterBase
|
||||
{
|
||||
public string? s { get; set; }
|
||||
|
||||
public ushort? p { get; set; }
|
||||
|
||||
[Quote]
|
||||
public string? k { get; set; }
|
||||
|
||||
public string? m { get; set; }
|
||||
|
||||
public string? t { get; set; }
|
||||
|
||||
[Optional]
|
||||
public string? O { get; set; }
|
||||
|
||||
[Optional]
|
||||
public string? G { get; set; }
|
||||
|
||||
[Optional]
|
||||
public string? o { get; set; }
|
||||
|
||||
[Optional]
|
||||
public string? g { get; set; }
|
||||
|
||||
public string? b { get; set; }
|
||||
|
||||
public ushort? l { get; set; }
|
||||
|
||||
public bool u { get; set; }
|
||||
|
||||
[Full]
|
||||
[Quote]
|
||||
[Optional]
|
||||
public string? acl { get; set; }
|
||||
await StartGuardAsync(Arguments.Format(arguments));
|
||||
return new Socks5Bridge(IPAddress.Loopback.ToString(), this.Socks5LocalPort(), server.Hostname);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user