Both the UAS and UAC in this section use the following connection object definition and associated functions:
Example A-1 Connection Object Definition for UAS and UAC typedef struct my_conn_obj {
void *my_conn_resv; /* for lib use */
int my_conn_fd;
struct sockaddr *my_conn_local;
struct sockaddr *my_conn_remote;
int my_conn_transport;
int my_conn_refcnt;
int my_conn_af;
pthread_mutex_t my_conn_lock;
uint32_t my_conn_flags;
int my_conn_thr;
int my_conn_timer1;
int my_conn_timer2;
int my_conn_timer4;
int my_conn_timerD;
} my_conn_obj_t;
int
my_conn_transport(sip_conn_object_t obj){
my_conn_obj_t *conn_obj;
int transport;
conn_obj = (my_conn_obj_t *)obj;
(void) pthread_mutex_lock(&conn_obj->my_conn_lock);
transport = conn_obj->my_conn_transport;
(void) pthread_mutex_unlock(&conn_obj->my_conn_lock);
return (transport);
}
boolean_t
my_conn_isreliable(sip_conn_object_t obj) {
my_conn_obj_t *conn_obj;
int transport;
conn_obj = (my_conn_obj_t *)obj;
(void) pthread_mutex_lock(&conn_obj->my_conn_lock);
transport = conn_obj->my_conn_transport;
(void) pthread_mutex_unlock(&conn_obj->my_conn_lock);
return (!(transport == IPPROTO_UDP));
}
boolean_t
my_conn_isstream(sip_conn_object_t obj)
{
my_conn_obj_t *conn_obj;
int transport;
conn_obj = (my_conn_obj_t *)obj;
(void) pthread_mutex_lock(&conn_obj->my_conn_lock);
transport = conn_obj->my_conn_transport;
(void) pthread_mutex_unlock(&conn_obj->my_conn_lock);
return (transport == IPPROTO_TCP);
}
void
my_conn_refhold(sip_conn_object_t obj)
{
my_conn_obj_t *conn_obj;
conn_obj = (my_conn_obj_t *)obj;
(void) pthread_mutex_lock(&conn_obj->my_conn_lock);
conn_obj->my_conn_refcnt++;
(void) pthread_mutex_unlock(&conn_obj->my_conn_lock);
}
void
my_conn_refrele(sip_conn_object_t obj)
{
my_conn_obj_t *conn_obj;
conn_obj = (my_conn_obj_t *)obj;
(void) pthread_mutex_lock(&conn_obj->my_conn_lock);
if (conn_obj->my_conn_refcnt <= 0) {
printf("my_conn_refrele: going to break!!\n");
}
assert(conn_obj->my_conn_refcnt > 0);
conn_obj->my_conn_refcnt--;
if (conn_obj->my_conn_refcnt > 0) {
(void) pthread_mutex_unlock(&conn_obj->my_conn_lock);
return;
}
assert(conn_obj->my_conn_flags & MY_CONN_DESTROYED);
(void) pthread_mutex_destroy(&conn_obj->my_conn_lock);
if (conn_obj->my_conn_local != NULL)
free(conn_obj->my_conn_local);
if (conn_obj->my_conn_remote != NULL)
free(conn_obj->my_conn_remote);
free(conn_obj);
}
int
my_conn_local(sip_conn_object_t obj, struct sockaddr *sa,
socklen_t *len)
{
my_conn_obj_t *conn_obj;
int alen;
conn_obj = (my_conn_obj_t *)obj;
(void) pthread_mutex_lock(&conn_obj->my_conn_lock);
if (conn_obj->my_conn_local == NULL) {
(void) pthread_mutex_unlock(&conn_obj->my_conn_lock);
return (-1);
}
if (conn_obj->my_conn_local->sa_family == AF_INET)
alen = sizeof (struct sockaddr_in);
else
alen = sizeof (struct sockaddr_in6);
if (*len < alen) {
(void) pthread_mutex_unlock(&conn_obj->my_conn_lock);
return (EINVAL);
}
bcopy(conn_obj->my_conn_local, sa, alen);
(void) pthread_mutex_unlock(&conn_obj->my_conn_lock);
*len = alen;
return (0);
}
int
my_conn_remote(sip_conn_object_t obj, struct sockaddr *sa,
socklen_t *len)
{
my_conn_obj_t *conn_obj;
int alen;
conn_obj = (my_conn_obj_t *)obj;
(void) pthread_mutex_lock(&conn_obj->my_conn_lock);
if (conn_obj->my_conn_remote == NULL) {
(void) pthread_mutex_unlock(&conn_obj->my_conn_lock);
return (-1);
}
if (conn_obj->my_conn_remote->sa_family == AF_INET)
alen = sizeof (struct sockaddr_in);
else
alen = sizeof (struct sockaddr_in6);
if (*len < alen) {
(void) pthread_mutex_unlock(&conn_obj->my_conn_lock);
return (EINVAL);
}
bcopy(conn_obj->my_conn_remote, sa, alen);
(void) pthread_mutex_unlock(&conn_obj->my_conn_lock);
*len = alen;
return (0);
}
int
my_conn_send(const sip_conn_object_t obj, char *msg, int msglen)
{
my_conn_obj_t *conn_obj;
size_t nleft;
int nwritten;
const char *ptr;
socklen_t tolen;
conn_obj = (my_conn_obj_t *)obj;
if (conn_obj->my_conn_fd == NULL)
return (EINVAL);
ptr = msg;
nleft = msglen;
if (conn_obj->my_conn_remote->sa_family == AF_INET)
tolen = sizeof (struct sockaddr_in);
else
tolen = sizeof (struct sockaddr_in6);
while (nleft > 0) {
if (conn_obj->my_conn_transport == IPPROTO_UDP) {
if ((nwritten = sendto(conn_obj->my_conn_fd,
ptr, nleft, 0, conn_obj->my_conn_remote,
tolen)) <= 0) {
return (-1);
}
} else {
if ((nwritten = write(conn_obj->my_conn_fd, ptr,
nleft)) <= 0) {
return (-1);
}
}
nleft -= nwritten;
ptr += nwritten;
}
return (0);
}
Additionally, both UAS and UAC register the following transaction and dialog state transition callback functions:
Example A-2 Transaction and Dialog State Transition Callback Functions for UAS and UACvoid
ulp_dialog_state_cb(sip_dialog_t dialog, sip_msg_t sip_msg,
int pstate, int nstate)
{
printf("\t\t\t%p %d ==> %d\n", dialog, pstate, nstate);
}
void
ulp_trans_state_cb(sip_transaction_t sip_trans, sip_msg_t sip_msg,
int pstate, int ostate)
{
char *bid;
sip_method_t method;
int err;
/* Not checking for err in the following functions */
if (sip_msg != NULL) {
if (sip_msg_is_request(sip_msg, &err)) {
method = sip_get_request_method(sip_msg, &err);
} else {
method = sip_get_callseq_method(sip_msg, NULL,
&err);
}
}
bid = sip_get_trans_branchid(sip_trans);
printf("\tTransaction (%s) %s\n\t\t\t%d ==> %d\n",
sip_msg == NULL ? "Null": sip_method_to_str(method),
bid, pstate, ostate);
free(bid);
}
Example A-3 Sample UAS Callback Reception Code
The UAS is a simple server that waits for an INVITE message and responds with a 2xx response and gets an ACK request back.
void
my_ulp_recv(sip_conn_object_t obj, sip_msg_t msg,
sip_dialog_t sip_dialog) {
sip_msg_t sip_msg_resp;
int resp_code;
int error;
sip_method_t method;
char *totag;
/* Drop if not a request */
if (!sip_msg_is_request(msg, &error))
return;
method = sip_get_request_method(msg, error);
if (error != 0)
return; /* error getting request method);
if (method == ACK) {
printf("ACK received\n");
return;
}
if (method != INVITE) {
printf("not processing %d request\n", method);
return;
}
/* Create an OK response */
resp_code = SIP_OK;
/* This will probably not be done for each incoming request */
totag = sip_guid();
if (totag == NULL) {
printf("error generating TO tag\n");
return;
}
sip_msg_resp = sip_create_response(msg, resp_code,
sip_get_resp_desc(resp_code), totag,
"sip:mycontactinfo@192.168.1.4");
if (sip_msg_resp == NULL) {
printf("error creating response\n");
return;
}
/* send message statefully */
sip_sendmsg(obj, sip_msg_resp, sip_dialog, SIP_SEND_STATEFUL);
/* free message */
sip_free_msg(sip_msg_resp);
/* free totag */
free(totag);
}
/*
* Main program:
* Stack initialization:
* Stack maintains dialogs.
* Dialog and transaction state transition callbacks
* registerd.
*/
sip_stack_init_t sip_init[1];
sip_io_pointers_t sip_io[1];
sip_ulp_pointers_t sip_ulp;
bzero(sip_init, sizeof (sip_stack_init_t));
bzero(sip_io, sizeof (sip_io_pointers_t));
sip_io->sip_conn_send = my_conn_send;
sip_io->sip_hold_conn_object = my_conn_refhold;
sip_io->sip_rel_conn_object = my_conn_refrele;
sip_io->sip_conn_is_stream = my_conn_isstream;
sip_io->sip_conn_is_reliable = my_conn_isreliable;
sip_io->sip_conn_remote_address = my_conn_remote;
sip_io->sip_conn_local_address = my_conn_local;
sip_io->sip_conn_transport = my_conn_transport;
sip_init->sip_version = SIP_STACK_VERSION;
sip_init->sip_io_pointers = sip_io;
bzero(&sip_ulp, sizeof (sip_ulp_pointers_t));
sip_ulp.sip_ulp_recv = my_ulp_recv;
sip_init->sip_stack_flags |= SIP_STACK_DIALOGS;
sip_ulp.sip_ulp_dlg_state_cb = my_dialog_cb;
sip_ulp.sip_ulp_trans_state_cb = ulp_trans_state_cb;
sip_init->sip_ulp_pointers = &sip_ulp;
/* Open a socket and accept a connection */
sock = socket(af, SOCK_STREAM, IPPROTO_TCP);
/* Check for socket creation error */
/* onoff is set to 1 */
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &onoff, sizeof (onoff));
/* check for setsockopt() error */
/* fill in bind information in sockaddr_in struct sa */
bind(sock, sa, slen);
/* check for bind error */
listen (sock, 5);
accept_fd = accept(sock, NULL, NULL);
/* check for accept error */
/*
* create a connection object, nobj is of type my_conn_obj_t
*/
nobj = malloc(sizeof (my_conn_obj_t));
/* check for memory failure */
nobj->my_conn_fd = accept_fd;
nobj->my_conn_transport = IPPROTO_TCP;
nobj->my_conn_refcnt = 1;
/* set address family in nobj->my_conn_af */
/* Initialize lock */
(void) pthread_mutex_init(&nobj->my_conn_lock, NULL);
/* Set local and remote addresses in nobj */
/* INITIALIZE connection object */
sip_init_conn_object((sip_conn_object_t)nobj);
/* Termination not shown */
for (;;) {
/*
* Read incoming message on the connection object
* my_conn_receive(), not shown, is an application function that
* reads on nobj->my_conn_fd into buf, nread is the length of
* the message read.
*/
nread = my_conn_receive(cobj, buf, MY_BUFLEN);
/* check for any error */
/* Call into the SIP stack for processing this message */
sip_process_new_packet((sip_conn_object_t)cobj, buf, nread);
}
Example A-4 Sample UAC Reception Code
The UAC is a simple client that sends an INVITE and responds with an ACK when it gets a 2xx final response.
/* dialog not used */
void
my_ulp_recv(sip_conn_object_t obj, sip_msg_t msg,
sip_dialog_t sip_dialog)
{
sip_msg_t ack_msg;
uint32_t resp;
int resp_code;
int err;
int transport;
/* Not checking for err */
if (sip_msg_is_request(msg, &err)) {
printf("received request\n");
return;
}
resp_code = sip_get_response_code(msg);
if (sip_get_callseq_method(msg, NULL, &err) != INVITE) {
printf("received response non-invite\n");
return;
}
if (!SIP_OK_RESP(resp_code)) {
printf("received non-2xx response %d\n", resp_code);
return;
}
ack_msg = sip_new_msg();
/* check for null ack_msg */
transport = my_conn_transport(obj);
sip_create_OKack(msg, ack_msg,
sip_proto_to_transport(transport));
/* check for failure */
/* Send the ACK message */
sip_sendmsg(obj, ack_msg, sip_dialog, SIP_SEND_STATEFUL);
/* free message */
sip_free_msg(ack_msg);
}
/* Function to create an request */
sip_msg_t
my_get_sip_req_msg(sip_method_t method, char *from_name,
char *from_uri, char *to_name, char *to_uri, char *from_tag,
char *to_tag, char *contact, char *transport, char *via_param,
char *sent_by, uint_t max_forward, uint_t cseq, char *callid)
{
sip_msg_t sip_msg;
sip_msg = sip_new_msg();
/* check for memory failure */
/* Check for failure in each case below */
/* Add request line */
sip_add_request_line(sip_msg, method, to_uri);
/* Add FROM */
sip_add_from(sip_msg, from_name, from_uri, from_tag, B_TRUE,
NULL);
/* Add TO */
sip_add_to(sip_msg, to_name, to_uri, to_tag, B_TRUE, NULL);
/* Add CONTACT */
sip_add_contact(sip_msg, NULL, contact, B_TRUE, NULL);
/* Add VIA, no port */
sip_add_via(sip_msg, transport, sent_by, 0, via_param);
/* Add Max-Forwards */
sip_add_maxforward(sip_msg, max_forward);
/* Add Call-Id */
sip_add_callid(sip_msg, callid);
/* Add Cseq */
sip_add_cseq(sip_msg, method, cseq);
}
/*
* Main program:
* Stack initialization:
* Stack maintains dialogs.
* Dialog and transaction state transition callbacks
* registerd.
*/
sip_stack_init_t sip_init[1];
sip_io_pointers_t sip_io[1];
sip_ulp_pointers_t sip_ulp;
bzero(sip_init, sizeof (sip_stack_init_t));
bzero(sip_io, sizeof (sip_io_pointers_t));
sip_io->sip_conn_send = my_conn_send;
sip_io->sip_hold_conn_object = my_conn_refhold;
sip_io->sip_rel_conn_object = my_conn_refrele;
sip_io->sip_conn_is_stream = my_conn_isstream;
sip_io->sip_conn_is_reliable = my_conn_isreliable;
sip_io->sip_conn_remote_address = my_conn_remote;
sip_io->sip_conn_local_address = my_conn_local;
sip_io->sip_conn_transport = my_conn_transport;
sip_init->sip_version = SIP_STACK_VERSION;
sip_init->sip_io_pointers = sip_io;
bzero(&sip_ulp, sizeof (sip_ulp_pointers_t));
sip_ulp.sip_ulp_recv = my_ulp_recv;
sip_init->sip_stack_flags |= SIP_STACK_DIALOGS;
sip_ulp.sip_ulp_dlg_state_cb = my_dialog_cb;
sip_ulp.sip_ulp_trans_state_cb = ulp_trans_state_cb;
sip_init->sip_ulp_pointers = &sip_ulp;
/* Open a socket and accept a connection */
sock = socket(af, SOCK_STREAM, IPPROTO_TCP);
/* Check for socket creation error */
/* connect to peer - sa is filled appropriately with peer info. */
connect(sock, sa, slen);
/* check for connect failure */
/*
* create a connection object, nobj is of type my_conn_obj_t
*/
obj = malloc(sizeof (my_conn_obj_t));
/* check for memory failure */
obj->my_conn_fd = accept_fd;
obj->my_conn_transport = IPPROTO_TCP;
obj->my_conn_refcnt = 1;
/* set address family in obj->my_conn_af */
/* Initialize lock */
(void) pthread_mutex_init(&obj->my_conn_lock, NULL);
/* Set local and remote addresses in obj */
/* INITIALIZE connection object */
sip_init_conn_object((sip_conn_object_t)obj);
/* Create an INVITE request */
req_msg = my_get_sip_req_msg(INVITE, "me", "sip:me@mydomain.com",
"you", "sip:user@example.com", "tag-from-01", NULL,
"sip:myhome.host.com",
sip_proto_to_transport(obj->my_conn_transport),
"branch=z9hG4bK-via-01", "192.168.2.3", 70, 111, NULL);
/* Send request statefully */
sip_sendmsg((sip_conn_object_t)obj, req_msg, NULL, SIP_SEND_STATEFUL);
/* free msg */
sip_free_msg(req_msg);
/* Termination condition not shown */
for (;;) {
/*
* Read incoming message on the connection object
* my_conn_receive(), not shown, is an application function that
* reads on nobj->my_conn_fd into buf, nread is the length of
* the message read.
*/
nread = my_conn_receive(obj, buf, MY_BUFLEN);
/* check for any error */
/* Call into the SIP stack for processing this message */
sip_process_new_packet((sip_conn_object_t)obj, buf, nread);
}