Example #1
0
/*
 * This is the public API to create, initialize, register, and start the
 * TCP listener.
 */
PJ_DEF(pj_status_t) pjsip_tcp_transport_start3(
					pjsip_endpoint *endpt,
					const pjsip_tcp_transport_cfg *cfg,
					pjsip_tpfactory **p_factory
					)
{
    pj_pool_t *pool;
    pj_sock_t sock = PJ_INVALID_SOCKET;
    struct tcp_listener *listener;
    pj_activesock_cfg asock_cfg;
    pj_activesock_cb listener_cb;
    pj_sockaddr *listener_addr;
    int addr_len;
    pj_bool_t has_listener = PJ_FALSE;
    pj_status_t status;

    /* Sanity check */
    PJ_ASSERT_RETURN(endpt && cfg->async_cnt, PJ_EINVAL);

    /* Verify that address given in a_name (if any) is valid */
    if (cfg->addr_name.host.slen) {
	pj_sockaddr tmp;

	status = pj_sockaddr_init(cfg->af, &tmp, &cfg->addr_name.host, 
				  (pj_uint16_t)cfg->addr_name.port);
	if (status != PJ_SUCCESS || !pj_sockaddr_has_addr(&tmp) ||
	    (cfg->af==pj_AF_INET() && 
	     tmp.ipv4.sin_addr.s_addr==PJ_INADDR_NONE)) 
	{
	    /* Invalid address */
	    return PJ_EINVAL;
	}
    }

    pool = pjsip_endpt_create_pool(endpt, "tcptp", POOL_LIS_INIT, 
				   POOL_LIS_INC);
    PJ_ASSERT_RETURN(pool, PJ_ENOMEM);


    listener = PJ_POOL_ZALLOC_T(pool, struct tcp_listener);
    listener->factory.pool = pool;
    listener->factory.type = cfg->af==pj_AF_INET() ? PJSIP_TRANSPORT_TCP :
						     PJSIP_TRANSPORT_TCP6;
    listener->factory.type_name = (char*)
		pjsip_transport_get_type_name(listener->factory.type);
    listener->factory.flag = 
	pjsip_transport_get_flag_from_type(listener->factory.type);
    listener->qos_type = cfg->qos_type;
    pj_memcpy(&listener->qos_params, &cfg->qos_params,
	      sizeof(cfg->qos_params));
    pj_memcpy(&listener->sockopt_params, &cfg->sockopt_params,
	      sizeof(cfg->sockopt_params));

    pj_ansi_strcpy(listener->factory.obj_name, "tcptp");
    if (listener->factory.type==PJSIP_TRANSPORT_TCP6)
	pj_ansi_strcat(listener->factory.obj_name, "6");

    status = pj_lock_create_recursive_mutex(pool, listener->factory.obj_name,
					    &listener->factory.lock);
    if (status != PJ_SUCCESS)
	goto on_error;

#if !(defined(PJSIP_TCP_TRANSPORT_DONT_CREATE_LISTENER) && \
      PJSIP_TCP_TRANSPORT_DONT_CREATE_LISTENER != 0)

    /* Create socket */
    status = pj_sock_socket(cfg->af, pj_SOCK_STREAM(), 0, &sock);
    if (status != PJ_SUCCESS)
	goto on_error;

    /* Apply QoS, if specified */
    status = pj_sock_apply_qos2(sock, cfg->qos_type, &cfg->qos_params, 
				2, listener->factory.obj_name, 
				"SIP TCP listener socket");

    /* Apply SO_REUSEADDR */
    if (cfg->reuse_addr) {
	int enabled = 1;
	status = pj_sock_setsockopt(sock, pj_SOL_SOCKET(), pj_SO_REUSEADDR(),
				    &enabled, sizeof(enabled));
	if (status != PJ_SUCCESS) {
	    PJ_PERROR(4,(listener->factory.obj_name, status,
		         "Warning: error applying SO_REUSEADDR"));
	}
    }

    /* Apply socket options, if specified */
    if (cfg->sockopt_params.cnt)
	status = pj_sock_setsockopt_params(sock, &cfg->sockopt_params);

#else
    PJ_UNUSED_ARG(addr_len);
#endif

    /* Bind address may be different than factory.local_addr because
     * factory.local_addr will be resolved below.
     */
    pj_sockaddr_cp(&listener->bound_addr, &cfg->bind_addr);

    /* Bind socket */
    listener_addr = &listener->factory.local_addr;
    pj_sockaddr_cp(listener_addr, &cfg->bind_addr);

#if !(defined(PJSIP_TCP_TRANSPORT_DONT_CREATE_LISTENER) && \
      PJSIP_TCP_TRANSPORT_DONT_CREATE_LISTENER != 0)

    status = pj_sock_bind(sock, listener_addr, 
			  pj_sockaddr_get_len(listener_addr));
    if (status != PJ_SUCCESS)
	goto on_error;

    /* Retrieve the bound address */
    addr_len = pj_sockaddr_get_len(listener_addr);
    status = pj_sock_getsockname(sock, listener_addr, &addr_len);
    if (status != PJ_SUCCESS)
	goto on_error;

#endif

    /* If published host/IP is specified, then use that address as the
     * listener advertised address.
     */
    if (cfg->addr_name.host.slen) {
	/* Copy the address */
	listener->factory.addr_name = cfg->addr_name;
	pj_strdup(listener->factory.pool, &listener->factory.addr_name.host, 
		  &cfg->addr_name.host);
	listener->factory.addr_name.port = cfg->addr_name.port;

    } else {
	/* No published address is given, use the bound address */

	/* If the address returns 0.0.0.0, use the default
	 * interface address as the transport's address.
	 */
	if (!pj_sockaddr_has_addr(listener_addr)) {
	    pj_sockaddr hostip;

	    status = pj_gethostip(listener->bound_addr.addr.sa_family,
	                          &hostip);
	    if (status != PJ_SUCCESS)
		goto on_error;

	    pj_sockaddr_copy_addr(listener_addr, &hostip);
	}

	/* Save the address name */
	sockaddr_to_host_port(listener->factory.pool, 
			      &listener->factory.addr_name, 
			      listener_addr);
    }

    /* If port is zero, get the bound port */
    if (listener->factory.addr_name.port == 0) {
	listener->factory.addr_name.port = pj_sockaddr_get_port(listener_addr);
    }

    pj_ansi_snprintf(listener->factory.obj_name, 
		     sizeof(listener->factory.obj_name),
		     "tcptp:%d",  listener->factory.addr_name.port);


#if !(defined(PJSIP_TCP_TRANSPORT_DONT_CREATE_LISTENER) && \
      PJSIP_TCP_TRANSPORT_DONT_CREATE_LISTENER != 0)

    /* Start listening to the address */
    status = pj_sock_listen(sock, PJSIP_TCP_TRANSPORT_BACKLOG);
    if (status != PJ_SUCCESS)
	goto on_error;


    /* Create active socket */
    pj_activesock_cfg_default(&asock_cfg);
    if (cfg->async_cnt > MAX_ASYNC_CNT) 
	asock_cfg.async_cnt = MAX_ASYNC_CNT;
    else
	asock_cfg.async_cnt = cfg->async_cnt;

#endif

    /* Create group lock */
    status = pj_grp_lock_create(pool, NULL, &listener->grp_lock);
    if (status != PJ_SUCCESS)
	return status;

    pj_grp_lock_add_ref(listener->grp_lock);
    pj_grp_lock_add_handler(listener->grp_lock, pool, listener,
			    &lis_on_destroy);

    asock_cfg.grp_lock = listener->grp_lock;

    pj_bzero(&listener_cb, sizeof(listener_cb));
    listener_cb.on_accept_complete = &on_accept_complete;

#if !(defined(PJSIP_TCP_TRANSPORT_DONT_CREATE_LISTENER) && \
      PJSIP_TCP_TRANSPORT_DONT_CREATE_LISTENER != 0)

    status = pj_activesock_create(pool, sock, pj_SOCK_STREAM(), &asock_cfg,
				  pjsip_endpt_get_ioqueue(endpt), 
				  &listener_cb, listener,
				  &listener->asock);

#endif

    /* Register to transport manager */
    listener->endpt = endpt;
    listener->tpmgr = pjsip_endpt_get_tpmgr(endpt);
    listener->factory.create_transport = lis_create_transport;
    listener->factory.destroy = lis_destroy;
    listener->is_registered = PJ_TRUE;
    status = pjsip_tpmgr_register_tpfactory(listener->tpmgr,
					    &listener->factory);
    if (status != PJ_SUCCESS) {
	listener->is_registered = PJ_FALSE;
	goto on_error;
    }

#if !(defined(PJSIP_TCP_TRANSPORT_DONT_CREATE_LISTENER) && \
      PJSIP_TCP_TRANSPORT_DONT_CREATE_LISTENER != 0)

    /* Start pending accept() operations */
    status = pj_activesock_start_accept(listener->asock, pool);
    if (status != PJ_SUCCESS)
	goto on_error;
	
    has_listener = PJ_TRUE;

#endif

    if (has_listener) {
        PJ_LOG(4,(listener->factory.obj_name, 
	         "SIP TCP listener ready for incoming connections at %.*s:%d",
	         (int)listener->factory.addr_name.host.slen,
	         listener->factory.addr_name.host.ptr,
	         listener->factory.addr_name.port));
    } else {
	PJ_LOG(4,(listener->factory.obj_name, "SIP TCP is ready "
		  "(client only)"));    
    }

    /* Return the pointer to user */
    if (p_factory) *p_factory = &listener->factory;

    return PJ_SUCCESS;

on_error:
    if (listener->asock==NULL && sock!=PJ_INVALID_SOCKET)
	pj_sock_close(sock);
    lis_destroy(&listener->factory);
    return status;
}
Example #2
0
/*
 * Common function to create TCP transport, called when pending accept() and
 * pending connect() complete.
 */
static pj_status_t tcp_create( struct tcp_listener *listener,
			       pj_pool_t *pool,
			       pj_sock_t sock, pj_bool_t is_server,
			       const pj_sockaddr *local,
			       const pj_sockaddr *remote,
			       struct tcp_transport **p_tcp)
{
    struct tcp_transport *tcp;
    pj_ioqueue_t *ioqueue;
    pj_activesock_cfg asock_cfg;
    pj_activesock_cb tcp_callback;
    const pj_str_t ka_pkt = PJSIP_TCP_KEEP_ALIVE_DATA;
    char print_addr[PJ_INET6_ADDRSTRLEN+10];
    pj_status_t status;
    

    PJ_ASSERT_RETURN(sock != PJ_INVALID_SOCKET, PJ_EINVAL);


    if (pool == NULL) {
	pool = pjsip_endpt_create_pool(listener->endpt, "tcp",
				       POOL_TP_INIT, POOL_TP_INC);
	PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
    }    

    /*
     * Create and initialize basic transport structure.
     */
    tcp = PJ_POOL_ZALLOC_T(pool, struct tcp_transport);
    tcp->is_server = is_server;
    tcp->sock = sock;
    /*tcp->listener = listener;*/
    pj_list_init(&tcp->delayed_list);
    tcp->base.pool = pool;

    pj_ansi_snprintf(tcp->base.obj_name, PJ_MAX_OBJ_NAME, 
		     (is_server ? "tcps%p" :"tcpc%p"), tcp);

    status = pj_atomic_create(pool, 0, &tcp->base.ref_cnt);
    if (status != PJ_SUCCESS) {
	goto on_error;
    }

    status = pj_lock_create_recursive_mutex(pool, "tcp", &tcp->base.lock);
    if (status != PJ_SUCCESS) {
	goto on_error;
    }

