예제 #1
0
static pjmedia_sdp_session *create_answer(int call_num, pj_pool_t *pool,
                                          const pjmedia_sdp_session *offer)
{
    const char* dir_attrs[] = { "sendrecv", "sendonly", "recvonly", "inactive" };
    const char *ice_attrs[] = {"ice-pwd", "ice-ufrag", "candidate"};
    pjmedia_sdp_session *answer = pjmedia_sdp_session_clone(pool, offer);
    pjmedia_sdp_attr *sess_dir_attr = NULL;
    unsigned mi;

    PJ_LOG(3,(THIS_FILE, "Call %d: creating answer:", call_num));

    answer->name = pj_str("sipecho");
    sess_dir_attr = find_remove_sdp_attrs(&answer->attr_count, answer->attr,
                                          PJ_ARRAY_SIZE(dir_attrs),
                                          dir_attrs);

    for (mi=0; mi<answer->media_count; ++mi) {
	pjmedia_sdp_media *m = answer->media[mi];
	pjmedia_sdp_attr *m_dir_attr;
	pjmedia_sdp_attr *dir_attr;
	const char *our_dir = NULL;
	pjmedia_sdp_conn *c;

	/* Match direction */
	m_dir_attr = find_remove_sdp_attrs(&m->attr_count, m->attr,
	                                   PJ_ARRAY_SIZE(dir_attrs),
	                                   dir_attrs);
	dir_attr = m_dir_attr ? m_dir_attr : sess_dir_attr;

	if (dir_attr) {
	    if (pj_strcmp2(&dir_attr->name, "sendonly")==0)
		our_dir = "recvonly";
	    else if (pj_strcmp2(&dir_attr->name, "inactive")==0)
		our_dir = "inactive";
	    else if (pj_strcmp2(&dir_attr->name, "recvonly")==0)
		our_dir = "inactive";

	    if (our_dir) {
		dir_attr = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_attr);
		dir_attr->name = pj_str((char*)our_dir);
		m->attr[m->attr_count++] = dir_attr;
	    }
	}

	/* Remove ICE attributes */
	find_remove_sdp_attrs(&m->attr_count, m->attr, PJ_ARRAY_SIZE(ice_attrs), ice_attrs);

	/* Done */
	c = m->conn ? m->conn : answer->conn;
	PJ_LOG(3,(THIS_FILE, "  Media %d, %.*s: %s <--> %.*s:%d",
		  mi, (int)m->desc.media.slen, m->desc.media.ptr,
		  (our_dir ? our_dir : "sendrecv"),
		  (int)c->addr.slen, c->addr.ptr, m->desc.port));
    }

    return answer;
}
예제 #2
0
static int parse_dtls_attrib(struct ast_sip_session_media *session_media,
	const struct pjmedia_sdp_media *stream)
{
	int i;
	struct ast_rtp_engine_dtls *dtls = ast_rtp_instance_get_dtls(session_media->rtp);

	for (i = 0; i < stream->attr_count; i++) {
		pjmedia_sdp_attr *attr = stream->attr[i];
		pj_str_t *value;

		if (!attr->value.ptr) {
			continue;
		}

		value = pj_strtrim(&attr->value);

		if (!pj_strcmp2(&attr->name, "setup")) {
			if (!pj_stricmp2(value, "active")) {
				dtls->set_setup(session_media->rtp, AST_RTP_DTLS_SETUP_ACTIVE);
			} else if (!pj_stricmp2(value, "passive")) {
				dtls->set_setup(session_media->rtp, AST_RTP_DTLS_SETUP_PASSIVE);
			} else if (!pj_stricmp2(value, "actpass")) {
				dtls->set_setup(session_media->rtp, AST_RTP_DTLS_SETUP_ACTPASS);
			} else if (!pj_stricmp2(value, "holdconn")) {
				dtls->set_setup(session_media->rtp, AST_RTP_DTLS_SETUP_HOLDCONN);
			} else {
				ast_log(LOG_WARNING, "Unsupported setup attribute value '%*s'\n", (int)value->slen, value->ptr);
			}
		} else if (!pj_strcmp2(&attr->name, "connection")) {
			if (!pj_stricmp2(value, "new")) {
				dtls->reset(session_media->rtp);
			} else if (!pj_stricmp2(value, "existing")) {
				/* Do nothing */
			} else {
				ast_log(LOG_WARNING, "Unsupported connection attribute value '%*s'\n", (int)value->slen, value->ptr);
			}
		} else if (!pj_strcmp2(&attr->name, "fingerprint")) {
			char hash_value[256], hash[32];
			char fingerprint_text[value->slen + 1];
			ast_copy_pj_str(fingerprint_text, value, sizeof(fingerprint_text));

			if (sscanf(fingerprint_text, "%31s %255s", hash, hash_value) == 2) {
				if (!strcasecmp(hash, "sha-1")) {
					dtls->set_fingerprint(session_media->rtp, AST_RTP_DTLS_HASH_SHA1, hash_value);
				} else if (!strcasecmp(hash, "sha-256")) {
					dtls->set_fingerprint(session_media->rtp, AST_RTP_DTLS_HASH_SHA256, hash_value);
				} else {
					ast_log(LOG_WARNING, "Unsupported fingerprint hash type '%s'\n",
					hash);
				}
			}
		}
	}
	ast_set_flag(session_media->srtp, AST_SRTP_CRYPTO_OFFER_OK);

	return 0;
}
예제 #3
0
파일: sdp.c 프로젝트: tibastral/symphonie
/* Validate SDP connetion info. */
static pj_status_t validate_sdp_conn(const pjmedia_sdp_conn *c)
{
    CHECK( c, PJ_EINVAL);
    CHECK( pj_strcmp2(&c->net_type, "IN")==0, PJMEDIA_SDP_EINCONN);
    CHECK( pj_strcmp2(&c->addr_type, "IP4")==0 ||
	   pj_strcmp2(&c->addr_type, "IP6")==0, 
	   PJMEDIA_SDP_EINCONN);
    CHECK( c->addr.slen != 0, PJMEDIA_SDP_EINCONN);

    return PJ_SUCCESS;
}
예제 #4
0
/*!
 * \internal
 * \brief Checks to make sure the request has the correct content type.
 *
 * \details This module supports the following media types: "text/plain".
 * Return unsupported otherwise.
 *
 * \param rdata The SIP request
 */
static enum pjsip_status_code check_content_type(const pjsip_rx_data *rdata)
{
	int res;
	if (rdata->msg_info.msg->body && rdata->msg_info.msg->body->len) {
		res = ast_sip_is_content_type(
			&rdata->msg_info.msg->body->content_type, "text", "plain");
	} else {
		res = rdata->msg_info.ctype &&
			!pj_strcmp2(&rdata->msg_info.ctype->media.type, "text") &&
			!pj_strcmp2(&rdata->msg_info.ctype->media.subtype, "plain");
	}

	return res ? PJSIP_SC_OK : PJSIP_SC_UNSUPPORTED_MEDIA_TYPE;
}
/*! \brief Helper function which determines if the address within SDP should be rewritten */
static int multihomed_rewrite_sdp(struct pjmedia_sdp_session *sdp)
{
	if (!sdp->conn) {
		return 0;
	}

	/* If the host address is used in the SDP replace it with the address of what this is going out on */
	if ((!pj_strcmp2(&sdp->conn->addr_type, "IP4") && !pj_strcmp2(&sdp->conn->addr, host_ipv4)) ||
		(!pj_strcmp2(&sdp->conn->addr_type, "IP6") && !pj_strcmp2(&sdp->conn->addr, host_ipv6))) {
		return 1;
	}

	return 0;
}
예제 #6
0
/**
 * This method is to parse and add the argument attribute to command structure.
 **/
