/* $Id$ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #define ADDR_LOOP "128.0.0.1" #define ADDR_LOOP_DGRAM "129.0.0.1" /** This structure describes incoming packet. */ struct recv_list { PJ_DECL_LIST_MEMBER(struct recv_list); pjsip_rx_data rdata; }; /** This structure is used to keep delayed send failure. */ struct send_list { PJ_DECL_LIST_MEMBER(struct send_list); pj_time_val sent_time; pj_ssize_t sent; pjsip_tx_data *tdata; void *token; void (*callback)(pjsip_transport*, void*, pj_ssize_t); }; /** This structure describes the loop transport. */ struct loop_transport { pjsip_transport base; pj_pool_t *pool; pj_thread_t *thread; pj_bool_t thread_quit_flag; pj_bool_t discard; int fail_mode; unsigned recv_delay; unsigned send_delay; struct recv_list recv_list; struct send_list send_list; }; /* Helper function to create "incoming" packet */ static struct recv_list *create_incoming_packet( struct loop_transport *loop, pjsip_tx_data *tdata ) { pj_pool_t *pool; struct recv_list *pkt; pool = pjsip_endpt_create_pool(loop->base.endpt, "rdata", PJSIP_POOL_RDATA_LEN, PJSIP_POOL_RDATA_INC+5); if (!pool) return NULL; pkt = PJ_POOL_ZALLOC_T(pool, struct recv_list); /* Initialize rdata. */ pkt->rdata.tp_info.pool = pool; pkt->rdata.tp_info.transport = &loop->base; /* Copy the packet. */ pj_memcpy(pkt->rdata.pkt_info.packet, tdata->buf.start, tdata->buf.cur - tdata->buf.start); pkt->rdata.pkt_info.len = tdata->buf.cur - tdata->buf.start; /* the source address */ pkt->rdata.pkt_info.src_addr.addr.sa_family = pj_AF_INET(); /* "Source address" info. */ pkt->rdata.pkt_info.src_addr_len = sizeof(pj_sockaddr_in); if (loop->base.key.type == PJSIP_TRANSPORT_LOOP) { pj_ansi_strcpy(pkt->rdata.pkt_info.src_name, ADDR_LOOP); } else { pj_ansi_strcpy(pkt->rdata.pkt_info.src_name, ADDR_LOOP_DGRAM); } pkt->rdata.pkt_info.src_port = loop->base.local_name.port; /* When do we need to "deliver" this packet. */ pj_gettimeofday(&pkt->rdata.pkt_info.timestamp); pkt->rdata.pkt_info.timestamp.msec += loop->recv_delay; pj_time_val_normalize(&pkt->rdata.pkt_info.timestamp); /* Done. */ return pkt; } /* Helper function to add pending notification callback. */ static pj_status_t add_notification( struct loop_transport *loop, pjsip_tx_data *tdata, pj_ssize_t sent, void *token, void (*callback)(pjsip_transport*, void*, pj_ssize_t)) { struct send_list *sent_status; pjsip_tx_data_add_ref(tdata); pj_lock_acquire(tdata->lock); sent_status = PJ_POOL_ALLOC_T(tdata->pool, struct send_list); pj_lock_release(tdata->lock); sent_status->sent = sent; sent_status->tdata = tdata; sent_status->token = token; sent_status->callback = callback; pj_gettimeofday(&sent_status->sent_time); sent_status->sent_time.msec += loop->send_delay; pj_time_val_normalize(&sent_status->sent_time); pj_lock_acquire(loop->base.lock); pj_list_push_back(&loop->send_list, sent_status); pj_lock_release(loop->base.lock); return PJ_SUCCESS; } /* Handler for sending outgoing message; called by transport manager. */ static pj_status_t loop_send_msg( pjsip_transport *tp, pjsip_tx_data *tdata, const pj_sockaddr_t *rem_addr, int addr_len, void *token, pjsip_transport_callback cb) { struct loop_transport *loop = (struct loop_transport*)tp; struct recv_list *recv_pkt; PJ_ASSERT_RETURN(tp && (tp->key.type == PJSIP_TRANSPORT_LOOP || tp->key.type == PJSIP_TRANSPORT_LOOP_DGRAM), PJ_EINVAL); PJ_UNUSED_ARG(rem_addr); PJ_UNUSED_ARG(addr_len); /* Need to send failure? */ if (loop->fail_mode) { if (loop->send_delay == 0) { return PJ_STATUS_FROM_OS(OSERR_ECONNRESET); } else { add_notification(loop, tdata, -PJ_STATUS_FROM_OS(OSERR_ECONNRESET), token, cb); return PJ_EPENDING; } } /* Discard any packets? */ if (loop->discard) return PJ_SUCCESS; /* Create rdata for the "incoming" packet. */ recv_pkt = create_incoming_packet(loop, tdata); if (!recv_pkt) return PJ_ENOMEM; /* If delay is not configured, deliver this packet now! */ if (loop->recv_delay == 0) { pj_ssize_t size_eaten; size_eaten = pjsip_tpmgr_receive_packet( loop->base.tpmgr, &recv_pkt->rdata); pj_assert(size_eaten == recv_pkt->rdata.pkt_info.len); PJ_UNUSED_ARG(size_eaten); pjsip_endpt_release_pool(loop->base.endpt, recv_pkt->rdata.tp_info.pool); } else { /* Otherwise if delay is configured, add the "packet" to the * receive list to be processed by worker thread. */ pj_lock_acquire(loop->base.lock); pj_list_push_back(&loop->recv_list, recv_pkt); pj_lock_release(loop->base.lock); } if (loop->send_delay != 0) { add_notification(loop, tdata, tdata->buf.cur - tdata->buf.start, token, cb); return PJ_EPENDING; } else { return PJ_SUCCESS; } } /* Handler to destroy the transport; called by transport manager */ static pj_status_t loop_destroy(pjsip_transport *tp) { struct loop_transport *loop = (struct loop_transport*)tp; PJ_ASSERT_RETURN(tp && (tp->key.type == PJSIP_TRANSPORT_LOOP || tp->key.type == PJSIP_TRANSPORT_LOOP_DGRAM), PJ_EINVAL); loop->thread_quit_flag = 1; /* Unlock transport mutex before joining thread. */ /// This raised assertion failed "mutex->owner == pj_thread_this()", /// where mutex->owner==NULL //pj_lock_release(tp->lock); pj_thread_join(loop->thread); pj_thread_destroy(loop->thread); /* Clear pending send notifications. */ while (!pj_list_empty(&loop->send_list)) { struct send_list *node = loop->send_list.next; /* Notify callback. */ if (node->callback) { (*node->callback)(&loop->base, node->token, -PJSIP_ESHUTDOWN); } pj_list_erase(node); pjsip_tx_data_dec_ref(node->tdata); } /* Clear "incoming" packets in the queue. */ while (!pj_list_empty(&loop->recv_list)) { struct recv_list *node = loop->recv_list.next; pj_list_erase(node); pjsip_endpt_release_pool(loop->base.endpt, node->rdata.tp_info.pool); } /* Self destruct.. heheh.. */ pj_lock_destroy(loop->base.lock); pj_atomic_destroy(loop->base.ref_cnt); pjsip_endpt_release_pool(loop->base.endpt, loop->base.pool); return PJ_SUCCESS; } /* Worker thread for loop transport. */ static int loop_transport_worker_thread(void *arg) { struct loop_transport *loop = (struct loop_transport*) arg; struct recv_list r; struct send_list s; pj_list_init(&r); pj_list_init(&s); while (!loop->thread_quit_flag) { pj_time_val now; pj_thread_sleep(1); pj_gettimeofday(&now); pj_lock_acquire(loop->base.lock); /* Move expired send notification to local list. */ while (!pj_list_empty(&loop->send_list)) { struct send_list *node = loop->send_list.next; /* Break when next node time is greater than now. */ if (PJ_TIME_VAL_GTE(node->sent_time, now)) break; /* Delete this from the list. */ pj_list_erase(node); /* Add to local list. */ pj_list_push_back(&s, node); } /* Move expired "incoming" packet to local list. */ while (!pj_list_empty(&loop->recv_list)) { struct recv_list *node = loop->recv_list.next; /* Break when next node time is greater than now. */ if (PJ_TIME_VAL_GTE(node->rdata.pkt_info.timestamp, now)) break; /* Delete this from the list. */ pj_list_erase(node); /* Add to local list. */ pj_list_push_back(&r, node); } pj_lock_release(loop->base.lock); /* Process send notification and incoming packet notification * without holding down the loop's mutex. */ while (!pj_list_empty(&s)) { struct send_list *node = s.next; pj_list_erase(node); /* Notify callback. */ if (node->callback) { (*node->callback)(&loop->base, node->token, node->sent); } /* Decrement tdata reference counter. */ pjsip_tx_data_dec_ref(node->tdata); } /* Process "incoming" packet. */ while (!pj_list_empty(&r)) { struct recv_list *node = r.next; pj_ssize_t size_eaten; pj_list_erase(node); /* Notify transport manager about the "incoming packet" */ size_eaten = pjsip_tpmgr_receive_packet(loop->base.tpmgr, &node->rdata); /* Must "eat" all the packets. */ pj_assert(size_eaten == node->rdata.pkt_info.len); PJ_UNUSED_ARG(size_eaten); /* Done. */ pjsip_endpt_release_pool(loop->base.endpt, node->rdata.tp_info.pool); } } return 0; } /* Start loop transport. */ PJ_DEF(pj_status_t) pjsip_loop_start( pjsip_endpoint *endpt, pjsip_transport **transport) { pj_pool_t *pool; struct loop_transport *loop; pj_status_t status; /* Create pool. */ pool = pjsip_endpt_create_pool(endpt, "loop", 4000, 4000); if (!pool) return PJ_ENOMEM; /* Create the loop structure. */ loop = PJ_POOL_ZALLOC_T(pool, struct loop_transport); /* Initialize transport properties. */ pj_ansi_snprintf(loop->base.obj_name, sizeof(loop->base.obj_name), "loop%p", loop); loop->base.pool = pool; status = pj_atomic_create(pool, 0, &loop->base.ref_cnt); if (status != PJ_SUCCESS) goto on_error; status = pj_lock_create_recursive_mutex(pool, "loop", &loop->base.lock); if (status != PJ_SUCCESS) goto on_error; loop->base.key.type = PJSIP_TRANSPORT_LOOP_DGRAM; //loop->base.key.rem_addr.addr.sa_family = pj_AF_INET(); loop->base.type_name = "LOOP-DGRAM"; loop->base.info = "LOOP-DGRAM"; loop->base.flag = PJSIP_TRANSPORT_DATAGRAM; loop->base.local_name.host = pj_str(ADDR_LOOP_DGRAM); loop->base.local_name.port = pjsip_transport_get_default_port_for_type((pjsip_transport_type_e) loop->base.key.type); loop->base.addr_len = sizeof(pj_sockaddr_in); loop->base.dir = PJSIP_TP_DIR_NONE; loop->base.endpt = endpt; loop->base.tpmgr = pjsip_endpt_get_tpmgr(endpt); loop->base.send_msg = &loop_send_msg; loop->base.destroy = &loop_destroy; pj_list_init(&loop->recv_list); pj_list_init(&loop->send_list); /* Create worker thread. */ status = pj_thread_create(pool, "loop", &loop_transport_worker_thread, loop, 0, PJ_THREAD_SUSPENDED, &loop->thread); if (status != PJ_SUCCESS) goto on_error; /* Register to transport manager. */ status = pjsip_transport_register( loop->base.tpmgr, &loop->base); if (status != PJ_SUCCESS) goto on_error; /* Start the thread. */ status = pj_thread_resume(loop->thread); if (status != PJ_SUCCESS) goto on_error; /* * Done. */ if (transport) *transport = &loop->base; return PJ_SUCCESS; on_error: if (loop->base.lock) pj_lock_destroy(loop->base.lock); if (loop->thread) pj_thread_destroy(loop->thread); if (loop->base.ref_cnt) pj_atomic_destroy(loop->base.ref_cnt); pjsip_endpt_release_pool(endpt, loop->pool); return status; } PJ_DEF(pj_status_t) pjsip_loop_set_discard( pjsip_transport *tp, pj_bool_t discard, pj_bool_t *prev_value ) { struct loop_transport *loop = (struct loop_transport*)tp; PJ_ASSERT_RETURN(tp && (tp->key.type == PJSIP_TRANSPORT_LOOP || tp->key.type == PJSIP_TRANSPORT_LOOP_DGRAM), PJ_EINVAL); if (prev_value) *prev_value = loop->discard; loop->discard = discard; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjsip_loop_set_failure( pjsip_transport *tp, int fail_flag, int *prev_value ) { struct loop_transport *loop = (struct loop_transport*)tp; PJ_ASSERT_RETURN(tp && (tp->key.type == PJSIP_TRANSPORT_LOOP || tp->key.type == PJSIP_TRANSPORT_LOOP_DGRAM), PJ_EINVAL); if (prev_value) *prev_value = loop->fail_mode; loop->fail_mode = fail_flag; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjsip_loop_set_recv_delay( pjsip_transport *tp, unsigned delay, unsigned *prev_value) { struct loop_transport *loop = (struct loop_transport*)tp; PJ_ASSERT_RETURN(tp && (tp->key.type == PJSIP_TRANSPORT_LOOP || tp->key.type == PJSIP_TRANSPORT_LOOP_DGRAM), PJ_EINVAL); if (prev_value) *prev_value = loop->recv_delay; loop->recv_delay = delay; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjsip_loop_set_send_callback_delay( pjsip_transport *tp, unsigned delay, unsigned *prev_value) { struct loop_transport *loop = (struct loop_transport*)tp; PJ_ASSERT_RETURN(tp && (tp->key.type == PJSIP_TRANSPORT_LOOP || tp->key.type == PJSIP_TRANSPORT_LOOP_DGRAM), PJ_EINVAL); if (prev_value) *prev_value = loop->send_delay; loop->send_delay = delay; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjsip_loop_set_delay( pjsip_transport *tp, unsigned delay ) { struct loop_transport *loop = (struct loop_transport*)tp; PJ_ASSERT_RETURN(tp && (tp->key.type == PJSIP_TRANSPORT_LOOP || tp->key.type == PJSIP_TRANSPORT_LOOP_DGRAM), PJ_EINVAL); loop->recv_delay = delay; loop->send_delay = delay; return PJ_SUCCESS; }