    tcp->base.key.type = listener->factory.type;
    pj_sockaddr_cp(&tcp->base.key.rem_addr, remote);
    tcp->base.type_name = (char*)pjsip_transport_get_type_name(
				(pjsip_transport_type_e)tcp->base.key.type);
    tcp->base.flag = pjsip_transport_get_flag_from_type(
				(pjsip_transport_type_e)tcp->base.key.type);

    tcp->base.info = (char*) pj_pool_alloc(pool, 64);
    pj_ansi_snprintf(tcp->base.info, 64, "%s to %s",
                     tcp->base.type_name,
                     pj_sockaddr_print(remote, print_addr,
                                       sizeof(print_addr), 3));

    tcp->base.addr_len = pj_sockaddr_get_len(remote);
    pj_sockaddr_cp(&tcp->base.local_addr, local);
    sockaddr_to_host_port(pool, &tcp->base.local_name, local);
    sockaddr_to_host_port(pool, &tcp->base.remote_name, remote);
    tcp->base.dir = is_server? PJSIP_TP_DIR_INCOMING : PJSIP_TP_DIR_OUTGOING;

    tcp->base.endpt = listener->endpt;
    tcp->base.tpmgr = listener->tpmgr;
    tcp->base.send_msg = &tcp_send_msg;
    tcp->base.do_shutdown = &tcp_shutdown;
    tcp->base.destroy = &tcp_destroy_transport;

    /* Create group lock */
    status = pj_grp_lock_create(pool, NULL, &tcp->grp_lock);
    if (status != PJ_SUCCESS)
	goto on_error;

    pj_grp_lock_add_ref(tcp->grp_lock);
    pj_grp_lock_add_handler(tcp->grp_lock, pool, tcp, &tcp_on_destroy);

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

    pj_bzero(&tcp_callback, sizeof(tcp_callback));
    tcp_callback.on_data_read = &on_data_read;
    tcp_callback.on_data_sent = &on_data_sent;
    tcp_callback.on_connect_complete = &on_connect_complete;

    ioqueue = pjsip_endpt_get_ioqueue(listener->endpt);
    status = pj_activesock_create(pool, sock, pj_SOCK_STREAM(), &asock_cfg,
				  ioqueue, &tcp_callback, tcp, &tcp->asock);
    if (status != PJ_SUCCESS) {
	goto on_error;
    }

    /* Register transport to transport manager */
    status = pjsip_transport_register(listener->tpmgr, &tcp->base);
    if (status != PJ_SUCCESS) {
	goto on_error;
    }

    tcp->is_registered = PJ_TRUE;

    /* Initialize keep-alive timer */
    tcp->ka_timer.user_data = (void*)tcp;
    tcp->ka_timer.cb = &tcp_keep_alive_timer;
    pj_ioqueue_op_key_init(&tcp->ka_op_key.key, sizeof(pj_ioqueue_op_key_t));
    pj_strdup(tcp->base.pool, &tcp->ka_pkt, &ka_pkt);

    /* Done setting up basic transport. */
    *p_tcp = tcp;

    PJ_LOG(4,(tcp->base.obj_name, "TCP %s transport created",
	      (tcp->is_server ? "server" : "client")));

    return PJ_SUCCESS;

on_error:
    tcp_destroy(&tcp->base, status);
    return status;
}
Example #3
0
PJ_DEF(pj_status_t) pjmedia_vid_port_create( pj_pool_t *pool,
					     const pjmedia_vid_port_param *prm,
					     pjmedia_vid_port **p_vid_port)
{
    pjmedia_vid_port *vp;
    const pjmedia_video_format_detail *vfd;
    char dev_name[64];
    char fmt_name[5];
    pjmedia_vid_dev_cb vid_cb;
    pj_bool_t need_frame_buf = PJ_FALSE;
    pj_status_t status;
    unsigned ptime_usec;
    pjmedia_vid_dev_param vparam;
    pjmedia_vid_dev_info di;
    unsigned i;

    PJ_ASSERT_RETURN(pool && prm && p_vid_port, PJ_EINVAL);
    PJ_ASSERT_RETURN(prm->vidparam.fmt.type == PJMEDIA_TYPE_VIDEO &&
                     prm->vidparam.dir != PJMEDIA_DIR_NONE &&
                     prm->vidparam.dir != PJMEDIA_DIR_CAPTURE_RENDER,
		     PJ_EINVAL);

    /* Retrieve the video format detail */
    vfd = pjmedia_format_get_video_format_detail(&prm->vidparam.fmt, PJ_TRUE);
    if (!vfd)
	return PJ_EINVAL;

    PJ_ASSERT_RETURN(vfd->fps.num, PJ_EINVAL);

    /* Allocate videoport */
    vp = PJ_POOL_ZALLOC_T(pool, pjmedia_vid_port);
    vp->pool = pj_pool_create(pool->factory, "video port", 500, 500, NULL);
    vp->role = prm->active ? ROLE_ACTIVE : ROLE_PASSIVE;
    vp->dir = prm->vidparam.dir;
//    vp->cap_size = vfd->size;

    vparam = prm->vidparam;
    dev_name[0] = '\0';

    /* Get device info */
    if (vp->dir & PJMEDIA_DIR_CAPTURE)
        status = pjmedia_vid_dev_get_info(prm->vidparam.cap_id, &di);
    else
        status = pjmedia_vid_dev_get_info(prm->vidparam.rend_id, &di);
    if (status != PJ_SUCCESS)
        return status;

    pj_ansi_snprintf(dev_name, sizeof(dev_name), "%s [%s]",
                     di.name, di.driver);

    for (i = 0; i < di.fmt_cnt; ++i) {
        if (prm->vidparam.fmt.id == di.fmt[i].id)
            break;
    }

    if (i == di.fmt_cnt) {
        /* The device has no no matching format. Pick one from
         * the supported formats, and later use converter to
         * convert it to the required format.
         */
        pj_assert(di.fmt_cnt != 0);
        vparam.fmt.id = di.fmt[0].id;
    }

    pj_strdup2_with_null(pool, &vp->dev_name, di.name);
    vp->stream_role = di.has_callback ? ROLE_ACTIVE : ROLE_PASSIVE;

    pjmedia_fourcc_name(vparam.fmt.id, fmt_name);

    PJ_LOG(4,(THIS_FILE,
	      "Opening device %s for %s: format=%s, size=%dx%d @%d:%d fps",
	      dev_name,
	      vid_dir_name(prm->vidparam.dir), fmt_name,
	      vfd->size.w, vfd->size.h,
	      vfd->fps.num, vfd->fps.denum));

    ptime_usec = PJMEDIA_PTIME(&vfd->fps);
    pjmedia_clock_src_init(&vp->clocksrc, PJMEDIA_TYPE_VIDEO,
                           prm->vidparam.clock_rate, ptime_usec);
    vp->sync_clocksrc.max_sync_ticks = 
        PJMEDIA_CLOCK_SYNC_MAX_RESYNC_DURATION *
        1000 / vp->clocksrc.ptime_usec;

    /* Create the video stream */
    pj_bzero(&vid_cb, sizeof(vid_cb));
    vid_cb.capture_cb = &vidstream_cap_cb;
    vid_cb.render_cb = &vidstream_render_cb;

    status = pjmedia_vid_dev_stream_create(&vparam, &vid_cb, vp,
				           &vp->strm);
    if (status != PJ_SUCCESS)
	goto on_error;

    PJ_LOG(4,(THIS_FILE,
	      "Device %s opened: format=%s, size=%dx%d @%d:%d fps",
	      dev_name, fmt_name,
	      vparam.fmt.det.vid.size.w, vparam.fmt.det.vid.size.h,
	      vparam.fmt.det.vid.fps.num, vparam.fmt.det.vid.fps.denum));

    /* Subscribe to device's events */
    pjmedia_event_subscribe(NULL, &vidstream_event_cb,
                            vp, vp->strm);

    if (vp->dir & PJMEDIA_DIR_CAPTURE) {
	pjmedia_format_copy(&vp->conv.conv_param.src, &vparam.fmt);
	pjmedia_format_copy(&vp->conv.conv_param.dst, &prm->vidparam.fmt);
    } else {
	pjmedia_format_copy(&vp->conv.conv_param.src, &prm->vidparam.fmt);
	pjmedia_format_copy(&vp->conv.conv_param.dst, &vparam.fmt);
    }

    status = create_converter(vp);
    if (status != PJ_SUCCESS)
	goto on_error;

    if (vp->role==ROLE_ACTIVE &&
        ((vp->dir & PJMEDIA_DIR_ENCODING) || vp->stream_role==ROLE_PASSIVE))
    {
        pjmedia_clock_param param;

	/* Active role is wanted, but our device is passive, so create
	 * master clocks to run the media flow. For encoding direction,
         * we also want to create our own clock since the device's clock
         * may run at a different rate.
	 */
	need_frame_buf = PJ_TRUE;
            
        param.usec_interval = PJMEDIA_PTIME(&vfd->fps);
        param.clock_rate = prm->vidparam.clock_rate;
        status = pjmedia_clock_create2(pool, &param,
                                       PJMEDIA_CLOCK_NO_HIGHEST_PRIO,
                                       (vp->dir & PJMEDIA_DIR_ENCODING) ?
                                       &enc_clock_cb: &dec_clock_cb,
                                       vp, &vp->clock);
        if (status != PJ_SUCCESS)
            goto on_error;

    } else if (vp->role==ROLE_PASSIVE) {
	vid_pasv_port *pp;

	/* Always need to create media port for passive role */
	vp->pasv_port = pp = PJ_POOL_ZALLOC_T(pool, vid_pasv_port);
	pp->vp = vp;
	pp->base.get_frame = &vid_pasv_port_get_frame;
	pp->base.put_frame = &vid_pasv_port_put_frame;
	pjmedia_port_info_init2(&pp->base.info, &vp->dev_name,
	                        PJMEDIA_SIG_VID_PORT,
			        prm->vidparam.dir, &prm->vidparam.fmt);

	if (vp->stream_role == ROLE_ACTIVE) {
	    need_frame_buf = PJ_TRUE;
	}
    }

    if (need_frame_buf) {
	const pjmedia_video_format_info *vfi;
	pjmedia_video_apply_fmt_param vafp;

	vfi = pjmedia_get_video_format_info(NULL, vparam.fmt.id);
	if (!vfi) {
	    status = PJ_ENOTFOUND;
	    goto on_error;
	}

	pj_bzero(&vafp, sizeof(vafp));
	vafp.size = vparam.fmt.det.vid.size;
	status = vfi->apply_fmt(vfi, &vafp);
	if (status != PJ_SUCCESS)
	    goto on_error;

        vp->frm_buf = PJ_POOL_ZALLOC_T(pool, pjmedia_frame);
        vp->frm_buf_size = vafp.framebytes;
        vp->frm_buf->buf = pj_pool_alloc(pool, vafp.framebytes);
        vp->frm_buf->size = vp->frm_buf_size;
        vp->frm_buf->type = PJMEDIA_FRAME_TYPE_NONE;

        status = pj_mutex_create_simple(pool, vp->dev_name.ptr,
                                        &vp->frm_mutex);
        if (status != PJ_SUCCESS)
            goto on_error;
    }

    *p_vid_port = vp;

    return PJ_SUCCESS;

on_error:
    pjmedia_vid_port_destroy(vp);
    return status;
}
Example #4
0
/* 
 * platform_strerror()
 *
 * Platform specific error message. This file is called by pj_strerror() 
 * in errno.c 
 */
