When and from where exactly would you set this?
In current Firefox code, the proxy is determined at the time the channel is created. This is logical, because the proxy determines which kind of physical channel is opened: via TCP directly, or SOCKS or a HTTP proxy, so that needs to happen at the time the channel is opened. Consequently, this can't be changed at any time the channel object exists.
There are several APIs how channels can come into being. If I could see - maybe even run (install a XPI, not a full browser build) - the code, that would help.
Does that code exist at all yet? From what I understood, you need that to segregate all requests made for one site from all requests for another site. Even if both contact google-analytics.com, the two should be separate, by means of a different SOCKS username. That would mean you'd need to intercept requests that the browser makes, not only modify those that you initiate. Does that intercept code exist yet? If you had only one global proxy so far, you might not have needed it, or maybe you have it already for other reasons. Such a hookup would be a task by itself.
holla. I'm not finding any traces of any username or password in the code. I had assumed that Mozilla currently has one global username and password for the SOCKS proxy. (I don't use SOCKS myself, and never have in the past.) But it seems there's no username/password code at all:
http://mxr.mozilla.org/comm-central/source/mozilla/netwerk/socket/nsSOCKSIOLayer.cpp#459
So, I'll have to add that, too.
Data flow:
My first approach would be to extend nsIProxyInfo with username and hostname, then to let nsISocketProvider.newSocket()/addToSocket() accept nsIProxyInfo instead of individual proxy values. This allows to pass the username from nsSocketTransport2.cpp to nsISOCKSIOLayer.cpp, which implements SOCKS and can send the username to the proxy.
Before nsSocketTransport: Luckily, nsISocketTransportService.createTransport() already accepts nsIProxyInfo. createTransport is called directly by the protocol implementations including http. nsHttpChannel implements nsIProxiesChannel, which allows access to the nsIProxyInfo.
So, you should be able to:
var httpChannel = .....QueryInterface(Ci.nsIHttpChannel);
assert(httpChannel instanceof Ci.nsIHttpChannel);
assert(httpChannel instanceof Ci.nsIProxiedChannel);
var proxy = httpChannel.proxyInfo;
proxy.username = "abc";
proxy.password = "def";
I have yet to verify when you would have to do that, and whether the APIs allows you to do that at all before the proxy connection is physically opened.
The above implements the SOCKS V4 and V5 protocols to send the username and (for V5 only) password.
There are also new prefs "network.proxy.socks_username" and "network.proxy.socks_password" to set them to static values to be used for all connections. This is not the usecase needed here for Tor, but I assume it's useful for other users, and for testing.
Speaking of testing, I don't know a SOCKS server that supports auth. I have tested with OpenSSH "ssh -D 1234 me@proxy", and that works. If I set SOCKS V5 and do not specify a username, the connection works and the remote site sees the IP address of the proxy. If I set SOCKS V5 and set the above username pref, and restart the browser (!), the connection fails entirely, presumably because OpenSSH doesn't support the auth method "username/password". If I set SOCKS V4 and set the above username pref, and restart the browser, the connection works, presumably because OpenSSH ignores the username sent in SOCKS V4 protocol. The two protocols differ a lot when it comes to authentication.
TODO:
Test: with a SOCKS V4 and a SOCKS V5 proxy that supports usernames.
Per-channel: Find out when exactly to set the username on the channel, and test that.