Archive for January, 2008

Hmmm [UPDATE]

Well, this updated version actually works with optimization turned on, doesn’t use custom structs for passing data.

It’s now using the sofia-sip way of passing type and value in multiple arguments, this one is still a simplified version though…

There’s some additional code for passing uri parameters each in a single tag now too.

Download: Tag based SIP URI generator example code #2

#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <stdlib.h>

typedef int switch_core_session_t;

char * sofia_printf_uri( switch_core_session_t *session, ... );

typedef enum {
        URI_TAG_TYPE_USERNAME = 1,
        URI_TAG_TYPE_DESTINATION,
        URI_TAG_TYPE_HOST,
        URI_TAG_TYPE_PORT,
        URI_TAG_TYPE_PARAMETER,
        URI_TAG_TYPE_PARAMETER_LIST,
        URI_TAG_TYPE_URIONLY
} uri_tag_type;

#define URI_TAG_USERNAME(x)                URI_TAG_TYPE_USERNAME, (x)
#define URI_TAG_DESTINATION(x)                URI_TAG_TYPE_DESTINATION, (x)
#define URI_TAG_HOST(x)                        URI_TAG_TYPE_HOST, (x)
#define URI_TAG_PARAMETER(n, v)                URI_TAG_TYPE_PARAMETER, (n), (v)
#define URI_TAG_PARAMETER_LIST(x)        URI_TAG_TYPE_PARAMETER_LIST, (x)

#define URI_TAG_PORT(x)                URI_TAG_TYPE_PORT, (x)
#define URI_TAG_URI_ONLY(x)        URI_TAG_TYPE_URIONLY, (x)

#define TAG_END                 NULL

#define URI_MAX_PARAMS                10

char * sofia_printf_uri( switch_core_session_t *session, ... )
{
        va_list ap;
        char *username = NULL,
                *destination = NULL,
                *host = NULL,
                *param_str = NULL,
                *uri_str = NULL;
        struct { char *name, *val; } param[URI_MAX_PARAMS];
        int tag, port = 0, uri_only = 0, num_params = 0, len_params = 0;

        va_start( ap, session );
        while( 1 ) {
                tag = va_arg( ap, int );
                if( !tag )
                        break;

                switch( tag ) {
                case URI_TAG_TYPE_USERNAME:
                        username = va_arg( ap, char * );
                        break;

                case URI_TAG_TYPE_DESTINATION:
                        destination = va_arg( ap, char * );
                        break;

                case URI_TAG_TYPE_HOST:
                        host = va_arg( ap, char * );
                        break;

                case URI_TAG_TYPE_PARAMETER:
                        param[num_params].name = va_arg( ap, char * );
                        param[num_params].val  = va_arg( ap, char * );
                        len_params += strlen(param[num_params].name) + strlen(param[num_params].val);
                        num_params++;
                        break;

                case URI_TAG_TYPE_PARAMETER_LIST:
                        param_str = va_arg( ap, char * );
                        num_params = -1;
                        break;

                case URI_TAG_TYPE_PORT:
                        port = va_arg( ap, int );
                        break;

                case URI_TAG_TYPE_URIONLY:
                        uri_only = va_arg( ap, int );
                        break;

                default:
                        printf( "Unknown tag type\n" );
                }
        }
        va_end( ap );

        /*
         * We need at least host and destination here
         */
        if( !host ) {
                printf( "no host tag specified\n" );
                return NULL;
        }

        if( !destination ) {
                printf( "no destination tag specified\n" );
                return NULL;
        }

        /*
         * Default value
         */
        if( !username )
                username = "user";

        /*
         * Ugly code ahead...
         * Now if we'd manually allocate memory here we could count string lengths
         * and reserve enough room for the uri incl. parameters in one go and
         * put the params in after snprint'ing the first part...
         * (saving us the trouble of using alloca here)
         */
        if( num_params > 0 ) {
                int n, len;
                char *p;

                param_str = alloca( len_params + num_params );
                if( !param_str )
                        return NULL;

                p = param_str;

                for( n=0; n < num_params; n++ ) {
                        len = strlen( param[n].name );

                        memcpy( p, param[n].name, len );
                        p += len;

                        *p++ = '=';

                        len = strlen( param[n].val );

                        memcpy( p, param[n].val, len );
                        p += len;

                        if( n < num_params - 1 )
                                *p++ = '&';
                }
                *p = '\0';

        } else if( num_params == 0 ) {
                param_str = "";
        }

        /*
         * The (fugly) output part
         */
        if( !uri_only ) {
                if( port ) {
                        printf( "\"%s\" <sip:%s@%s:%d>%s%s\n",
                                username,
                                destination,
                                host,
                                port,
                                num_params ? ";" : "",
                                param_str );
                } else {
                        printf( "\"%s\" <sip:%s@%s>%s%s\n",
                                username,
                                destination,
                                host,
                                num_params ? ";" : "",
                                param_str );
                }
        } else {
                if( port ) {
                        printf( "sip:%s@%s:%d%s%s\n",
                                destination,
                                host,
                                port,
                                num_params ? ";" : "",
                                param_str );
                } else {
                        printf( "sip:%s@%s%s%s\n",
                                destination,
                                host,
                                num_params ? ";" : "",
                                param_str );
                }
        }
        return uri_str;
}

