private static void HandleEncrypt(Argument arg) { var isEncrypting = arg.arg == "--encrypt"; var alg = arg.GetParaOrThrow(0); var key = arg.GetParaOrThrow(1); var inputFile = arg.GetParaOrNull(2); var outputFile = arg.GetParaOrNull(3) ?? (inputFile != null ? inputFile + ".enc" : null); var input = inputFile.IsNullOrEmpty() ? Console.OpenStandardInput() : File.OpenRead(inputFile); var output = outputFile.IsNullOrEmpty() ? Console.OpenStandardOutput() : File.Open(outputFile, FileMode.Create); var encryptStreamFunc = Ss.GetCipherByName(alg).GetEncryptionStreamFunc(key); IMyStream from, to; if (isEncrypting) { from = MyStream.FromStream(input); to = encryptStreamFunc(MyStream.FromStream(output)); } else { from = encryptStreamFunc(MyStream.FromStream(input)); to = MyStream.FromStream(output); } Task.Run(async() => { await MyStream.StreamCopy(from, to, -1, true); await output.FlushAsync(); output.Dispose(); }).Wait(); }
public override async void OnNewConnection(TcpClient client) { var stream = GetMyStreamFromSocket(client.Client); try { var bs = BufferPool.GlobalGetBs(8 * 1024, false); var r = await stream.ReadAsyncR(bs); if (r <= 0) { return; } bs.Len = r; var ch = new TlsStream.ClientHello(); TlsStream.ParseClientHelloRecord(bs, ref ch, out _); if (ch.Sni == null) { return; } var conn = InConnectionTcp.Create(this, new AddrPort(ch.Sni, dest_port), new MyStreamWrapper(stream) { Queue = bs }); await HandleIncommingConnection(conn); } catch (Exception e) { Logger.exception(e, Logging.Level.Error, "OnNewConnection"); } finally { MyStream.CloseWithTimeout(stream).Forget(); } }
public override async Task <ConnectResult> ProtectedConnect(ConnectArgument arg) { var dest = arg.Dest; var baseResult = await ConnectHelper.Connect(this, server.WithDefaultPort(80), connect_timeout); if (!baseResult.Ok) { return(baseResult); } try { var dataStream = baseResult.Stream; var asStream = MyStream.ToStream(dataStream); var sw = new StringWriter(new StringBuilder(1024)); var destStr = dest.ToString(); HttpClient.WriteHttpRequestHeader(sw, "CONNECT", destStr, new Dictionary <string, string> { ["Host"] = destStr }); await dataStream.WriteAsync(NaiveUtils.GetUTF8Bytes(sw.ToString())); var responseStr = await NaiveUtils.ReadStringUntil(asStream, NaiveUtils.DoubleCRLFBytes); var sr = new StringReader(responseStr); var response = HttpClient.ReadHttpResponseHeader(sr); if (response.StatusCode != "200") { throw new Exception($"remote server response '{response.StatusCode} {response.ReasonPhrase}'"); } return(CreateConnectResultWithStream(dataStream)); } catch (Exception) { MyStream.CloseWithTimeout(baseResult.Stream); throw; } }
public override async Task HandleTcpConnection(InConnectionTcp connection) { Exception e = null; ConnectResult connectResult = null; try { connectResult = await Connect(connection); } catch (Exception ex) when(if_failed != null) { Logging.exception(ex, Logging.Level.Error, $"{this}: {connection} failed ({connectResult.FailedReason}), redirecting to {if_failed}."); connection.RedirectTo(if_failed); return; } if (!connectResult.Ok && if_failed != null) { Logging.warning($": {connection} failed ({connectResult.FailedReason}), redirecting to {if_failed}."); connection.RedirectTo(if_failed); return; } try { if (connectResult.Ok) { await connection.HandleAndPutStream(this, connectResult.Stream, connectResult.WhenCanRead); } else { await connection.HandleAndGetStream(connectResult); } } finally { if (connectResult.Ok) { MyStream.CloseWithTimeout(connectResult.Stream); } } }
protected override Task OnFinish() { var dataStream = DataStream; if (dataStream != null) { MyStream.CloseWithTimeout(dataStream).Forget(); } return(base.OnFinish()); }
private void SetCurrentConfig(LoadedConfig loadedConfig) { CurrentConfig = loadedConfig; if (!loadedConfig.SocketImpl.IsNullOrEmpty()) { MyStream.SetSocketImpl(loadedConfig.SocketImpl); } MyStream.TwoWayCopier.DefaultUseLoggerAsVerboseLogger = IsDebugFlagEnabled("copier_v"); Channel.Debug = IsDebugFlagEnabled("mux_v"); YASocket.Debug = IsDebugFlagEnabled("ya_v"); Logging.AsyncLogging = IsDebugFlagEnabled("asynclog"); ConfigTomlLoaded?.Invoke(loadedConfig.TomlTable); }
public static async Task <ConnectResult> Connect(IAdapter adapter, AddrPort dest, int timeoutSeconds) { Socket socket; try { socket = await NaiveUtils.ConnectTcpAsync(dest, timeoutSeconds * 1000); } catch (Exception e) { return(new ConnectResult(adapter, ConnectResultEnum.Failed) { FailedReason = e.Message, Exception = e }); } return(new ConnectResult(adapter, MyStream.FromSocket(socket, adapter.GetAdapter().socket_impl))); }
public async Task Run() { var left = Left; var right = Right; int halfCloseTimeout = (10 * 1000) + NaiveUtils.Random.Next(-1000, 1000); const int forceCloseTimeout = 10 * 1000; try { var readFromRight = CopierFromRight.CopyAndShutdown(); var readFromLeft = CopierFromLeft.CopyAndShutdown(); var tasks = new Task[] { readFromRight, readFromLeft }; string stringFromTask(Task t) => t == readFromRight ? $"{right} -> {left}" : $"{left} -> {right}"; // waiting for half closing. var compeletedTask = await Task.WhenAny(tasks).CAF(); if (compeletedTask.IsFaulted) { var exception = compeletedTask.Exception.InnerException; Logger?.exception(exception, Logging.Level.Warning, $"stream copying exception, force closing. ({stringFromTask(compeletedTask)})"); return; } var anotherTask = compeletedTask == readFromRight ? readFromLeft : readFromRight; // waiting for full closing with timeout. if (await anotherTask.WithTimeout(halfCloseTimeout)) { Logger?.warning($"keeping half closed for {halfCloseTimeout} ms, force closing. ({stringFromTask(anotherTask)})"); } else { if (anotherTask.IsFaulted) { Logger?.exception(anotherTask.Exception.InnerException, Logging.Level.Warning, $"half closed waiting exception. {stringFromTask(anotherTask)}"); } } } catch (Exception e) { Logger?.exception(e, Logging.Level.Error, $"Relay task ({left.SafeToStr()} <-> {right.SafeToStr()})"); } finally { var t1 = MyStream.CloseWithTimeout(left, forceCloseTimeout); var t2 = MyStream.CloseWithTimeout(right, forceCloseTimeout); //await t1; await t2; } }
public override void Stop() { base.Stop(); var stream = DataStream; if (stream == null) { stream = ConnectResult.Stream; } if (stream == null) { Controller.Logger.warning(this + ": Can not get the stream, failed to stop."); } else { Controller.Logger.info("Closing stream " + stream + " to stop connection " + this); MyStream.CloseWithTimeout(stream); } }
public override async Task <ConnectResult> ProtectedConnect(ConnectArgument arg) { var dest = arg.Dest; var baseResult = await ConnectHelper.Connect(this, server, connect_timeout); if (!baseResult.Ok) { return(baseResult); } try { var dataStream = getEncryptionStream(baseResult.Stream); var bytes = dest.ToSocks5Bytes(); await dataStream.WriteAsync(bytes); return(CreateConnectResultWithStream(dataStream)); } catch (Exception) { MyStream.CloseWithTimeout(baseResult.Stream); throw; } }
private async void UDPListen() { try { var anyEP = new IPEndPoint(0, 0); var socket = new Socket(listen.AddressFamily, SocketType.Dgram, ProtocolType.Udp); socket.Bind(listen); udpClient = MyStream.FromSocket(socket); while (true) { var r = await udpClient.ReadFromAsyncR(64 * 1024, anyEP); Task.Run(() => HandleUdpReceiveResult(r)).Forget(); } } catch (Exception e) { Logger.exception(e, Logging.Level.Warning, "UDP listener stopped"); } finally { udpClient?.Close(); udpClient = null; } }
public async void Start() { try { if (!await socks5svr.ProcessAsync()) { return; } this.Dest.Host = socks5svr.TargetAddr; this.Dest.Port = socks5svr.TargetPort; this.DataStream = _stream; if (_adapter.fastreply) { await OnConnectionResult(new ConnectResult(null, ConnectResultEnum.OK)).CAF(); } await _adapter.HandleIncommingConnection(this, outRef); } catch (Exception e) { _adapter.Logger.exception(e, Logging.Level.Warning, "listener"); } finally { MyStream.CloseWithTimeout(_stream).Forget(); } }
public static IMyStream ToMyStream(this Stream stream) { return(MyStream.FromStream(stream)); }
public static Stream ToStream(this IMyStream myStream) { return(MyStream.ToStream(myStream)); }
public static Task RelayWith(this IMyStream stream1, IMyStream stream2) { return(MyStream.Relay(stream1, stream2)); }
public async Task <SocketStream> ConnectAsync() { _socket = await NaiveUtils.ConnectTcpAsync(new AddrPort(_socksAddr, _socksPort), 0); var _ns = MyStream.FromSocket(_socket); var user = _username; var pass = _password ?? ""; byte[] buffer = user == null ? new byte[] { SOCKS_VER, 1, NOAUTH } : new byte[] { SOCKS_VER, AUTH_METH_SUPPORT, NOAUTH, USER_PASS_AUTH }; await _ns.WriteAsyncR(buffer); await _ns.ReadFullAsyncR(new BytesSegment(buffer, 0, 2)); if (buffer[1] == NOAUTH) { // nothing to do. } else if (buffer[1] == USER_PASS_AUTH) { byte[] credentials = new byte[user.Length + pass.Length + 3]; var pos = 0; credentials[pos++] = 1; credentials[pos++] = (byte)user.Length; pos += Encoding.ASCII.GetBytes(user, 0, user.Length, credentials, pos); credentials[pos++] = (byte)pass.Length; pos += Encoding.ASCII.GetBytes(pass, 0, pass.Length, credentials, pos); await _ns.WriteAsyncR(credentials); await _ns.ReadFullAsyncR(new BytesSegment(buffer, 0, 2)); if (buffer[1] != SOCKS_CMD_SUCCSESS) { throw new SocksRefuseException("Invalid username or password."); } } else { _socket.Close(); throw new SocksAuthException(); } byte addrType = GetAddressType(); byte[] address = GetDestAddressBytes(addrType, _destAddr); byte[] port = GetDestPortBytes(_destPort); buffer = new byte[4 + port.Length + address.Length]; buffer[0] = SOCKS_VER; buffer[1] = CMD_CONNECT; buffer[2] = 0x00; //reserved buffer[3] = addrType; address.CopyTo(buffer, 4); port.CopyTo(buffer, 4 + address.Length); await _ns.WriteAsyncR(buffer); buffer = new byte[256]; await _ns.ReadFullAsyncR(new BytesSegment(buffer, 0, 4)); if (buffer[1] != SOCKS_CMD_SUCCSESS) { throw new SocksRefuseException($"remote socks5 server returns {new BytesView(buffer, 0, 4)}"); } switch (buffer[3]) { case 1: await _ns.ReadFullAsyncR(new BytesSegment(buffer, 0, 4 + 2)); break; case 3: await _ns.ReadFullAsyncR(new BytesSegment(buffer, 0, 1)); await _ns.ReadFullAsyncR(new BytesSegment(buffer, 0, buffer[0])); break; case 4: await _ns.ReadFullAsyncR(new BytesSegment(buffer, 0, 16 + 2)); break; default: throw new Exception("Not supported addr type: " + buffer[3]); } return(_ns); }
static SocketStream CreateUdpSocket() => MyStream.FromSocket(new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp));
static void MainContinued(ArgParseResult ar, Controller controller) { LogFileWriter logWriter = null; ar.TryGetValue("--cmd", out var argcmd); if (argcmd == null) { Logging.info(NameWithVertionText); if (GuiMode) { Logging.info("(GUI mode)"); LogStdout = false; } } if (ar.TryGetValue("--log-file", out var logFile)) { Logging.info($"Logging file: {logFile.FirstParaOrThrow}"); logWriter = new LogFileWriter(logFile.FirstParaOrThrow, Logging.RootLogger); logWriter.Start(); logWriter.WriteHistoryLog(); } if (ar.TryGetValue("-c", out var v)) { specifiedConfigPath = v.FirstParaOrThrow; Logging.info($"configuation file: {specifiedConfigPath}"); } else if (ar.ContainsKey("--config-stdin")) { Logging.info("reading configuration from stdin until EOF..."); specifiedConfigContent = Console.In.ReadToEnd(); Logging.info($"configuration read {specifiedConfigContent.Length} chars"); } if (ar.TryGetValue("--socket-impl", out var socketImpl)) { MyStream.SetSocketImpl(socketImpl.FirstParaOrThrow); } long lastPackets = 0, lastBytes = 0; void updateTitle() { lock (CmdConsole.ConsoleOnStdIO.Lock) { var p = MyStream.TotalCopiedPackets; var b = MyStream.TotalCopiedBytes; Console.Title = $"{controller.RunningConnections}/{controller.TotalHandledConnections} current/total connections | relayed {p:N0} Δ{p - lastPackets:N0} packets / {b:N0} Δ{b - lastBytes:N0} bytes - {BuildInfo.AppName}"; lastPackets = p; lastBytes = b; } } bool titleUpdateRunning = false; Timer titleUpdateTimer = null; titleUpdateTimer = new Timer((x) => { if (titleUpdateRunning) { updateTitle(); } else { titleUpdateTimer.Change(-1, -1); Console.Title = BuildInfo.AppName; } }); controller.ConfigTomlLoaded += (x) => { if (x.TryGetValue <string>("log_file", out var log_file)) { log_file = controller.ProcessFilePath(log_file); if (logWriter?.LogFile != log_file) { logWriter?.Stop(); if (log_file != null) { logWriter = new LogFileWriter(log_file, Logging.RootLogger); logWriter.Start(); } } } var toRun = x.TryGetValue("update_title", Environment.OSVersion.Platform == PlatformID.Win32NT) && !GuiMode; if (toRun != titleUpdateRunning) { if (toRun) { titleUpdateRunning = true; titleUpdateTimer.Change(1000, 1000); } else { titleUpdateRunning = false; } } }; if (ar.ContainsKey("--force-jit")) { ForceJit(); } else if (ar.ContainsKey("--force-jit-async")) { Task.Run(() => ForceJit()); } if (argcmd == null || specifiedConfigPath != null || specifiedConfigContent != null) { if (specifiedConfigPath != null) { Commands.loadController(controller, specifiedConfigPath); } else if (specifiedConfigContent != null) { controller.FuncGetConfigFile = () => Controller.ConfigFile.FromContent(specifiedConfigContent); controller.Load(); controller.Start(); } else { var paths = GetConfigFilePaths(); controller.LoadConfigFileFromMultiPaths(paths); controller.Start(); } } var cmdHub = new CommandHub(); CommandHub = cmdHub; cmdHub.Prompt = $"{BuildInfo.AppName}>"; Commands.AddCommands(cmdHub, controller, null, new string[] { "all" }); cmdHub.AddCmdHandler("newbie", (cmd) => Commands.NewbieWizard(cmd, controller, specifiedConfigPath ?? configFilePath)); cmdHub.AddCmdHandler("ver", (cmd) => cmd.WriteLine(NameWithVertionText)); if (Environment.OSVersion.Platform == PlatformID.Win32NT) { cmdHub.AddCmdHandler("openfolder", (cmd) => { if (cmd.args.Length == 0) { Process.Start("explorer", "."); } else if (cmd.args.Length > 1) { goto WRONT_ARG; } else if (cmd.args[0] == "exe") { OpenFolerAndShowFile(Process.GetCurrentProcess().MainModule.FileName); } else if (cmd.args[0] == "config") { OpenFolerAndShowFile(controller.CurrentConfig.FilePath); } else { goto WRONT_ARG; } return; WRONT_ARG: cmd.statusCode = 1; cmd.WriteLine("wrong arguments"); void OpenFolerAndShowFile(string fileName) => Process.Start("explorer", $"/select, \"{fileName}\""); }, "Usage: openfolder [exe|config]"); } if (argcmd != null) { HandleArgCmd(argcmd, cmdHub); return; } #if NS_WINFORM cmdHub.AddCmdHandler("gui", (cmd) => { var winform = new WinForm.WinFormController(controller); winform.StartUIThread(); winform.Invoke(() => winform.ShowControllerForm(false)); }); if (GuiMode) { return; } #endif if (ar.ContainsKey("--no-cli")) { goto WAITFOREVER; } if (InteractiveConsole(cmdHub)) { Environment.Exit(0); return; } WAITFOREVER: #if NS_WINFORM if (GuiMode) { return; } #endif while (true) { Thread.Sleep(int.MaxValue); } }
private async Task <Tuple <IMsgStream, ImuxSession> > GetMsgStream(HttpConnection p, Settings settings, NaiveProtocol.Request req) { var realKey = settings.realKey; const string XumPrefix = "chs2:"; bool isImux = req.additionalString.StartsWith(XumPrefix); ImuxSession imux = null; string encryptType = ""; if (req.extraStrings.Length > 0) { encryptType = req.extraStrings[0]; } if (!isImux && req.additionalString != "channels") { Logger.warning($"{p.remoteEP}: protocol not supported"); return(null); } IMsgStream msgStream = null; if (isImux) { var arr = NaiveUtils.DeserializeArray(req.additionalString.Substring(XumPrefix.Length)); var sessionId = arr[0]; int wsCount = Int32.Parse(arr[1]), wssoCount = 0, httpCount = 0; var connId = Int32.Parse(arr[2]); if (arr.Count > 3) { wssoCount = Int32.Parse(arr[3]); httpCount = Int32.Parse(arr[4]); } var connCount = wsCount + wssoCount + httpCount; int imuxMax = settings.imux_max; if (imuxMax < 0) { imuxMax = 16; } if (connCount > imuxMax) { Logger.warning($"{p.remoteEP}: IMUX count requesting ({connCount}) > imux_max ({imuxMax})"); return(null); } IMsgStream wsOrHttp; if (connId < connCount - httpCount) { wsOrHttp = await HandleWebsocket(p, realKey, encryptType); } else { p.setStatusCode("200 OK"); p.setHeader(HttpHeaders.KEY_Transfer_Encoding, HttpHeaders.VALUE_Transfer_Encoding_chunked); await p.EndResponseAsync(); var baseStream = MyStream.FromStream(p.SwitchProtocol()); var msf = new HttpChunkedEncodingMsgStream(baseStream); NaiveProtocol.ApplyEncryption(msf, realKey, encryptType); wsOrHttp = msf; } lock (imuxSessions) { if (imuxSessions.TryGetValue(sessionId, out imux) == false) { imux = new ImuxSession(sessionId, connCount) { WsCount = wsCount, WssoCount = wssoCount, HttpCount = httpCount }; imuxSessions.Add(sessionId, imux); NaiveUtils.SetTimeout(10 * 1000, () => { if (imux.ConnectedCount != imux.Count) { Logger.warning($"IMUX (id={imux.SessionId}, count={imux.ConnectedCount}/{imux.Count}) timed out"); imux.WhenEnd.SetResult(null); } }); } if (imux.HandleConnection(wsOrHttp, connId)) { // all connections are connected to the imux session, return the msg stream. msgStream = imux.MuxStream; } } if (msgStream == null) { // wait for the end of the imux session, prevent it from being disconncected. await imux.WhenEnd.Task; return(null); } } else { msgStream = await HandleWebsocket(p, realKey, encryptType); } return(Tuple.Create(msgStream, imux)); }
public IMyStream GetMyStreamFromSocket(Socket socket) => MyStream.FromSocket(socket, socket_impl);