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; }
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; }
/* 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; }
/*! * \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; }
/** * 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; }
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; }
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; }
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; }
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; }
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; }
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; }
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; }
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; }
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; }
/// 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; }
/*! \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; }
/*! \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; }
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; }
////////////////////////////////////////////////////////////////////////// // 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; }
/* 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; }
/* 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; }
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; }
/* 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; }
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; }
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; }
/*! \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); }