int main( void )
{
        switch_core_session_t session;

        printf( "\nCreating full uri...\n" );
        sofia_printf_uri( &session,
                                URI_TAG_USERNAME( "foobar" ),
                                URI_TAG_HOST( "konata" ),
                                URI_TAG_PORT( 1234 ),
                                URI_TAG_DESTINATION( "myext" ),
                                URI_TAG_PARAMETER( "transport", "tls" ),
                                URI_TAG_PARAMETER( "rport", "12345" ),
                                TAG_END );

        printf( "\nCreating uri only (output w/o username)...\n" );
        sofia_printf_uri( &session,
                                URI_TAG_USERNAME( "foobar" ),
                                URI_TAG_HOST( "konata" ),
                                URI_TAG_PORT( 1234 ),
                                URI_TAG_DESTINATION( "myext" ),
                                URI_TAG_URI_ONLY( 1 ),
                                URI_TAG_PARAMETER( "transport", "tls" ),
                                TAG_END );

        printf( "\nCreating full uri w/o port...\n" );
        sofia_printf_uri( &session,
                                URI_TAG_USERNAME( "foobar" ),
                                URI_TAG_HOST( "konata" ),
                                URI_TAG_DESTINATION( "myext" ),
                                URI_TAG_PARAMETER_LIST( "transport=tls;bla=foo" ),
                                TAG_END );

        return 0;
}

Hmmm

Sofia-SIP’s parameter tags are pretty nice and since i’ve got this bad habit of reinventing the wheel for learning purposes,here’s what i came up with in ~30 minutes.

(Note: I didn’t look at the Sofia-SIP tag handling code, this little example is based on the stdarg(3) manpage content…)

Download: Tag based SIP URI generator example code



#include <stdio.h>
#include <stdarg.h>

typedef int switch_core_session_t;

char * sofia_printf_uri( switch_core_session_t *session, ... );

typedef enum {
        URI_TAG_USERNAME,
        URI_TAG_DESTINATION,
        URI_TAG_HOST,
        URI_TAG_PORT,
        URI_TAG_PARAMETERS,
        URI_TAG_URIONLY
} uri_tag_type;

/*
 * union?
 */
struct uri_generic_tag {
        uri_tag_type type;
};

struct uri_string_tag {
        uri_tag_type type;
        char *val;
};

struct uri_int_tag {
        uri_tag_type type;
        int val;
};

/*
 * Tag helper macros
 */
