/* * 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; }
/* * 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; }
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, ¶m, 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; }
/* * 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; }
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, ®istrar.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, ®_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(®istrar_uri); if (rc != 0) goto on_return; /* Send error on refresh without destroy on callback */ rc = refresh_error(®istrar_uri, PJ_FALSE); if (rc != 0) goto on_return; /* Send error on refresh, destroy on callback */ rc = refresh_error(®istrar_uri, PJ_TRUE); if (rc != 0) goto on_return; /* Updating contact */ rc = update_test(®istrar_uri); if (rc != 0) goto on_return; /* Send error during auth, don't destroy on callback */ rc = auth_send_error(®istrar_uri, PJ_FALSE); if (rc != 0) goto on_return; /* Send error during auth, destroy on callback */ rc = auth_send_error(®istrar_uri, PJ_FALSE); if (rc != 0) goto on_return; on_return: if (registrar.mod.id != -1) { pjsip_endpt_unregister_module(endpt, ®istrar.mod); } if (send_mod.mod.id != -1) { pjsip_endpt_unregister_module(endpt, &send_mod.mod); } if (udp) { pjsip_transport_dec_ref(udp); } return rc; }
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, ¶m, &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; }
/* * 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 }
/** * 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); } } }
/* * 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 }
/* * 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 }
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; }
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; }
/* * 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; }
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; }
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; }
/* * 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; }