int platform_strerror( pj_os_err_type os_errcode, 
                       char *buf, pj_size_t bufsize)
{
    int len = 0;
    PJ_DECL_UNICODE_TEMP_BUF(wbuf,128);

    pj_assert(buf != NULL);
    pj_assert(bufsize >= 0);

    /*
     * MUST NOT check stack here.
     * This function might be called from PJ_CHECK_STACK() itself!
       //PJ_CHECK_STACK();
     */

    if (!len) {
#if defined(PJ_HAS_ERROR_STRING) && (PJ_HAS_ERROR_STRING!=0)
	int i;
        for (i = 0; gaErrorList[i].msg; ++i) {
            if (gaErrorList[i].code == os_errcode) {
                len = strlen(gaErrorList[i].msg);
		if ((pj_size_t)len >= bufsize) {
		    len = bufsize-1;
		}
		pj_memcpy(buf, gaErrorList[i].msg, len);
		buf[len] = '\0';
                break;
            }
        }
#endif	/* PJ_HAS_ERROR_STRING */

    }


    if (!len) {
#if PJ_NATIVE_STRING_IS_UNICODE
	len = FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM 
			     | FORMAT_MESSAGE_IGNORE_INSERTS,
			     NULL,
			     os_errcode,
			     MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 
			     wbuf,
			     sizeof(wbuf),
			     NULL);
	if (len) {
	    pj_unicode_to_ansi(wbuf, len, buf, bufsize);
	}
#else
	len = FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM 
			     | FORMAT_MESSAGE_IGNORE_INSERTS,
			     NULL,
			     os_errcode,
			     MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 
			     buf,
			     bufsize,
			     NULL);
	buf[bufsize-1] = '\0';