#define TAG_GENERIC_FLAG(typeval) \
       ({ struct uri_generic_tag __uri_gen_##__LINE__ = { .type = typeval }; &__uri_gen_##__LINE__; })

#define TAG_GENERIC_STRING(typeval, x) \
       ({ struct uri_string_tag __uri_str_##__LINE__ = { .type = typeval, .val = x }; &__uri_str_##__LINE__; })

#define TAG_GENERIC_INT(typeval, x) \
       ({ struct uri_int_tag __uri_int_##__LINE__ = { .type = typeval, .val = x }; &__uri_int_##__LINE__; })

/*
 * Actual tags
 */
#define TAG_USERNAME(x)          TAG_GENERIC_STRING(URI_TAG_USERNAME, x)
#define TAG_DESTINATION(x)       TAG_GENERIC_STRING(URI_TAG_DESTINATION, x)
#define TAG_HOST(x)              TAG_GENERIC_STRING(URI_TAG_HOST, x)
#define TAG_PARAMETERS(x)        TAG_GENERIC_STRING(URI_TAG_PARAMETERS, x)

#define TAG_PORT(x)              TAG_GENERIC_INT(URI_TAG_PORT, x)
#define TAG_URI_ONLY(x)          TAG_GENERIC_INT(URI_TAG_URIONLY, x)

#define TAG_END                  NULL

char * sofia_printf_uri( switch_core_session_t *session, ... )
{
        struct uri_generic_tag *tag;
        va_list ap;
        char *username = NULL,
                *destination = NULL,
                *host = NULL,
                *params = NULL,
                *uri_str = NULL;
        int port = 0, uri_only = 0;

        va_start( ap, session );
        while( 1 ) {
                tag = va_arg( ap, void * );
                if( !tag )
                        break;

                switch( tag->type ) {
                case URI_TAG_USERNAME:
                        username = ((struct uri_string_tag *)tag)->val;
                        break;

                case URI_TAG_DESTINATION:
                        destination = ((struct uri_string_tag *)tag)->val;
                        break;

                case URI_TAG_HOST:
                        host = ((struct uri_string_tag *)tag)->val;
                        break;

                case URI_TAG_PARAMETERS:
                        params = ((struct uri_string_tag *)tag)->val;
                        break;

                case URI_TAG_PORT:
                        port = ((struct uri_int_tag *)tag)->val;
                        break;

                case URI_TAG_URIONLY:
                        uri_only = ((struct uri_int_tag *)tag)->val;
                        break;

                default:
                        printf( "Unknown tag type\n" );
                }
        }
        va_end( ap );

        /*
         * We need at least host and destination here
         */
        if( !host )
                return NULL;

        if( !destination )
                return NULL;

        /*
         * Default value
         */
        if( !username )
                username = "user";

        /*
         * The (fugly) output part
         */
        if( !uri_only ) {
                if( port ) {
                        printf( "\"%s\" <sip:%s@%s:%d>%s%s\n",
                                username,
                                destination,
                                host,
                                port,
                                params ? ";" : "",
                                params ? params : "" );
                } else {
                        printf( "\"%s\" <sip:%s@%s>%s%s\n",
                                username,
                                destination,
                                host,
                                params ? ";" : "",
                                params ? params : "" );
                }
        } else {
                if( port ) {
                        printf( "sip:%s@%s:%d%s%s\n",
                                destination,
                                host,
                                port,
                                params ? ";" : "",
                                params ? params : "" );
                } else {
                        printf( "sip:%s@%s%s%s\n",
                                destination,
                                host,
                                params ? ";" : "",
                                params ? params : "" );
                }
        }
        return uri_str;
}

int main( void )
{
        switch_core_session_t session;

        printf( "\nCreating full uri...\n" );
        sofia_printf_uri( &session,
                                TAG_USERNAME( "foobar" ),
                                TAG_HOST( "konata" ),
                                TAG_PORT( 1234 ),
                                TAG_DESTINATION( "myext" ),
                                TAG_END );

        printf( "\nCreating uri only (output w/o username)...\n" );
        sofia_printf_uri( &session,
                                TAG_USERNAME( "foobar" ),
                                TAG_HOST( "konata" ),
                                TAG_PORT( 1234 ),
                                TAG_DESTINATION( "myext" ),
                                TAG_URI_ONLY( 1 ),
                                TAG_END );

        printf( "\nCreating full uri w/o port...\n" );
        sofia_printf_uri( &session,
                                TAG_USERNAME( "foobar" ),
                                TAG_HOST( "konata" ),
                                TAG_DESTINATION( "myext" ),
                                TAG_END );

        return 0;
}

mod_sofia TLS status

A short overview of the current status of TLS support in mod_sofia.

Things that work:

originating calls via sip profile name, like:

sofia/default/user@remote;transport=tls or {sip_transport=tls}sofia/default/user@remote

will use TLS (or any other supported transport type)

Receiving calls works too, but i’m currently extracting the transport from the contact header and i’m not 100% sure that’s working when there’s a proxy in the middle.

What i’m working on right now:

Originating calls using a gateway. Specifying the transport using:

sofia/gateway/testgw/1234;transport=tls

already works, overriding the register transport this way is working too (e.g. registering via TLS and dialing via SCTP, not sure we really want to support that though (?)).

Using {sip_transport=…} to change the transport for dialing doesn’t work for gateways at the moment. The sip_transport variable is handled in sofia_glue_do_invite(), while the gateway params and the invite_contact are created/handled in sofia_outgoing_channel(). Fixing this means either moving code from sofia_glue…() to sofia_outgoing channel() or creating a new invite_contact for gateways in sofia_glue…(). We’ll see…

Changes so far:

– keep track of transport type in tech_pvt->transport and gateway->register_transport.

– fix handling of gateway dialing params in sofia_outgoing channel

– remove some code duplication

– transport string comparisons using strncasecmp now

– create a new invite_contact if the gateway’s register_transport and the “invite” transport differ

What needs to be done:

Check (and change) transport handling of the presence stuff.

What i’m thinking about:

Something that could be handy for (fully working) IPv6 support…

Extend / change the sofia_transport_t type and enum to a set of flags, the lower bits handling the transport protocol (TCP, UDP, SCTP, TLS) and some of the upper bits the network protocol (IP / IPv6).

E.g.:

enum {

SOFIA_TRANSPORT_UNKNOWN = 0,

SOFIA_TRANSPORT_UDP = (1 << 0),

SOFIA_TRANSPORT_TCP = (1<<1),

SOFIA_TRANSPORT_TCP_TLS = (1<<2),

SOFIA_TRANSPORT_SCTP = (1<<3),

SOFIA_TRANSPORT_SCTP_TLS = (1<<4),

SOFIA_TRANSPORT_IP6 = (1<<24)

};

#define sofia_transport_is_ip6(x) (x & SOFIA_TRANSPORT_IP6)

#define sofia_transport_type(x) (x & 0x00FFFFFF)

#define sofia_transport_proto(x) (x & 0xFF000000)