/*! * \brief Pass WebSocket data into pjsip transport manager. */ static int transport_read(void *data) { struct transport_read_data *read_data = data; struct ws_transport *newtransport = read_data->transport; struct ast_websocket *session = newtransport->ws_session; pjsip_rx_data *rdata = &newtransport->rdata; int recvd; pj_str_t buf; int pjsip_pkt_len; pj_gettimeofday(&rdata->pkt_info.timestamp); pjsip_pkt_len = PJSIP_MAX_PKT_LEN < read_data->payload_len ? PJSIP_MAX_PKT_LEN : read_data->payload_len; pj_memcpy(rdata->pkt_info.packet, read_data->payload, pjsip_pkt_len); rdata->pkt_info.len = pjsip_pkt_len; rdata->pkt_info.zero = 0; pj_sockaddr_parse(pj_AF_UNSPEC(), 0, pj_cstr(&buf, ast_sockaddr_stringify(ast_websocket_remote_address(session))), &rdata->pkt_info.src_addr); rdata->pkt_info.src_addr.addr.sa_family = pj_AF_INET(); rdata->pkt_info.src_addr_len = sizeof(rdata->pkt_info.src_addr); pj_ansi_strcpy(rdata->pkt_info.src_name, ast_sockaddr_stringify_host(ast_websocket_remote_address(session))); rdata->pkt_info.src_port = ast_sockaddr_port(ast_websocket_remote_address(session)); recvd = pjsip_tpmgr_receive_packet(rdata->tp_info.transport->tpmgr, rdata); pj_pool_reset(rdata->tp_info.pool); return (read_data->payload_len == recvd) ? 0 : -1; }
void SipIceTransport::onRecv() { rdata_.pkt_info.len += ice_->recv(comp_id_, (uint8_t*)rdata_.pkt_info.packet+rdata_.pkt_info.len, sizeof(rdata_.pkt_info.packet)-rdata_.pkt_info.len); rdata_.pkt_info.zero = 0; pj_gettimeofday(&rdata_.pkt_info.timestamp); auto eaten = pjsip_tpmgr_receive_packet(rdata_.tp_info.transport->tpmgr, &rdata_); /* Move unprocessed data to the front of the buffer */ auto rem = rdata_.pkt_info.len - eaten; if (rem > 0 && rem != rdata_.pkt_info.len) { std::move(rdata_.pkt_info.packet + eaten, rdata_.pkt_info.packet + eaten + rem, rdata_.pkt_info.packet); } rdata_.pkt_info.len = rem; /* Reset pool */ pj_pool_reset(rdata_.tp_info.pool); }
/* * udp_on_read_complete() * * This is callback notification from ioqueue that a pending recvfrom() * operation has completed. */ static void udp_on_read_complete( pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_read) { /* See https://trac.pjsip.org/repos/ticket/1197 */ enum { MAX_IMMEDIATE_PACKET = 50 }; pjsip_rx_data_op_key *rdata_op_key = (pjsip_rx_data_op_key*) op_key; pjsip_rx_data *rdata = rdata_op_key->rdata; struct udp_transport *tp = (struct udp_transport*)rdata->tp_info.transport; int i; pj_status_t status; /* Don't do anything if transport is closing. */ if (tp->is_closing) { tp->is_closing++; return; } /* Don't do anything if transport is being paused. */ if (tp->is_paused) return; /* * The idea of the loop is to process immediate data received by * pj_ioqueue_recvfrom(), as long as i < MAX_IMMEDIATE_PACKET. When * i is >= MAX_IMMEDIATE_PACKET, we force the recvfrom() operation to * complete asynchronously, to allow other sockets to get their data. */ for (i=0;; ++i) { enum { MIN_SIZE = 32 }; pj_uint32_t flags; /* Report the packet to transport manager. Only do so if packet size * is relatively big enough for a SIP packet. */ if (bytes_read > MIN_SIZE) { pj_ssize_t size_eaten; const pj_sockaddr *src_addr = &rdata->pkt_info.src_addr; /* Init pkt_info part. */ rdata->pkt_info.len = bytes_read; rdata->pkt_info.zero = 0; pj_gettimeofday(&rdata->pkt_info.timestamp); if (src_addr->addr.sa_family == pj_AF_INET()) { pj_ansi_strcpy(rdata->pkt_info.src_name, pj_inet_ntoa(src_addr->ipv4.sin_addr)); rdata->pkt_info.src_port = pj_ntohs(src_addr->ipv4.sin_port); } else { pj_inet_ntop(pj_AF_INET6(), pj_sockaddr_get_addr(&rdata->pkt_info.src_addr), rdata->pkt_info.src_name, sizeof(rdata->pkt_info.src_name)); rdata->pkt_info.src_port = pj_ntohs(src_addr->ipv6.sin6_port); } size_eaten = pjsip_tpmgr_receive_packet(rdata->tp_info.transport->tpmgr, rdata); if (size_eaten < 0) { pj_assert(!"It shouldn't happen!"); size_eaten = rdata->pkt_info.len; } /* Since this is UDP, the whole buffer is the message. */ rdata->pkt_info.len = 0; } else if (bytes_read <= MIN_SIZE) { /* TODO: */ } else if (-bytes_read != PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK) && -bytes_read != PJ_STATUS_FROM_OS(OSERR_EINPROGRESS) && -bytes_read != PJ_STATUS_FROM_OS(OSERR_ECONNRESET)) { /* Report error to endpoint. */ PJSIP_ENDPT_LOG_ERROR((rdata->tp_info.transport->endpt, rdata->tp_info.transport->obj_name, (pj_status_t)-bytes_read, "Warning: pj_ioqueue_recvfrom()" " callback error")); } if (i >= MAX_IMMEDIATE_PACKET) { /* Force ioqueue_recvfrom() to return PJ_EPENDING */ flags = PJ_IOQUEUE_ALWAYS_ASYNC; } else { flags = 0; } /* Reset pool. * Need to copy rdata fields to temp variable because they will * be invalid after pj_pool_reset(). */ { pj_pool_t *rdata_pool = rdata->tp_info.pool; struct udp_transport *rdata_tp ; unsigned rdata_index; rdata_tp = (struct udp_transport*)rdata->tp_info.transport; rdata_index = (unsigned)(unsigned long)(pj_ssize_t) rdata->tp_info.tp_data; pj_pool_reset(rdata_pool); init_rdata(rdata_tp, rdata_index, rdata_pool, &rdata); /* Change some vars to point to new location after * pool reset. */ op_key = &rdata->tp_info.op_key.op_key; } /* Only read next packet if transport is not being paused. This * check handles the case where transport is paused while endpoint * is still processing a SIP message. */ if (tp->is_paused) return; /* Read next packet. */ bytes_read = sizeof(rdata->pkt_info.packet); rdata->pkt_info.src_addr_len = sizeof(rdata->pkt_info.src_addr); status = pj_ioqueue_recvfrom(key, op_key, rdata->pkt_info.packet, &bytes_read, flags, &rdata->pkt_info.src_addr, &rdata->pkt_info.src_addr_len); if (status == PJ_SUCCESS) { /* Continue loop. */ pj_assert(i < MAX_IMMEDIATE_PACKET); } else if (status == PJ_EPENDING) { break; } else { if (i < MAX_IMMEDIATE_PACKET) { /* Report error to endpoint if this is not EWOULDBLOCK error.*/ if (status != PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK) && status != PJ_STATUS_FROM_OS(OSERR_EINPROGRESS) && status != PJ_STATUS_FROM_OS(OSERR_ECONNRESET)) { PJSIP_ENDPT_LOG_ERROR((rdata->tp_info.transport->endpt, rdata->tp_info.transport->obj_name, status, "Warning: pj_ioqueue_recvfrom")); } /* Continue loop. */ bytes_read = 0; } else { /* This is fatal error. * Ioqueue operation will stop for this transport! */ PJSIP_ENDPT_LOG_ERROR((rdata->tp_info.transport->endpt, rdata->tp_info.transport->obj_name, status, "FATAL: pj_ioqueue_recvfrom() error, " "UDP transport stopping! Error")); break; } } } }
/* Worker thread for loop transport. */ static int loop_transport_worker_thread(void *arg) { struct loop_transport *loop = 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); /* Done. */ pjsip_endpt_release_pool(loop->base.endpt, node->rdata.tp_info.pool); } } return 0; }
/* 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, void (*cb)(pjsip_transport *transport, void *token, pj_ssize_t sent_bytes)) { 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); 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; } }