#endif

	if (len) {
	    /* Remove trailing newlines. */
	    while (len && (buf[len-1] == '\n' || buf[len-1] == '\r')) {
		buf[len-1] = '\0';
		--len;
	    }
	}
    }

    if (!len) {
	len = pj_ansi_snprintf( buf, bufsize, "Win32 error code %u", 
				(unsigned)os_errcode);
	if (len < 0 || len >= (int)bufsize)
	    len = bufsize-1;
	buf[len] = '\0';
    }

    return len;
}
Example #5
0
int regc_test(void)
{
    struct test_rec {
	unsigned		 check_contact;
	unsigned		 add_xuid_param;

	const char		*title;
	char			*alt_registrar;
	unsigned		 contact_cnt;
	char			*contacts[4];
	unsigned		 expires;
	struct registrar_cfg	 server_cfg;
	struct client		 client_cfg;
    } test_rec[] = 
    {
	/* immediate error */
	{
	    OFF,			    /* check_contact	*/
	    OFF,			    /* add_xuid_param	*/
	    "immediate error",		    /* title		*/
	    "sip:unresolved-host-xyy",	    /* alt_registrar	*/
	    1,				    /* contact cnt	*/
	    { "sip:[email protected]:5060" },  /* contacts[]	*/
	    600,			    /* expires		*/

	    /* registrar config: */
	    /* respond	code	auth	  contact   exp_prm expires more_contacts */
	    { PJ_FALSE,	200,	PJ_FALSE, NONE,	    0,	    0,	    {NULL, 0}},

	    /* client expected results: */
	    /* error	code	have_reg    expiration	contact_cnt auth?*/
	    { PJ_FALSE,	503,	PJ_FALSE,   -1,		0,	    PJ_FALSE}
	},

	/* timeout test */
	{
	    OFF,			    /* check_contact	*/
	    OFF,			    /* add_xuid_param	*/
	    "timeout test (takes ~32 secs)",/* title		*/
	    NULL,			    /* alt_registrar	*/
	    1,				    /* contact cnt	*/
	    { "sip:[email protected]:5060" },  /* contacts[]	*/
	    600,			    /* expires		*/

	    /* registrar config: */
	    /* respond	code	auth	  contact   exp_prm expires more_contacts */
	    { PJ_FALSE,	200,	PJ_FALSE, NONE,	    0,	    0,	    {NULL, 0}},

	    /* client expected results: */
	    /* error	code	have_reg    expiration	contact_cnt auth? */
	    { PJ_FALSE,	408,	PJ_FALSE,   -1,		0,	    PJ_FALSE}
	},

	/* Basic successful registration scenario:
	 * a good registrar returns the Contact header as is and
	 * add expires parameter. In this test no additional bindings
	 * are returned.
	 */
	{
	    ON_OFF,			    /* check_contact	*/
	    ON_OFF,			    /* add_xuid_param	*/
	    "basic",			    /* title		*/
	    NULL,			    /* alt_registrar	*/
	    1,				    /* contact cnt	*/
	    { "<sip:[email protected]:5060;transport=udp;x-param=1234>" },  /* contacts[]	*/
	    600,			    /* expires		*/

	    /* registrar config: */
	    /* respond	code	auth	  contact  exp_prm expires more_contacts */
	    { PJ_TRUE,	200,	PJ_FALSE, EXACT,    75,	    65,	    {NULL, 0}},

	    /* client expected results: */
	    /* error	code	have_reg    expiration	contact_cnt auth?*/
	    { PJ_FALSE,	200,	PJ_TRUE,   75,		1,	    PJ_FALSE}
	},

	/* Basic successful registration scenario with authentication
	 */
	{
	    ON_OFF,			    /* check_contact	*/
	    ON_OFF,			    /* add_xuid_param	*/
	    "authentication",		    /* title		*/
	    NULL,			    /* alt_registrar	*/
	    1,				    /* contact cnt	*/
	    { "<sip:[email protected]:5060;transport=udp;x-param=1234>" },  /* contacts[]	*/
	    600,			    /* expires		*/

	    /* registrar config: */
	    /* respond	code	auth	  contact  exp_prm expires more_contacts */
	    { PJ_TRUE,	200,	PJ_TRUE,  EXACT,    75,	    65,	    {NULL, 0}},

	    /* client expected results: */
	    /* error	code	have_reg    expiration	contact_cnt auth?*/
	    { PJ_FALSE,	200,	PJ_TRUE,   75,		1,	    PJ_TRUE}
	},

	/* a good registrar returns the Contact header as is and
	 * add expires parameter. Also it adds bindings from other
	 * clients in this test.
	 */
	{
	    ON_OFF,			    /* check_contact	*/
	    ON,				    /* add_xuid_param	*/
	    "more bindings in response",    /* title		*/
	    NULL,			    /* alt_registrar	*/
	    1,				    /* contact cnt	*/
	    { "<sip:[email protected]:5060;transport=udp>" },  /* contacts[]	*/
	    600,			    /* expires		*/

	    /* registrar config: */
	    /* respond	code	auth	  contact  exp_prm expires more_contacts */
	    { PJ_TRUE,	200,	PJ_FALSE, EXACT,    75,	    65,	    {"<sip:a@a>;expires=70", 0}},

	    /* client expected results: */
	    /* error	code	have_reg    expiration	contact_cnt auth?*/
	    { PJ_FALSE,	200,	PJ_TRUE,   75,		2,	    PJ_FALSE}
	},


	/* a bad registrar returns modified Contact header, but it
	 * still returns all parameters intact. In this case
	 * the expiration is taken from the expires param because
	 * of matching xuid param or because the number of
	 * Contact header matches.
	 */
	{
	    ON_OFF,			    /* check_contact	*/
	    ON_OFF,			    /* add_xuid_param	*/
	    "registrar modifies Contact header",    /* title		*/
	    NULL,			    /* alt_registrar	*/
	    1,				    /* contact cnt	*/
	    { "<sip:[email protected]:5060>" },  /* contacts[]	*/
	    600,			    /* expires		*/

	    /* registrar config: */
	    /* respond	code	auth	  contact   exp_prm expires more_contacts */
	    { PJ_TRUE,	200,	PJ_FALSE, MODIFIED, 75,	    65,	    {NULL, 0}},

	    /* client expected results: */
	    /* error	code	have_reg    expiration	contact_cnt auth?*/
	    { PJ_FALSE,	200,	PJ_TRUE,   75,		1,	    PJ_FALSE}
	},


	/* a bad registrar returns modified Contact header, but it
	 * still returns all parameters intact. In addition it returns
	 * bindings from other clients.
	 *
	 * In this case the expiration is taken from the expires param 
	 * because add_xuid_param is enabled.
	 */
	{
	    ON_OFF,			    /* check_contact	*/
	    ON,				    /* add_xuid_param	*/
	    "registrar modifies Contact header and add bindings",    /* title		*/
	    NULL,			    /* alt_registrar	*/
	    1,				    /* contact cnt	*/
	    { "<sip:[email protected]:5060>" },  /* contacts[]	*/
	    600,			    /* expires		*/

	    /* registrar config: */
	    /* respond	code	auth	  contact   exp_prm expires more_contacts */
	    { PJ_TRUE,	200,	PJ_FALSE, MODIFIED, 75,	    65,	    {"<sip:a@a>;expires=70", 0}},

	    /* client expected results: */
	    /* error	code	have_reg    expiration	contact_cnt auth?*/
	    { PJ_FALSE,	200,	PJ_TRUE,   75,		2,	    PJ_FALSE}
	},


	/* a bad registrar returns completely different Contact and
	 * all parameters are gone. In this case the expiration is 
	 * also taken from the expires param since the number of 
	 * header matches.
	 */
	{
	    ON_OFF,			    /* check_contact	*/
	    ON_OFF,			    /* add_xuid_param	*/
	    "registrar replaces Contact header",    /* title		*/
	    NULL,			    /* alt_registrar	*/
	    1,				    /* contact cnt	*/
	    { "<sip:[email protected]:5060>" },  /* contacts[]	*/
	    600,			    /* expires		*/

	    /* registrar config: */
	    /* respond	code	auth	  contact   exp_prm expires more_contacts */
	    { PJ_TRUE,	202,	PJ_FALSE, NONE,	    0,	    65,	    {"<sip:a@A>;expires=75", 0}},

	    /* client expected results: */
	    /* error	code	have_reg    expiration	contact_cnt auth?*/
	    { PJ_FALSE,	202,	PJ_TRUE,   75,		1,	    PJ_FALSE}
	},


	/* a bad registrar returns completely different Contact (and
	 * all parameters are gone) and it also includes bindings from
	 * other clients.
	 * In this case the expiration is taken from the Expires header.
	 */
	{
	    ON_OFF,			    /* check_contact	*/
	    ON_OFF,			    /* add_xuid_param	*/
	    " as above with additional bindings",    /* title		*/
	    NULL,			    /* alt_registrar	*/
	    1,				    /* contact cnt	*/
	    { "<sip:[email protected]:5060>" },  /* contacts[]	*/
	    600,			    /* expires		*/

	    /* registrar config: */
	    /* respond	code	auth	  contact   exp_prm expires more_contacts */
	    { PJ_TRUE,	200,	PJ_FALSE, NONE,	    0,	    65,	    {"<sip:a@A>;expires=75, <sip:b@B;expires=70>", 0}},

	    /* client expected results: */
	    /* error	code	have_reg    expiration	contact_cnt auth?*/
	    { PJ_FALSE,	200,	PJ_TRUE,   65,		2,	    PJ_FALSE}
	},

	/* the registrar doesn't return any bindings, but for some
	 * reason it includes an Expires header.
	 * In this case the expiration is taken from the Expires header.
	 */
	{
	    ON_OFF,			    /* check_contact	*/
	    ON_OFF,			    /* add_xuid_param	*/
	    "no Contact but with Expires",  /* title		*/
	    NULL,			    /* alt_registrar	*/
	    1,				    /* contact cnt	*/
	    { "<sip:[email protected]:5060>" },  /* contacts[]	*/
	    600,			    /* expires		*/

	    /* registrar config: */
	    /* respond	code	auth	  contact   exp_prm expires more_contacts */
	    { PJ_TRUE,	200,	PJ_FALSE, NONE,	    0,	    65,	    {NULL, 0}},

	    /* client expected results: */
	    /* error	code	have_reg    expiration	contact_cnt auth?*/
	    { PJ_FALSE,	200,	PJ_TRUE,   65,		0,	    PJ_FALSE}
	},

	/* Neither Contact header nor Expires header are present.
	 * In this case the expiration is taken from the request.
	 */
	{
	    ON_OFF,			    /* check_contact	*/
	    ON_OFF,			    /* add_xuid_param	*/
	    "no Contact and no Expires",    /* title		*/
	    NULL,			    /* alt_registrar	*/
	    1,				    /* contact cnt	*/
	    { "<sip:[email protected]:5060>" },/* contacts[]	*/
	    600,			    /* expires		*/

	    /* registrar config: */
	    /* respond	code	auth	  contact   exp_prm expires more_contacts */
	    { PJ_TRUE,	200,	PJ_FALSE, NONE,	    0,	    0,	    {NULL, 0}},

	    /* client expected results: */
	    /* error	code	have_reg    expiration	contact_cnt auth?*/
	    { PJ_FALSE,	200,	PJ_TRUE,   600,		0,	    PJ_FALSE}
	},
    };

    unsigned i;
    pj_sockaddr_in addr;
    pjsip_transport *udp = NULL;
    pj_uint16_t port; 
    char registrar_uri_buf[80];
    pj_str_t registrar_uri;
    int rc = 0;

    pj_sockaddr_in_init(&addr, 0, 0);

    /* Acquire existing transport, if any */
    rc = pjsip_endpt_acquire_transport(endpt, PJSIP_TRANSPORT_UDP, &addr, sizeof(addr), NULL, &udp);
    if (rc == PJ_SUCCESS) {
	port = pj_sockaddr_get_port(&udp->local_addr);
	pjsip_transport_dec_ref(udp);
	udp = NULL;
    } else {
	rc = pjsip_udp_transport_start(endpt, NULL, NULL, 1, &udp);
	if (rc != PJ_SUCCESS) {
	    app_perror("   error creating UDP transport", rc);
	    rc = -2;
	    goto on_return;
	}

	port = pj_sockaddr_get_port(&udp->local_addr);
    }

    /* Register registrar module */
    rc = pjsip_endpt_register_module(endpt, &registrar.mod);
    if (rc != PJ_SUCCESS) {
	app_perror("   error registering module", rc);
	rc = -3;
	goto on_return;
    }

    /* Register send module */
    rc = pjsip_endpt_register_module(endpt, &send_mod.mod);
    if (rc != PJ_SUCCESS) {
	app_perror("   error registering module", rc);
	rc = -3;
	goto on_return;
    }

    pj_ansi_snprintf(registrar_uri_buf, sizeof(registrar_uri_buf),
		    "sip:127.0.0.1:%d", (int)port);
    registrar_uri = pj_str(registrar_uri_buf);

    for (i=0; i<PJ_ARRAY_SIZE(test_rec); ++i) {
	struct test_rec *t = &test_rec[i];
	unsigned j, x;
	pj_str_t reg_uri;
	pj_str_t contacts[8];

	/* Fill in the registrar address if it's not specified */
	if (t->alt_registrar == NULL) {
	    reg_uri = registrar_uri;
	} else {
	    reg_uri = pj_str(t->alt_registrar);
	}

	/* Build contact pj_str_t's */
	for (j=0; j<t->contact_cnt; ++j) {
	    contacts[j] = pj_str(t->contacts[j]);
	}

	/* Normalize more_contacts field */
	if (t->server_cfg.more_contacts.ptr)
	    t->server_cfg.more_contacts.slen = strlen(t->server_cfg.more_contacts.ptr);

	/* Do tests with three combinations:
	 *  - check_contact on/off
	 *  - add_xuid_param on/off
	 *  - destroy_on_callback on/off
	 */
	for (x=1; x<=2; ++x) {
	    unsigned y;

	    if ((t->check_contact & x) == 0)
		continue;

	    pjsip_cfg()->regc.check_contact = (x-1);

	    for (y=1; y<=2; ++y) {
		unsigned z;

		if ((t->add_xuid_param & y) == 0)
		    continue;

		pjsip_cfg()->regc.add_xuid_param = (y-1);

		for (z=0; z<=1; ++z) {
		    char new_title[200];

		    t->client_cfg.destroy_on_cb = z;

		    sprintf(new_title, "%s [check=%d, xuid=%d, destroy=%d]", 
			    t->title, pjsip_cfg()->regc.check_contact,
			    pjsip_cfg()->regc.add_xuid_param, z);
		    rc = do_test(new_title, &t->server_cfg, &t->client_cfg, 
				 &reg_uri, t->contact_cnt, contacts, 
				 t->expires, PJ_FALSE, NULL);
		    if (rc != 0)
			goto on_return;
		}

	    }
	}

	/* Sleep between test groups to avoid using up too many
	 * active transactions.
	 */
	pj_thread_sleep(1000);
    }

    /* keep-alive test */
    rc = keep_alive_test(&registrar_uri);
    if (rc != 0)
	goto on_return;

    /* Send error on refresh without destroy on callback */
    rc = refresh_error(&registrar_uri, PJ_FALSE);
    if (rc != 0)
	goto on_return;

    /* Send error on refresh, destroy on callback */
    rc = refresh_error(&registrar_uri, PJ_TRUE);
    if (rc != 0)
	goto on_return;

    /* Updating contact */
    rc = update_test(&registrar_uri);
    if (rc != 0)
	goto on_return;

    /* Send error during auth, don't destroy on callback */
    rc = auth_send_error(&registrar_uri, PJ_FALSE);
    if (rc != 0)
	goto on_return;

    /* Send error during auth, destroy on callback */
    rc = auth_send_error(&registrar_uri, PJ_FALSE);
    if (rc != 0)
	goto on_return;

on_return:
    if (registrar.mod.id != -1) {
	pjsip_endpt_unregister_module(endpt, &registrar.mod);
    }
    if (send_mod.mod.id != -1) {
	pjsip_endpt_unregister_module(endpt, &send_mod.mod);
    }
    if (udp) {
	pjsip_transport_dec_ref(udp);
    }
    return rc;
}
Example #6
0
int jbuf_main(void)
{
    FILE *input;
    pj_bool_t data_eof = PJ_FALSE;
    int old_log_level;
    int rc = 0;
    const char* input_filename = "Jbtest.dat";
    const char* input_search_path[] = { 
	"../build",
	"pjmedia/build",
	"build"
    };

    /* Try to open test data file in the working directory */
    input = fopen(input_filename, "rt");

    /* If that fails, try to open test data file in specified search paths */
    if (input == NULL) {
	char input_path[PJ_MAXPATH];
	int i;

	for (i = 0; !input && i < PJ_ARRAY_SIZE(input_search_path); ++i) {
	    pj_ansi_snprintf(input_path, PJ_MAXPATH, "%s/%s",
			     input_search_path[i],
			     input_filename);
	    input = fopen(input_path, "rt");
	}
    }
    
    /* Failed to open test data file. */
    if (input == NULL) {
	printf("Failed to open test data file, Jbtest.dat\n");
	return -1;
    }

    old_log_level = pj_log_get_level();
    pj_log_set_level(5);

    while (rc == 0 && !data_eof) {
	pj_str_t jb_name = {"JBTEST", 6};
	pjmedia_jbuf *jb;
	pj_pool_t *pool;
	pjmedia_jb_state state;
	pj_uint16_t last_seq = 0;
	pj_uint16_t seq = 1;
	char line[1024], *p = NULL;

	test_param_t param;
	test_cond_t cond;

	param.adaptive = PJ_TRUE;
	param.init_prefetch = JB_INIT_PREFETCH;
	param.min_prefetch = JB_MIN_PREFETCH;
	param.max_prefetch = JB_MAX_PREFETCH;

	cond.burst = -1;
	cond.delay = -1;
	cond.delay_min = -1;
	cond.discard = -1;
	cond.empty = -1;
	cond.lost = -1;

	printf("\n\n");

	/* Parse test session title, param, and conditions */
	do {
	    p = fgets(line, sizeof(line), input);
	} while (p && parse_test_headers(line, &param, &cond));

	/* EOF test data */
	if (p == NULL)
	    break;

	//printf("======================================================\n");

	/* Initialize test session */
	pool = pj_pool_create(mem, "JBPOOL", 256*16, 256*16, NULL);
	pjmedia_jbuf_create(pool, &jb_name, 1, JB_PTIME, JB_BUF_SIZE, &jb);
	pjmedia_jbuf_reset(jb);

	if (param.adaptive) {
	    pjmedia_jbuf_set_adaptive(jb, 
				      param.init_prefetch, 
				      param.min_prefetch,
				      param.max_prefetch);
	} else {
	    pjmedia_jbuf_set_fixed(jb, param.init_prefetch);
	}

#ifdef REPORT
	pjmedia_jbuf_get_state(jb, &state);
	printf("Initial\tsize=%d\tprefetch=%d\tmin.pftch=%d\tmax.pftch=%d\n", 
	       state.size, state.prefetch, state.min_prefetch, 
	       state.max_prefetch);
#endif


	/* Test session start */
	while (1) {
	    char c;
	    
	    /* Get next line of test data */
	    if (!p || *p == 0) {
		p = fgets(line, sizeof(line), input);
		if (p == NULL) {
		    data_eof = PJ_TRUE;
		    break;
		}
	    }

	    /* Get next char of test data */
	    c = *p++;

	    /* Skip spaces */
	    if (isspace(c))
		continue;

	    /* Print comment line */
	    if (c == '#') {
#ifdef PRINT_COMMENT
		while (*p && isspace(*p)) ++p;
		if (*p) printf("..%s", p);
#endif
		*p = 0;
		continue;
	    }

	    /* Process test data */
	    if (!process_test_data(c, jb, &seq, &last_seq))
		break;
	}

	/* Print JB states */
	pjmedia_jbuf_get_state(jb, &state);
	printf("------------------------------------------------------\n");
	printf("Summary:\n");
	printf("  size=%d prefetch=%d\n", state.size, state.prefetch);
	printf("  delay (min/max/avg/dev)=%d/%d/%d/%d ms\n",
	       state.min_delay, state.max_delay, state.avg_delay, 
	       state.dev_delay);
	printf("  lost=%d discard=%d empty=%d burst(avg)=%d\n", 
	       state.lost, state.discard, state.empty, state.avg_burst);

	/* Evaluate test session */
	if (cond.burst >= 0 && (int)state.avg_burst > cond.burst) {
	    printf("! 'Burst' should be %d, it is %d\n", 
		   cond.burst, state.avg_burst);
	    rc |= 1;
	}
	if (cond.delay >= 0 && (int)state.avg_delay/JB_PTIME > cond.delay) {
	    printf("! 'Delay' should be %d, it is %d\n", 
		   cond.delay, state.avg_delay/JB_PTIME);
	    rc |= 2;
	}
	if (cond.delay_min >= 0 && (int)state.min_delay/JB_PTIME > cond.delay_min) {
	    printf("! 'Minimum delay' should be %d, it is %d\n", 
		   cond.delay_min, state.min_delay/JB_PTIME);
	    rc |= 32;
	}
	if (cond.discard >= 0 && (int)state.discard > cond.discard) {
	    printf("! 'Discard' should be %d, it is %d\n",
		   cond.discard, state.discard);
	    rc |= 4;
	}
	if (cond.empty >= 0 && (int)state.empty > cond.empty) {
	    printf("! 'Empty' should be %d, it is %d\n", 
		   cond.empty, state.empty);
	    rc |= 8;
	}
	if (cond.lost >= 0 && (int)state.lost > cond.lost) {
	    printf("! 'Lost' should be %d, it is %d\n", 
		   cond.lost, state.lost);
	    rc |= 16;
	}

	pjmedia_jbuf_destroy(jb);
	pj_pool_release(pool);
    }

    fclose(input);
    pj_log_set_level(old_log_level);

    return rc;
}
Example #7
0
/*
 * pj_thread_create(...)
 */
