static void tls_init_shutdown(struct tls_transport *tls, pj_status_t status) { pjsip_tp_state_callback state_cb; if (tls->close_reason == PJ_SUCCESS) tls->close_reason = status; if (tls->base.is_shutdown || tls->base.is_destroying) return; /* Prevent immediate transport destroy by application, as transport * state notification callback may be stacked and transport instance * must remain valid at any point in the callback. */ pjsip_transport_add_ref(&tls->base); /* Notify application of transport disconnected state */ state_cb = pjsip_tpmgr_get_state_cb(tls->base.tpmgr); if (state_cb) { pjsip_transport_state_info state_info; pjsip_tls_state_info tls_info; pj_ssl_sock_info ssl_info; /* Init transport state info */ pj_bzero(&state_info, sizeof(state_info)); state_info.status = tls->close_reason; if (tls->ssock && pj_ssl_sock_get_info(tls->ssock, &ssl_info) == PJ_SUCCESS) { pj_bzero(&tls_info, sizeof(tls_info)); tls_info.ssl_sock_info = &ssl_info; state_info.ext_info = &tls_info; } (*state_cb)(&tls->base, PJSIP_TP_STATE_DISCONNECTED, &state_info); } /* check again */ if (tls->base.is_shutdown || tls->base.is_destroying) { pjsip_transport_dec_ref(&tls->base); return; } /* We can not destroy the transport since high level objects may * still keep reference to this transport. So we can only * instruct transport manager to gracefully start the shutdown * procedure for this transport. */ pjsip_transport_shutdown(&tls->base); /* Now, it is ok to destroy the transport. */ pjsip_transport_dec_ref(&tls->base); }
/* * This callback is called by SSL socket when pending accept() operation * has completed. */ static pj_bool_t on_accept_complete(pj_ssl_sock_t *ssock, pj_ssl_sock_t *new_ssock, const pj_sockaddr_t *src_addr, int src_addr_len) { struct tls_listener *listener; struct tls_transport *tls; pj_ssl_sock_info ssl_info; char addr[PJ_INET6_ADDRSTRLEN+10]; pjsip_tp_state_callback state_cb; pj_bool_t is_shutdown; pj_status_t status; PJ_UNUSED_ARG(src_addr_len); listener = (struct tls_listener*) pj_ssl_sock_get_user_data(ssock); PJ_ASSERT_RETURN(new_ssock, PJ_TRUE); PJ_LOG(4,(listener->factory.obj_name, "TLS listener %.*s:%d: got incoming TLS connection " "from %s, sock=%d", (int)listener->factory.addr_name.host.slen, listener->factory.addr_name.host.ptr, listener->factory.addr_name.port, pj_sockaddr_print(src_addr, addr, sizeof(addr), 3), new_ssock)); /* Retrieve SSL socket info, close the socket if this is failed * as the SSL socket info availability is rather critical here. */ status = pj_ssl_sock_get_info(new_ssock, &ssl_info); if (status != PJ_SUCCESS) { pj_ssl_sock_close(new_ssock); return PJ_TRUE; } /* * Incoming connection! * Create TLS transport for the new socket. */ status = tls_create( listener, NULL, new_ssock, PJ_TRUE, (const pj_sockaddr_in*)&listener->factory.local_addr, (const pj_sockaddr_in*)src_addr, NULL, &tls); if (status != PJ_SUCCESS) return PJ_TRUE; /* Set the "pending" SSL socket user data */ pj_ssl_sock_set_user_data(new_ssock, tls); /* Prevent immediate transport destroy as application may access it * (getting info, etc) in transport state notification callback. */ pjsip_transport_add_ref(&tls->base); /* If there is verification error and verification is mandatory, shutdown * and destroy the transport. */ if (ssl_info.verify_status && listener->tls_setting.verify_client) { if (tls->close_reason == PJ_SUCCESS) tls->close_reason = PJSIP_TLS_ECERTVERIF; pjsip_transport_shutdown(&tls->base); } /* Notify transport state to application */ state_cb = pjsip_tpmgr_get_state_cb(tls->base.tpmgr); if (state_cb) { pjsip_transport_state_info state_info; pjsip_tls_state_info tls_info; pjsip_transport_state tp_state; /* Init transport state info */ pj_bzero(&tls_info, sizeof(tls_info)); pj_bzero(&state_info, sizeof(state_info)); tls_info.ssl_sock_info = &ssl_info; state_info.ext_info = &tls_info; /* Set transport state based on verification status */ if (ssl_info.verify_status && listener->tls_setting.verify_client) { tp_state = PJSIP_TP_STATE_DISCONNECTED; state_info.status = PJSIP_TLS_ECERTVERIF; } else { tp_state = PJSIP_TP_STATE_CONNECTED; state_info.status = PJ_SUCCESS; } (*state_cb)(&tls->base, tp_state, &state_info); } /* Release transport reference. If transport is shutting down, it may * get destroyed here. */ is_shutdown = tls->base.is_shutdown; pjsip_transport_dec_ref(&tls->base); if (is_shutdown) return PJ_TRUE; status = tls_start_read(tls); if (status != PJ_SUCCESS) { PJ_LOG(3,(tls->base.obj_name, "New transport cancelled")); tls_init_shutdown(tls, status); tls_destroy(&tls->base, status); } else { /* Start keep-alive timer */ if (PJSIP_TLS_KEEP_ALIVE_INTERVAL) { pj_time_val delay = {PJSIP_TLS_KEEP_ALIVE_INTERVAL, 0}; pjsip_endpt_schedule_timer(listener->endpt, &tls->ka_timer, &delay); tls->ka_timer.id = PJ_TRUE; pj_gettimeofday(&tls->last_activity); } } return PJ_TRUE; }
/* * This callback is called by active socket when pending accept() operation * has completed. */ static pj_bool_t on_accept_complete(pj_activesock_t *asock, pj_sock_t sock, const pj_sockaddr_t *src_addr, int src_addr_len) { struct tcp_listener *listener; struct tcp_transport *tcp; char addr[PJ_INET6_ADDRSTRLEN+10]; pjsip_tp_state_callback state_cb; pj_sockaddr tmp_src_addr; pj_status_t status; PJ_UNUSED_ARG(src_addr_len); listener = (struct tcp_listener*) pj_activesock_get_user_data(asock); PJ_ASSERT_RETURN(sock != PJ_INVALID_SOCKET, PJ_TRUE); PJ_LOG(4,(listener->factory.obj_name, "TCP listener %.*s:%d: got incoming TCP connection " "from %s, sock=%d", (int)listener->factory.addr_name.host.slen, listener->factory.addr_name.host.ptr, listener->factory.addr_name.port, pj_sockaddr_print(src_addr, addr, sizeof(addr), 3), sock)); /* Apply QoS, if specified */ status = pj_sock_apply_qos2(sock, listener->qos_type, &listener->qos_params, 2, listener->factory.obj_name, "incoming SIP TCP socket"); /* tcp_create() expect pj_sockaddr, so copy src_addr to temporary var, * just in case. */ pj_bzero(&tmp_src_addr, sizeof(tmp_src_addr)); pj_sockaddr_cp(&tmp_src_addr, src_addr); /* * Incoming connection! * Create TCP transport for the new socket. */ status = tcp_create( listener, NULL, sock, PJ_TRUE, &listener->factory.local_addr, &tmp_src_addr, &tcp); if (status == PJ_SUCCESS) { status = tcp_start_read(tcp); if (status != PJ_SUCCESS) { PJ_LOG(3,(tcp->base.obj_name, "New transport cancelled")); tcp_destroy(&tcp->base, status); } else { /* Start keep-alive timer */ if (PJSIP_TCP_KEEP_ALIVE_INTERVAL) { pj_time_val delay = {PJSIP_TCP_KEEP_ALIVE_INTERVAL, 0}; pjsip_endpt_schedule_timer(listener->endpt, &tcp->ka_timer, &delay); tcp->ka_timer.id = PJ_TRUE; pj_gettimeofday(&tcp->last_activity); } /* Notify application of transport state accepted */ state_cb = pjsip_tpmgr_get_state_cb(tcp->base.tpmgr); if (state_cb) { pjsip_transport_state_info state_info; pj_bzero(&state_info, sizeof(state_info)); (*state_cb)(&tcp->base, PJSIP_TP_STATE_CONNECTED, &state_info); } } } return PJ_TRUE; }