RejectedSoftware Forums

Sign up

proxying a tcp connection

I'm currently writing a socks server. It works well, except the proxying itself. I'm currently using the following as a way to test the protocol stuff works (it does), but it obviously doesnt scale:

while (con.connected && sockscon.connected)
{
	try
	{
		while (con.dataAvailableForRead)
		{
			sockscon.write(con, con.leastSize);
			yield();
		}
		while (sockscon.dataAvailableForRead)
		{
			con.write(sockscon, sockscon.leastSize);
			yield();
		}
		yield();
		sleep(1.msecs);
	}
	catch
	{
		break;
	}						
}

How would I go about efficiently proxying one tcp connection into another?

Re: proxying a tcp connection

Am 11.10.2013 14:27, schrieb Matthew Fong:

I'm currently writing a socks server. It works well, except the proxying itself. I'm currently using the following as a way to test the protocol stuff works (it does), but it obviously doesnt scale:

while (con.connected && sockscon.connected)
{
	try
	{
		while (con.dataAvailableForRead)
		{
			sockscon.write(con, con.leastSize);
			yield();
		}
		while (sockscon.dataAvailableForRead)
		{
			con.write(sockscon, sockscon.leastSize);
			yield();
		}
		yield();
		sleep(1.msecs);
	}
	catch
	{
		break;
	}						
}

How would I go about efficiently proxying one tcp connection into another?

The Probably the best way is to use different tasks to handle the read
and write directions:

void proxyConnection(TCPConnection con, TCPConnection sockscon)
{
	scope (exit) {
		// make sure that everything gets closed in case of
		// some exception that doesn't result in a disconnect
		if (con.connected) con.close();
		if (sockscon.connected) sockscon.close();
	}

	auto t = runTask({
		try sockscon.write(con); catch {}
	});

	try con.write(sockscon); catch {}
	t.join(); // wait for the task to finish
}

Re: proxying a tcp connection

On Fri, 11 Oct 2013 19:34:50 +0200, Sönke Ludwig wrote:

Am 11.10.2013 14:27, schrieb Matthew Fong:

I'm currently writing a socks server. It works well, except the proxying itself. I'm currently using the following as a way to test the protocol stuff works (it does), but it obviously doesnt scale:

while (con.connected && sockscon.connected)
{
	try
	{
		while (con.dataAvailableForRead)
		{
			sockscon.write(con, con.leastSize);
			yield();
		}
		while (sockscon.dataAvailableForRead)
		{
			con.write(sockscon, sockscon.leastSize);
			yield();
		}
		yield();
		sleep(1.msecs);
	}
	catch
	{
		break;
	}						
}

How would I go about efficiently proxying one tcp connection into another?

The Probably the best way is to use different tasks to handle the read
and write directions:

void proxyConnection(TCPConnection con, TCPConnection sockscon)
{
	scope (exit) {
		// make sure that everything gets closed in case of
		// some exception that doesn't result in a disconnect
		if (con.connected) con.close();
		if (sockscon.connected) sockscon.close();
	}

	auto t = runTask({
		try sockscon.write(con); catch {}
	});

	try con.write(sockscon); catch {}
	t.join(); // wait for the task to finish
}

Thanks! I tried it and it works well. I didn't try it before because I thought accessing the same TCPConnection from different tasks wouldn't be a good idea.

Re: proxying a tcp connection

Am 12.10.2013 11:25, schrieb Matthew Fong:

Thanks! I tried it and it works well. I didn't try it before because I thought accessing the same TCPConnection from different tasks wouldn't be a good idea.

It's okay, under the condition that it is guaranteed to not have
concurrent reads or concurrent writes. But it still needs to be
documented somewhere other than the change log.

Re: proxying a tcp connection

Bit of a necro, but I'm doing something similar but ran into a few issues and am looking for advice.

In my case I'm proxying two connections as above, but both come in from listen sockets and thus have their own fibers. It would seem natural to use the two fibers for the two proxying directions, but I'm not sure how to most cleanly get the TCPConnection objects passed between the two.