pj_status_t pj_thread_create( pj_pool_t *pool,
				      const char *thread_name,
				      pj_thread_proc *proc,
				      void *arg,
				      pj_size_t stack_size,
				      unsigned flags,
				      pj_thread_t **ptr_thread)
{
#if PJ_HAS_THREADS
    pj_thread_t *rec;
    pthread_attr_t thread_attr;
    void *stack_addr;
    int rc;

    PJ_UNUSED_ARG(stack_addr);

    PJ_CHECK_STACK();
    PJ_ASSERT_RETURN(pool && proc && ptr_thread, PJ_EINVAL);

    /* Create thread record and assign name for the thread */
    rec = (struct pj_thread_t*) pj_pool_zalloc(pool, sizeof(pj_thread_t));
    PJ_ASSERT_RETURN(rec, PJ_ENOMEM);

    /* Set name. */
    if (!thread_name)
	thread_name = "thr%p";

    if (strchr(thread_name, '%')) {
	pj_ansi_snprintf(rec->obj_name, PJ_MAX_OBJ_NAME, thread_name, rec);
    } else {
	strncpy(rec->obj_name, thread_name, PJ_MAX_OBJ_NAME);
	rec->obj_name[PJ_MAX_OBJ_NAME-1] = '\0';
    }

    /* Set default stack size */
    if (stack_size == 0)
	stack_size = PJ_THREAD_DEFAULT_STACK_SIZE;

#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
    rec->stk_size = stack_size;
    rec->stk_max_usage = 0;
#endif

    /* Emulate suspended thread with mutex. */
    if (flags & PJ_THREAD_SUSPENDED) {
	rc = pj_mutex_create_simple(pool, NULL, &rec->suspended_mutex);
	if (rc != PJ_SUCCESS) {
	    return rc;
	}

	pj_mutex_lock(rec->suspended_mutex);
    } else {
	pj_assert(rec->suspended_mutex == NULL);
    }


    /* Init thread attributes */
    pthread_attr_init(&thread_attr);

#if defined(PJ_THREAD_SET_STACK_SIZE) && PJ_THREAD_SET_STACK_SIZE!=0
    /* Set thread's stack size */
    rc = pthread_attr_setstacksize(&thread_attr, stack_size);
    if (rc != 0)
	return PJ_RETURN_OS_ERROR(rc);
#endif	/* PJ_THREAD_SET_STACK_SIZE */


#if defined(PJ_THREAD_ALLOCATE_STACK) && PJ_THREAD_ALLOCATE_STACK!=0
    /* Allocate memory for the stack */
    stack_addr = pj_pool_alloc(pool, stack_size);
    PJ_ASSERT_RETURN(stack_addr, PJ_ENOMEM);

    rc = pthread_attr_setstackaddr(&thread_attr, stack_addr);
    if (rc != 0)
	return PJ_RETURN_OS_ERROR(rc);
#endif	/* PJ_THREAD_ALLOCATE_STACK */


    /* Create the thread. */
    rec->proc = proc;
    rec->arg = arg;
    rc = pthread_create( &rec->thread, &thread_attr, &thread_main, rec);
    if (rc != 0) {
	return PJ_RETURN_OS_ERROR(rc);
    }

    *ptr_thread = rec;

    PJ_LOG(6, (rec->obj_name, "Thread created"));
    return PJ_SUCCESS;
#else
    pj_assert(!"Threading is disabled!");
    return PJ_EINVALIDOP;
#endif
}
Example #8
0
/**
 * Private: process pager message.
 *	    This may trigger pjsua_ui_on_pager() or pjsua_ui_on_typing().
 */
void pjsua_im_process_pager(int call_id, const pj_str_t *from,
			    const pj_str_t *to, pjsip_rx_data *rdata)
{
    pjsip_contact_hdr *contact_hdr;
    pj_str_t contact;
    pjsip_msg_body *body = rdata->msg_info.msg->body;

#if 0
    /* Ticket #693: allow incoming MESSAGE without message body */
    /* Body MUST have been checked before */
    pj_assert(body != NULL);
#endif


    /* Build remote contact */
    contact_hdr = (pjsip_contact_hdr*)
		  pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT,
				     NULL);
    if (contact_hdr && contact_hdr->uri) {
	contact.ptr = (char*) pj_pool_alloc(rdata->tp_info.pool, 
				    	    PJSIP_MAX_URL_SIZE);
	contact.slen = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
				       contact_hdr->uri, contact.ptr,
				       PJSIP_MAX_URL_SIZE);
    } else {
	contact.slen = 0;
    }

    if (body && pj_stricmp(&body->content_type.type, &STR_MIME_APP)==0 &&
	pj_stricmp(&body->content_type.subtype, &STR_MIME_ISCOMPOSING)==0)
    {
	/* Expecting typing indication */
	pj_status_t status;
	pj_bool_t is_typing;

	status = pjsip_iscomposing_parse(rdata->tp_info.pool, (char*)body->data,
					 body->len, &is_typing, NULL, NULL,
					 NULL );
	if (status != PJ_SUCCESS) {
	    pjsua_perror(THIS_FILE, "Invalid MESSAGE body", status);
	    return;
	}

	if (pjsua_var.ua_cfg.cb.on_typing) {
	    (*pjsua_var.ua_cfg.cb.on_typing)(call_id, from, to, &contact,
					     is_typing);
	}

	if (pjsua_var.ua_cfg.cb.on_typing2) {
	    pjsua_acc_id acc_id;

	    if (call_id == PJSUA_INVALID_ID) {
		acc_id = pjsua_acc_find_for_incoming(rdata);
	    } else {
		pjsua_call *call = &pjsua_var.calls[call_id];
		acc_id = call->acc_id;
	    }


	    (*pjsua_var.ua_cfg.cb.on_typing2)(call_id, from, to, &contact,
					      is_typing, rdata, acc_id);
	}

    } else {
	pj_str_t mime_type;
	char buf[256];
	pjsip_media_type *m;
	pj_str_t text_body;
	
	/* Save text body */
	if (body) {
	    text_body.ptr = (char*)rdata->msg_info.msg->body->data;
	    text_body.slen = rdata->msg_info.msg->body->len;

	    /* Get mime type */
	    m = &rdata->msg_info.msg->body->content_type;
	    mime_type.ptr = buf;
	    mime_type.slen = pj_ansi_snprintf(buf, sizeof(buf),
					      "%.*s/%.*s",
					      (int)m->type.slen,
					      m->type.ptr,
					      (int)m->subtype.slen,
					      m->subtype.ptr);
	    if (mime_type.slen < 1)
		mime_type.slen = 0;


	} else {
	    text_body.ptr = mime_type.ptr = "";
	    text_body.slen = mime_type.slen = 0;
	}

	if (pjsua_var.ua_cfg.cb.on_pager) {
	    (*pjsua_var.ua_cfg.cb.on_pager)(call_id, from, to, &contact, 
					    &mime_type, &text_body);
	}

	if (pjsua_var.ua_cfg.cb.on_pager2) {
	    pjsua_acc_id acc_id;

	    if (call_id == PJSUA_INVALID_ID) {
		acc_id = pjsua_acc_find_for_incoming(rdata);
	    } else {
		pjsua_call *call = &pjsua_var.calls[call_id];
		acc_id = call->acc_id;
	    }

	    (*pjsua_var.ua_cfg.cb.on_pager2)(call_id, from, to, &contact, 
					     &mime_type, &text_body, rdata,
					     acc_id);
	}
    }
}
Example #9
0
/*
 * pj_sem_create()
 */
pj_status_t pj_sem_create( pj_pool_t *pool,
				   const char *name,
				   unsigned initial,
				   unsigned max,
				   pj_sem_t **ptr_sem)
{
#if PJ_HAS_THREADS
    pj_sem_t *sem;

    PJ_CHECK_STACK();
    PJ_ASSERT_RETURN(pool != NULL && ptr_sem != NULL, PJ_EINVAL);

    sem = PJ_POOL_ALLOC_T(pool, pj_sem_t);
    PJ_ASSERT_RETURN(sem, PJ_ENOMEM);

#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
    /* MacOS X doesn't support anonymous semaphore */
    {
	char sem_name[PJ_GUID_MAX_LENGTH+1];
	pj_str_t nam;

	/* We should use SEM_NAME_LEN, but this doesn't seem to be
	 * declared anywhere? The value here is just from trial and error
	 * to get the longest name supported.
	 */
#	define MAX_SEM_NAME_LEN	23

	/* Create a unique name for the semaphore. */
	if (PJ_GUID_STRING_LENGTH <= MAX_SEM_NAME_LEN) {
	    nam.ptr = sem_name;
	    pj_generate_unique_string(&nam);
	    sem_name[nam.slen] = '\0';
	} else {
	    pj_create_random_string(sem_name, MAX_SEM_NAME_LEN);
	    sem_name[MAX_SEM_NAME_LEN] = '\0';
	}

	/* Create semaphore */
	sem->sem = sem_open(sem_name, O_CREAT|O_EXCL, S_IRUSR|S_IWUSR,
			    initial);
	if (sem->sem == SEM_FAILED)
	    return PJ_RETURN_OS_ERROR(pj_get_native_os_error());

	/* And immediately release the name as we don't need it */
	sem_unlink(sem_name);
    }
#else
    sem->sem = PJ_POOL_ALLOC_T(pool, sem_t);
    if (sem_init( sem->sem, 0, initial) != 0)
	return PJ_RETURN_OS_ERROR(pj_get_native_os_error());
#endif

    /* Set name. */
    if (!name) {
	name = "sem%p";
    }
    if (strchr(name, '%')) {
	pj_ansi_snprintf(sem->obj_name, PJ_MAX_OBJ_NAME, name, sem);
    } else {
	strncpy(sem->obj_name, name, PJ_MAX_OBJ_NAME);
	sem->obj_name[PJ_MAX_OBJ_NAME-1] = '\0';
    }

    PJ_LOG(6, (sem->obj_name, "Semaphore created"));

    *ptr_sem = sem;
    return PJ_SUCCESS;
#else
    *ptr_sem = (pj_sem_t*)1;
    return PJ_SUCCESS;
#endif
}
Example #10
0
/*
 * pj_thread_register(..)
 */
pj_status_t pj_thread_register ( const char *cstr_thread_name,
					 pj_thread_desc desc,
					 pj_thread_t **ptr_thread)
{
#if PJ_HAS_THREADS
    char stack_ptr;
    pj_status_t rc;
    pj_thread_t *thread = (pj_thread_t *)desc;
    pj_str_t thread_name = pj_str((char*)cstr_thread_name);

    /* Size sanity check. */
    if (sizeof(pj_thread_desc) < sizeof(pj_thread_t)) {
	pj_assert(!"Not enough pj_thread_desc size!");
	return PJ_EBUG;
    }

    /* Warn if this thread has been registered before */
    if (pj_thread_local_get (thread_tls_id) != 0) {
	// 2006-02-26 bennylp:
	//  This wouldn't work in all cases!.
	//  If thread is created by external module (e.g. sound thread),
	//  thread may be reused while the pool used for the thread descriptor
	//  has been deleted by application.
	//*thread_ptr = (pj_thread_t*)pj_thread_local_get (thread_tls_id);
        //return PJ_SUCCESS;
	PJ_LOG(4,(THIS_FILE, "Info: possibly re-registering existing "
			     "thread"));
    }

    /* On the other hand, also warn if the thread descriptor buffer seem to
     * have been used to register other threads.
     */
    pj_assert(thread->signature1 != SIGNATURE1 ||
	      thread->signature2 != SIGNATURE2 ||
	      (thread->thread == pthread_self()));

    /* Initialize and set the thread entry. */
    pj_bzero(desc, sizeof(struct pj_thread_t));
    thread->thread = pthread_self();
    thread->signature1 = SIGNATURE1;
    thread->signature2 = SIGNATURE2;

    if(cstr_thread_name && pj_strlen(&thread_name) < sizeof(thread->obj_name)-1)
	pj_ansi_snprintf(thread->obj_name, sizeof(thread->obj_name),
			 cstr_thread_name, thread->thread);
    else
	pj_ansi_snprintf(thread->obj_name, sizeof(thread->obj_name),
			 "thr%p", (void*)thread->thread);

    rc = pj_thread_local_set(thread_tls_id, thread);
    if (rc != PJ_SUCCESS) {
	pj_bzero(desc, sizeof(struct pj_thread_t));
	return rc;
    }

#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
    thread->stk_start = &stack_ptr;
    thread->stk_size = 0xFFFFFFFFUL;
    thread->stk_max_usage = 0;
#else
    PJ_UNUSED_ARG(stack_ptr);
#endif

    *ptr_thread = thread;
    return PJ_SUCCESS;
#else
    pj_thread_t *thread = (pj_thread_t*)desc;
    *ptr_thread = thread;
    return PJ_SUCCESS;
#endif
}
Example #11
0
PJ_END_DECL
#endif

