[dcc2] Comments

myndzi myndzi at gmail.com
Sun May 23 05:00:40 EDT 2004


This may actually work this time...! Re-post follows:

Excuse me if this message contains anything that's been discussed
before; I"ve just joined the list and felt the need to bring up a few
things I noticed (from minor details to more major ones), and I didn't
see most or any of them being discussed in a quick skim through the
archives.

I stopped by the site today and noticed that the XML trash was done
away with. That was the only part keeping me from implementing DCC2 as
a mIRC addon, so I started work on it today. 7.5k later I have a few
comments, suggestions, etc. that I feel should be addressed.

First and foremost, there are some discrepancies and ambiguities in
the documents themselves. Because the connection draft is older than
the other, it references XML which is not present in the Transfers
draft.

<Edited> I had written a few paragraphs regarding connection tokens
here stemming from some confusion of my own. Perhaps it would be
advantageous to specifically mark each phase of connection negotiation
(change the initial DCC2 CTCP to DCC2 PUBLISH, for example, and the
second DCC2 ACCEPT to DCC2 FINALIZE). This leads into my next comment,
which is simply that it seems fairly easy for two misconfigured or
improperly implemented clients to get into a loop sending DCC2 Accept
CTCPS back and forth between themselves.

As CTCPs were defined, it was specified that any automated response to
a CTCP request (PRIVMSG encapsulated in 0x01) should be sent as a CTCP
reply (NOTICE encapsulated in 0x01) to avoid just this problem.
However, since it can be quite likely there is a third message in the
sequence, the current DCC2 protocol doesn't fit into this scheme very
well. In the previous paragraph I suggested explicitly naming each
step of the handshaking; I believe that would work well to remedy this
issue.

And now to the protocol itself. I noticed a couple things that could
use some work. For the first one, I will use a real-world example.

I have a friend on IRC who is behind an extremely strict firewall. The
only way he can access IRC is by connecting through a bounce or proxy
running on port 443, as port 443 cannot be proxied by his webproxy and
is therefore the only unfirewalled port he can make direct TCP
connections on. Currently, DCC2 has no way for someone in a situation
like my friend to specify which ports are available for him to
establish _outgoing_ connections on.

The NAT flag is a step in the right direction, but perhaps something
more specific would be useful, such as InPorts=23,80
OutPorts=1024-1034,5000. If these were implemented as both Publish
tokens and Connect tokens, it would give whichever side needs to open
the listening socket enough information to choose a port that can be
connected to. The NAT token would become an alias for InPorts=0
OutPorts=1-65535 (where 0 would specify no available ports;
alternately leaving the token out entirely could signify the same
thing).

The last thing I have to say for this e-mail regards the examples
again, but I am unable to determine if it is a part of the protocol
itself or not. In most or all of the examples, it shows the last
message repeating redundant information that was given in the previous
message. Since SID is [supposed to be] a guaranteed unique way to
identify a transaction, it wolud seem that things such as "Filename"
and "Multi" needn't be echoed back by the receiving end, and indeed,
these tokens may only lead to confusion or conflict. It would seem
that "Filename" and "Multi" need only be specified by the sending
client, while "Port" and "IPv4" (or "IPv6") need only be specified by
the client establishing the listening socket. The third and final
CTCP, if any, should only be a way for the initiating client to
provide its IP and Port once it has been decided that the initiating
client will be doing the listening.

In fact, since the IP and Port of the first client are all that need
be contained in the final CTCP, there's really no reason they
shouldn't be in the first CTCP (with the exception of size constraints
imposed by the IRC server itself). I believe 400 bytes is more than
enough space to get what needs getting across, especially with
abbreviated token names and such as I saw discussed in one thread. The
main source of length would be filenames (arbitrarily long) and
possibly IPv6 addresses, though it seems most of them can still be
abbreviated quite nicely. (And while we are talking about abbreviating
things, the text-based "long ip" used in conventional DCCs _is_
shorter in most cases than the corresponding dotted-quad form, and
could be shortened even further by using hexadecimal notation.)

In conclusion; aside from the inconsistencies mentioned in the docs,
this is something like what I suggest for the general flow of things,
with brackets around parts that seem likely choices for shortening:

ClientA: DCC2 PUB[LISH] App[lication]=[IRC]File IPv4=192.168.0.2
IPv6=::C0A8:6464 InPorts=1024-1034,1500 [OutPorts=*] Sec=SSL3,TLS
SID=1 File="Some band - A reasonably long title (makes for a decent
example).mp3" Size=5123456
(208 characters long without brackets, we've got plenty of room. You
suffer more potential delay from sending two separate commands,
especially when the second relies on a reply to the first, than you do
simply by sending a longer first line. More on this in a moment. [1])