I've tried to simply "send" one connection object across but of course that complains about mutable aliasing... which is exactly what I want in this case since there's no way for the language to know at compile time that what I'm doing is safe (i.e. only writing in one fiber, only reading in another). Making the TCPConnection "shared" causes other issues as most of the functionality doesn't work. This is yet another case where I don't even want parallelism (i.e. threading), just coroutines and the attempts to enforce "safety" are tiresome. It's a bit silly that the above example has exactly the same "sharing issue" but apparently it's all good since it's shared on the stack/closure vs. "sent" to another fiber.

I also tried simply having one of the fibers store its TCPConnection somewhere and then have the second one just pick it up as per the code segment above. The problem is, if you allow the first fiber to return/terminate, vibe closes/reuses the TCPConnection... thus you need to have the fiber sit there sleeping just to avoid falling through to the cleanup code which - while it does work - is hardly elegant.

The second part of the issue is that I need clean termination if one of the peers disconnects. My plan for this was for each of the two fibers to .interrupt the other and let each close their own sockets. However one needs to be a bit careful because closing a socket involves it taking both a reader and writer lock, so indeed you need both the fibers to sync out of the proxy loop before either returns. The above code can just do the cleanup after the join of course, but I'm not sure how to handle this with two fibers launched from listen sockets... other than somehow breaking the TCPConnection away from one and letting it terminate with the connection still alive, but unowned.

Thanks in advance for any advice!

[As an aside, I find the whole D concurrency design to be awkward and not really that helpful at avoiding errors (and I say that as someone who spends a significant amount of time doing parallel programming for work). I get the idea and know that people mean well, but I just don't think it helps to avoiding many non-trivial data races and it requires legitimate race-free code to bypass the illusion of "safety" frequently. But that's a rant for another day I guess :)]

Re: proxying a tcp connection

On Sat, 22 Feb 2014 22:41:24 GMT, punkUser wrote:

Bit of a necro, but I'm doing something similar but ran into a few issues and am looking for advice.

In my case I'm proxying two connections as above, but both come in from listen sockets and thus have their own fibers. It would seem natural to use the two fibers for the two proxying directions, but I'm not sure how to most cleanly get the TCPConnection objects passed between the two.

I also had this same questioning a while back for websockets. I wanted to connect two peers together to exchange messages. The ideal solution is to think tasks rather than sockets. Each socket is open in one task, and your task can exchange data with other tasks which, in turn, can write to sockets.

Thus, you'd have to create a global/static Task[string] aa that your connections mutually know about, and then you can use vibe.core.concurrency to Task.send and Task.receive. While you're waiting for information with Task.receive, there's yielding going on so as long as you loop through it you should be good to keep the socket ownership.

e.g.
Task[string] aa;
someConnection(HTTPServerRequest req, HTTPServerResponse res){
string tmp;
while(tmp = aa[req.params.peerId].receive!String)
res.write(tmp);
}

This is an HTTP request/response for simplification, but for raw tcp the socket read/write and task send/receive methods would work the same. Just think as tasks like processes and send/receive as IPC. Also, you should take a look at this book which helped me understand these concepts more easily (internally vibe is very kernel-like!)

http://books.google.ca/books?id=RzP3iH0BSW0C&lpg=PA99&ots=mpcCbSzUY_&dq=real-time%20concepts%20for%20embedded%20systems%20task%20message%20queue&pg=PA68#v=onepage&q&f=false

  • "real-time concepts for embedded systems" p.97

Re: proxying a tcp connection

Thus, you'd have to create a global/static Task[string] aa that your connections mutually know about

Right, I should have been clear... this is effectively what I'm already doing and it "works", but it's inelegant IMO. My follow-up here was basically to point out that global data - and indeed closures/delegates themselves - don't attempt to be "safe" in terms fo data races, but yet the concurrency interface in D (and thus vibe mirrors it) makes an ill-fated attempt to do it.

So yeah, I can basically bypass the aliasing analysis by working with shared global/stack data here and sync the fibers appropriately, but I argue that shows a bit of a weakness of the current concurrency model. I should be able to just pass stuff across in messages, but the attempt at making it "safe" makes the interface largely useless syntactic sugar with very little additional expressive power over a simple manual event.

Anyways this isn't a critique of vibe so much as D concurrency, but I thought it worth bringing up the discussion in the context of vibe since there are a large number of use cases that expose the issues.

(And again, yes I get the whole attempt at thread safety and vibe you don't have to be multithreaded yada yada, but I maintain that even in a multithreaded situation it does far more harm than good.)