static pj_status_t init_mutex(pj_mutex_t *mutex, const char *name, int type)
{
#if PJ_HAS_THREADS
    pthread_mutexattr_t attr;
    int rc;

    PJ_CHECK_STACK();

    rc = pthread_mutexattr_init(&attr);
    if (rc != 0)
	return PJ_RETURN_OS_ERROR(rc);

    if (type == PJ_MUTEX_SIMPLE) {
#if (defined(PJ_LINUX) && PJ_LINUX!=0) || \
    defined(PJ_HAS_PTHREAD_MUTEXATTR_SETTYPE)
	rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_FAST_NP);
#elif (defined(PJ_RTEMS) && PJ_RTEMS!=0) || \
       defined(PJ_PTHREAD_MUTEXATTR_T_HAS_RECURSIVE)
	/* Nothing to do, default is simple */
#else
	rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL);
#endif
    } else {
#if (defined(PJ_LINUX) && PJ_LINUX!=0) || \
     defined(PJ_HAS_PTHREAD_MUTEXATTR_SETTYPE)
	rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE_NP);
#elif (defined(PJ_RTEMS) && PJ_RTEMS!=0) || \
       defined(PJ_PTHREAD_MUTEXATTR_T_HAS_RECURSIVE)
	// Phil Torre <*****@*****.**>:
	// The RTEMS implementation of POSIX mutexes doesn't include
	// pthread_mutexattr_settype(), so what follows is a hack
	// until I get RTEMS patched to support the set/get functions.
	//
	// More info:
	//   newlib's pthread also lacks pthread_mutexattr_settype(),
	//   but it seems to have mutexattr.recursive.
	PJ_TODO(FIX_RTEMS_RECURSIVE_MUTEX_TYPE)
	attr.recursive = 1;
#else
	rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
#endif
    }

    if (rc != 0) {
	return PJ_RETURN_OS_ERROR(rc);
    }

    rc = pthread_mutex_init(&mutex->mutex, &attr);
    if (rc != 0) {
	return PJ_RETURN_OS_ERROR(rc);
    }

    rc = pthread_mutexattr_destroy(&attr);
    if (rc != 0) {
	pj_status_t status = PJ_RETURN_OS_ERROR(rc);
	pthread_mutex_destroy(&mutex->mutex);
	return status;
    }

#if PJ_DEBUG
    /* Set owner. */
    mutex->nesting_level = 0;
    mutex->owner = NULL;
    mutex->owner_name[0] = '\0';
#endif

    /* Set name. */
    if (!name) {
	name = "mtx%p";
    }
    if (strchr(name, '%')) {
	pj_ansi_snprintf(mutex->obj_name, PJ_MAX_OBJ_NAME, name, mutex);
    } else {
	strncpy(mutex->obj_name, name, PJ_MAX_OBJ_NAME);
	mutex->obj_name[PJ_MAX_OBJ_NAME-1] = '\0';
    }

    PJ_LOG(6, (mutex->obj_name, "Mutex created"));
    return PJ_SUCCESS;
#else /* PJ_HAS_THREADS */
    return PJ_SUCCESS;
#endif
}
/*
 * Internal function for collecting codec info and param from the SDP media.
 */
static pj_status_t get_video_codec_info_param(pjmedia_vid_stream_info *si,
        pj_pool_t *pool,
        pjmedia_vid_codec_mgr *mgr,
        const pjmedia_sdp_media *local_m,
        const pjmedia_sdp_media *rem_m)
{
    unsigned pt = 0;
    const pjmedia_vid_codec_info *p_info;
    pj_status_t status;

    pt = pj_strtoul(&local_m->desc.fmt[0]);

    /* Get payload type for receiving direction */
    si->rx_pt = pt;

    /* Get codec info and payload type for transmitting direction. */
    if (pt < 96) {
        /* For static payload types, get the codec info from codec manager. */
        status = pjmedia_vid_codec_mgr_get_codec_info(mgr, pt, &p_info);
        if (status != PJ_SUCCESS)
            return status;

        si->codec_info = *p_info;

        /* Get payload type for transmitting direction.
         * For static payload type, pt's are symetric.
         */
        si->tx_pt = pt;
    } else {
        const pjmedia_sdp_attr *attr;
        pjmedia_sdp_rtpmap *rtpmap;
        pjmedia_codec_id codec_id;
        pj_str_t codec_id_st;
        unsigned i;

        /* Determine payload type for outgoing channel, by finding
         * dynamic payload type in remote SDP that matches the answer.
         */
        si->tx_pt = 0xFFFF;
        for (i=0; i<rem_m->desc.fmt_count; ++i) {
            if (pjmedia_sdp_neg_fmt_match(NULL,
                                          (pjmedia_sdp_media*)local_m, 0,
                                          (pjmedia_sdp_media*)rem_m, i, 0) ==
                    PJ_SUCCESS)
            {
                /* Found matched codec. */
                si->tx_pt = pj_strtoul(&rem_m->desc.fmt[i]);
                break;
            }
        }

        if (si->tx_pt == 0xFFFF)
            return PJMEDIA_EMISSINGRTPMAP;

        /* For dynamic payload types, get codec name from the rtpmap */
        attr = pjmedia_sdp_media_find_attr(local_m, &ID_RTPMAP,
                                           &local_m->desc.fmt[0]);
        if (attr == NULL)
            return PJMEDIA_EMISSINGRTPMAP;

        status = pjmedia_sdp_attr_to_rtpmap(pool, attr, &rtpmap);
        if (status != PJ_SUCCESS)
            return status;

        /* Then get the codec info from the codec manager */
        pj_ansi_snprintf(codec_id, sizeof(codec_id), "%.*s/",
                         (int)rtpmap->enc_name.slen, rtpmap->enc_name.ptr);
        codec_id_st = pj_str(codec_id);
        i = 1;
        status = pjmedia_vid_codec_mgr_find_codecs_by_id(mgr, &codec_id_st,
                 &i, &p_info, NULL);
        if (status != PJ_SUCCESS)
            return status;

        si->codec_info = *p_info;
    }


    /* Request for codec with the correct packing for streaming */
    si->codec_info.packings = PJMEDIA_VID_PACKING_PACKETS;

    /* Now that we have codec info, get the codec param. */
    si->codec_param = PJ_POOL_ALLOC_T(pool, pjmedia_vid_codec_param);
    status = pjmedia_vid_codec_mgr_get_default_param(mgr,
             &si->codec_info,
             si->codec_param);

    /* Adjust encoding bitrate, if higher than remote preference. The remote
     * bitrate preference is read from SDP "b=TIAS" line in media level.
     */
    if ((si->dir & PJMEDIA_DIR_ENCODING) && rem_m->bandw_count) {
        unsigned i, bandw = 0;

        for (i = 0; i < rem_m->bandw_count; ++i) {
            const pj_str_t STR_BANDW_MODIFIER_TIAS = { "TIAS", 4 };
            if (!pj_stricmp(&rem_m->bandw[i]->modifier,
                            &STR_BANDW_MODIFIER_TIAS))
            {
                bandw = rem_m->bandw[i]->value;
                break;
            }
        }

        if (bandw) {
            pjmedia_video_format_detail *enc_vfd;
            enc_vfd = pjmedia_format_get_video_format_detail(
                          &si->codec_param->enc_fmt, PJ_TRUE);
            if (!enc_vfd->avg_bps || enc_vfd->avg_bps > bandw)
                enc_vfd->avg_bps = bandw * 3 / 4;
            if (!enc_vfd->max_bps || enc_vfd->max_bps > bandw)
                enc_vfd->max_bps = bandw;
        }
    }

    /* Get remote fmtp for our encoder. */
    pjmedia_stream_info_parse_fmtp(pool, rem_m, si->tx_pt,
                                   &si->codec_param->enc_fmtp);

    /* Get local fmtp for our decoder. */
    pjmedia_stream_info_parse_fmtp(pool, local_m, si->rx_pt,
                                   &si->codec_param->dec_fmtp);

    /* When direction is NONE (it means SDP negotiation has failed) we don't
     * need to return a failure here, as returning failure will cause
     * the whole SDP to be rejected. See ticket #:
     *	http://
     *
     * Thanks Alain Totouom
     */
    if (status != PJ_SUCCESS && si->dir != PJMEDIA_DIR_NONE)
        return status;

    return PJ_SUCCESS;
}
Example #13
0
static int print_attr(char *buffer, unsigned length,
		      const pj_stun_attr_hdr *ahdr)
{
    char *p = buffer, *end = buffer + length;
    const char *attr_name = pj_stun_get_attr_name(ahdr->type);
    char attr_buf[32];
    int len;

    if (*attr_name == '?') {
	pj_ansi_snprintf(attr_buf, sizeof(attr_buf), "Attr 0x%x", 
			 ahdr->type);
	attr_name = attr_buf;
    }

    len = pj_ansi_snprintf(p, end-p,
			   "  %s: length=%d",
			   attr_name,
			   (int)ahdr->length);
    APPLY();


    switch (ahdr->type) {
    case PJ_STUN_ATTR_MAPPED_ADDR:
    case PJ_STUN_ATTR_RESPONSE_ADDR:
    case PJ_STUN_ATTR_SOURCE_ADDR:
    case PJ_STUN_ATTR_CHANGED_ADDR:
    case PJ_STUN_ATTR_REFLECTED_FROM:
    case PJ_STUN_ATTR_XOR_PEER_ADDR:
    case PJ_STUN_ATTR_XOR_RELAYED_ADDR:
    case PJ_STUN_ATTR_XOR_MAPPED_ADDR:
    case PJ_STUN_ATTR_XOR_REFLECTED_FROM:
    case PJ_STUN_ATTR_ALTERNATE_SERVER:
	{
	    char addr[PJ_INET6_ADDRSTRLEN];
	    const pj_stun_sockaddr_attr *attr;
	    pj_uint16_t af;

	    attr = (const pj_stun_sockaddr_attr*)ahdr;
	    af = attr->sockaddr.addr.sa_family;

	    if ((af == pj_AF_INET()) || (af == pj_AF_INET6())) {
		len = pj_ansi_snprintf(p, end-p,
				       ", %s addr=%s\n",
				       (af == pj_AF_INET())?"IPv4":"IPv6",
				       pj_sockaddr_print(&attr->sockaddr,
				           		addr, sizeof(addr),3));
	    } else {
		len = pj_ansi_snprintf(p, end-p,
				       ", INVALID ADDRESS FAMILY!\n");
	    }
	    APPLY();
	}
	break;

    case PJ_STUN_ATTR_CHANNEL_NUMBER:
	{
	    const pj_stun_uint_attr *attr;

	    attr = (const pj_stun_uint_attr*)ahdr;
	    len = pj_ansi_snprintf(p, end-p,
				   ", chnum=%u (0x%x)\n",
				   (int)PJ_STUN_GET_CH_NB(attr->value),
				   (int)PJ_STUN_GET_CH_NB(attr->value));
	    APPLY();
	}
	break;

    case PJ_STUN_ATTR_CHANGE_REQUEST:
    case PJ_STUN_ATTR_LIFETIME:
    case PJ_STUN_ATTR_BANDWIDTH:
    case PJ_STUN_ATTR_REQ_ADDR_TYPE:
    case PJ_STUN_ATTR_EVEN_PORT:
    case PJ_STUN_ATTR_REQ_TRANSPORT:
    case PJ_STUN_ATTR_TIMER_VAL:
    case PJ_STUN_ATTR_PRIORITY:
    case PJ_STUN_ATTR_FINGERPRINT:
    case PJ_STUN_ATTR_REFRESH_INTERVAL:
    case PJ_STUN_ATTR_ICMP:
	{
	    const pj_stun_uint_attr *attr;

	    attr = (const pj_stun_uint_attr*)ahdr;
	    len = pj_ansi_snprintf(p, end-p,
				   ", value=%u (0x%x)\n",
				   (pj_uint32_t)attr->value,
				   (pj_uint32_t)attr->value);
	    APPLY();
	}
	break;

    case PJ_STUN_ATTR_USERNAME:
    case PJ_STUN_ATTR_PASSWORD:
    case PJ_STUN_ATTR_REALM:
    case PJ_STUN_ATTR_NONCE:
    case PJ_STUN_ATTR_SOFTWARE:
	{
	    const pj_stun_string_attr *attr;

	    attr = (pj_stun_string_attr*)ahdr;
	    len = pj_ansi_snprintf(p, end-p,
				   ", value=\"%.*s\"\n",
				   (int)attr->value.slen,
				   attr->value.ptr);
	    APPLY();
	}
	break;

    case PJ_STUN_ATTR_ERROR_CODE:
	{
	    const pj_stun_errcode_attr *attr;

	    attr = (const pj_stun_errcode_attr*) ahdr;
	    len = pj_ansi_snprintf(p, end-p,
				   ", err_code=%d, reason=\"%.*s\"\n",
				   attr->err_code,
				   (int)attr->reason.slen,
				   attr->reason.ptr);
	    APPLY();
	}
	break;

    case PJ_STUN_ATTR_UNKNOWN_ATTRIBUTES:
	{
	    const pj_stun_unknown_attr *attr;
	    unsigned j;

	    attr = (const pj_stun_unknown_attr*) ahdr;

	    len = pj_ansi_snprintf(p, end-p,
				   ", unknown list:");
	    APPLY();

	    for (j=0; j<attr->attr_count; ++j) {
		len = pj_ansi_snprintf(p, end-p,
				       " %d",
				       (int)attr->attrs[j]);
		APPLY();
	    }
	}
	break;

    case PJ_STUN_ATTR_MESSAGE_INTEGRITY:
	{
	    const pj_stun_msgint_attr *attr;

	    attr = (const pj_stun_msgint_attr*) ahdr;
	    len = print_binary(p, (unsigned)(end-p), attr->hmac, 20);
	    APPLY();
	}
	break;

    case PJ_STUN_ATTR_DATA:
	{
	    const pj_stun_binary_attr *attr;

	    attr = (const pj_stun_binary_attr*) ahdr;
	    len = print_binary(p, (unsigned)(end-p), attr->data, attr->length);
	    APPLY();
	}
	break;
    case PJ_STUN_ATTR_ICE_CONTROLLED:
    case PJ_STUN_ATTR_ICE_CONTROLLING:
    case PJ_STUN_ATTR_RESERVATION_TOKEN:
	{
	    const pj_stun_uint64_attr *attr;
	    pj_uint8_t data[8];
	    int i;

	    attr = (const pj_stun_uint64_attr*) ahdr;

	    for (i=0; i<8; ++i)
		data[i] = ((const pj_uint8_t*)&attr->value)[7-i];

	    len = print_binary(p, (unsigned)(end-p), data, 8);
	    APPLY();
	}
	break;
    case PJ_STUN_ATTR_USE_CANDIDATE:
    case PJ_STUN_ATTR_DONT_FRAGMENT:
    default:
	len = pj_ansi_snprintf(p, end-p, "\n");
	APPLY();
	break;
    }

    return (int)(p-buffer);

on_return:
    return len;
}
Example #14
0
/*
 * pj_thread_create(...)
 */