ClientB: DCC2 ACC[EPT] IPv4 Port=1028 SSL3 Offset=1098765 SID=1
(ClientB cannot accept an incoming connection on any port, but _must_
select a port to connect to ClientA on from the offered InPorts. Also
demonstrates resuming file)

Alternately:
ClientB: DCC2 ACC[EPT] IPv4=192.168.0.3 Port=1030 SSL3 SID=1
(ClientB should always take the opportunity to accept the connection
if presented, it will establish the connection sooner)

So now the only remaining issue is, how does ClientB know when to
connect to ClientA? ClientA could send a "connect to me now!" message,
which wouldn't gain us anything on the original 2-3 message handshake,
so let's disregard that a moment. Probably the easiest way is for
ClientB to simply try to connect a certain amount of time after
replying, with a period for retries. ClientB could also multi-target
its reply to ClientA to itself, and begin to try to connect upon
receiving the reply; this has advantages and disadvantages. Most
servers support the NOTICE nick,nick format (remember, our second CTCP
is going to be a NOTICE -> CTCP REPLY), and this will ensure that
ClientB doesn't time out or hammer ClientA in the event that ClientB's
messages are being held on the server (More on this in a moment [2]).
It's not likely that ClientB will be able to connect immediately
anyway, so a delay of 200-500 ms or longer and retries still applies.

Now, let me get sidetracked for a moment...

[1] There is a mechanism on most (all?)  IRCDs that controls flooding
via a timestamp and the connected user's RecvQ. On Bahamut (and I'm
reasonably sure that most IRCDs follow a similar or identical
pattern), you are penalized 2 seconds for every line you send, with an
extra penalty of 1 second per set of 120 characters excluding the
first. This means that if you send a line of the max length (somewhat
less than 512 bytes, I believe it only counts the actual content...
not the source address and such), you will be penalized 6 seconds.
Additionally, multitargetting often imposes an extra penalty, but
often only past a certain number of targets.

Using our example of 208 bytes, we would be penalized 3 seconds.
Sending two lines separately would gain a penalty of at least 4
seconds.

[2] Now, you are given some leeway with the server. If your penalty is
under 10 seconds it will continue to faithfully relay your messages as
fast as it can. However, if you exceed your penalty, it will stop
sending your messages until you drop back under the limit, at which
point your next message or messages gets the opportunity to push you
back above the limit again... this is why, if you've ever pasted a lot
of text accidentally and gotten kicked from a channel for it, you
weren't able to rejoin immediately. If, while the server is holding
your messages, you manage to fill up your RecvQ entirely (the holding
place in memory for said messages), the server will disconnect you
with the quit message 'Excess Flood'.

Also I should mention that eggdrops and other scripts or clients that
queue outgoing messages have been known to get so backed up that by
the time the CTCP going out to indicate that the client is listening
for a connection actually gets sent, the socket itself has long since
timed out. This is the reason for the comment about multi-targetting
your reply to yourself; while it doesn't "feel right" for use in a
protocol, it's worth keeping these factors in mind when designing
what's going to happen.

Don't forget that after each message in the handshake, the whole thing
has the chance to be aborted without notice, so having 3 messages in
the handshake greatly increases the potential delays as far as 1)
response from the other side due to lag, the aforementioned flood
mechanism, netsplits, quits, or other reasons, and 2) timeouts due to
lack of response, of which there must (may?) be a maximum of two: one
after each message sent by ClientA.


All in all, it appears we are between a rock and a hard place if
ClientB can't accept connections from ClientA. The most elegant
solution probably goes like this:

ClientA DCC2 G[o]A[head] SID=1

...which still requires a third message (which I would strongly advise
be made a NOTICE along with the DCC2 ACCEPT), but it's a nice brief
one saying "I got your message and I'm waiting for your call".



On the off chance that any of you know me, you know my tendency to
write like this ;) I swear I intended only to write a couple
paragraphs outlining a few things I thought needed to be addressed.
Regardless, one thing led to another and here I am with a veritable
novel on my hands. I am looking forward to the finalization of DCC2,
it's been a long time in coming. I'll stick around for a while to view
replies etc. so tell me what you think, eh?

-myndzi


More information about the dcc2 mailing list