Closed Bug 1523367 Opened 1 year ago Closed 1 month ago

Support equivalent of Chromium's `--host-resolver-rules` in Necko

Categories

(Testing :: General, enhancement, P3)

Version 3
enhancement

Tracking

(Not tracked)

RESOLVED FIXED

People

(Reporter: nalexander, Unassigned)

References

(Blocks 1 open bug)

Details

While investigating Bug 1522133, I ran into Chromium's --host-resolver-rules. These rules allow to do (at least) two things:

  1. Hard-code DNS name resolution to fixed IP addresses, for example with a rule like MAP * 127.0.0.1. This is equivalent to setting the pref network.dns.forceResolve="127.0.0.1", added by Bug 1361099.

  2. Map ports for requests, for example with a rule like MAP *:80 127.0.0.1:8080.

I'd like to get some feedback from the Necko team on how to support something like 2.) in Necko.

As far as I can tell this is "transparent to the request". I.e., the socket connection would be opened to remote port 127.0.0.1:8080, but the HTTP header would still be host foo.com (i.e., without port 8080). I had a quick skim through the Necko code and this abstraction doesn't appear to exist: that is, it's not clear that we can differentiate between the "request ports" and the "socket ports". But I'm no Necko expert; maybe this is possible in the nsIChannel abstraction.

It's also not clear to me if this can be done with an HTTP/HTTPS proxy. That is, suppose I want to achieve the equivalent behaviour but I can't modify Necko itself. If I tell Firefox to route all traffic through a proxy, can the proxy itself do the port mapping silently? Is this visible to consumers?

This doesn't really block Bug 1522133, but it sure would be useful.

valentin: I see that you reviewed McManus's changes for Bug 1361099. Can you take a stab at orienting me on this, or redirect to somebody more suitable? Many thanks!

Flags: needinfo?(valentin.gosu)

(In reply to Nick Alexander :nalexander [he/him] from comment #0)

While investigating Bug 1522133, I ran into Chromium's --host-resolver-rules. These rules allow to do (at least) two things:

  1. Hard-code DNS name resolution to fixed IP addresses, for example with a rule like MAP * 127.0.0.1. This is equivalent to setting the pref network.dns.forceResolve="127.0.0.1", added by Bug 1361099.

  2. Map ports for requests, for example with a rule like MAP *:80 127.0.0.1:8080.

I'd like to get some feedback from the Necko team on how to support something like 2.) in Necko.

As far as I can tell this is "transparent to the request". I.e., the socket connection would be opened to remote port 127.0.0.1:8080, but the HTTP header would still be host foo.com (i.e., without port 8080). I had a quick skim through the Necko code and this abstraction doesn't appear to exist: that is, it's not clear that we can differentiate between the "request ports" and the "socket ports". But I'm no Necko expert; maybe this is possible in the nsIChannel abstraction.

That is correct, at the moment we have no way of differentiating between the two. We could, but I'm not sure it's worth the extra complexity.

It's also not clear to me if this can be done with an HTTP/HTTPS proxy. That is, suppose I want to achieve the equivalent behaviour but I can't modify Necko itself. If I tell Firefox to route all traffic through a proxy, can the proxy itself do the port mapping silently? Is this visible to consumers?

A proxy would work. The web requests shouldn't see that we're using a proxy, and the port mapping should be fairly straight-forward.

Flags: needinfo?(valentin.gosu)
Priority: -- → P3

Just an update on this. I talked further with Valentin on Vidyo. He suggests that it would be possible to do this port mapping in Necko, and probably not that hard -- but it would violate a lot of assumptions up and down the stack.

So I started to look into proxying, and discovered that a solution based on goproxy is trivial. Like, really trivial. Modifying this example ever so slightly, the following is sufficient:

package main

import (
	"flag"
	"fmt"
	"github.com/elazarl/goproxy"
	"log"
	"net"
	"net/http"
	"strings"
)

func main() {
	verbose := flag.Bool("v", false, "should every proxy request be logged to stdout")
	addr := flag.String("addr", ":4040", "proxy listen address")
	flag.Parse()
	proxy := goproxy.NewProxyHttpServer()
	proxy.Tr.Dial = func(network, addr string) (c net.Conn, err error) {
		rewritten := addr
		if strings.HasSuffix(rewritten, ":80") {
			rewritten = "127.0.0.1:8080"
		} else if strings.HasSuffix(rewritten, ":443") {
			rewritten = "127.0.0.1:8081"
		}

		return net.Dial(network, rewritten)
			}
	proxy.Verbose = *verbose
	log.Fatal(http.ListenAndServe(*addr, proxy))
}

Running that on :4040, and WPR on :8080 and :8081, and setting network.proxy.{http,https} = "localhost:4040", I have success. (With two certs, for goproxy and for WPR, but we can address that.)

Now, we could run this proxy separately, or we could integrate it into WPR, or we could try to make WPR actually be a full proxy for HTTPS traffic, and then grow support for this remapping. I will file an issue against WPR to investigate the latter two bits.

(In reply to Nick Alexander :nalexander [he/him] from comment #3)

Now, we could run this proxy separately, or we could integrate it into WPR, or we could try to make WPR actually be a full proxy for HTTPS traffic, and then grow support for this remapping. I will file an issue against WPR to investigate the latter two bits.

Not surprisingly, I'm not the first to consider this. I commented on a (closed) ticket against WprGo (that's how they seem to refer to it in their issue tracked) outlining my use case. Let's see if we get any response!

Duplicate of this bug: 1580911
Depends on: 1596799

I think we now have a 99% equivalent with two preferences:

  • network.dns.forceResolve to force the same IP for all hosts
  • network.socket.forcePort to remap ports to one or more different ports to actually connect to

Closing, please reopen if more work is needed (I take this bug a bit like a meta bug now)

Status: NEW → RESOLVED
Closed: 1 month ago
Resolution: --- → FIXED
You need to log in before you can comment on or make changes to this bug.