PJ_DEF(pj_status_t) pj_thread_create( pj_pool_t *pool, 
                                      const char *thread_name,
				      pj_thread_proc *proc, 
                                      void *arg,
				      pj_size_t stack_size, 
				      unsigned flags,
                                      pj_thread_t **thread_ptr)
{
    DWORD dwflags = 0;
    pj_thread_t *rec;

#if defined(PJ_WIN32_WINPHONE8) && PJ_WIN32_WINPHONE8
    PJ_UNUSED_ARG(stack_size);
#endif

    PJ_CHECK_STACK();
    PJ_ASSERT_RETURN(pool && proc && thread_ptr, PJ_EINVAL);

    /* Set flags */
    if (flags & PJ_THREAD_SUSPENDED)
	dwflags |= CREATE_SUSPENDED;

    /* Create thread record and assign name for the thread */
    rec = (struct pj_thread_t*) pj_pool_calloc(pool, 1, sizeof(pj_thread_t));
    if (!rec)
	return PJ_ENOMEM;

    /* Set name. */
    if (!thread_name)
	thread_name = "thr%p";

    if (strchr(thread_name, '%')) {
	pj_ansi_snprintf(rec->obj_name, PJ_MAX_OBJ_NAME, thread_name, rec);
    } else {
	pj_ansi_strncpy(rec->obj_name, thread_name, PJ_MAX_OBJ_NAME);
	rec->obj_name[PJ_MAX_OBJ_NAME-1] = '\0';
    }

    PJ_LOG(6, (rec->obj_name, "Thread created"));

#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
    rec->stk_size = stack_size ? (pj_uint32_t)stack_size : 0xFFFFFFFFUL;
    rec->stk_max_usage = 0;
#endif

    /* Create the thread. */
    rec->proc = proc;
    rec->arg = arg;

#if defined(PJ_WIN32_WINPHONE8) && PJ_WIN32_WINPHONE8
    rec->hthread = CreateThreadRT(NULL, 0,
				  thread_main, rec,
				  dwflags, NULL);
#else
    rec->hthread = CreateThread(NULL, stack_size,
			        thread_main, rec,
			        dwflags, &rec->idthread);
#endif

    if (rec->hthread == NULL)
	return PJ_RETURN_OS_ERROR(GetLastError());

    /* Success! */
    *thread_ptr = rec;
    return PJ_SUCCESS;
}
Example #15
0
PJ_END_DECL


