/* The oSIP library implements the Session Initiation Protocol (SIP -rfc3261-) Copyright (C) 2001,2002,2003 Aymeric MOIZARD jack@atosc.org This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include "fsm.h" osip_statemachine_t *nist_fsm; osip_statemachine_t * __nist_get_fsm () { return nist_fsm; } void __nist_unload_fsm () { transition_t *transition; osip_statemachine_t *statemachine = __nist_get_fsm (); for (transition = statemachine->transitions; transition != NULL; transition = statemachine->transitions) { REMOVE_ELEMENT(statemachine->transitions, transition); osip_free (transition); } osip_free (statemachine->transitions); osip_free (statemachine); } void __nist_load_fsm () { transition_t *transition; nist_fsm = (osip_statemachine_t *) osip_malloc (sizeof (osip_statemachine_t)); nist_fsm->transitions = NULL; transition = (transition_t *) osip_malloc (sizeof (transition_t)); transition->state = NIST_PRE_TRYING; transition->type = RCV_REQUEST; transition->method = (void (*)(void *, void *)) &nist_rcv_request; ADD_ELEMENT (nist_fsm->transitions, transition); /* This can be used to announce request but is useless, as the transaction cannot send any response yet! transition = (transition_t *) osip_malloc(sizeof(transition_t)); transition->state = NIST_TRYING; transition->type = RCV_REQUEST; transition->method = (void(*)(void *,void *))&nist_rcv_request; osip_list_add(nist_fsm->transitions,transition,-1); */ transition = (transition_t *) osip_malloc (sizeof (transition_t)); transition->state = NIST_TRYING; transition->type = SND_STATUS_1XX; transition->method = (void (*)(void *, void *)) &nist_snd_1xx; ADD_ELEMENT (nist_fsm->transitions, transition); transition = (transition_t *) osip_malloc (sizeof (transition_t)); transition->state = NIST_TRYING; transition->type = SND_STATUS_2XX; transition->method = (void (*)(void *, void *)) &nist_snd_23456xx; ADD_ELEMENT (nist_fsm->transitions, transition); transition = (transition_t *) osip_malloc (sizeof (transition_t)); transition->state = NIST_TRYING; transition->type = SND_STATUS_3456XX; transition->method = (void (*)(void *, void *)) &nist_snd_23456xx; ADD_ELEMENT (nist_fsm->transitions, transition); transition = (transition_t *) osip_malloc (sizeof (transition_t)); transition->state = NIST_PROCEEDING; transition->type = SND_STATUS_1XX; transition->method = (void (*)(void *, void *)) &nist_snd_1xx; ADD_ELEMENT (nist_fsm->transitions, transition); transition = (transition_t *) osip_malloc (sizeof (transition_t)); transition->state = NIST_PROCEEDING; transition->type = SND_STATUS_2XX; transition->method = (void (*)(void *, void *)) &nist_snd_23456xx; ADD_ELEMENT (nist_fsm->transitions, transition); transition = (transition_t *) osip_malloc (sizeof (transition_t)); transition->state = NIST_PROCEEDING; transition->type = SND_STATUS_3456XX; transition->method = (void (*)(void *, void *)) &nist_snd_23456xx; ADD_ELEMENT (nist_fsm->transitions, transition); transition = (transition_t *) osip_malloc (sizeof (transition_t)); transition->state = NIST_PROCEEDING; transition->type = RCV_REQUEST; transition->method = (void (*)(void *, void *)) &nist_rcv_request; ADD_ELEMENT (nist_fsm->transitions, transition); transition = (transition_t *) osip_malloc (sizeof (transition_t)); transition->state = NIST_COMPLETED; transition->type = TIMEOUT_J; transition->method = (void (*)(void *, void *)) &osip_nist_timeout_j_event; ADD_ELEMENT (nist_fsm->transitions, transition); transition = (transition_t *) osip_malloc (sizeof (transition_t)); transition->state = NIST_COMPLETED; transition->type = RCV_REQUEST; transition->method = (void (*)(void *, void *)) &nist_rcv_request; ADD_ELEMENT (nist_fsm->transitions, transition); } static void nist_handle_transport_error (osip_transaction_t * nist, int err) { __osip_transport_error_callback (OSIP_NIST_TRANSPORT_ERROR, nist, err); __osip_transaction_set_state (nist, NIST_TERMINATED); __osip_kill_transaction_callback (OSIP_NIST_KILL_TRANSACTION, nist); /* TODO: MUST BE DELETED NOW */ } void nist_rcv_request (osip_transaction_t * nist, osip_event_t * evt) { int i; osip_t *osip = (osip_t *) nist->config; if (nist->state == NIST_PRE_TRYING) /* announce new REQUEST */ { /* Here we have ist->orig_request == NULL */ nist->orig_request = evt->sip; if (MSG_IS_REGISTER (evt->sip)) __osip_message_callback (OSIP_NIST_REGISTER_RECEIVED, nist, nist->orig_request); else if (MSG_IS_BYE (evt->sip)) __osip_message_callback (OSIP_NIST_BYE_RECEIVED, nist, nist->orig_request); else if (MSG_IS_OPTIONS (evt->sip)) __osip_message_callback (OSIP_NIST_OPTIONS_RECEIVED, nist, nist->orig_request); else if (MSG_IS_INFO (evt->sip)) __osip_message_callback (OSIP_NIST_INFO_RECEIVED, nist, nist->orig_request); else if (MSG_IS_CANCEL (evt->sip)) __osip_message_callback (OSIP_NIST_CANCEL_RECEIVED, nist, nist->orig_request); else if (MSG_IS_NOTIFY (evt->sip)) __osip_message_callback (OSIP_NIST_NOTIFY_RECEIVED, nist, nist->orig_request); else if (MSG_IS_SUBSCRIBE (evt->sip)) __osip_message_callback (OSIP_NIST_SUBSCRIBE_RECEIVED, nist, nist->orig_request); else __osip_message_callback (OSIP_NIST_UNKNOWN_REQUEST_RECEIVED, nist, nist->orig_request); } else /* NIST_PROCEEDING or NIST_COMPLETED */ { /* delete retransmission */ osip_message_free (evt->sip); __osip_message_callback (OSIP_NIST_REQUEST_RECEIVED_AGAIN, nist, nist->orig_request); if (nist->last_response != NULL) /* retransmit last response */ { osip_via_t *via; via = (osip_via_t *) osip_list_get (&nist->last_response->vias, 0); if (via) { char *host; int port; osip_generic_param_t *maddr; osip_generic_param_t *received; osip_generic_param_t *rport; osip_via_param_get_byname (via, "maddr", &maddr); osip_via_param_get_byname (via, "received", &received); osip_via_param_get_byname (via, "rport", &rport); /* 1: user should not use the provided information (host and port) if they are using a reliable transport. Instead, they should use the already open socket attached to this transaction. */ /* 2: check maddr and multicast usage */ if (maddr != NULL) host = maddr->gvalue; /* we should check if this is a multicast address and use set the "ttl" in this case. (this must be done in the UDP message (not at the SIP layer) */ else if (received != NULL) host = received->gvalue; else host = via->host; if (rport == NULL || rport->gvalue == NULL) { if (via->port != NULL) port = osip_atoi (via->port); else port = 5060; } else port = osip_atoi (rport->gvalue); i = osip->cb_send_message (nist, nist->last_response, host, port, nist->out_socket); } else i = -1; if (i != 0) { nist_handle_transport_error (nist, i); return; } else { if (MSG_IS_STATUS_1XX (nist->last_response)) __osip_message_callback (OSIP_NIST_STATUS_1XX_SENT, nist, nist->last_response); else if (MSG_IS_STATUS_2XX (nist->last_response)) __osip_message_callback (OSIP_NIST_STATUS_2XX_SENT_AGAIN, nist, nist->last_response); else __osip_message_callback (OSIP_NIST_STATUS_3456XX_SENT_AGAIN, nist, nist->last_response); return; } } /* we are already in the proper state */ return; } /* we come here only if it was the first REQUEST received */ __osip_transaction_set_state (nist, NIST_TRYING); } void nist_snd_1xx (osip_transaction_t * nist, osip_event_t * evt) { int i; osip_via_t *via; osip_t *osip = (osip_t *) nist->config; if (nist->last_response != NULL) { osip_message_free (nist->last_response); } nist->last_response = evt->sip; via = (osip_via_t *) osip_list_get (&nist->last_response->vias, 0); if (via) { char *host; int port; osip_generic_param_t *maddr; osip_generic_param_t *received; osip_generic_param_t *rport; osip_via_param_get_byname (via, "maddr", &maddr); osip_via_param_get_byname (via, "received", &received); osip_via_param_get_byname (via, "rport", &rport); /* 1: user should not use the provided information (host and port) if they are using a reliable transport. Instead, they should use the already open socket attached to this transaction. */ /* 2: check maddr and multicast usage */ if (maddr != NULL) host = maddr->gvalue; /* we should check if this is a multicast address and use set the "ttl" in this case. (this must be done in the UDP message (not at the SIP layer) */ else if (received != NULL) host = received->gvalue; else host = via->host; if (rport == NULL || rport->gvalue == NULL) { if (via->port != NULL) port = osip_atoi (via->port); else port = 5060; } else port = osip_atoi (rport->gvalue); i = osip->cb_send_message (nist, nist->last_response, host, port, nist->out_socket); } else i = -1; if (i != 0) { nist_handle_transport_error (nist, i); return; } else __osip_message_callback (OSIP_NIST_STATUS_1XX_SENT, nist, nist->last_response); __osip_transaction_set_state (nist, NIST_PROCEEDING); } void nist_snd_23456xx (osip_transaction_t * nist, osip_event_t * evt) { int i; osip_via_t *via; osip_t *osip = (osip_t *) nist->config; if (nist->last_response != NULL) { osip_message_free (nist->last_response); } nist->last_response = evt->sip; via = (osip_via_t *) osip_list_get (&nist->last_response->vias, 0); if (via) { char *host; int port; osip_generic_param_t *maddr; osip_generic_param_t *received; osip_generic_param_t *rport; osip_via_param_get_byname (via, "maddr", &maddr); osip_via_param_get_byname (via, "received", &received); osip_via_param_get_byname (via, "rport", &rport); /* 1: user should not use the provided information (host and port) if they are using a reliable transport. Instead, they should use the already open socket attached to this transaction. */ /* 2: check maddr and multicast usage */ if (maddr != NULL) host = maddr->gvalue; /* we should check if this is a multicast address and use set the "ttl" in this case. (this must be done in the UDP message (not at the SIP layer) */ else if (received != NULL) host = received->gvalue; else host = via->host; if (rport == NULL || rport->gvalue == NULL) { if (via->port != NULL) port = osip_atoi (via->port); else port = 5060; } else port = osip_atoi (rport->gvalue); i = osip->cb_send_message (nist, nist->last_response, host, port, nist->out_socket); } else i = -1; if (i != 0) { nist_handle_transport_error (nist, i); return; } else { if (EVT_IS_SND_STATUS_2XX (evt)) __osip_message_callback (OSIP_NIST_STATUS_2XX_SENT, nist, nist->last_response); else if (MSG_IS_STATUS_3XX (nist->last_response)) __osip_message_callback (OSIP_NIST_STATUS_3XX_SENT, nist, nist->last_response); else if (MSG_IS_STATUS_4XX (nist->last_response)) __osip_message_callback (OSIP_NIST_STATUS_4XX_SENT, nist, nist->last_response); else if (MSG_IS_STATUS_5XX (nist->last_response)) __osip_message_callback (OSIP_NIST_STATUS_5XX_SENT, nist, nist->last_response); else __osip_message_callback (OSIP_NIST_STATUS_6XX_SENT, nist, nist->last_response); } if (nist->state != NIST_COMPLETED) /* start J timer */ { osip_gettimeofday (&nist->nist_context->timer_j_start, NULL); add_gettimeofday (&nist->nist_context->timer_j_start, nist->nist_context->timer_j_length); } __osip_transaction_set_state (nist, NIST_COMPLETED); } void osip_nist_timeout_j_event (osip_transaction_t * nist, osip_event_t * evt) { nist->nist_context->timer_j_length = -1; nist->nist_context->timer_j_start.tv_sec = -1; __osip_transaction_set_state (nist, NIST_TERMINATED); __osip_kill_transaction_callback (OSIP_NIST_KILL_TRANSACTION, nist); }