[Nice] New API and refactor
Youness Alaoui
youness.alaoui at collabora.co.uk
Tue May 6 17:51:53 PDT 2008
Hi,
As some of you know, I'm working on a refactor of libnice. I'm not sure
yet whether the external API will change much or not, but the internal
API and code needs major refactoring.
The way I currently see it is : modular, layered, simple and beautiful!
There are of course multiple angle at which we could start designing the
code, I currently want to concentrate on the modularity of libnice,
mainly, have the STUN part of it as a completely separate module.
Here are two possible layering designs :
First one :
APP
|
v
ICE -> STUN
|
v
Socket
Second one :
APP
|
v
ICE
|
v
STUN
|
v
Socket
let me be clear, do we want to have the ICE agent use the STUN library
to build messages, then send them on the socket, and when it receives a
message, try to parse it with the STUN library to see if it's a valid
stun message, then handle it, otherwise, give it to the application
(first design)...
Or do we want to have the ICE agent build messages using the STUN
library, then tell the lib to send the message, and when it receives a
message, the STUN library handles it, if it's a stun message, it will
tell the ICE agent that it has an inbound STUN message, if it's not,
then it tell the ICE agent that a data message was received.
In other words, should the STUN library be put between the socket and
the ICE agent, or should it only be *used* by the ICE agent to build and
parse messages.
Now, for the first layering design, I have two different API designs
which I will first explain :
Since I am better with code than words, let's talk C instead...
I'm currently looking exclusively at the STUN library and API, and
here's what I have so far for a design (incomplete) :
struct StunMessage {
guint16 type;
guint8 id[16];
GList *attributes; /* StunAttribute * list */
}
struct StunAttribute {
guint16 type;
guint16 size;
guint8 *value;
}
Sending :
StunMessage * msg = stun_message_new(STUN_RFC3489BIS);
stun_message_add_attribute_uint32 (msg, STUN_USE_CANDIDATE, use_cand);
stun_message_add_attribute (msg, STUN_ATTRIBUTE_USERNAME, username,
strlen(username));
stun_message_add_integrity(msg, password,
USE_SHORT_TERM_CREDENTIALS_MECHANISM);
stun_message_add_fingerprint(msg);
gchar *buffer = stun_message_pack(msg, &len);
stun_message_free(msg);
sendto(..., buffer, ...);
g_free(buffer);
Receiving :
static const guint16 attribute_list[] = { STUN_USE_CANIDATE,
STUN_ICE_CONTROLLING, ..., NULL}; /* List of comprehension-attributes
supported by the client of the lib */
StunMessage *msg = stun_message_create(attribute_list, buffer, len); /*
NULL if not a valid stun message */
stun_message_has_attribute(msg, STUN_ATTRIBUTE_FINGERPRINT);
gboolean valid = stun_message_check_fingerprint(msg);
gboolean got_attr = stun_message_get_attribute_uint32(msg,
STUN_ICE_CONTROLING, &value);
my issue is that I would like to have a StunAgent added, so we would
create one StunAgent with all our parameters (comprehension-attributes
that we support, RFC compliance (3489 or 3489bis), extensions to use
(FINGERPRINT), mechanisms (authentication/integrity mechanism to use :
long-term or short-term), etc... and we would use that agent to handle
all our stun needs, like for example :
StunAgent *stun = stun_agent_new (STUN_RFC3489BIS, attribute_list,
USE_FINGERPRINT_EXTENSION, USE_SHORT_TERM_CREDENTIALS_MECHANISM ...);
stun_agent_set_credentials(agent, username, password);
Sending :
StunMessage *msg = stun_agent_new_message (stun);
stun_message_add_attribute_uint32 (msg, STUN_USE_CANDIDATE, use_cand);
gchar *buf = stun_message_pack (msg, &len);
Receiving :
StunMessage *msg = stun_agent_parse_message (stun, buffer, len);
gboolean got_attr = stun_message_get_attribute_uint32(msg,
STUN_ICE_CONTROLING, &value);
so, the first design (with no StunAgent), is the preferred design chosen
by Olivier, while I prefer the StunAgent design
The reasoning behind Olivier's decision is (he will confirm this and
correct me if I'm wrong) is that the stun library should be very simple
and minimalistic and should only be used for parsing and generating stun
messages, while I think that the behaviors defined in the STUN RFC
should be handled by the STUN library itself (the STUN behavior defined
in ICE would still be handled by the ICE agent) since there are
different behaviors that are defined in the STUN RFC like the
authentification and fingerprint behaviors. Also note that I don't want
the ICE agent to keep track of the list of transaction ids it generated
for the sent requests to make sure that any STUN response corresponds to
a STUN request sent (+ any possible security related algorithms).
quickly put, the second layering design would have something like this :
StunAgent *stun = stun_agent_new (STUN_RFC3489BIS, attribute_list,
USE_FINGERPRINT_EXTENSION, USE_SHORT_TERM_CREDENTIALS_MECHANISM ...);
stun_agent_set_credentials(agent, username, password);
stun_agent_set_inbound_stun_callback(agent, nice_inbound_stun,
nice_agent); /* params : stun_agent, callback, user_data */
stun_agent_set_inbound_data_callback(agent, nice_inbound_data, nice_agent);
Sending would not change, apart from stun_message_pack which would
become stun_message_send, while receiving would only have the change of
having the code in the nice_inbound_stun and no more need for calling
stun_agent_parse_message since we would receive a pre-parsed
StunMessage* as argument.
So now my question to you is : what do you think? How do you think the
stun library should work and which design+layering do you prefer ?
Do you have some suggestions concerning the API to have?
I will write a more detailed API design (stun.h) if you think that would
help.
I'm sorry if anything I said is unclear and please don't hesitate to ask
for any clarifications.
Thanks,
Youness.
More information about the Nice
mailing list