/* Get Symbian phone model info, returning length of model info */
unsigned pj_symbianos_get_model_info(char *buf, unsigned buf_size)
{
    pj_str_t model_name;

    /* Get machine UID */
    TInt hal_val;
    HAL::Get(HAL::EMachineUid, hal_val);
    pj_ansi_snprintf(buf, buf_size, "0x%08X", hal_val);
    pj_strset2(&model_name, buf);

    /* Get model name */
    const pj_str_t st_copyright = {"(C)", 3};
    const pj_str_t st_nokia = {"Nokia", 5};
    char tmp_buf[64];
    pj_str_t tmp_str;

    _LIT(KModelFilename,"Z:\\resource\\versions\\model.txt");
    RFile file;
    RFs fs;
    TInt err;
    
    fs.Connect(1);
    err = file.Open(fs, KModelFilename, EFileRead);
    if (err == KErrNone) {
	TFileText text;
	text.Set(file);
	TBuf16<64> ModelName16;
	err = text.Read(ModelName16);
	if (err == KErrNone) {
	    TPtr8 ptr8((TUint8*)tmp_buf, sizeof(tmp_buf));
	    ptr8.Copy(ModelName16);
	    pj_strset(&tmp_str, tmp_buf, ptr8.Length());
	    pj_strtrim(&tmp_str);
	}
	file.Close();
    }
    fs.Close();
    if (err != KErrNone)
	goto on_return;
    
    /* The retrieved model name is usually in long format, e.g: 
     * "© Nokia N95 (01.01)", "(C) Nokia E52". As we need only
     * the short version, let's clean it up.
     */
    
    /* Remove preceding non-ASCII chars, e.g: "©" */
    char *p = tmp_str.ptr;
    while (!pj_isascii(*p)) { p++; }
    pj_strset(&tmp_str, p, tmp_str.slen - (p - tmp_str.ptr));
    
    /* Remove "(C)" */
    p = pj_stristr(&tmp_str, &st_copyright);
    if (p) {
	p += st_copyright.slen;
	pj_strset(&tmp_str, p, tmp_str.slen - (p - tmp_str.ptr));
    }

    /* Remove "Nokia" */
    p = pj_stristr(&tmp_str, &st_nokia);
    if (p) {
	p += st_nokia.slen;
	pj_strset(&tmp_str, p, tmp_str.slen - (p - tmp_str.ptr));
    }
    
    /* Remove language version, e.g: "(01.01)" */
    p = pj_strchr(&tmp_str, '(');
    if (p) {
	tmp_str.slen = p - tmp_str.ptr;
    }
    
    pj_strtrim(&tmp_str);
    
    if (tmp_str.slen == 0)
	goto on_return;
    
    if ((unsigned)tmp_str.slen > buf_size - model_name.slen - 3)
	tmp_str.slen = buf_size - model_name.slen - 3;
    
    pj_strcat2(&model_name, "(");
    pj_strcat(&model_name, &tmp_str);
    pj_strcat2(&model_name, ")");
    
    /* Zero terminate */
    buf[model_name.slen] = '\0';
    
on_return:
    return model_name.slen;
}
Example #16
0
PJ_DEF(pj_status_t) pjsip_tls_transport_start2( pjsip_endpoint *endpt,
 					        const pjsip_tls_setting *opt,
					        const pj_sockaddr *local,
					        const pjsip_host_port *a_name,
					        unsigned async_cnt,
					        pjsip_tpfactory **p_factory)
{
    pj_pool_t *pool;
    pj_bool_t is_ipv6;
    int af;
    struct tls_listener *listener;
    pj_ssl_sock_param ssock_param;
    pj_sockaddr *listener_addr;
    pj_bool_t has_listener;
    pj_status_t status;

    /* Sanity check */
    PJ_ASSERT_RETURN(endpt && async_cnt, PJ_EINVAL);

    is_ipv6 = (local && local->addr.sa_family == pj_AF_INET6());
    af = is_ipv6 ? pj_AF_INET6() : pj_AF_INET();

    /* Verify that address given in a_name (if any) is valid */
    if (a_name && a_name->host.slen) {
	pj_sockaddr tmp;

	status = pj_sockaddr_init(af, &tmp, &a_name->host,
				  (pj_uint16_t)a_name->port);
	if (status != PJ_SUCCESS || !pj_sockaddr_has_addr(&tmp) ||
	    (!is_ipv6 && tmp.ipv4.sin_addr.s_addr == PJ_INADDR_NONE))
	{
	    /* Invalid address */
	    return PJ_EINVAL;
	}
    }

    pool = pjsip_endpt_create_pool(endpt, "tlslis", POOL_LIS_INIT, 
				   POOL_LIS_INC);
    PJ_ASSERT_RETURN(pool, PJ_ENOMEM);

    listener = PJ_POOL_ZALLOC_T(pool, struct tls_listener);
    listener->factory.pool = pool;
    if (is_ipv6)
	listener->factory.type = PJSIP_TRANSPORT_TLS6;
    else
	listener->factory.type = PJSIP_TRANSPORT_TLS;
    listener->factory.type_name = (char*)
		pjsip_transport_get_type_name(listener->factory.type);
    listener->factory.flag = 
	pjsip_transport_get_flag_from_type(listener->factory.type);

    pj_ansi_strcpy(listener->factory.obj_name, "tlslis");
    if (is_ipv6)
	pj_ansi_strcat(listener->factory.obj_name, "6");

    if (opt)
	pjsip_tls_setting_copy(pool, &listener->tls_setting, opt);
    else
	pjsip_tls_setting_default(&listener->tls_setting);

    status = pj_lock_create_recursive_mutex(pool, listener->factory.obj_name,
					    &listener->factory.lock);
    if (status != PJ_SUCCESS)
	goto on_error;

    if (async_cnt > MAX_ASYNC_CNT) 
	async_cnt = MAX_ASYNC_CNT;

    /* Build SSL socket param */
    pj_ssl_sock_param_default(&ssock_param);
    ssock_param.sock_af = af;
    ssock_param.cb.on_accept_complete = &on_accept_complete;
    ssock_param.cb.on_data_read = &on_data_read;
    ssock_param.cb.on_data_sent = &on_data_sent;
    ssock_param.async_cnt = async_cnt;
    ssock_param.ioqueue = pjsip_endpt_get_ioqueue(endpt);
    ssock_param.require_client_cert = listener->tls_setting.require_client_cert;
    ssock_param.timeout = listener->tls_setting.timeout;
    ssock_param.user_data = listener;
    ssock_param.verify_peer = PJ_FALSE; /* avoid SSL socket closing the socket
					 * due to verification error */
    if (ssock_param.send_buffer_size < PJSIP_MAX_PKT_LEN)
	ssock_param.send_buffer_size = PJSIP_MAX_PKT_LEN;
    if (ssock_param.read_buffer_size < PJSIP_MAX_PKT_LEN)
	ssock_param.read_buffer_size = PJSIP_MAX_PKT_LEN;
    ssock_param.ciphers_num = listener->tls_setting.ciphers_num;
    ssock_param.ciphers = listener->tls_setting.ciphers;
    ssock_param.qos_type = listener->tls_setting.qos_type;
    ssock_param.qos_ignore_error = listener->tls_setting.qos_ignore_error;
    pj_memcpy(&ssock_param.qos_params, &listener->tls_setting.qos_params,
	      sizeof(ssock_param.qos_params));

    has_listener = PJ_FALSE;

    switch(listener->tls_setting.method) {
    case PJSIP_TLSV1_METHOD:
	ssock_param.proto = PJ_SSL_SOCK_PROTO_TLS1;
	break;
    case PJSIP_SSLV2_METHOD:
	ssock_param.proto = PJ_SSL_SOCK_PROTO_SSL2;
	break;
    case PJSIP_SSLV3_METHOD:
	ssock_param.proto = PJ_SSL_SOCK_PROTO_SSL3;
	break;
    case PJSIP_SSLV23_METHOD:
	ssock_param.proto = PJ_SSL_SOCK_PROTO_SSL23;
	break;
    default:
	ssock_param.proto = PJ_SSL_SOCK_PROTO_DEFAULT;
	break;
    }

    /* Create SSL socket */
    status = pj_ssl_sock_create(pool, &ssock_param, &listener->ssock);
    if (status != PJ_SUCCESS)
	goto on_error;

    /* Bind address may be different than factory.local_addr because
     * factory.local_addr will be resolved below.
     */
    listener_addr = &listener->factory.local_addr;
    if (local) {
	pj_sockaddr_cp((pj_sockaddr_t*)listener_addr, 
		       (const pj_sockaddr_t*)local);
	pj_sockaddr_cp(&listener->bound_addr, local);
    } else {
	pj_sockaddr_init(af, listener_addr, NULL, 0);
	pj_sockaddr_init(af, &listener->bound_addr, NULL, 0);
    }

    /* Check if certificate/CA list for SSL socket is set */
    if (listener->tls_setting.cert_file.slen ||
	listener->tls_setting.ca_list_file.slen) 
    {
	status = pj_ssl_cert_load_from_files(pool,
			&listener->tls_setting.ca_list_file,
			&listener->tls_setting.cert_file,
			&listener->tls_setting.privkey_file,
			&listener->tls_setting.password,
			&listener->cert);
	if (status != PJ_SUCCESS)
	    goto on_error;

	status = pj_ssl_sock_set_certificate(listener->ssock, pool, 
					     listener->cert);
	if (status != PJ_SUCCESS)
	    goto on_error;
    }

    /* Start accepting incoming connections. Note that some TLS/SSL backends
     * may not support for SSL socket server.
     */
    has_listener = PJ_FALSE;

    status = pj_ssl_sock_start_accept(listener->ssock, pool, 
			  (pj_sockaddr_t*)listener_addr, 
			  pj_sockaddr_get_len((pj_sockaddr_t*)listener_addr));
    if (status == PJ_SUCCESS || status == PJ_EPENDING) {
	pj_ssl_sock_info info;
	has_listener = PJ_TRUE;

	/* Retrieve the bound address */
	status = pj_ssl_sock_get_info(listener->ssock, &info);
	if (status == PJ_SUCCESS)
	    pj_sockaddr_cp(listener_addr, (pj_sockaddr_t*)&info.local_addr);
    } else if (status != PJ_ENOTSUP) {
	goto on_error;
    }

    /* If published host/IP is specified, then use that address as the
     * listener advertised address.
     */
    if (a_name && a_name->host.slen) {
	/* Copy the address */
	listener->factory.addr_name = *a_name;
	pj_strdup(listener->factory.pool, &listener->factory.addr_name.host, 
		  &a_name->host);
	listener->factory.addr_name.port = a_name->port;

    } else {
	/* No published address is given, use the bound address */

	/* If the address returns 0.0.0.0, use the default
	 * interface address as the transport's address.
	 */
	if (!pj_sockaddr_has_addr(listener_addr)) {
	    pj_sockaddr hostip;

	    status = pj_gethostip(af, &hostip);
	    if (status != PJ_SUCCESS)
		goto on_error;

	    pj_sockaddr_copy_addr(listener_addr, &hostip);
	}

	/* Save the address name */
	sockaddr_to_host_port(listener->factory.pool, 
			      &listener->factory.addr_name, listener_addr);
    }

    /* If port is zero, get the bound port */
    if (listener->factory.addr_name.port == 0) {
	listener->factory.addr_name.port = pj_sockaddr_get_port(listener_addr);
    }

    pj_ansi_snprintf(listener->factory.obj_name, 
		     sizeof(listener->factory.obj_name),
		     "tlslis:%d",  listener->factory.addr_name.port);

    /* Register to transport manager */
    listener->endpt = endpt;
    listener->tpmgr = pjsip_endpt_get_tpmgr(endpt);
    listener->factory.create_transport2 = lis_create_transport;
    listener->factory.destroy = lis_destroy;
    listener->is_registered = PJ_TRUE;
    status = pjsip_tpmgr_register_tpfactory(listener->tpmgr,
					    &listener->factory);
    if (status != PJ_SUCCESS) {
	listener->is_registered = PJ_FALSE;
	goto on_error;
    }

    if (has_listener) {
	PJ_LOG(4,(listener->factory.obj_name, 
		 "SIP TLS listener is ready for incoming connections "
		 "at %.*s:%d",
		 (int)listener->factory.addr_name.host.slen,
		 listener->factory.addr_name.host.ptr,
		 listener->factory.addr_name.port));
    } else {
	PJ_LOG(4,(listener->factory.obj_name, "SIP TLS is ready "
		  "(client only)"));
    }

    /* Return the pointer to user */
    if (p_factory) *p_factory = &listener->factory;

    return PJ_SUCCESS;

on_error:
    lis_destroy(&listener->factory);
    return status;
}
Example #17
0
/*
 * Add new buddy.
 */
PJ_DEF(pj_status_t) pjsua_buddy_add( const pjsua_buddy_config *cfg,
				     pjsua_buddy_id *p_buddy_id)
{
    pjsip_name_addr *url;
    pjsua_buddy *buddy;
    pjsip_sip_uri *sip_uri;
    int index;
    pj_str_t tmp;

    PJ_ASSERT_RETURN(pjsua_var.buddy_cnt <= 
			PJ_ARRAY_SIZE(pjsua_var.buddy),
		     PJ_ETOOMANY);

    PJSUA_LOCK();

    /* Find empty slot */
    for (index=0; index<(int)PJ_ARRAY_SIZE(pjsua_var.buddy); ++index) {
	if (pjsua_var.buddy[index].uri.slen == 0)
	    break;
    }

    /* Expect to find an empty slot */
    if (index == PJ_ARRAY_SIZE(pjsua_var.buddy)) {
	PJSUA_UNLOCK();
	/* This shouldn't happen */
	pj_assert(!"index < PJ_ARRAY_SIZE(pjsua_var.buddy)");
	return PJ_ETOOMANY;
    }

    buddy = &pjsua_var.buddy[index];

    /* Create pool for this buddy */
    if (buddy->pool) {
	pj_pool_reset(buddy->pool);
    } else {
	char name[PJ_MAX_OBJ_NAME];
	pj_ansi_snprintf(name, sizeof(name), "buddy%03d", index);
	buddy->pool = pjsua_pool_create(name, 512, 256);
    }

    /* Init buffers for presence subscription status */
    buddy->term_reason.ptr = (char*) 
			     pj_pool_alloc(buddy->pool, 
					   PJSUA_BUDDY_SUB_TERM_REASON_LEN);

    /* Get name and display name for buddy */
    pj_strdup_with_null(buddy->pool, &tmp, &cfg->uri);
    url = (pjsip_name_addr*)pjsip_parse_uri(buddy->pool, tmp.ptr, tmp.slen,
					    PJSIP_PARSE_URI_AS_NAMEADDR);

    if (url == NULL) {
	pjsua_perror(THIS_FILE, "Unable to add buddy", PJSIP_EINVALIDURI);
	pj_pool_release(buddy->pool);
	buddy->pool = NULL;
	PJSUA_UNLOCK();
	return PJSIP_EINVALIDURI;
    }

    /* Only support SIP schemes */
    if (!PJSIP_URI_SCHEME_IS_SIP(url) && !PJSIP_URI_SCHEME_IS_SIPS(url)) {
	pj_pool_release(buddy->pool);
	buddy->pool = NULL;
	PJSUA_UNLOCK();
	return PJSIP_EINVALIDSCHEME;
    }

    /* Reset buddy, to make sure everything is cleared with default
     * values
     */
    reset_buddy(index);

    /* Save URI */
    pjsua_var.buddy[index].uri = tmp;

    sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(url->uri);
    pjsua_var.buddy[index].name = sip_uri->user;
    pjsua_var.buddy[index].display = url->display;
    pjsua_var.buddy[index].host = sip_uri->host;
    pjsua_var.buddy[index].port = sip_uri->port;
    pjsua_var.buddy[index].monitor = cfg->subscribe;
    if (pjsua_var.buddy[index].port == 0)
	pjsua_var.buddy[index].port = 5060;

    /* Save user data */
    pjsua_var.buddy[index].user_data = (void*)cfg->user_data;

    if (p_buddy_id)
	*p_buddy_id = index;

    pjsua_var.buddy_cnt++;

    PJSUA_UNLOCK();

    pjsua_buddy_subscribe_pres(index, cfg->subscribe);

    return PJ_SUCCESS;
}