ReSIProcate is an object oriented SIP interface and stack implemented in C++. The reSIProcate approach emphasizes consistency, type safety, and ease of use.
A central component of any SIP service is handling of SIP messages and their parts. A SIP message consists of headers, request/status line, and body.
Contents[hide ]
|
Headers are accessed from messages with the header method. The header method is overloaded so that its return value is appopriate for each type of header. The actual header method is determined by the header type token passed to the overloaded header method.
Each header type defined in RFC3261 has a corresponding header access token. For example, the header access tokens for To and From headers are h_To and h_From. The rule for determing the header access token from a header as named in RFC3261 is to remove all dashes from the header name and prefix the result with "h_". For example, "Content-Disposition" becomes h_ContentDisposition.
Given an existing message, fetching the To header is simply:
const NameAddr& to = message->header(h_To);
Each header-field-value type has a function that may be used to determine whether or not it is well-formed.
if(message->header(h_To).isWellFormed())
{
// Do stuff with the header
}
Right now, isWellFormed() is minimally picky; it will only return false if the header-field-value is unparsable. It does not report the presence of forbidden characters, unless their presence makes it impossible to parse the header. No method exists to carry out a full, pedantic syntax check on a header-field-value. This functionality may be added in the future.
The header methods are both accessors and setters. Accessing a header that isn't in the message creates the header in a default state if the message is non-const.
To set an empty message's To header:
SipMessage message;
// displayName and uri are accessor/setter methods on NameAddr, the storage class
// for To headers.
message.header(h_To).displayName() = "Speedy Shannon";
message.header(h_To).uri() = Uri("[email protected]");
Please note: If a header is malformed, and it has not yet been parsed, these accessors/setters will throw a ParseBuffer::Exception!
The header methods are used also to access existing headers. If you want to make sure that you are accessing an existing header and are not creating a default header, use the exists method. The exists method is overloaded with the same access tokens.
SipMessage* message;
if (!message->exists(h_To))
{
// complain bitterly
...
}
else
{
NameAddr& to = message->header(h_To);
...
}
However, if the message variable is declared const, the header methods will not create a default instance of a missing header, but will throw SipMessage::Exception. This is a typical mode when working with incoming messages.
try
{
const SipMessage* message;
To::Type& to = message->header(h_To);
...
}
catch (SipMessage::Exception e)
{
// complain bitterly
...
}
The remove method is also overloaded for the access tokens. Removing a header that does not exist is a no-op.
SipMessage* message = ...;
message->remove(h_RecordRoutes);
Each header type is either a single instance or multiple instance. For example, the header type To is single instance while the header type Record-Route is multiple instance. The return types differ accordingly.
Multiple instance headers are accessed through a collection of the appropriate header type. As a programming hint, access tokens for multiple headers are pluralized.
Similarly, the collection of each header type is named the pluralized header type. Below is an example accessing a collection of NameAddr instances.
NameAddrs& rr = message.header(h_RecordRoutes);
The collection of header values can be iterated in the usual stl like fashion.
for (NameAddrs::iterator i = rr.begin(); i != rr.end(); ++i)
{
NameAddr& r = *i;
...
}
All collections of header values support begin, end, empty, size, front, back, push_back, push_front, reverse, and clear. Each collection is specific to the header type so no casting is necessary.
NameAddr na;
na.displayName() = "Alice";
na.uri() = Uri("sip:[email protected]");
rr.push_back(na);
The request/status lines are special cases of headers. They are accessed by the header method with the header type tokens h_RequestLine and h_StatusLine. A message may have one or the other of these headers but never both. To determine if a message has a Request Line, use:
if (message->isRequest())
{
...
}
Similarly for Status Line:
if (message->isResponse())
{
...
}
Note that a newly created message has neither a request or status line. The application must add one or the other for the message to be well formed.
A message body is accessed with the getContents method. The retured value is of type Contents*, an abstract type. The return value must be cast (dynamic_cast is recommended for runtime type safety) to be used. The content type of a message can be determined by examining the Content-Type header of the message.
New message contents are created by instantiating an instance of a type derived from Contents. For example, SdpContents. A variety of content types are currently supported, including mulitpart, signed, and Pkcs7. New content types can be created either inside or outside of the reSIP library (see Creating a New Contents Type).
Setting the contents of a message takes care of setting the Content-Type and Content-Length of the message.
Pkcs7* pres = new Pkcs7();
...
message->setContents(pres);
Alternatively, pass the newly created contents as an auto_ptr and avoid the copy:
std::auto_ptr<Contents> pres(new Pkcs7());
...
message->setContents(pres);
Recursive multipart contents are supported.
Every RFC 3261 header has a corresponding access token. However, many of the headers have identical form. For example. The To and From header values both consist of a display name and a URI. The To and From headers are managed programmatically as NameAddr instances. The class that manages each header type is responsible for parsing header text, providing storage and access during the life of the message, and serializing the header value to text for transmission.
The table below shows the reSIP types for each of the built in RFC headers currently supported by reSIP. The reSIP type is the return type of a SipMessage header call with the access token as its argument. Plural values mean that the return value is a collection of the type indicated by the singular. The plural type exists in reSIP and is an STL like container.
RFC Name | reSIP Access Token | reSIP Type |
---|---|---|
Accept | h_Accepts | Mimes |
Accept-Encoding | h_AcceptEncodings | Tokens |
Accept-Language | h_AcceptLanguages | Tokens |
Alert-Info | h_AlertInfos | GenericUris |
Allow | h_Allows | Tokens |
Authentication-Info | h_AuthenticationInfos | Auths |
Authorization | h_Authorizations | Auths |
Call-ID | h_CallID, h_CallId | CallID, CallId |
Call-Info | h_CallInfos | GenericUris |
Contact | h_Contacts | NameAddrs |
Content-Disposition | h_ContentDisposition | Tokens |
Content-Encoding | h_ContentEncoding | Tokens |
Content-Language | h_ContentLanguages | Tokens |
Content-Length | h_ContentLength | UInt32Category |
Content-Type | h_ContentType | Mime |
Content-Transfer-Encoding | h_ContentTransferEncoding | StringCategory |
CSeq | h_CSeq | CSeqCategory |
Date | h_Date | DateCategory |
Error-Info | h_ErrorInfos | GenericUris |
Expires | h_Expires | UInt32Category |
From | h_From | NameAddr |
In-ReplyTo | h_InReplyTo | CallID, CallId |
Max-Forwards | h_MaxForwards | UInt32Category |
MIME-Version | h_MIMEVersion | Tokens |
Min-Expires | h_MinExpires | UInt32Category |
Organization | h_Organization | StringCategory |
Priority | h_Priority | Tokens |
Proxy-Authenticate | h_ProxyAuthenticates | Auths |
Proxy-Authorization | h_ProxyAuthorizations | Auths |
Proxy-Require | h_ProxyRequires | Tokens |
Record-Route | h_RecordRoutes | NameAddrs |
Reply-To | h_ReplyTo | NameAddr |
Require | h_Requires | Tokens |
Retry-After | h_RetryAfter | UInt32Category |
Route | h_Routes | NameAddrs |
Server | h_Server | StringCategory |
Subject | h_Subject | StringCategory |
Supported | h_Supporteds | Tokens |
Timestamp | h_Timestamp | StringCategory |
To | h_To | NameAddr |
Unsupported | h_Unsupporteds | Tokens |
User-Agent | h_UserAgent | StringCategory |
Via | h_Vias | Vias |
Warning | h_Warnings | WarningCategories |
WWW-Authenticate | h_WWWAuthenticates | Auths |
The following table lists each of the reSIP types for managing headers. A complete list of accessors is included for each type. Recall that many headers are multi-valued; the return type in the multi-valued cases must be iterated to get to the types shown. Multi-values headers are identified with multi-valued .
RFC name:
Request-Line
Description:
The first line of a request message. Does not correspond to a header proper
but is accessed with the header interface in reSIP.
Example:
INVITE sip:[email protected] SIP/2.0
Parts:
RFC Name accessor reSIP type settable
--------------------------------------------------------------
Request-URI uri() Uri yes
Method getMethod() MethodTypes yes
Method unknownMethodName() Data yes
SIP-Version getSipVersion() Data no
RFC Headers: <none>
RFC name:
Status-Line
Description:
The first line of a response message. Does not correspond to a header proper
but is accessed with the header interface in reSIP.
Example:
SIP/2.0 200 OK
Parts:
RFC Name accessor reSIP type settable
--------------------------------------------------------------
Status-Code statusCode() int yes
SIP-Version getSipVersion() Data no
Reason-Phrase reason() Data yes
RFC Headers: <none>
RFC name:
challenge
Description:
Identifies the authentication scheme in a challenge response.
Example:
Digest-Authenticate: username="Alice", realm="atlanta.com",
nonce="84a4cc6f3082121f32b42a2187831a9e",
response="7587245234b3434cc3412213e5f113a5432"
Parts:
RFC Name accessor reSIP type settable
----------------------------------------------------------
auth-scheme scheme() Data yes
RFC Headers:
Note that the parameters to the Auth type are comma separated. reSIP correctly interprets the commas as separating parameters and not header values.
RFC name:
CSeq
Description:
Places the message in sequence in the call.
Example:
CSeq: 314159 INVITE
Parts:
RFC Name accessor reSIP type settable
--------------------------------------------------------------
sequence() int yes
Method method() MethodTypes yes
unknownMethodName() Data no
RFC Headers:
RFC name:
Call-ID
Description:
Uniquely identifies the call.
Example:
Call-ID: [email protected]
Parts:
RFC Name accessor reSIP type settable
----------------------------------------------------------
value() Data yes
RFC Headers:
Note: C++ type synonym for CallID
RFC name:
SIP-date
Description:
Human readable date string.
Example:
Date: Sat, 13 Nov 2010 23:29:00 GMT
Parts:
RFC Name accessor reSIP type settable
----------------------------------------------------------
wkday dayOfWeek() DayOfWeek yes
date1
dayOfMonth int yes
month() int yes
year() int yes
time
hour() int yes
minute() int yes
second() int yes
RFC Headers:
RFC name:
absoluteURI
Description:
Non-structured human readable URI.
Example:
Alert-Info: <http://www.example.com/sounds/moo.wav>
Parts:
RFC Name accessor reSIP type settable
----------------------------------------------------------
uri() Data yes
RFC Headers:
RFC name: expires
Description:
Seconds to expiration.
Example:
Expires: 5
Parts:
RFC Name accessor reSIP type settable
----------------------------------------------------------
value() int yes
RFC Headers:
RFC name:
media-type
Description:
Mime type and sub-type.
Example:
Content-Type: application/sdp
Parts:
RFC Name accessor reSIP type settable
----------------------------------------------------------
m-type type() Data yes
m-subtype subType() Data yes
RFC Headers:
RFC name:
name-addr
Description:
URI and display name.
Example:
To: Bob <sip:[email protected]>
Parts:
RFC Name accessor reSIP type settable
----------------------------------------------------------
display-name displayName() Data yes
addr-spec uri() Uri yes
RFC Headers:
RFC name:
TEXT-UTF8-TRIM
Description:
Unstructured human readable text.
Example:
Organization: Boxes by Bob
Parts:
RFC Name accessor reSIP type settable
----------------------------------------------------------
value() Data yes
RFC Headers:
RFC name:
token
Description:
A word.
Example:
Accept-Encoding: gzip
Parts:
RFC Name accessor reSIP type settable
----------------------------------------------------------
value() Data yes
RFC Headers:
RFC name:
1*DIGIT
Description:
An integer.
Example:
Max-Forwards: 70
Parts:
RFC Name accessor reSIP type settable
----------------------------------------------------------
value() int yes
comment comment() Data yes
RFC Headers:
RFC name:
via-parm
Description:
Via entry.
Example:
Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bK776asdhds
Parts:
RFC Name accessor reSIP type settable
------------------------------------------------------------
protocol-name protocolName() Data yes
protocol-version protocolVersion() Data yes
transport transport() Data yes
host sentHost() Data yes
port sentPort() int yes
RFC RFC Headers:
RFC name:
warning-value
Description:
Example:
Parts:
RFC Name accessor reSIP type settable
--------------------------------------------------------
warn-code code() int yes
warn-agent hostname() Data yes
warn-text text() Data yes
RFC Headers:
Parameters are accessed from headers. The syntax is similar to header access. Like headers, there is an overloaded accessor method. For parameters, this method is param
. The argument to param
is a parameter type token indicating the parameter to be accessed. The parameter type token always begins with "p_" and the rest is camelCase starting with a lowercase.
For example:
const Data& tag = msg->header(h_To).param(p_tag);
Like headers, there is also an overloaded exists
method and an overloaded remove
method. For both methods, the argument is again the parameter type token.
According to the grammar, each header has a set of acceptable parameters. Some headers accept no parameters. reSIP makes a simplifying assumption; all headers can have all parameters. While it is the goal of reSIP that every legal SIP message be parseable, reSIP does not strictly enforce production of legal SIP. In practice, correct usage will result in legal SIP, but it is not very difficult to use reSIP to produce a problematic message. Take home lesson -- the reSIP programmer must take responsibilty when adding parameters to a header.
A corollary to this simplifying assumption is that the form of a parameter is independent of the header it appears in. A ttl parameter must always be followed by an integer even when used in a header that does not specify the syntax for a ttl parameter. (!dlb! potential compatibility issue)
Parameters, like headers, corresponds to a small set of classes that manage parsing, accesing, and serializing to text.
RFC name:
Description:
May have RFC 3261 marker, may have reSIP specific data, may have client
data.
Example:
Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bK776asdhds
Parts:
accessor reSIP type settable
----------------------------------------------------
hasMagicCookie() bool no
getTransactionId() Data no
incrementTransportSequence void no
reset(const Data&) void yes
clientData() Data yes
RCF Parameters:
branch
RFC name:
token
Description:
Quoted or unquoted. Unquoted must be single word.
Example:
Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bK776asdhds
Parts:
accessor reSIP type settable
------------------------------------------------
value() Data yes
isQuoted() bool no
setQuoted(bool) void yes
RFC parameters:
RFC name:
Description:
Has no value; is not followed by "=".
Example:
Record-Route: <sip:p1.example.com;lr>
Parts:
accessor reSIP type settable
------------------------------------------------
value() bool yes
RFC parameters:
lr
RFC name:
qvalue
Description:
0-1 inclusive, up to three digits after decimal point.
Example:
Accept-Language: da, en-gb;q=0.8, en;q=0.7
Parts:
accessor reSIP type settable
------------------------------------------------
value() float yes
RFC parameters:
q
RFC name:
Description:
Integer
Example:
sip:[email protected];maddr=239.255.255.1;ttl=15
Parts:
accessor reSIP type settable
------------------------------------------------
value() int yes
RFC parameters:
RFC name:
quoted-string
Description:
Quoted text.
Example:
Authorization: Digest username="bob",
realm="biloxi.com",
nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
uri="sip:[email protected]",
qop=auth,
nc=00000001,
cnonce="0a4f113b",
response="6629fae49393a05397450978507c4ef1",
opaque="5ccc069c403ebaf9f0171e9517f40e41"
Parts:
accessor reSIP type settable
------------------------------------------------
value() Data yes
RFC Parameters:
RFC name:
rport
Description:
May have a value or not.
Example:
Via: SIP/2.0/UDP whistler.gloo.net:6064;rport=6064;received=192.168.2.220;branch=z9hG4bK-kcD23-4-1
Parts:
accessor reSIP type settable
--------------------------------------------
port() int yes
hasValue() bool no
RFC Parameters:
RFC name:
addr-spec
Description:
URI
Example:
sip:alice:[email protected];transport=tcp
Parts:
RFC Name accessor reSIP type settable
------------------------------------------------------------
header embedded() SipMessage yes
userinfo+hostport getAor() Data no
userinfo+host getAorNoPort() Data no
hasEmbedded() bool no
host host() Data yes
host opaque() Data yes
password password() Data yes
port port() int yes
userinfo user() Data yes // note: does not include user parameters
userParameters() Data yes
scheme() Data yes
A URI can have parameters. This can be confusing when a Uri is part of a NameAddr header which can also have parameters. The NameAddr parameters can be disambiguated from the Uri parameters by enclosing the Uri in angle brackets. However, in general, parameter attribution can be ambiguous. When parameters can be interpreted to belong the either a Uri or NameAddr, the Uri receives the parameters:
The NameAddr receives any remaining parameters.
RFC name:
message-body
Description:
Base class for all content types. Each derived content type defines its own
parse, accessors and stream rendering.
Example:
--boundary42
Content-Type: message/sip
INVITE sip:[email protected] SIP/2.0
Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8
To: Bob <[email protected]>
From: Alice <[email protected]>;tag=1928301774
Call-ID: a84b4c76e66710
CSeq: 314159 INVITE
Max-Forwards: 70
Date: Thu, 21 Feb 2002 13:02:03 GMT
Contact: <sip:[email protected]>
Content-Type: application/sdp
Content-Length: 147
v=0
o=UserA 2890844526 2890844526 IN IP4 here.com
s=Session SDP
c=IN IP4 pc33.atlanta.com
t=0 0
m=audio 49172 RTP/AVP 0
a=rtpmap:0 PCMU/8000
--boundary42
Content-Type: application/pkcs7-signature; name=smime.p7s
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename=smime.p7s;
handling=required
ghyHhHUujhJhjH77n8HHGTrfvbnj756tbB9HG4VQpfyF467GhIGfHfYT6
4VQpfyF467GhIGfHfYT6jH77n8HHGghyHhHUujhJh756tbB9HGTrfvbnj
n8HHGTrfvhJhjH776tbB9HG4VQbnj7567GhIGfHfYT6ghyHhHUujpfyF4
7GhIGfHfYT64VQbnj756
--boundary42-
Parts:
accessor reSIP type settable notes
----------------------------------------------------------
exists bool no
remove void no
header <various> yes
// shared header types
H_ContentType::Type& header(const H_ContentType& headerType) const;
H_ContentDisposition::Type& header(const H_ContentDisposition& headerType) const;
H_ContentTransferEncoding::Type& header(const H_ContentTransferEncoding& headerType) const;
H_ContentLanguages::Type& header(const H_ContentLanguages& headerType) const;
// MIME specific header types
H_ContentID::Type& header(const H_ContentID& headerType) const;
H_ContentDescription::Type& header(const H_ContentDescription& headerType) const;
int& verion() {return mVersion;}
int& minorVersion() {return mMinorVersion;}
class NewContents : public Contents
{
public:
static const NewContents Empty;
NewContents();
NewContents(const Data& text);
NewContents(HeaderFieldValue* hfv, const Mime& contentType);
NewContents(const Data& data, const Mime& contentType);
NewContents(const NewContents& rhs);
virtual ~NewContents();
NewContents& operator=(const NewContents& rhs);
virtual Contents* clone() const;
virtual Data getBodyData() const;
static const Mime& getStaticType() ;
virtual std::ostream& encodeParsed(std::ostream& str) const;
virtual void parse(ParseBuffer& pb);
// new content specific interfaces...
static bool init();
private:
// new content specific store...
};
SipMessage is an object-oriented representation of a message. A SipMessage can be created from the serialized form received from the wire or can be built up in an application and serialized and sent over the wire.
A detailed listing of the supported headers and their storage/parser type is here .
SipMessage has an array of built in headers and a list of unknown/extension headers. In either case, the header value or values are accessed with the overloaded header method and a header access token.
std::cerr << msg->header(h_To).uri().host() << std::endl;
The header access token is also used to check for the existence of a header:
if (msg->exists(h_Routes))
{
....
The header acesss token is also used to remove a header. It is not an error to remove a header that is not present in the message.
msg->remove(h_Warnings);
The header method will create a header value if the request header is not present in the message and the message is not const. So headers can be added to non-const messages simply by accessing the header.
msg->header(h_To) = NameAddr("Joe <[email protected]>");
If the message is const, accessing a header that is not present in the message will raise a SipMessage::Exception. Often, a SIP element will check if the header exists before accessing it rather than handle the exception.
Some headers have multiple values. The values of a multi-valued header are maintained in a container class named after the type contained. For example, msg->header(h_Contacts) returns a reference to NameAddrs. The containers have STL container syntax.
for (NameAddrs::const_iterator i = msg->header(h_Contacts).begin(); i != msg->header(h_Contacts.end(); ++i)
{
const NameAddr& na = *i;
...;
Multi-valued header values correspond to comma separated values with one header or multiple header lines each with their own header. When serialized to the wire, multiple values are emitted comma separated.
reSIP represents a message body as a derived Content instance. The contents instance associated with the SipMessage is accessible:
SdpContents* sdp = dynamic_cast<SdpContents*>(msg->getContents());
SipMessage also acts somewhat like an auto_ptr<Contents>. SipMessage supports releaseContents() and
std::auto_ptr<Contents> contents(new SdpContents));
// populate the sdp... then set into message
msg->setContents(contents);
The setContents that takes a Contents* copies the contents through the pointer before assigning it to the SipMessage.
SipMessage can deterine if it is a request:
if (msg->isRequest())
{
...
SipMessage can determine if it is a response:
if (msg->isResponse())
{
...
A message can be neither a request or a response if it is locally created (as opposed to being deserialzed from the wire) and has neither a request nor status line.
SIP is an open specification. Elements may add headers that are not generally known. Extension headers are always of type StringCategory and are multiple valued but not comma separated. Extension headers may have parameters, however, not all stacks will consider semi-colons in extension headers significant, so extension header parameters may cause interop problems.
Extension headers are declared, for example:
static const resip::ExtensionHeader h_XFoos("X-Foo");
They are used similarly to built-in headers; the h_XFoos declared above is a header access token.
msg->header(h_XFoos).push_back(StringCategory("value"));
SIP is an open specification. Elements may add parameters that are not generally known. Extension parameters are always of type Data. An extension parameter may not have a value; that is, it can act as an exists parameter. An extension parameter may occur on any header including an extension header.
Extension parameters are declared, for example:
static const resip::ExtensionParameter p_xstid("x-stid");
Extension parameters are be used like other parameters:
msg->header(h_To).param(p_xstid) = "2134";
RFC name | reSIP access token | reSIP type |
---|---|---|
access-type | p_accessType | DataParameter |
algorithm | p_algorithm | DataParameter |
boundary | p_boundary | DataParameter |
branch | p_branch | BranchParameter |
charset | p_charset | DataParameter |
cnonce | p_cnonce | QuotedDataParameter |
comp | p_comp | DataParameter |
d-alg | p_dAlg | DataParameter |
d-qop | p_dQop | DataParameter |
d-ver | p_dVer | QuotedDataParameter |
directory | p_directory | DataParameter |
domain | p_domain | QuotedDataParameter |
duration | p_duration | IntegerParameter |
expiration | p_expiration | IntegerParameter |
expires | p_expires | IntegerParameter |
filename | p_filename | DataParameter |
from-tag | p_fromTag | DataParameter |
handling | p_handling | DataParameter |
id | p_id | DataParameter |
lr | p_lr | ExistsParameter |
maddr | p_maddr | DataParameter |
method | p_method | DataParameter |
micalg | p_micalg | DataParameter |
mobility | p_mobility | DataParameter |
mode | p_mode | DataParameter |
name | p_name | DataParameter |
nc | p_nc | DataParameter |
nonce | p_nonce | QuotedDataParameter |
opaque | p_opaque | QuotedDataParameter |
permission | p_permission | DataParameter |
protocol | p_protocol | DataParameter // should be QuotedDataParameter ? |
purpose | p_purpose | DataParameter |
q | p_q | FloatParameter |
realm | p_realm | QuotedDataParameter |
reason | p_reason | DataParameter |
received | p_received | DataParameter |
response | p_response | QuotedDataParameter |
retry-after | p_retryAfter | IntegerParameter |
rport | p_rport | RportParameter |
server | p_server | DataParameter |
site | p_site | DataParameter |
size | p_size | DataParameter |
smime-type | p_smimeType | DataParameter |
stale | p_stale | DataParameter |
tag | p_tag | DataParameter |
to-tag | p_toTag | DataParameter |
transport | p_transport | DataParameter |
ttl | p_ttl | IntegerParameter |
uri | p_uri | QuotedDataParameter |
user | p_user | DataParameter |
username | p_username | DataParameter // should be QuotedDataParameter ? |