Exemplo n.º 1
0
/*
 * Callback from TURN session when state has changed
 */
static void turn_on_state(pj_turn_session *sess, 
			  pj_turn_state_t old_state,
			  pj_turn_state_t new_state)
{
    pj_turn_sock *turn_sock = (pj_turn_sock*) 
			   pj_turn_session_get_user_data(sess);
    pj_status_t status;

    if (turn_sock == NULL) {
	/* We've been destroyed */
	return;
    }

    /* Notify app first */
    if (turn_sock->cb.on_state) {
	(*turn_sock->cb.on_state)(turn_sock, old_state, new_state);
    }

    /* Make sure user hasn't destroyed us in the callback */
    if (turn_sock->sess && new_state == PJ_TURN_STATE_RESOLVED) {
	pj_turn_session_info info;
	pj_turn_session_get_info(turn_sock->sess, &info);
	new_state = info.state;
    }

    if (turn_sock->sess && new_state == PJ_TURN_STATE_RESOLVED) {
	/*
	 * Once server has been resolved, initiate outgoing TCP
	 * connection to the server.
	 */
	pj_turn_session_info info;
	char addrtxt[PJ_INET6_ADDRSTRLEN+8];
	int sock_type;
	pj_sock_t sock;
	pj_activesock_cfg asock_cfg;
	pj_activesock_cb asock_cb;
	pj_sockaddr bound_addr, *cfg_bind_addr;
	pj_uint16_t max_bind_retry;

	/* Close existing connection, if any. This happens when
	 * we're switching to alternate TURN server when either TCP
	 * connection or ALLOCATE request failed.
	 */
	if (turn_sock->active_sock) {
	    pj_activesock_close(turn_sock->active_sock);
	    turn_sock->active_sock = NULL;
	}

	/* Get server address from session info */
	pj_turn_session_get_info(sess, &info);

	if (turn_sock->conn_type == PJ_TURN_TP_UDP)
	    sock_type = pj_SOCK_DGRAM();
	else
	    sock_type = pj_SOCK_STREAM();

	/* Init socket */
	status = pj_sock_socket(turn_sock->af, sock_type, 0, &sock);
	if (status != PJ_SUCCESS) {
	    pj_turn_sock_destroy(turn_sock);
	    return;
	}

	/* Bind socket */
	cfg_bind_addr = &turn_sock->setting.bound_addr;
	max_bind_retry = MAX_BIND_RETRY;
	if (turn_sock->setting.port_range &&
	    turn_sock->setting.port_range < max_bind_retry)
	{
	    max_bind_retry = turn_sock->setting.port_range;
	}
	pj_sockaddr_init(turn_sock->af, &bound_addr, NULL, 0);
	if (cfg_bind_addr->addr.sa_family == pj_AF_INET() || 
	    cfg_bind_addr->addr.sa_family == pj_AF_INET6())
	{
	    pj_sockaddr_cp(&bound_addr, cfg_bind_addr);
	}
	status = pj_sock_bind_random(sock, &bound_addr,
				     turn_sock->setting.port_range,
				     max_bind_retry);
	if (status != PJ_SUCCESS) {
	    pj_turn_sock_destroy(turn_sock);
	    return;
	}

	/* Apply QoS, if specified */
	status = pj_sock_apply_qos2(sock, turn_sock->setting.qos_type,
				    &turn_sock->setting.qos_params, 
				    (turn_sock->setting.qos_ignore_error?2:1),
				    turn_sock->pool->obj_name, NULL);
	if (status != PJ_SUCCESS && !turn_sock->setting.qos_ignore_error) {
	    pj_turn_sock_destroy(turn_sock);
	    return;
	}

	/* Apply socket buffer size */
	if (turn_sock->setting.so_rcvbuf_size > 0) {
	    unsigned sobuf_size = turn_sock->setting.so_rcvbuf_size;
	    status = pj_sock_setsockopt_sobuf(sock, pj_SO_RCVBUF(),
					      PJ_TRUE, &sobuf_size);
	    if (status != PJ_SUCCESS) {
		pj_perror(3, turn_sock->obj_name, status,
			  "Failed setting SO_RCVBUF");
	    } else {
		if (sobuf_size < turn_sock->setting.so_rcvbuf_size) {
		    PJ_LOG(4, (turn_sock->obj_name, 
			       "Warning! Cannot set SO_RCVBUF as configured,"
			       " now=%d, configured=%d", sobuf_size,
			       turn_sock->setting.so_rcvbuf_size));
		} else {
		    PJ_LOG(5, (turn_sock->obj_name, "SO_RCVBUF set to %d",
			       sobuf_size));
		}
	    }
	}
	if (turn_sock->setting.so_sndbuf_size > 0) {
	    unsigned sobuf_size = turn_sock->setting.so_sndbuf_size;
	    status = pj_sock_setsockopt_sobuf(sock, pj_SO_SNDBUF(),
					      PJ_TRUE, &sobuf_size);
	    if (status != PJ_SUCCESS) {
		pj_perror(3, turn_sock->obj_name, status,
			  "Failed setting SO_SNDBUF");
	    } else {
		if (sobuf_size < turn_sock->setting.so_sndbuf_size) {
		    PJ_LOG(4, (turn_sock->obj_name, 
			       "Warning! Cannot set SO_SNDBUF as configured,"
			       " now=%d, configured=%d", sobuf_size,
			       turn_sock->setting.so_sndbuf_size));
		} else {
		    PJ_LOG(5, (turn_sock->obj_name, "SO_SNDBUF set to %d",
			       sobuf_size));
		}
	    }
	}

	/* Create active socket */
	pj_activesock_cfg_default(&asock_cfg);
	asock_cfg.grp_lock = turn_sock->grp_lock;

	pj_bzero(&asock_cb, sizeof(asock_cb));
	asock_cb.on_data_read = &on_data_read;
	asock_cb.on_connect_complete = &on_connect_complete;
	status = pj_activesock_create(turn_sock->pool, sock,
				      sock_type, &asock_cfg,
				      turn_sock->cfg.ioqueue, &asock_cb, 
				      turn_sock,
				      &turn_sock->active_sock);
	if (status != PJ_SUCCESS) {
	    pj_turn_sock_destroy(turn_sock);
	    return;
	}

	PJ_LOG(5,(turn_sock->pool->obj_name,
		  "Connecting to %s", 
		  pj_sockaddr_print(&info.server, addrtxt, 
				    sizeof(addrtxt), 3)));

	/* Initiate non-blocking connect */
#if PJ_HAS_TCP
	status=pj_activesock_start_connect(turn_sock->active_sock, 
					   turn_sock->pool,
					   &info.server, 
					   pj_sockaddr_get_len(&info.server));
	if (status == PJ_SUCCESS) {
	    on_connect_complete(turn_sock->active_sock, PJ_SUCCESS);
	} else if (status != PJ_EPENDING) {
	    pj_turn_sock_destroy(turn_sock);
	    return;
	}
#else
	on_connect_complete(turn_sock->active_sock, PJ_SUCCESS);
#endif

	/* Done for now. Subsequent work will be done in 
	 * on_connect_complete() callback.
	 */
    }

    if (new_state >= PJ_TURN_STATE_DESTROYING && turn_sock->sess) {
	pj_time_val delay = {0, 0};

	turn_sock->sess = NULL;
	pj_turn_session_set_user_data(sess, NULL);

	pj_timer_heap_cancel_if_active(turn_sock->cfg.timer_heap,
	                               &turn_sock->timer, 0);
	pj_timer_heap_schedule_w_grp_lock(turn_sock->cfg.timer_heap,
	                                  &turn_sock->timer,
	                                  &delay, TIMER_DESTROY,
	                                  turn_sock->grp_lock);
    }
}
Exemplo n.º 2
0
static void destroy_relay(void)
{
    if (g.relay) {
	pj_turn_sock_destroy(g.relay);
    }
}
Exemplo n.º 3
0
static int destroy_test(pj_stun_config  *stun_cfg,
                        pj_bool_t with_dns_srv,
                        pj_bool_t in_callback,
                        pj_bool_t use_ipv6)
{
    struct test_session_cfg test_cfg =
    {
        {   /* Client cfg */
            PJ_TRUE,	    /* DNS SRV */
            0xFFFF	    /* Destroy on state */
        },
        {   /* Server cfg */
            0xFFFFFFFF,	    /* flags */
            PJ_TRUE,	    /* respond to allocate  */
            PJ_TRUE	    /* respond to refresh   */
        }
    };
    struct test_session *sess;
    int target_state;
    int rc;

    PJ_LOG(3,("", "  destroy test %s %s",
              (in_callback? "in callback" : ""),
              (with_dns_srv? "with DNS srv" : "")
             ));

    test_cfg.client.enable_dns_srv = with_dns_srv;
    set_server_flag(&test_cfg, use_ipv6);

    for (target_state=PJ_TURN_STATE_RESOLVING; target_state<=PJ_TURN_STATE_READY; ++target_state) {
        enum { TIMEOUT = 60 };
        pjlib_state pjlib_state;
        pj_turn_session_info info;
        pj_time_val tstart;

        capture_pjlib_state(stun_cfg, &pjlib_state);

        PJ_LOG(3,("", "   %s", pj_turn_state_name((pj_turn_state_t)target_state)));

        if (in_callback)
            test_cfg.client.destroy_on_state = target_state;

        rc = create_test_session(stun_cfg, &test_cfg, &sess);
        if (rc != 0)
            return rc;

        if (in_callback) {
            pj_gettimeofday(&tstart);
            rc = 0;
            while (sess->turn_sock) {
                pj_time_val now;

                poll_events(stun_cfg, 100, PJ_FALSE);

                pj_gettimeofday(&now);
                if (now.sec - tstart.sec > TIMEOUT) {
                    rc = -7;
                    break;
                }
            }

        } else {
            pj_gettimeofday(&tstart);
            rc = 0;
            while (sess->turn_sock) {
                pj_time_val now;

                poll_events(stun_cfg, 1, PJ_FALSE);

                pj_turn_sock_get_info(sess->turn_sock, &info);

                if (info.state >= target_state) {
                    pj_turn_sock_destroy(sess->turn_sock);
                    break;
                }

                pj_gettimeofday(&now);
                if (now.sec - tstart.sec > TIMEOUT) {
                    rc = -8;
                    break;
                }
            }
        }


        if (rc != 0) {
            PJ_LOG(3,("", "    error: timeout"));
            return rc;
        }

        poll_events(stun_cfg, 1000, PJ_FALSE);
        destroy_session(sess);

        rc = check_pjlib_state(stun_cfg, &pjlib_state);
        if (rc != 0) {
            PJ_LOG(3,("", "    error: memory/timer-heap leak detected"));
            return rc;
        }
    }

    return 0;
}
Exemplo n.º 4
0
/*
 * Callback from TURN session when state has changed
 */