static pj_status_t add_arg_node(pj_cli_t *cli,
				pj_xml_node *xml_node,
				pj_cli_cmd_spec *cmd,
				pj_cli_arg_spec *arg,
				pj_cli_get_dyn_choice get_choice)
{    
    pj_xml_attr *attr;
    pj_status_t status = PJ_SUCCESS;
    pj_xml_node *sub_node = xml_node;

    if (cmd->arg_cnt >= PJ_CLI_MAX_ARGS)
	return PJ_CLI_ETOOMANYARGS;
    
    pj_bzero(arg, sizeof(*arg));
    attr = sub_node->attr_head.next;
    arg->optional = PJ_FALSE;
    arg->validate = PJ_TRUE;
    while (attr != &sub_node->attr_head) {	
	if (!pj_stricmp2(&attr->name, "name")) {
	    pj_strassign(&arg->name, &attr->value);
	} else if (!pj_stricmp2(&attr->name, "id")) {
	    arg->id = pj_strtol(&attr->value);
	} else if (!pj_stricmp2(&attr->name, "type")) {
	    if (!pj_stricmp2(&attr->value, "text")) {
		arg->type = PJ_CLI_ARG_TEXT;
	    } else if (!pj_stricmp2(&attr->value, "int")) {
		arg->type = PJ_CLI_ARG_INT;
	    } else if (!pj_stricmp2(&attr->value, "choice")) {
		/* Get choice value */
		add_choice_node(cli, xml_node, arg, get_choice);
	    } 
	} else if (!pj_stricmp2(&attr->name, "desc")) {
	    pj_strassign(&arg->desc, &attr->value);
	} else if (!pj_stricmp2(&attr->name, "optional")) {
	    if (!pj_strcmp2(&attr->value, "1")) {
		arg->optional = PJ_TRUE;
	    }
	} else if (!pj_stricmp2(&attr->name, "validate")) {
	    if (!pj_strcmp2(&attr->value, "1")) {
		arg->validate = PJ_TRUE;
	    } else {
		arg->validate = PJ_FALSE;
	    }	
	} 
	attr = attr->next;
    }
    cmd->arg_cnt++;
    return status;
}
예제 #7
0
파일: sdp.c 프로젝트: tibastral/symphonie
PJ_DEF(pj_status_t) pjmedia_sdp_attr_get_fmtp( const pjmedia_sdp_attr *attr,
					       pjmedia_sdp_fmtp *fmtp)
{
    const char *p = attr->value.ptr;
    const char *end = attr->value.ptr + attr->value.slen;
    pj_str_t token;

    PJ_ASSERT_RETURN(pj_strcmp2(&attr->name, "fmtp")==0, PJ_EINVALIDOP);

    /* fmtp BNF:
     *	a=fmtp:<format> <format specific parameter>
     */

    /* Get format. */
    token.ptr = (char*)p;
    while (pj_isdigit(*p) && p!=end)
	++p;
    token.slen = p - token.ptr;
    if (token.slen == 0)
	return PJMEDIA_SDP_EINFMTP;

    fmtp->fmt = token;

    /* Expecting space after format. */
    if (*p != ' ') return PJMEDIA_SDP_EINFMTP;

    /* Get space. */
    ++p;

    /* Set the remaining string as fmtp format parameter. */
    fmtp->fmt_param.ptr = (char*)p;
    fmtp->fmt_param.slen = end - p;

    return PJ_SUCCESS;
}
예제 #8
0
static int setup_sdes_srtp(struct ast_sip_session_media *session_media,
	const struct pjmedia_sdp_media *stream)
{
	int i;

	for (i = 0; i < stream->attr_count; i++) {
		pjmedia_sdp_attr *attr;
		RAII_VAR(char *, crypto_str, NULL, ast_free);

		/* check the stream for the required crypto attribute */
		attr = stream->attr[i];
		if (pj_strcmp2(&attr->name, "crypto")) {
			continue;
		}

		crypto_str = ast_strndup(attr->value.ptr, attr->value.slen);
		if (!crypto_str) {
			return -1;
		}

		if (setup_srtp(session_media)) {
			return -1;
		}

		if (!ast_sdp_crypto_process(session_media->rtp, session_media->srtp, crypto_str)) {
			/* found a valid crypto attribute */
			return 0;
		}

		ast_debug(1, "Ignoring crypto offer with unsupported parameters: %s\n", crypto_str);
	}

	/* no usable crypto attributes found */
	return -1;
}
예제 #9
0
static pj_status_t server_get_password( const pj_stun_msg *msg,
					void *user_data, 
				        const pj_str_t *realm,
				        const pj_str_t *username,
					pj_pool_t *pool,
					pj_stun_passwd_type *data_type,
					pj_str_t *data)
{
    PJ_UNUSED_ARG(msg);
    PJ_UNUSED_ARG(user_data);
    PJ_UNUSED_ARG(pool);

    if (server->auth_type == PJ_STUN_AUTH_SHORT_TERM) {
	if (realm && realm->slen) {
	    PJ_LOG(4,(THIS_FILE, "    server expecting short term"));
	    return -1;
	}
    } else {
	if (realm==NULL || realm->slen==0) {
	    PJ_LOG(4,(THIS_FILE, "    realm not present"));
	    return -1;
	}
    }

    if (pj_strcmp2(username, USERNAME) != 0) {
	PJ_LOG(4,(THIS_FILE, "    wrong username"));
	return -1;
    }

    *data_type = PJ_STUN_PASSWD_PLAIN;
    *data = pj_str(PASSWORD);

    return PJ_SUCCESS;
}
예제 #10
0
static pj_bool_t my_on_rx_request(pjsip_rx_data *rdata)
{
    /* Check that this is our request. */
    if (pj_strcmp2(&rdata->msg_info.cid->id, CALL_ID_HDR) == 0) {
	/* It is! */
	/* Send response. */
	pjsip_tx_data *tdata;
	pjsip_response_addr res_addr;
	pj_status_t status;

	status = pjsip_endpt_create_response( endpt, rdata, 200, NULL, &tdata);
	if (status != PJ_SUCCESS) {
	    recv_status = status;
	    return PJ_TRUE;
	}
	status = pjsip_get_response_addr( tdata->pool, rdata, &res_addr);
	if (status != PJ_SUCCESS) {
	    recv_status = status;
	    pjsip_tx_data_dec_ref(tdata);
	    return PJ_TRUE;
	}
	status = pjsip_endpt_send_response( endpt, &res_addr, tdata, NULL, NULL);
	if (status != PJ_SUCCESS) {
	    recv_status = status;
	    pjsip_tx_data_dec_ref(tdata);
	    return PJ_TRUE;
	}
	return PJ_TRUE;
    }
    
    /* Not ours. */
    return PJ_FALSE;
}
예제 #11
0
static pj_bool_t my_on_rx_response(pjsip_rx_data *rdata)
{
    if (pj_strcmp2(&rdata->msg_info.cid->id, CALL_ID_HDR) == 0) {
	pj_get_timestamp(&my_recv_time);
	recv_status = PJ_SUCCESS;
	return PJ_TRUE;
    }
    return PJ_FALSE;
}
static int verify_part(pjsip_multipart_part *part,
		       char *h_content_type,
		       char *h_content_subtype,
		       char *boundary,
		       int h_content_length,
		       const char *body)
{
    pjsip_ctype_hdr *ctype_hdr = NULL;
    pjsip_clen_hdr *clen_hdr = NULL;
    pjsip_hdr *hdr;
    pj_str_t the_body;

    hdr = part->hdr.next;
    while (hdr != &part->hdr) {
	if (hdr->type == PJSIP_H_CONTENT_TYPE)
	    ctype_hdr = (pjsip_ctype_hdr*)hdr;
	else if (hdr->type == PJSIP_H_CONTENT_LENGTH)
	    clen_hdr = (pjsip_clen_hdr*)hdr;
	hdr = hdr->next;
    }

    if (h_content_type) {
	pjsip_media_type mt;

	if (ctype_hdr == NULL)
	    return -10;

	init_media_type(&mt, h_content_type, h_content_subtype, boundary);

	if (pjsip_media_type_cmp(&ctype_hdr->media, &mt, 2) != 0)
	    return -20;

    } else {
	if (ctype_hdr)
	    return -30;
    }

    if (h_content_length >= 0) {
	if (clen_hdr == NULL)
	    return -50;
	if (clen_hdr->len != h_content_length)
	    return -60;
    } else {
	if (clen_hdr)
	    return -70;
    }

    the_body.ptr = (char*)part->body->data;
    the_body.slen = part->body->len;

    if (pj_strcmp2(&the_body, body) != 0)
	return -90;

    return 0;
}
예제 #13
0
PJ_DEF(pj_status_t) pjmedia_sdp_attr_get_rtcp(const pjmedia_sdp_attr *attr,
					      pjmedia_sdp_rtcp_attr *rtcp)
{
    pj_scanner scanner;
    pj_str_t token;
    pj_status_t status = -1;
    PJ_USE_EXCEPTION;

    PJ_ASSERT_RETURN(pj_strcmp2(&attr->name, "rtcp")==0, PJ_EINVALIDOP);

    init_sdp_parser();

    /* fmtp BNF:
     *	a=rtcp:<port> [nettype addrtype address]
     */

    pj_scan_init(&scanner, (char*)attr->value.ptr, attr->value.slen,
		 PJ_SCAN_AUTOSKIP_WS, &on_scanner_error);

    /* Init */
    rtcp->net_type.slen = rtcp->addr_type.slen = rtcp->addr.slen = 0;

    /* Parse */
    PJ_TRY {

	/* Get the port */
	pj_scan_get(&scanner, &cs_token, &token);
	rtcp->port = pj_strtoul(&token);

	/* Have address? */
	if (!pj_scan_is_eof(&scanner)) {

	    /* Get network type */
	    pj_scan_get(&scanner, &cs_token, &rtcp->net_type);

	    /* Get address type */
	    pj_scan_get(&scanner, &cs_token, &rtcp->addr_type);

	    /* Get the address */
	    pj_scan_get(&scanner, &cs_token, &rtcp->addr);

	}

	status = PJ_SUCCESS;

    }
    PJ_CATCH_ANY {
	status = PJMEDIA_SDP_EINRTCP;
    }
    PJ_END;

    pj_scan_fini(&scanner);
    return status;
}
예제 #14
0
파일: sock.c 프로젝트: tibastral/symphonie
static int format_test(void)
{
    pj_str_t s = pj_str(ADDRESS);
    unsigned char *p;
    pj_in_addr addr;
    char zero[64];
    pj_sockaddr_in addr2;
    const pj_str_t *hostname;

    PJ_LOG(3,("test", "...format_test()"));
    
    /* pj_inet_aton() */
    if (pj_inet_aton(&s, &addr) != 1)
	return -10;
    
    /* Check the result. */
    p = (unsigned char*)&addr;
    if (p[0]!=A0 || p[1]!=A1 || p[2]!=A2 || p[3]!=A3) {
	PJ_LOG(3,("test", "  error: mismatched address. p0=%d, p1=%d, "
			  "p2=%d, p3=%d", p[0] & 0xFF, p[1] & 0xFF, 
			   p[2] & 0xFF, p[3] & 0xFF));
	return -15;
    }

    /* pj_inet_ntoa() */
    p = (unsigned char*) pj_inet_ntoa(addr);
    if (!p)
	return -20;

    if (pj_strcmp2(&s, (char*)p) != 0)
	return -30;

    /* Test that pj_sockaddr_in_init() initialize the whole structure, 
     * including sin_zero.
     */
    pj_sockaddr_in_init(&addr2, 0, 1000);
    pj_bzero(zero, sizeof(zero));
    if (pj_memcmp(addr2.sin_zero, zero, sizeof(addr2.sin_zero)) != 0)
	return -35;

    /* pj_gethostname() */
    hostname = pj_gethostname();
    if (!hostname || !hostname->ptr || !hostname->slen)
	return -40;

    PJ_LOG(3,("test", "....hostname is %.*s", 
	      (int)hostname->slen, hostname->ptr));

    /* pj_gethostaddr() */


    return 0;
}
예제 #15
0
static struct ast_sip_aor *find_aor(struct ast_sip_endpoint *endpoint, pjsip_uri *uri)
{
	char *configured_aors, *aor_name;
	pjsip_sip_uri *sip_uri;
	char *domain_name;
	RAII_VAR(struct ast_str *, id, NULL, ast_free);

