Browser support of TCP proxy

You said "If the network is slow, the OS may buffer up to bigger packets." Bigger packets may have redundant data, does the OS automatically clear the redundant data? Or is it just Nagle's algorithm and does not have redundant data?

If more than a specific amount time has passed since the last packet while there is still data buffered but not enough for a full packet, Nagle's algorithm will send a smaller packet anyway. There is no redundant data in this packet. It only sends whatever you wrote, so it is smaller than the maximum size.

1 Like

There is Direct Sockets API.

websocat provides an example of WebSocket to TCP socket.

You can use an browser extension to intercept HTTP(S) POST requests and respond with a a different response or use Native Messaging to launch and control a TCP socket connection from the browser.

How can I intercept POST packets in TLS connections? TLS is encrypted, I think the POST packets are also encrypted.

Cleartext TCP proxy is possible with std::io::copy() , but people who care about their privacy must use encrypted TCP proxy, std::io::copy() waits until the connection shutdowns, so it's impossible to encrypt data. I think std::net::TcpStream::read() is good, since it immediately returns after it read 1 packet, so it's possible to encrypt data.

I have no idea how to do this in Rust. I haven't gotten that far because Rust requires over 1GB to install the toolchain, and I'm running on a temporary file system on RAM.

Using a browser extension with debugger set in manifest.json and Chrome DevTools Protocol you can do something like this

import { bytesArrToBase64, base64ToBytesArr } from './base64-encode-decode.js';

const encoder = new TextEncoder();
chrome.debugger.onEvent.addListener(async ({ tabId }, message, params) => {
  console.log(tabId, message, params);
  if (
    message === 'Fetch.requestPaused' &&
    /^chrome-extension|ext_stream/.test(params.request.url)
  ) {
    await chrome.debugger.sendCommand({ tabId }, 'Fetch.fulfillRequest', {
      responseCode: 200,
      requestId: params.requestId,
      requestHeaders: params.request.headers,
      body: bytesArrToBase64(
        encoder.encode(
          JSON.stringify([...Uint8Array.from({ length: 1764 }, () => 255)])
        )
      ),
    });
  } else {
    await chrome.debugger.sendCommand({ tabId }, 'Fetch.continueRequest', {
      requestId: params.requestId,
    });
  }
});

chrome.action.onClicked.addListener(async (tab) => {
  const tabId = tab.id;
  await chrome.debugger.attach({tabId}, '1.3');
  await chrome.debugger.sendCommand({tabId}, 'Network.enable');
  await chrome.debugger.sendCommand({tabId}, 'Fetch.enable');
});

For my use case I am posting an empty string from arbotrary Web pages and replacing that empty string with real-time PCM

  let { readable, writable } = new TransformStream();
  (async _ => {
    for await (const _ of (async function* stream() {
      while (true) {
        yield (await fetch('./?ext_stream', {method:'post', body:'', cache: 'no-store'})).body.pipeTo(writable, {
          preventClose: true,
        });
      }
    })());
  })();

readable.pipeTo(new WritableStream({
  write(v) {
    console.log(v);
  }, close() {
    console.log('close');
  }
}));

// Stop streaming
await writable.close();

How can I intercept POST packets in TLS connections?

Note, because we can also enable Native Messaging in a browser extension we can replace the body of a POST request with a local (or remote) TCP stream from the Native Messaging host.

Technically you can redirect any request with declarativeNetRequest and other options.

I haven't been able to get this How to play with the API? · Issue #46 · WICG/direct-sockets · GitHub working, yet.

If there's a TcpStream s , doing:

let a = Arc::new(Mutex::new(s.try_clone().unwrap()));
let b = a.lock().unwrap();

The variable a has a clone of s, not s itself. Since it's a clone of s, if s itself is reading or writing data, will the .lock() method for b get blocked to wait for s finishes the jobs?

No, it will only block on anyone that called lock() on a (or a clone of it). Note that TcpStream can be read and written by multiple threads at the same time. Both through try_clone and the Read and Write implementations for &TcpStream. The OS will transparently handle any locking this internally needs.

How to hide TcpStream quantity over proxy? Tor, aka, The Onion Router, can use a single Entry Node to hide how many sites (the quantity) you visited from ISP; if Tor use different Entry Nodes for different sites, ISP can see how many sites you visited, even if ISP doesn't know the contents.

If you want to use Tor you can either connect to your local Tor client over the SOCKS protocol or you can use Arti which is a Rust implementation of Tor that may end up replacing the current C implementation of Tor as official implementation in the future.