SIP API Developer's Guide

Appendix A Examples of Use

Example of UAS and UAC Use

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 UAC

void
	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@123.1.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", "123.1.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);
	}