	if (ast_strlen_zero(endpoint->aors)) {
		return NULL;
	}

	sip_uri = pjsip_uri_get_uri(uri);
	domain_name = ast_alloca(sip_uri->host.slen + 1);
	ast_copy_pj_str(domain_name, &sip_uri->host, sip_uri->host.slen + 1);

	configured_aors = ast_strdupa(endpoint->aors);

	/* Iterate the configured AORs to see if the user or the user+domain match */
	while ((aor_name = strsep(&configured_aors, ","))) {
		struct ast_sip_domain_alias *alias = NULL;

		if (!pj_strcmp2(&sip_uri->user, aor_name)) {
			break;
		}

		if (!id && !(id = ast_str_create(sip_uri->user.slen + sip_uri->host.slen + 2))) {
			return NULL;
		}

		ast_str_set(&id, 0, "%.*s@", (int)sip_uri->user.slen, sip_uri->user.ptr);
		if ((alias = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "domain_alias", domain_name))) {
			ast_str_append(&id, 0, "%s", alias->domain);
			ao2_cleanup(alias);
		} else {
			ast_str_append(&id, 0, "%s", domain_name);
		}

		if (!strcmp(aor_name, ast_str_buffer(id))) {
			ast_free(id);
			break;
		}
	}

	if (ast_strlen_zero(aor_name)) {
		return NULL;
	}

	return ast_sip_location_retrieve_aor(aor_name);
}
/*!
 * \brief Lookup callback for authentication verification
 *
 * This function is called when we call pjsip_auth_srv_verify(). It
 * expects us to verify that the realm and account name from the
 * Authorization header is correct. We are then supposed to supply
 * a password or MD5 sum of credentials.
 *
 * \param pool A memory pool we can use for allocations
 * \param realm The realm from the Authorization header
 * \param acc_name the user from the Authorization header
 * \param[out] info The credentials we need to fill in
 * \retval PJ_SUCCESS Successful authentication
 * \retval other Unsuccessful
 */
