You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

Program.cs 9.6 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. using Shadowsocks.Models;
  2. using Splat;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.CommandLine;
  6. using System.CommandLine.Invocation;
  7. using System.Diagnostics;
  8. using System.Linq;
  9. using System.Net;
  10. using System.Text;
  11. using System.Threading;
  12. using System.Threading.Tasks;
  13. namespace Shadowsocks.CLI
  14. {
  15. internal class Program
  16. {
  17. private static Task<int> Main(string[] args)
  18. {
  19. var clientCommand = new Command("client", "Shadowsocks client.");
  20. clientCommand.AddAlias("c");
  21. clientCommand.AddOption(new Option<Backend>("--backend", "Shadowsocks backend to use. Available backends: shadowsocks-rust, v2ray, legacy, pipelines."));
  22. clientCommand.AddOption(new Option<string?>("--listen", "Address and port to listen on for both SOCKS5 and HTTP proxy."));
  23. clientCommand.AddOption(new Option<string?>("--listen-socks", "Address and port to listen on for SOCKS5 proxy."));
  24. clientCommand.AddOption(new Option<string?>("--listen-http", "Address and port to listen on for HTTP proxy."));
  25. clientCommand.AddOption(new Option<string>("--server-address", "Address of the remote Shadowsocks server to connect to."));
  26. clientCommand.AddOption(new Option<int>("--server-port", "Port of the remote Shadowsocks server to connect to."));
  27. clientCommand.AddOption(new Option<string>("--method", "Encryption method to use for remote Shadowsocks server."));
  28. clientCommand.AddOption(new Option<string?>("--password", "Password to use for remote Shadowsocks server."));
  29. clientCommand.AddOption(new Option<string?>("--key", "Encryption key (NOT password!) to use for remote Shadowsocks server."));
  30. clientCommand.AddOption(new Option<string?>("--plugin", "Plugin binary path."));
  31. clientCommand.AddOption(new Option<string?>("--plugin-opts", "Plugin options."));
  32. clientCommand.AddOption(new Option<string?>("--plugin-args", "Plugin startup arguments."));
  33. clientCommand.Handler = CommandHandler.Create(
  34. async (Backend backend, string? listen, string? listenSocks, string? listenHttp, string serverAddress, int serverPort, string method, string? password, string? key, string? plugin, string? pluginOpts, string? pluginArgs, CancellationToken cancellationToken) =>
  35. {
  36. Locator.CurrentMutable.RegisterConstant<ConsoleLogger>(new());
  37. if (string.IsNullOrEmpty(listenSocks))
  38. {
  39. LogHost.Default.Error("You must specify SOCKS5 listen address and port.");
  40. return;
  41. }
  42. Client.Legacy? legacyClient = null;
  43. Client.Pipelines? pipelinesClient = null;
  44. switch (backend)
  45. {
  46. case Backend.SsRust:
  47. LogHost.Default.Error("Not implemented.");
  48. break;
  49. case Backend.V2Ray:
  50. LogHost.Default.Error("Not implemented.");
  51. break;
  52. case Backend.Legacy:
  53. if (!string.IsNullOrEmpty(password))
  54. {
  55. legacyClient = new();
  56. legacyClient.Start(listenSocks, serverAddress, serverPort, method, password, plugin, pluginOpts, pluginArgs);
  57. }
  58. else
  59. LogHost.Default.Error("The legacy backend requires password.");
  60. break;
  61. case Backend.Pipelines:
  62. pipelinesClient = new();
  63. await pipelinesClient.Start(listenSocks, serverAddress, serverPort, method, password, key, plugin, pluginOpts, pluginArgs);
  64. break;
  65. default:
  66. LogHost.Default.Error("Not implemented.");
  67. break;
  68. }
  69. while (!cancellationToken.IsCancellationRequested)
  70. {
  71. await Task.Delay(TimeSpan.FromHours(1.00), cancellationToken);
  72. Console.WriteLine("An hour has passed.");
  73. }
  74. switch (backend)
  75. {
  76. case Backend.SsRust:
  77. LogHost.Default.Error("Not implemented.");
  78. break;
  79. case Backend.V2Ray:
  80. LogHost.Default.Error("Not implemented.");
  81. break;
  82. case Backend.Legacy:
  83. legacyClient?.Stop();
  84. break;
  85. case Backend.Pipelines:
  86. pipelinesClient?.Stop();
  87. break;
  88. default:
  89. LogHost.Default.Error("Not implemented.");
  90. break;
  91. }
  92. });
  93. var serverCommand = new Command("server", "Shadowsocks server.");
  94. serverCommand.AddAlias("s");
  95. serverCommand.Handler = CommandHandler.Create(
  96. () =>
  97. {
  98. Console.WriteLine("Not implemented.");
  99. });
  100. var convertConfigCommand = new Command("convert-config", "Convert between different config formats. Supported formats: SIP002 links, SIP008 delivery JSON, and V2Ray JSON (outbound only).");
  101. convertConfigCommand.AddOption(new Option<string[]?>("--from-urls", "URL conversion sources. Multiple URLs are supported. Supported protocols are ss:// and https://."));
  102. convertConfigCommand.AddOption(new Option<string[]?>("--from-sip008-json", "SIP008 JSON conversion sources. Multiple JSON files are supported."));
  103. convertConfigCommand.AddOption(new Option<string[]?>("--from-v2ray-json", "V2Ray JSON conversion sources. Multiple JSON files are supported."));
  104. convertConfigCommand.AddOption(new Option<bool>("--prefix-group-name", "Whether to prefix group name to server names after conversion."));
  105. convertConfigCommand.AddOption(new Option<bool>("--to-urls", "Convert to ss:// links and print."));
  106. convertConfigCommand.AddOption(new Option<string?>("--to-sip008-json", "Convert to SIP008 JSON and save to the specified path."));
  107. convertConfigCommand.AddOption(new Option<string?>("--to-v2ray-json", "Convert to V2Ray JSON and save to the specified path."));
  108. convertConfigCommand.Handler = CommandHandler.Create(
  109. async (string[]? fromUrls, string[]? fromSip008Json, string[]? fromV2rayJson, bool prefixGroupName, bool toUrls, string? toSip008Json, string? toV2rayJson, CancellationToken cancellationToken) =>
  110. {
  111. var configConverter = new ConfigConverter(prefixGroupName);
  112. try
  113. {
  114. if (fromUrls != null)
  115. {
  116. var uris = new List<Uri>();
  117. foreach (var url in fromUrls)
  118. {
  119. if (Uri.TryCreate(url, UriKind.Absolute, out var uri))
  120. uris.Add(uri);
  121. else
  122. Console.WriteLine($"Invalid URL: {url}");
  123. }
  124. await configConverter.FromUrls(uris, cancellationToken);
  125. }
  126. if (fromSip008Json != null)
  127. await configConverter.FromSip008Json(fromSip008Json, cancellationToken);
  128. if (fromV2rayJson != null)
  129. await configConverter.FromV2rayJson(fromV2rayJson, cancellationToken);
  130. if (toUrls)
  131. {
  132. var uris = configConverter.ToUrls();
  133. foreach (var uri in uris)
  134. Console.WriteLine(uri.AbsoluteUri);
  135. }
  136. if (!string.IsNullOrEmpty(toSip008Json))
  137. await configConverter.ToSip008Json(toSip008Json, cancellationToken);
  138. if (!string.IsNullOrEmpty(toV2rayJson))
  139. await configConverter.ToV2rayJson(toV2rayJson, cancellationToken);
  140. }
  141. catch (Exception ex)
  142. {
  143. Console.WriteLine(ex.Message);
  144. }
  145. });
  146. var utilitiesCommand = new Command("utilities", "Shadowsocks-related utilities.")
  147. {
  148. convertConfigCommand,
  149. };
  150. utilitiesCommand.AddAlias("u");
  151. utilitiesCommand.AddAlias("util");
  152. utilitiesCommand.AddAlias("utils");
  153. var rootCommand = new RootCommand("CLI for Shadowsocks server and client implementation in C#.")
  154. {
  155. clientCommand,
  156. serverCommand,
  157. utilitiesCommand,
  158. };
  159. Console.OutputEncoding = Encoding.UTF8;
  160. return rootCommand.InvokeAsync(args);
  161. }
  162. }
  163. }