static void turn_on_state(pj_turn_session *sess, 
			  pj_turn_state_t old_state,
			  pj_turn_state_t new_state)
{
    pj_turn_sock *turn_sock = (pj_turn_sock*) 
			   pj_turn_session_get_user_data(sess);
    pj_status_t status;

    if (turn_sock == NULL) {
	/* We've been destroyed */
	return;
    }

    /* Notify app first */
    if (turn_sock->cb.on_state) {
	(*turn_sock->cb.on_state)(turn_sock, old_state, new_state);
    }

    /* Make sure user hasn't destroyed us in the callback */
    if (turn_sock->sess && new_state == PJ_TURN_STATE_RESOLVED) {
	pj_turn_session_info info;
	pj_turn_session_get_info(turn_sock->sess, &info);
	new_state = info.state;
    }

    if (turn_sock->sess && new_state == PJ_TURN_STATE_RESOLVED) {
	/*
	 * Once server has been resolved, initiate outgoing TCP
	 * connection to the server.
	 */
	pj_turn_session_info info;
	char addrtxt[PJ_INET6_ADDRSTRLEN+8];
	int sock_type;
	pj_sock_t sock;
	pj_activesock_cb asock_cb;

	/* Close existing connection, if any. This happens when
	 * we're switching to alternate TURN server when either TCP
	 * connection or ALLOCATE request failed.
	 */
	if (turn_sock->active_sock) {
	    pj_activesock_close(turn_sock->active_sock);
	    turn_sock->active_sock = NULL;
	}

	/* Get server address from session info */
	pj_turn_session_get_info(sess, &info);

	if (turn_sock->conn_type == PJ_TURN_TP_UDP)
	    sock_type = pj_SOCK_DGRAM();
	else
	    sock_type = pj_SOCK_STREAM();

	/* Init socket */
	status = pj_sock_socket(turn_sock->af, sock_type, 0, &sock);
	if (status != PJ_SUCCESS) {
	    pj_turn_sock_destroy(turn_sock);
	    return;
	}

        /* Apply QoS, if specified */
	status = pj_sock_apply_qos2(sock, turn_sock->setting.qos_type,
				    &turn_sock->setting.qos_params, 
				    (turn_sock->setting.qos_ignore_error?2:1),
				    turn_sock->pool->obj_name, NULL);
	if (status != PJ_SUCCESS && !turn_sock->setting.qos_ignore_error) {
	    pj_turn_sock_destroy(turn_sock);
	    return;
	}

	/* Create active socket */
	pj_bzero(&asock_cb, sizeof(asock_cb));
	asock_cb.on_data_read = &on_data_read;
	asock_cb.on_connect_complete = &on_connect_complete;
	status = pj_activesock_create(turn_sock->pool, sock,
				      sock_type, NULL,
				      turn_sock->cfg.ioqueue, &asock_cb, 
				      turn_sock,
				      &turn_sock->active_sock);
	if (status != PJ_SUCCESS) {
	    pj_turn_sock_destroy(turn_sock);
	    return;
	}

	PJ_LOG(5,(turn_sock->pool->obj_name,
		  "Connecting to %s", 
		  pj_sockaddr_print(&info.server, addrtxt, 
				    sizeof(addrtxt), 3)));

	/* Initiate non-blocking connect */
#if PJ_HAS_TCP
	status=pj_activesock_start_connect(turn_sock->active_sock, 
					   turn_sock->pool,
					   &info.server, 
					   pj_sockaddr_get_len(&info.server));
	if (status == PJ_SUCCESS) {
	    on_connect_complete(turn_sock->active_sock, PJ_SUCCESS);
	} else if (status != PJ_EPENDING) {
	    pj_turn_sock_destroy(turn_sock);
	    return;
	}
#else
	on_connect_complete(turn_sock->active_sock, PJ_SUCCESS);
#endif

	/* Done for now. Subsequent work will be done in 
	 * on_connect_complete() callback.
	 */
    }

    if (new_state >= PJ_TURN_STATE_DESTROYING && turn_sock->sess) {
	pj_time_val delay = {0, 0};

	turn_sock->sess = NULL;
	pj_turn_session_set_user_data(sess, NULL);

	if (turn_sock->timer.id) {
	    pj_timer_heap_cancel(turn_sock->cfg.timer_heap, &turn_sock->timer);
	    turn_sock->timer.id = 0;
	}

	turn_sock->timer.id = TIMER_DESTROY;
	pj_timer_heap_schedule(turn_sock->cfg.timer_heap, &turn_sock->timer, 
			       &delay);
    }
}
Exemplo n.º 5
0
static int state_progression_test(pj_stun_config  *stun_cfg,
                                  pj_bool_t use_ipv6)
{
    struct test_session_cfg test_cfg =
    {
        {   /* Client cfg */
            PJ_TRUE,	    /* DNS SRV */
            0xFFFF	    /* Destroy on state */
        },
        {   /* Server cfg */
            0xFFFFFFFF,	    /* flags */
            PJ_TRUE,	    /* respond to allocate  */
            PJ_TRUE	    /* respond to refresh   */
        }
    };
    struct test_session *sess;
    unsigned i;
    int rc = 0;

    PJ_LOG(3,("", "  state progression tests - (%s)",use_ipv6?"IPv6":"IPv4"));

    set_server_flag(&test_cfg, use_ipv6);
    for (i=0; i<=1; ++i) {
        enum { TIMEOUT = 60 };
        pjlib_state pjlib_state;
        pj_turn_session_info info;
        struct test_result result;
        pj_time_val tstart;

        PJ_LOG(3,("", "   %s DNS SRV resolution",
                  (i==0? "without" : "with")));

        capture_pjlib_state(stun_cfg, &pjlib_state);

        test_cfg.client.enable_dns_srv = i;

        rc = create_test_session(stun_cfg, &test_cfg, &sess);
        if (rc != 0)
            return rc;

        pj_bzero(&info, sizeof(info));

        /* Wait until state is READY */
        pj_gettimeofday(&tstart);
        while (sess->turn_sock) {
            pj_time_val now;

            poll_events(stun_cfg, 10, PJ_FALSE);
            rc = pj_turn_sock_get_info(sess->turn_sock, &info);
            if (rc!=PJ_SUCCESS)
                break;

            if (info.state >= PJ_TURN_STATE_READY)
                break;

            pj_gettimeofday(&now);
            if (now.sec - tstart.sec > TIMEOUT) {
                PJ_LOG(3,("", "    timed-out"));
                break;
            }
        }

        if (info.state != PJ_TURN_STATE_READY) {
            PJ_LOG(3,("", "    error: state is not READY"));
            destroy_session(sess);
            return -130;
        }

        /* Deallocate */
        pj_turn_sock_destroy(sess->turn_sock);

        /* Wait for couple of seconds.
         * We can't poll the session info since the session may have
         * been destroyed
         */
        poll_events(stun_cfg, 2000, PJ_FALSE);
        sess->turn_sock = NULL;
        pj_memcpy(&result, &sess->result, sizeof(result));
        destroy_session(sess);

        /* Check the result */
        if ((result.state_called & (1<<PJ_TURN_STATE_RESOLVING)) == 0) {
            PJ_LOG(3,("", "    error: PJ_TURN_STATE_RESOLVING is not called"));
            return -140;
        }

        if ((result.state_called & (1<<PJ_TURN_STATE_RESOLVED)) == 0) {
            PJ_LOG(3,("", "    error: PJ_TURN_STATE_RESOLVED is not called"));
            return -150;
        }

        if ((result.state_called & (1<<PJ_TURN_STATE_ALLOCATING)) == 0) {
            PJ_LOG(3,("", "    error: PJ_TURN_STATE_ALLOCATING is not called"));
            return -155;
        }

        if ((result.state_called & (1<<PJ_TURN_STATE_READY)) == 0) {
            PJ_LOG(3,("", "    error: PJ_TURN_STATE_READY is not called"));
            return -160;
        }

        if ((result.state_called & (1<<PJ_TURN_STATE_DEALLOCATING)) == 0) {
            PJ_LOG(3,("", "    error: PJ_TURN_STATE_DEALLOCATING is not called"));
            return -170;
        }

        if ((result.state_called & (1<<PJ_TURN_STATE_DEALLOCATED)) == 0) {
            PJ_LOG(3,("", "    error: PJ_TURN_STATE_DEALLOCATED is not called"));
            return -180;
        }

        if ((result.state_called & (1<<PJ_TURN_STATE_DESTROYING)) == 0) {
            PJ_LOG(3,("", "    error: PJ_TURN_STATE_DESTROYING is not called"));
            return -190;
        }

        poll_events(stun_cfg, 500, PJ_FALSE);
        rc = check_pjlib_state(stun_cfg, &pjlib_state);
        if (rc != 0) {
            PJ_LOG(3,("", "    error: memory/timer-heap leak detected"));
            return rc;
        }
    }

    if (use_ipv6)
        rc = state_progression_test(stun_cfg, 0);

    return rc;
}