static pj_status_t digest_lookup(pj_pool_t *pool, const pj_str_t *realm,
		const pj_str_t *acc_name, pjsip_cred_info *info)
{
	RAII_VAR(struct ast_sip_auth *, auth, get_auth(), ao2_cleanup);
	if (!auth) {
		return PJSIP_SC_FORBIDDEN;
	}

	if (auth->type == AST_SIP_AUTH_TYPE_ARTIFICIAL) {
		return PJSIP_SC_FORBIDDEN;
	}

	if (pj_strcmp2(realm, auth->realm)) {
		return PJSIP_SC_FORBIDDEN;
	}
	if (pj_strcmp2(acc_name, auth->auth_user)) {
		return PJSIP_SC_FORBIDDEN;
	}

	pj_strdup2(pool, &info->realm, auth->realm);
	pj_strdup2(pool, &info->username, auth->auth_user);

	switch (auth->type) {
	case AST_SIP_AUTH_TYPE_USER_PASS:
		pj_strdup2(pool, &info->data, auth->auth_pass);
		info->data_type = PJSIP_CRED_DATA_PLAIN_PASSWD;
		break;
	case AST_SIP_AUTH_TYPE_MD5:
		pj_strdup2(pool, &info->data, auth->md5_creds);
		info->data_type = PJSIP_CRED_DATA_DIGEST;
		break;
	default:
		return PJSIP_SC_FORBIDDEN;
	}
	return PJ_SUCCESS;
}
static int find_challenge(const pjsip_rx_data *rdata, const struct ast_sip_auth *auth)
{
	struct pjsip_authorization_hdr *auth_hdr = (pjsip_authorization_hdr *) &rdata->msg_info.msg->hdr;
	int challenge_found = 0;
	char nonce[64];

	while ((auth_hdr = (pjsip_authorization_hdr *) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_AUTHORIZATION, auth_hdr->next))) {
		ast_copy_pj_str(nonce, &auth_hdr->credential.digest.nonce, sizeof(nonce));
		if (check_nonce(nonce, rdata, auth) && !pj_strcmp2(&auth_hdr->credential.digest.realm, auth->realm)) {
			challenge_found = 1;
			break;
		}
	}

	return challenge_found;
}
예제 #18
0
static pj_bool_t server_verify_nonce(const pj_stun_msg *msg,
				     void *user_data,
				     const pj_str_t *realm,
				     const pj_str_t *username,
				     const pj_str_t *nonce)
{
    PJ_UNUSED_ARG(msg);
    PJ_UNUSED_ARG(user_data);
    PJ_UNUSED_ARG(realm);
    PJ_UNUSED_ARG(username);

    if (pj_strcmp2(nonce, NONCE) != 0)
	return PJ_FALSE;

    return PJ_TRUE;
}
예제 #19
0
/// Checks whether the supplied message contains the extension in the
/// Supported header.
pj_bool_t PJUtils::msg_supports_extension(pjsip_msg* msg, const char* extension)
{
  pjsip_supported_hdr* supported_hdr = (pjsip_supported_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_SUPPORTED, NULL);
  if (!supported_hdr)
  {
    return PJ_FALSE;
  }
  for (unsigned ii = 0; ii < supported_hdr->count; ++ii)
  {
    if (pj_strcmp2(&supported_hdr->values[ii], extension) == 0)
    {
      return PJ_TRUE;
    }
  }
  return PJ_FALSE;
}
예제 #20
0
/*! \brief Callback for when T.38 reinvite SDP is created */
static int t38_reinvite_sdp_cb(struct ast_sip_session *session, pjmedia_sdp_session *sdp)
{
	int stream;

	/* Move the image media stream to the front and have it as the only stream, pjmedia will fill in
	 * dummy streams for the rest
	 */
	for (stream = 0; stream < sdp->media_count++; ++stream) {
		if (!pj_strcmp2(&sdp->media[stream]->desc.media, "image")) {
			sdp->media[0] = sdp->media[stream];
			sdp->media_count = 1;
			break;
		}
	}

	return 0;
}
예제 #21
0
/*! \brief figure out if media stream has crypto lines for sdes */
static int media_stream_has_crypto(const struct pjmedia_sdp_media *stream)
{
	int i;

	for (i = 0; i < stream->attr_count; i++) {
		pjmedia_sdp_attr *attr;

		/* check the stream for the required crypto attribute */
		attr = stream->attr[i];
		if (pj_strcmp2(&attr->name, "crypto")) {
			continue;
		}

		return 1;
	}

	return 0;
}
예제 #22
0
static pjmedia_sdp_attr * find_remove_sdp_attrs(unsigned *cnt,
                                                pjmedia_sdp_attr *attr[],
                                                unsigned cnt_attr_to_remove,
                                                const char* attr_to_remove[])
{
    pjmedia_sdp_attr *found_attr = NULL;
    int i;

    for (i=0; i<(int)*cnt; ++i) {
	unsigned j;
	for (j=0; j<cnt_attr_to_remove; ++j) {
	    if (pj_strcmp2(&attr[i]->name, attr_to_remove[j])==0) {
		if (!found_attr) found_attr = attr[i];
		pj_array_erase(attr, sizeof(attr[0]), *cnt, i);
		--(*cnt);
		--i;
		break;
	    }
	}
    }

    return found_attr;
}
예제 #23
0
//////////////////////////////////////////////////////////////////////////
// Request handler to receive out-of-dialog NOTIFY (from Asterisk)
static pj_bool_t on_rx_request(pjsip_rx_data *rdata)
{
	if (strstr(pj_strbuf(&rdata->msg_info.msg->line.req.method.name),
		"NOTIFY"))
	{
		pjsip_generic_string_hdr * hdr;
		pj_str_t did_str = pj_str("Event");
		hdr = (pjsip_generic_string_hdr*) pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &did_str, NULL);
		if (!hdr) return false;

		// We have an event header, now determine if it's contents are "message-summary"
		if (pj_strcmp2(&hdr->hvalue, "message-summary")) return false;

		pjsip_msg_body * body_p = rdata->msg_info.msg->body;

		wchar_t* buf = (wchar_t*)pj_pool_alloc(app_config.pool, body_p->len);
		buf = PJ_STRING_TO_NATIVE((char*)body_p->data, buf, body_p->len);

		// Process body message as desired...
		if (strncmp((char*)body_p->data, "Messages-Waiting: yes", body_p->len) != 0)
		{
			if (cb_mwi != 0) cb_mwi(1, buf);
		}
		else
		{
			if (cb_mwi != 0) cb_mwi(0, buf);
		}
		PJ_LOG(3,(THIS_FILE,"MWI message: %s", buf));
	}

	pjsip_endpt_respond_stateless(pjsip_ua_get_endpt(pjsip_ua_instance()),
		rdata, 200, NULL,
		NULL, NULL);

	return PJ_TRUE;
}
예제 #24
0
파일: sdp_neg.c 프로젝트: deveck/Deveck.TAM
/* Try to match offer with answer. */
static pj_status_t match_offer(pj_pool_t *pool,
			       const pjmedia_sdp_media *offer,
			       const pjmedia_sdp_media *preanswer,
			       const pjmedia_sdp_media *orig_local,
			       pjmedia_sdp_media **p_answer)
{
    unsigned i;
    pj_bool_t offer_has_codec = 0,
	      offer_has_telephone_event = 0,
	      offer_has_other = 0,
	      found_matching_codec = 0,
	      found_matching_telephone_event = 0,
	      found_matching_other = 0;
    unsigned pt_answer_count = 0;
    pj_str_t pt_answer[PJMEDIA_MAX_SDP_FMT];
    pjmedia_sdp_media *answer;

    /* With the addition of telephone-event and dodgy MS RTC SDP, 
     * the answer generation algorithm looks really shitty...
     */
    for (i=0; i<offer->desc.fmt_count; ++i) {
	unsigned j;
	
	if (pj_isdigit(*offer->desc.fmt[i].ptr)) {
	    /* This is normal/standard payload type, where it's identified
	     * by payload number.
	     */
	    unsigned pt;

	    pt = pj_strtoul(&offer->desc.fmt[i]);
	    
	    if (pt < 96) {
		/* For static payload type, it's enough to compare just
		 * the payload number.
		 */

		offer_has_codec = 1;

		/* We just need to select one codec. 
		 * Continue if we have selected matching codec for previous 
		 * payload.
		 */
		if (found_matching_codec)
		    continue;

		/* Find matching codec in local descriptor. */
		for (j=0; j<preanswer->desc.fmt_count; ++j) {
		    unsigned p;
		    p = pj_strtoul(&preanswer->desc.fmt[j]);
		    if (p == pt && pj_isdigit(*preanswer->desc.fmt[j].ptr)) {
			found_matching_codec = 1;
			pt_answer[pt_answer_count++] = preanswer->desc.fmt[j];
			break;
		    }
		}

	    } else {
		/* This is dynamic payload type.
		 * For dynamic payload type, we must look the rtpmap and
		 * compare the encoding name.
		 */
		const pjmedia_sdp_attr *a;
		pjmedia_sdp_rtpmap or_;
		pj_bool_t is_codec;

		/* Get the rtpmap for the payload type in the offer. */
		a = pjmedia_sdp_media_find_attr2(offer, "rtpmap", 
						 &offer->desc.fmt[i]);
		if (!a) {
		    pj_assert(!"Bug! Offer should have been validated");
		    return PJMEDIA_SDP_EMISSINGRTPMAP;
		}
		pjmedia_sdp_attr_get_rtpmap(a, &or_);

		if (!pj_strcmp2(&or_.enc_name, "telephone-event")) {
		    offer_has_telephone_event = 1;
		    if (found_matching_telephone_event)
			continue;
		    is_codec = 0;
		} else {
		    offer_has_codec = 1;
		    if (found_matching_codec)
			continue;
		    is_codec = 1;
		}
		
		/* Find paylaod in our initial SDP with matching 
		 * encoding name and clock rate.
		 */
		for (j=0; j<preanswer->desc.fmt_count; ++j) {
		    a = pjmedia_sdp_media_find_attr2(preanswer, "rtpmap", 
						     &preanswer->desc.fmt[j]);
		    if (a) {
			pjmedia_sdp_rtpmap lr;
			pjmedia_sdp_attr_get_rtpmap(a, &lr);

			/* See if encoding name, clock rate, and
			 * channel count  match 
			 */
			if (!pj_stricmp(&or_.enc_name, &lr.enc_name) &&
			    or_.clock_rate == lr.clock_rate &&
			    (pj_strcmp(&or_.param, &lr.param)==0 ||
			     (or_.param.slen==1 && *or_.param.ptr=='1'))) 
			{
			    /* Match! */
			    if (is_codec) {
				/* Further check for G7221, negotiate bitrate. */
				if (pj_strcmp2(&or_.enc_name, "G7221")  == 0 &&
				    match_g7221(offer, i, preanswer, j) == 0)
				{
				    continue;
				}
				found_matching_codec = 1;
			    } else {
				found_matching_telephone_event = 1;
			    }

			    pt_answer[pt_answer_count++] = preanswer->desc.fmt[j];
			    break;
			}
		    }
		}
	    }

	} else {
	    /* This is a non-standard, brain damaged SDP where the payload
	     * type is non-numeric. It exists e.g. in Microsoft RTC based
	     * UA, to indicate instant messaging capability.
	     * Example:
	     *	- m=x-ms-message 5060 sip null
	     */
	    offer_has_other = 1;
	    if (found_matching_other)
		continue;

	    for (j=0; j<preanswer->desc.fmt_count; ++j) {
		if (!pj_strcmp(&offer->desc.fmt[i], &preanswer->desc.fmt[j])) {
		    /* Match */
		    found_matching_other = 1;
		    pt_answer[pt_answer_count++] = preanswer->desc.fmt[j];
		    break;
		}
	    }
	}
    }

    /* See if all types of offer can be matched. */
    if (offer_has_codec && !found_matching_codec) {
	return PJMEDIA_SDPNEG_NOANSCODEC;
    }

    /* If this comment is removed, negotiation will fail if remote has offered
       telephone-event and local is not configured with telephone-event

    if (offer_has_telephone_event && !found_matching_telephone_event) {
	return PJMEDIA_SDPNEG_NOANSTELEVENT;
    }
    */

    if (offer_has_other && !found_matching_other) {
	return PJMEDIA_SDPNEG_NOANSUNKNOWN;
    }

    /* Seems like everything is in order.
     * Build the answer by cloning from local media, but rearrange the payload
     * to suit the offer.
     */
    answer = pjmedia_sdp_media_clone(pool, orig_local);
    for (i=0; i<pt_answer_count; ++i) {
	unsigned j;
	for (j=i; j<answer->desc.fmt_count; ++j) {
	    if (!pj_strcmp(&answer->desc.fmt[j], &pt_answer[i]))
		break;
	}
	pj_assert(j != answer->desc.fmt_count);
	str_swap(&answer->desc.fmt[i], &answer->desc.fmt[j]);
    }
    
    /* Remove unwanted local formats. */
    for (i=pt_answer_count; i<answer->desc.fmt_count; ++i) {
	pjmedia_sdp_attr *a;

	/* Remove rtpmap for this format */
	a = pjmedia_sdp_media_find_attr2(answer, "rtpmap", 
					 &answer->desc.fmt[i]);
	if (a) {
	    pjmedia_sdp_media_remove_attr(answer, a);
	}

	/* Remove fmtp for this format */
	a = pjmedia_sdp_media_find_attr2(answer, "fmtp", 
					 &answer->desc.fmt[i]);
	if (a) {
	    pjmedia_sdp_media_remove_attr(answer, a);
	}
    }
    answer->desc.fmt_count = pt_answer_count;

    /* If offer has zero port, set our answer with zero port too */
    if (offer->desc.port==0)
	answer->desc.port = 0;

    /* Update media direction. */
    update_media_direction(pool, offer, answer);

    *p_answer = answer;
    return PJ_SUCCESS;
}
예제 #25
0
파일: sdp_neg.c 프로젝트: deveck/Deveck.TAM
/* Update single local media description to after receiving answer
 * from remote.
 */
static pj_status_t process_m_answer( pj_pool_t *pool,
				     pjmedia_sdp_media *offer,
				     pjmedia_sdp_media *answer,
				     pj_bool_t allow_asym)
{
    unsigned i;

    /* Check that the media type match our offer. */

    if (pj_strcmp(&answer->desc.media, &offer->desc.media)!=0) {
	/* The media type in the answer is different than the offer! */
	return PJMEDIA_SDPNEG_EINVANSMEDIA;
    }


    /* Check that transport in the answer match our offer. */

    /* At this point, transport type must be compatible, 
     * the transport instance will do more validation later.
     */
    if (pjmedia_sdp_transport_cmp(&answer->desc.transport, 
				  &offer->desc.transport) 
	!= PJ_SUCCESS)
    {
	return PJMEDIA_SDPNEG_EINVANSTP;
    }


    /* Check if remote has rejected our offer */
    
    if (answer->desc.port == 0) {
	
	/* Remote has rejected our offer. 
	 * Set our port to zero too in active SDP.
	 */
	offer->desc.port = 0;
    }


    /* Process direction attributes */
    update_media_direction(pool, answer, offer);
 
    /* If asymetric media is allowed, then just check that remote answer has 
     * codecs that are within the offer. 
     *
     * Otherwise if asymetric media is not allowed, then we will choose only
     * one codec in our initial offer to match the answer.
     */
    if (allow_asym) {
	for (i=0; i<answer->desc.fmt_count; ++i) {
	    unsigned j;
	    pj_str_t *rem_fmt = &answer->desc.fmt[i];

	    for (j=0; j<offer->desc.fmt_count; ++j) {
		if (pj_strcmp(rem_fmt, &answer->desc.fmt[j])==0)
		    break;
	    }

	    if (j != offer->desc.fmt_count) {
		/* Found at least one common codec. */
		break;
	    }
	}

	if (i == answer->desc.fmt_count) {
	    /* No common codec in the answer! */
	    return PJMEDIA_SDPNEG_EANSNOMEDIA;
	}

	PJ_TODO(CHECK_SDP_NEGOTIATION_WHEN_ASYMETRIC_MEDIA_IS_ALLOWED);

    } else {
	/* Remove all format in the offer that has no matching answer */
	for (i=0; i<offer->desc.fmt_count;) {
	    unsigned pt;
	    pj_uint32_t j;
	    pj_str_t *fmt = &offer->desc.fmt[i];
	    

	    /* Find matching answer */
	    pt = pj_strtoul(fmt);

	    if (pt < 96) {
		for (j=0; j<answer->desc.fmt_count; ++j) {
		    if (pj_strcmp(fmt, &answer->desc.fmt[j])==0)
			break;
		}
	    } else {
		/* This is dynamic payload type.
		 * For dynamic payload type, we must look the rtpmap and
		 * compare the encoding name.
		 */
		const pjmedia_sdp_attr *a;
		pjmedia_sdp_rtpmap or_;

		/* Get the rtpmap for the payload type in the offer. */
		a = pjmedia_sdp_media_find_attr2(offer, "rtpmap", fmt);
		if (!a) {
		    pj_assert(!"Bug! Offer should have been validated");
		    return PJ_EBUG;
		}
		pjmedia_sdp_attr_get_rtpmap(a, &or_);

		/* Find paylaod in answer SDP with matching 
		 * encoding name and clock rate.
		 */
		for (j=0; j<answer->desc.fmt_count; ++j) {
		    a = pjmedia_sdp_media_find_attr2(answer, "rtpmap", 
						     &answer->desc.fmt[j]);
		    if (a) {
			pjmedia_sdp_rtpmap ar;
			pjmedia_sdp_attr_get_rtpmap(a, &ar);

			/* See if encoding name, clock rate, and channel
			 * count match 
			 */
			if (!pj_stricmp(&or_.enc_name, &ar.enc_name) &&
			    or_.clock_rate == ar.clock_rate &&
			    (pj_stricmp(&or_.param, &ar.param)==0 ||
			     (ar.param.slen==1 && *ar.param.ptr=='1')))
			{
			    /* Further check for G7221, negotiate bitrate. */
			    if (pj_strcmp2(&or_.enc_name, "G7221") == 0) {
				if (match_g7221(offer, i, answer, j))
				    break;
			    } else {
				/* Match! */
				break;
			    }
			}
		    }
		}
	    }

	    if (j == answer->desc.fmt_count) {
		/* This format has no matching answer.
		 * Remove it from our offer.
		 */
		pjmedia_sdp_attr *a;

		/* Remove rtpmap associated with this format */
		a = pjmedia_sdp_media_find_attr2(offer, "rtpmap", fmt);
		if (a)
		    pjmedia_sdp_media_remove_attr(offer, a);

		/* Remove fmtp associated with this format */
		a = pjmedia_sdp_media_find_attr2(offer, "fmtp", fmt);
		if (a)
		    pjmedia_sdp_media_remove_attr(offer, a);

		/* Remove this format from offer's array */
		pj_array_erase(offer->desc.fmt, sizeof(offer->desc.fmt[0]),
			       offer->desc.fmt_count, i);
		--offer->desc.fmt_count;

	    } else {
		++i;
	    }
	}

	/* Arrange format in the offer so the order match the priority
	 * in the answer
	 */
	for (i=0; i<answer->desc.fmt_count; ++i) {
	    unsigned j;
	    pj_str_t *fmt = &answer->desc.fmt[i];

	    for (j=i; j<offer->desc.fmt_count; ++j) {
		if (pj_strcmp(fmt, &offer->desc.fmt[j])==0) {
		    str_swap(&offer->desc.fmt[i], &offer->desc.fmt[j]);
		    break;
		}
	    }
	}
    }

    /* Looks okay */
    return PJ_SUCCESS;
}
예제 #26
0
static int run_client_test(const char *title,

			   pj_bool_t server_responding,
			   pj_stun_auth_type server_auth_type,

			   pj_stun_auth_type client_auth_type,
			   const char *realm,
			   const char *username,
			   const char *nonce,
			   const char *password,
			   pj_bool_t dummy_mi,

			   pj_bool_t expected_error,
			   pj_status_t expected_code,
			   const char *expected_realm,
			   const char *expected_nonce,
			   
			   int (*more_check)(void))
{
    pj_pool_t *pool;
    pj_stun_session_cb sess_cb;
    pj_stun_auth_cred cred;
    pj_stun_tx_data *tdata;
    pj_status_t status;
    int rc = 0;
    
    PJ_LOG(3,(THIS_FILE, "   %s test", title));

    /* Create client */
    pool = pj_pool_create(mem, "client", 1000, 1000, NULL);
    client = PJ_POOL_ZALLOC_T(pool, struct client);
    client->pool = pool;
    client->responding = PJ_TRUE;

    /* Create STUN session */
    pj_bzero(&sess_cb, sizeof(sess_cb));
    sess_cb.on_request_complete = &client_on_request_complete;
    sess_cb.on_send_msg = &client_send_msg;
    status = pj_stun_session_create(&stun_cfg, "client", &sess_cb, PJ_FALSE, NULL, &client->sess);
    if (status != PJ_SUCCESS) {
	destroy_client_server();
	return -200;
    }

    /* Create semaphore */
    status = pj_sem_create(pool, "client", 0, 1, &client->test_complete);
    if (status != PJ_SUCCESS) {
	destroy_client_server();
	return -205;
    }

    /* Create client socket */
    status = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &client->sock);
    if (status != PJ_SUCCESS) {
	destroy_client_server();
	return -210;
    }

    /* Bind client socket */
    status = pj_sock_bind_in(client->sock, 0, 0);
    if (status != PJ_SUCCESS) {
	destroy_client_server();
	return -220;
    }

    /* Create client thread */
    status = pj_thread_create(pool, "client", &client_thread, NULL, 0, 0, &client->thread);
    if (status != PJ_SUCCESS) {
	destroy_client_server();
	return -230;
    }

    /* Initialize credential */
    pj_bzero(&cred, sizeof(cred));
    cred.type = PJ_STUN_AUTH_CRED_STATIC;
    if (realm) cred.data.static_cred.realm = pj_str((char*)realm);
    if (username) cred.data.static_cred.username = pj_str((char*)username);
    if (nonce) cred.data.static_cred.nonce = pj_str((char*)nonce);
    if (password) cred.data.static_cred.data = pj_str((char*)password);
    cred.data.static_cred.data_type = PJ_STUN_PASSWD_PLAIN;
    status = pj_stun_session_set_credential(client->sess, client_auth_type, &cred);
    if (status != PJ_SUCCESS) {
	destroy_client_server();
	return -240;
    }

    /* Create the server */
    status = create_std_server(server_auth_type, server_responding);
    if (status != 0) {
	destroy_client_server();
	return status;
    }

    /* Create request */
    status = pj_stun_session_create_req(client->sess, PJ_STUN_BINDING_REQUEST, 
					PJ_STUN_MAGIC, NULL, &tdata);
    if (status != PJ_SUCCESS) {
	destroy_client_server();
	return -250;
    }

    /* Add our own attributes if client authentication is set to none */
    if (client_auth_type == PJ_STUN_AUTH_NONE) {
	pj_str_t tmp;
	if (realm)
	    pj_stun_msg_add_string_attr(tdata->pool, tdata->msg, PJ_STUN_ATTR_REALM, pj_cstr(&tmp, realm));
	if (username)
	    pj_stun_msg_add_string_attr(tdata->pool, tdata->msg, PJ_STUN_ATTR_USERNAME, pj_cstr(&tmp, username));
	if (nonce)
	    pj_stun_msg_add_string_attr(tdata->pool, tdata->msg, PJ_STUN_ATTR_NONCE, pj_cstr(&tmp, nonce));
	if (password) {
	    // ignored
	}
	if (dummy_mi) {
	    pj_stun_msgint_attr *mi;

	    pj_stun_msgint_attr_create(tdata->pool, &mi);
	    pj_stun_msg_add_attr(tdata->msg, &mi->hdr);
	}
	   
    }

    /* Send the request */
    status = pj_stun_session_send_msg(client->sess, NULL, PJ_FALSE, PJ_TRUE, &server->addr,
				      pj_sockaddr_get_len(&server->addr), tdata);
    if (status != PJ_SUCCESS) {
	destroy_client_server();
	return -270;
    }

    /* Wait until test complete */
    pj_sem_wait(client->test_complete);


    /* Verify response */
    if (expected_error) {
	if (expected_code != client->response_status) {
	    char e1[PJ_ERR_MSG_SIZE], e2[PJ_ERR_MSG_SIZE];

	    pj_strerror(expected_code, e1, sizeof(e1));
	    pj_strerror(client->response_status, e2, sizeof(e2));

	    PJ_LOG(3,(THIS_FILE, "    err: expecting %d (%s) but got %d (%s) response",
		      expected_code, e1, client->response_status, e2));
	    rc = -500;
	} 

    } else {
	int res_code = 0;
	pj_stun_realm_attr *arealm;
	pj_stun_nonce_attr *anonce;

	if (client->response_status != 0) {
	    PJ_LOG(3,(THIS_FILE, "    err: expecting successful operation but got error %d", 
		      client->response_status));
	    rc = -600;
	    goto done;
	} 

	if (PJ_STUN_IS_ERROR_RESPONSE(client->response->hdr.type)) {
	    pj_stun_errcode_attr *aerr = NULL;

	    aerr = (pj_stun_errcode_attr*)
		   pj_stun_msg_find_attr(client->response, 
					 PJ_STUN_ATTR_ERROR_CODE, 0);
	    if (aerr == NULL) {
		PJ_LOG(3,(THIS_FILE, "    err: received error response without ERROR-CODE"));
		rc = -610;
		goto done;
	    }

	    res_code = aerr->err_code;
	} else {
	    res_code = 0;
	}

	/* Check that code matches */
	if (expected_code != res_code) {
	    PJ_LOG(3,(THIS_FILE, "    err: expecting response code %d but got %d",
		      expected_code, res_code));
	    rc = -620;
	    goto done;
	}

	/* Find REALM and NONCE attributes */
	arealm = (pj_stun_realm_attr*)
	         pj_stun_msg_find_attr(client->response, PJ_STUN_ATTR_REALM, 0);
	anonce = (pj_stun_nonce_attr*)
	         pj_stun_msg_find_attr(client->response, PJ_STUN_ATTR_NONCE, 0);

	if (expected_realm) {
	    if (arealm == NULL) {
		PJ_LOG(3,(THIS_FILE, "    err: expecting REALM in esponse"));
		rc = -630;
		goto done;
	    }
	    if (pj_strcmp2(&arealm->value, expected_realm)!=0) {
		PJ_LOG(3,(THIS_FILE, "    err: REALM mismatch in response"));
		rc = -640;
		goto done;
	    }
	} else {
	    if (arealm != NULL) {
		PJ_LOG(3,(THIS_FILE, "    err: non expecting REALM in response"));
		rc = -650;
		goto done;
	    }
	}

	if (expected_nonce) {
	    if (anonce == NULL) {
		PJ_LOG(3,(THIS_FILE, "    err: expecting NONCE in esponse"));
		rc = -660;
		goto done;
	    }
	    if (pj_strcmp2(&anonce->value, expected_nonce)!=0) {
		PJ_LOG(3,(THIS_FILE, "    err: NONCE mismatch in response"));
		rc = -670;
		goto done;
	    }
	} else {
	    if (anonce != NULL) {
		PJ_LOG(3,(THIS_FILE, "    err: non expecting NONCE in response"));
		rc = -680;
		goto done;
	    }
	}
    }

    /* Our tests are okay so far. Let caller do some more tests if
     * it wants to.
     */
    if (rc==0 && more_check) {
	rc = (*more_check)();
    }


done:
    destroy_client_server();
    return rc;
}
예제 #27
0
파일: sdp.c 프로젝트: tibastral/symphonie
/* Validate SDP session descriptor. */
PJ_DEF(pj_status_t) pjmedia_sdp_validate(const pjmedia_sdp_session *sdp)
{
    unsigned i;
    const pj_str_t STR_RTPMAP = { "rtpmap", 6 };

    CHECK( sdp != NULL, PJ_EINVAL);

    /* Validate origin line. */
    CHECK( sdp->origin.user.slen != 0, PJMEDIA_SDP_EINORIGIN);
    CHECK( pj_strcmp2(&sdp->origin.net_type, "IN")==0, 
	   PJMEDIA_SDP_EINORIGIN);
    CHECK( pj_strcmp2(&sdp->origin.addr_type, "IP4")==0 ||
	   pj_strcmp2(&sdp->origin.addr_type, "IP6")==0, 
	   PJMEDIA_SDP_EINORIGIN);
    CHECK( sdp->origin.addr.slen != 0, PJMEDIA_SDP_EINORIGIN);

    /* Validate subject line. */
    CHECK( sdp->name.slen != 0, PJMEDIA_SDP_EINNAME);

    /* Ignore start and stop time. */

    /* If session level connection info is present, validate it. */
    if (sdp->conn) {
	pj_status_t status = validate_sdp_conn(sdp->conn);
	if (status != PJ_SUCCESS)
	    return status;
    }

    /* Validate each media. */
    for (i=0; i<sdp->media_count; ++i) {
	const pjmedia_sdp_media *m = sdp->media[i];
	unsigned j;

	/* Validate the m= line. */
	CHECK( m->desc.media.slen != 0, PJMEDIA_SDP_EINMEDIA);
	CHECK( m->desc.transport.slen != 0, PJMEDIA_SDP_EINMEDIA);
	CHECK( m->desc.fmt_count != 0 || m->desc.port==0, PJMEDIA_SDP_ENOFMT);

	/* If media level connection info is present, validate it. */
	if (m->conn) {
	    pj_status_t status = validate_sdp_conn(m->conn);
	    if (status != PJ_SUCCESS)
		return status;
	}

	/* If media doesn't have connection info, then connection info
	 * must be present in the session.
	 */
	if (m->conn == NULL) {
	    if (sdp->conn == NULL)
		return PJMEDIA_SDP_EMISSINGCONN;
	}

	/* Verify payload type. */
	for (j=0; j<m->desc.fmt_count; ++j) {

	    /* Arrgh noo!! Payload type can be non-numeric!!
	     * RTC based programs sends "null" for instant messaging!
	     */
	    if (pj_isdigit(*m->desc.fmt[j].ptr)) {
		unsigned pt = pj_strtoul(&m->desc.fmt[j]);

		/* Payload type is between 0 and 127. 
		 */
		CHECK( pt <= 127, PJMEDIA_SDP_EINPT);

		/* If port is not zero, then for each dynamic payload type, an
		 * rtpmap attribute must be specified.
		 */
		if (m->desc.port != 0 && pt >= 96) {
		    const pjmedia_sdp_attr *a;

		    a = pjmedia_sdp_media_find_attr(m, &STR_RTPMAP, 
						    &m->desc.fmt[j]);
		    CHECK( a != NULL, PJMEDIA_SDP_EMISSINGRTPMAP);
		}
	    }
	}
    }

    /* Looks good. */
    return PJ_SUCCESS;
}
예제 #28
0
파일: sdp.c 프로젝트: tibastral/symphonie
PJ_DEF(pj_status_t) pjmedia_sdp_attr_get_rtpmap( const pjmedia_sdp_attr *attr,
						 pjmedia_sdp_rtpmap *rtpmap)
{
    pj_scanner scanner;
    pj_str_t token;
    pj_status_t status = -1;
    char term = 0;
    PJ_USE_EXCEPTION;

    PJ_ASSERT_RETURN(pj_strcmp2(&attr->name, "rtpmap")==0, PJ_EINVALIDOP);

    PJ_ASSERT_RETURN(attr->value.slen != 0, PJMEDIA_SDP_EINATTR);

    /* Check if input is null terminated, and null terminate if
     * necessary. Unfortunately this may crash the application if
     * attribute was allocated from a read-only memory location.
     * But this shouldn't happen as attribute's value normally is
     * null terminated.
     */
    if (attr->value.ptr[attr->value.slen] != 0 &&
	attr->value.ptr[attr->value.slen] != '\r')
    {
	pj_assert(!"Shouldn't happen");
	term = attr->value.ptr[attr->value.slen];
	attr->value.ptr[attr->value.slen] = '\0';
    }

    pj_scan_init(&scanner, (char*)attr->value.ptr, attr->value.slen,
		 PJ_SCAN_AUTOSKIP_WS, &on_scanner_error);

    /* rtpmap sample:
     *	a=rtpmap:98 L16/16000/2.
     */

    /* Init */
    rtpmap->pt.slen = rtpmap->param.slen = rtpmap->enc_name.slen = 0;
    rtpmap->clock_rate = 0;

    /* Parse */
    PJ_TRY {

	/* Get payload type. */
	pj_scan_get(&scanner, &cs_token, &rtpmap->pt);


	/* Get encoding name. */
	pj_scan_get(&scanner, &cs_token, &rtpmap->enc_name);

	/* Expecting '/' after encoding name. */
	if (pj_scan_get_char(&scanner) != '/') {
	    status = PJMEDIA_SDP_EINRTPMAP;
	    goto on_return;
	}


	/* Get the clock rate. */
	pj_scan_get(&scanner, &cs_digit, &token);
	rtpmap->clock_rate = pj_strtoul(&token);

	/* Expecting either '/' or EOF */
	if (*scanner.curptr == '/') {
	    pj_scan_get_char(&scanner);
	    rtpmap->param.ptr = scanner.curptr;
	    rtpmap->param.slen = scanner.end - scanner.curptr;
	} else {
	    rtpmap->param.slen = 0;
	}

	status = PJ_SUCCESS;
    }
    PJ_CATCH(SYNTAX_ERROR) {
	status = PJMEDIA_SDP_EINRTPMAP;
    }
    PJ_END;


on_return:
    pj_scan_fini(&scanner);
    if (term) {
	attr->value.ptr[attr->value.slen] = term;
    }
    return status;
}
예제 #29
0
static int parse_url_test()
{
    struct test_data
    {
	char *url;
	pj_status_t result;
	const char *username;
	const char *passwd;
	const char *host;
	int port;
	const char *path;
    } test_data[] =
    {
	/* Simple URL without '/' in the end */
        {"http://www.pjsip.org", PJ_SUCCESS, "", "", "www.pjsip.org", 80, "/"},

        /* Simple URL with port number but without '/' in the end */
        {"http://pjsip.org:8080", PJ_SUCCESS, "", "", "pjsip.org", 8080, "/"},

        /* URL with path */
        {"http://127.0.0.1:280/Joomla/index.php?option=com_content&task=view&id=5&Itemid=6",
        	PJ_SUCCESS, "", "", "127.0.0.1", 280,
        	"/Joomla/index.php?option=com_content&task=view&id=5&Itemid=6"},

	/* URL with port and path */
	{"http://pjsip.org:81/about-us/", PJ_SUCCESS, "", "", "pjsip.org", 81, "/about-us/"},

	/* unsupported protocol */
	{"ftp://www.pjsip.org", PJ_ENOTSUP, "", "", "", 80, ""},

	/* invalid format */
	{"http:/pjsip.org/about-us/", PJLIB_UTIL_EHTTPINURL, "", "", "", 80, ""},

	/* invalid port number */
	{"http://pjsip.org:xyz/", PJLIB_UTIL_EHTTPINPORT, "", "", "", 80, ""},

	/* with username and password */
	{"http://*****:*****@pjsip.org", PJ_SUCCESS, "user", "pass", "pjsip.org", 80, "/"},

	/* password only*/
	{"http://:[email protected]", PJ_SUCCESS, "", "pass", "pjsip.org", 80, "/"},

	/* user only*/
	{"http://user:@pjsip.org", PJ_SUCCESS, "user", "", "pjsip.org", 80, "/"},

	/* empty username and passwd*/
	{"http://:@pjsip.org", PJ_SUCCESS, "", "", "pjsip.org", 80, "/"},

	/* '@' character in username and path */
	{"http://[email protected]/@", PJ_SUCCESS, "user", "", "pjsip.org", 80, "/@"},

	/* '@' character in path */
	{"http://pjsip.org/@", PJ_SUCCESS, "", "", "pjsip.org", 80, "/@"},

	/* '@' character in path */
	{"http://pjsip.org/one@", PJ_SUCCESS, "", "", "pjsip.org", 80, "/one@"},

	/* Invalid URL */
	{"http://:", PJ_EINVAL, "", "", "", 0, ""},

	/* Invalid URL */
	{"http://@", PJ_EINVAL, "", "", "", 0, ""},

	/* Invalid URL */
	{"http", PJ_EINVAL, "", "", "", 0, ""},

	/* Invalid URL */
	{"http:/", PJ_EINVAL, "", "", "", 0, ""},

	/* Invalid URL */
	{"http://", PJ_EINVAL, "", "", "", 0, ""},

	/* Invalid URL */
	{"http:///", PJ_EINVAL, "", "", "", 0, ""},

	/* Invalid URL */
	{"http://@/", PJ_EINVAL, "", "", "", 0, ""},

	/* Invalid URL */
	{"http:///@", PJ_EINVAL, "", "", "", 0, ""},

	/* Invalid URL */
	{"http://:::", PJ_EINVAL, "", "", "", 0, ""},
    };
    unsigned i;

    for (i=0; i<PJ_ARRAY_SIZE(test_data); ++i) {
	struct test_data *ptd;
	pj_http_url hurl;
	pj_status_t status;

	ptd = &test_data[i];

	PJ_LOG(3, (THIS_FILE, ".. %s", ptd->url));
	status = parse_url(ptd->url, &hurl);

	if (status != ptd->result) {
	    PJ_LOG(3,(THIS_FILE, "%d", status));
	    return -11;
	}
	if (status != PJ_SUCCESS)
	    continue;
	if (pj_strcmp2(&hurl.username, ptd->username))
	    return -12;
	if (pj_strcmp2(&hurl.passwd, ptd->passwd))
	    return -13;
	if (pj_strcmp2(&hurl.host, ptd->host))
	    return -14;
	if (hurl.port != ptd->port)
	    return -15;
	if (pj_strcmp2(&hurl.path, ptd->path))
	    return -16;
    }

    return 0;
}
예제 #30
0
/*! \brief Function which processes ICE attributes in an audio stream */
static void process_ice_attributes(struct ast_sip_session *session, struct ast_sip_session_media *session_media,
				   const struct pjmedia_sdp_session *remote, const struct pjmedia_sdp_media *remote_stream)
{
	struct ast_rtp_engine_ice *ice;
	const pjmedia_sdp_attr *attr;
	char attr_value[256];
	unsigned int attr_i;

	/* If ICE support is not enabled or available exit early */
	if (!session->endpoint->media.rtp.ice_support || !(ice = ast_rtp_instance_get_ice(session_media->rtp))) {
		return;
	}

	attr = pjmedia_sdp_media_find_attr2(remote_stream, "ice-ufrag", NULL);
	if (!attr) {
		attr = pjmedia_sdp_attr_find2(remote->attr_count, remote->attr, "ice-ufrag", NULL);
	}
	if (attr) {
		ast_copy_pj_str(attr_value, (pj_str_t*)&attr->value, sizeof(attr_value));
		ice->set_authentication(session_media->rtp, attr_value, NULL);
	} else {
		return;
	}

	attr = pjmedia_sdp_media_find_attr2(remote_stream, "ice-pwd", NULL);
	if (!attr) {
		attr = pjmedia_sdp_attr_find2(remote->attr_count, remote->attr, "ice-pwd", NULL);
	}
	if (attr) {
		ast_copy_pj_str(attr_value, (pj_str_t*)&attr->value, sizeof(attr_value));
		ice->set_authentication(session_media->rtp, NULL, attr_value);
	} else {
		return;
	}

	if (pjmedia_sdp_media_find_attr2(remote_stream, "ice-lite", NULL)) {
		ice->ice_lite(session_media->rtp);
	}

	/* Find all of the candidates */
	for (attr_i = 0; attr_i < remote_stream->attr_count; ++attr_i) {
		char foundation[32], transport[32], address[PJ_INET6_ADDRSTRLEN + 1], cand_type[6], relay_address[PJ_INET6_ADDRSTRLEN + 1] = "";
		unsigned int port, relay_port = 0;
		struct ast_rtp_engine_ice_candidate candidate = { 0, };

		attr = remote_stream->attr[attr_i];

		/* If this is not a candidate line skip it */
		if (pj_strcmp2(&attr->name, "candidate")) {
			continue;
		}

		ast_copy_pj_str(attr_value, (pj_str_t*)&attr->value, sizeof(attr_value));

		if (sscanf(attr_value, "%31s %30u %31s %30u %46s %30u typ %5s %*s %23s %*s %30u", foundation, &candidate.id, transport,
			(unsigned *)&candidate.priority, address, &port, cand_type, relay_address, &relay_port) < 7) {
			/* Candidate did not parse properly */
			continue;
		}

		candidate.foundation = foundation;
		candidate.transport = transport;

		ast_sockaddr_parse(&candidate.address, address, PARSE_PORT_FORBID);
		ast_sockaddr_set_port(&candidate.address, port);

		if (!strcasecmp(cand_type, "host")) {
			candidate.type = AST_RTP_ICE_CANDIDATE_TYPE_HOST;
		} else if (!strcasecmp(cand_type, "srflx")) {
			candidate.type = AST_RTP_ICE_CANDIDATE_TYPE_SRFLX;
		} else if (!strcasecmp(cand_type, "relay")) {
			candidate.type = AST_RTP_ICE_CANDIDATE_TYPE_RELAYED;
		} else {
			continue;
		}

		if (!ast_strlen_zero(relay_address)) {
			ast_sockaddr_parse(&candidate.relay_address, relay_address, PARSE_PORT_FORBID);
		}

		if (relay_port) {
			ast_sockaddr_set_port(&candidate.relay_address, relay_port);
		}

		ice->add_remote_candidate(session_media->rtp, &candidate);
	}

	ice->set_role(session_media->rtp, pjmedia_sdp_neg_was_answer_remote(session->inv_session->neg) == PJ_TRUE ?
		AST_RTP_ICE_ROLE_CONTROLLING : AST_RTP_ICE_ROLE_CONTROLLED);
	ice->start(session_media->rtp);
}