/** Compare two session descriptions */ int sdp_session_cmp(sdp_session_t const *a, sdp_session_t const *b) { int rv; sdp_bandwidth_t const *ab, *bb; sdp_attribute_t const *aa, *ba; sdp_media_t const *am, *bm; if ((rv = (a != NULL) - (b != NULL))) return rv; if (a == b) return 0; if ((rv = (a->sdp_version[0] - b->sdp_version[0]))) return rv; if ((rv = sdp_origin_cmp(a->sdp_origin, b->sdp_origin))) return rv; if ((rv = su_strcmp(a->sdp_subject, b->sdp_subject))) return rv; if ((rv = su_strcmp(a->sdp_information, b->sdp_information))) return rv; if ((rv = su_strcmp(a->sdp_uri, b->sdp_uri))) return rv; if ((rv = sdp_list_cmp(a->sdp_emails, b->sdp_emails))) return rv; if ((rv = sdp_list_cmp(a->sdp_phones, b->sdp_phones))) return rv; if ((rv = sdp_connection_cmp(a->sdp_connection, b->sdp_connection))) return rv; for (ab = a->sdp_bandwidths, bb = b->sdp_bandwidths; ab || bb; ab = ab->b_next, bb = bb->b_next) if ((rv = sdp_bandwidth_cmp(a->sdp_bandwidths, b->sdp_bandwidths))) return rv; if ((rv = sdp_time_cmp(a->sdp_time, b->sdp_time))) return rv; if ((rv = sdp_key_cmp(a->sdp_key, b->sdp_key))) return rv; for (aa = a->sdp_attributes, ba = b->sdp_attributes; aa || bb; aa = aa->a_next, ba = ba->a_next) if ((rv = sdp_attribute_cmp(aa, ba))) return rv; for (am = a->sdp_media, bm = b->sdp_media; am || bm; am = am->m_next, bm = bm->m_next) if ((rv = sdp_media_cmp(am, bm))) return rv; return 0; }
/** * Updates the modified copy of local SDP based * on application provided local SDP and remote SDP. */ static int offer_answer_step(soa_session_t *ss, enum offer_answer_action action, char const *by) { soa_static_session_t *sss = (soa_static_session_t *)ss; sdp_session_t *local = ss->ss_local->ssd_sdp; sdp_session_t local0[1]; sdp_session_t *user = ss->ss_user->ssd_sdp; unsigned user_version = ss->ss_user_version; sdp_session_t *remote = ss->ss_remote->ssd_sdp; unsigned remote_version = ss->ss_remote_version; int fresh = 0; sdp_origin_t o[1] = {{ sizeof(o) }}; sdp_connection_t *c, c0[1] = {{ sizeof(c0) }}; char c0_buffer[64]; sdp_time_t t[1] = {{ sizeof(t) }}; int *u2s = NULL, *s2u = NULL, *tbf; sdp_session_t *latest = NULL, *previous = NULL; char const *phrase = "Internal Media Error"; su_home_t tmphome[SU_HOME_AUTO_SIZE(8192)]; su_home_auto(tmphome, sizeof tmphome); SU_DEBUG_7(("soa_static_offer_answer_action(%p, %s): called\n", (void *)ss, by)); if (user == NULL) return soa_set_status(ss, 500, "No session set by user"); if (action == generate_offer) remote = NULL; else if (remote == NULL) return soa_set_status(ss, 500, "No remote SDP"); /* Pre-negotiation Step: Expand truncated remote SDP */ if (local && remote) switch (action) { case generate_answer: case process_answer: if (sdp_media_count(remote, sdp_media_any, "*", (sdp_proto_e)0, (sdp_text_t)0) < sdp_media_count(local, sdp_media_any, "*", (sdp_proto_e)0, (sdp_text_t)0)) { SU_DEBUG_5(("%s: remote %s is truncated: expanding\n", by, action == generate_answer ? "offer" : "answer")); remote = soa_sdp_expand_media(tmphome, remote, local); if (remote == NULL) return soa_set_status(ss, 500, "Cannot expand remote session"); } default: break; } /* Step A: Create local SDP session (based on user-supplied SDP) */ if (local == NULL) switch (action) { case generate_offer: case generate_answer: SU_DEBUG_7(("soa_static(%p, %s): %s\n", (void *)ss, by, "generating local description")); fresh = 1; local = local0; *local = *user, local->sdp_media = NULL; o->o_username = "******"; o->o_address = c0; c0->c_address = c0_buffer; if (!local->sdp_origin) local->sdp_origin = o; break; case process_answer: default: goto internal_error; } /* Step B: upgrade local SDP (add m= lines to it) */ switch (action) { case generate_offer: /* Upgrade local SDP based on user SDP */ if (local != local0 && ss->ss_local_user_version == user_version) break; if (local != local0) *local0 = *local, local = local0; SU_DEBUG_7(("soa_static(%p, %s): %s\n", (void *)ss, by, "upgrade with local description")); if (soa_sdp_upgrade(ss, tmphome, local, user, NULL, &u2s, &s2u) < 0) goto internal_error; break; case generate_answer: /* Upgrade local SDP based on remote SDP */ if (ss->ss_local_user_version == user_version && ss->ss_local_remote_version == remote_version) break; if (1) { if (local != local0) *local0 = *local, local = local0; SU_DEBUG_7(("soa_static(%p, %s): %s\n", (void *)ss, by, "upgrade with remote description")); if (soa_sdp_upgrade(ss, tmphome, local, user, remote, &u2s, &s2u) < 0) goto internal_error; } break; case process_answer: default: break; } /* Step C: reject media */ switch (action) { case generate_offer: /* Local media is marked as rejected already in upgrade phase */ break; case generate_answer: case process_answer: if (ss->ss_local_remote_version == remote_version) break; if (soa_sdp_reject_is_needed(local, remote)) { if (local != local0) { *local0 = *local, local = local0; #define DUP_LOCAL(local) \ do { \ if (!local->sdp_media) break; \ local->sdp_media = \ sdp_media_dup_all(tmphome, local->sdp_media, local); \ if (!local->sdp_media) \ goto internal_error; \ } while (0) DUP_LOCAL(local); } SU_DEBUG_7(("soa_static(%p, %s): %s\n", (void *)ss, by, "marking rejected media")); soa_sdp_reject(tmphome, local, remote); } break; default: break; } /* Step D: Set media mode bits */ switch (action) { int const *s2u_; case generate_offer: case generate_answer: case process_answer: s2u_ = s2u; if (!s2u_) s2u_ = sss->sss_s2u; if (soa_sdp_mode_set(user, s2u_, local, remote, ss->ss_hold, 1)) { if (local != local0) { *local0 = *local, local = local0; DUP_LOCAL(local); } soa_sdp_mode_set(user, s2u_, local, remote, ss->ss_hold, 0); } break; default: break; } /* Step E: Upgrade codecs by answer. */ switch (action) { case process_answer: /* Upgrade local SDP based on remote SDP */ if (ss->ss_local_remote_version == remote_version) break; if (1 /* We don't have good test for codecs */) { SU_DEBUG_7(("soa_static(%p, %s): %s\n", (void *)ss, by, "upgrade codecs with remote description")); if (local != local0) { *local0 = *local, local = local0; DUP_LOCAL(local); } soa_sdp_session_upgrade_rtpmaps(ss, local, remote); } break; case generate_offer: case generate_answer: default: break; } /* Step F0: Initialize o= line */ if (fresh) { if (user->sdp_origin) { o->o_username = user->sdp_origin->o_username; if (user->sdp_origin->o_address) o->o_address = user->sdp_origin->o_address; if (user->sdp_origin->o_id) o->o_id = user->sdp_origin->o_id; if (user->sdp_origin->o_version && user->sdp_origin->o_version != o->o_version) { o->o_version = user->sdp_origin->o_version; o->o_version--; } } if (soa_init_sdp_origin_with_session(ss, o, c0_buffer, local) < 0) { phrase = "Cannot Get IP Address for Session Description"; goto internal_error; } local->sdp_origin = o; } /* Step F: Update c= line(s) */ switch (action) { sdp_connection_t *user_c, *local_c; case generate_offer: case generate_answer: user_c = user->sdp_connection; if (!soa_check_sdp_connection(user_c)) user_c = NULL; local_c = local->sdp_connection; if (!soa_check_sdp_connection(local_c)) local_c = NULL; if (ss->ss_local_user_version != user_version || local_c == NULL || (user_c != NULL && sdp_connection_cmp(local_c, user_c))) { sdp_media_t *m; if (user_c) c = user_c; else c = local->sdp_origin->o_address; /* Every m= line (even rejected one) must have a c= line * or there must be a c= line at session level */ for (m = local->sdp_media; m; m = m->m_next) if (m->m_connections == NULL) break; if (m) { if (local != local0) { *local0 = *local, local = local0; DUP_LOCAL(local); } local->sdp_connection = c; } } break; default: break; } soa_description_free(ss, ss->ss_previous); su_free(ss->ss_home, sss->sss_previous.u2s), sss->sss_previous.u2s = NULL; su_free(ss->ss_home, sss->sss_previous.s2u), sss->sss_previous.s2u = NULL; if (u2s) { u2s = u2s_alloc(ss->ss_home, u2s); s2u = u2s_alloc(ss->ss_home, s2u); if (!u2s || !s2u) goto internal_error; } if (ss->ss_local->ssd_sdp != local && sdp_session_cmp(ss->ss_local->ssd_sdp, local)) { int bump; switch (action) { case generate_offer: bump = sdp_session_cmp(local, sss->sss_latest); break; case generate_answer: bump = 1; break; case process_answer: default: bump = 0; break; } if (bump) { /* Upgrade the version number */ if (local->sdp_origin != o) *o = *local->sdp_origin, local->sdp_origin = o; o->o_version++; } /* Do sanity checks for the created SDP */ if (!local->sdp_subject) /* s= is mandatory */ local->sdp_subject = "-"; if (!local->sdp_time) /* t= is mandatory */ local->sdp_time = t; if (action == generate_offer) { /* Keep a copy of previous session state */ int *previous_u2s = u2s_alloc(ss->ss_home, sss->sss_u2s); int *previous_s2u = u2s_alloc(ss->ss_home, sss->sss_s2u); if ((sss->sss_u2s && !previous_u2s) || (sss->sss_s2u && !previous_s2u)) goto internal_error; *ss->ss_previous = *ss->ss_local; memset(ss->ss_local, 0, (sizeof *ss->ss_local)); ss->ss_previous_user_version = ss->ss_local_user_version; ss->ss_previous_remote_version = ss->ss_local_remote_version; sss->sss_previous.u2s = previous_u2s; sss->sss_previous.s2u = previous_s2u; } SU_DEBUG_7(("soa_static(%p, %s): %s\n", (void *)ss, by, "storing local description")); /* Update the unparsed and pretty-printed descriptions */ if (soa_description_set(ss, ss->ss_local, local, NULL, 0) < 0) { if (action == generate_offer) { /* Remove 2nd reference to local session state */ memset(ss->ss_previous, 0, (sizeof *ss->ss_previous)); ss->ss_previous_user_version = 0; ss->ss_previous_remote_version = 0; su_free(ss->ss_home, sss->sss_previous.u2s), sss->sss_previous.u2s = NULL; su_free(ss->ss_home, sss->sss_previous.s2u), sss->sss_previous.s2u = NULL; } su_free(ss->ss_home, u2s), su_free(ss->ss_home, s2u); goto internal_error; } if (bump) { latest = sdp_session_dup(ss->ss_home, ss->ss_local->ssd_sdp); previous = sss->sss_latest; } } if (u2s) { tbf = sss->sss_u2s, sss->sss_u2s = u2s, su_free(ss->ss_home, tbf); tbf = sss->sss_s2u, sss->sss_s2u = s2u, su_free(ss->ss_home, tbf); } /* Update version numbers */ switch (action) { case generate_offer: ss->ss_local_user_version = user_version; sss->sss_latest = latest; break; case generate_answer: ss->ss_local_user_version = user_version; ss->ss_local_remote_version = remote_version; sss->sss_latest = latest; break; case process_answer: ss->ss_local_remote_version = remote_version; default: break; } if (previous) su_free(ss->ss_home, previous); su_home_deinit(tmphome); return 0; internal_error: su_home_deinit(tmphome); return soa_set_status(ss, 500, phrase); }
/** Compare two media (m=) fields */ int sdp_media_cmp(sdp_media_t const *a, sdp_media_t const *b) { int rv; sdp_connection_t const *ac, *bc; sdp_bandwidth_t const *ab, *bb; sdp_rtpmap_t const *arm, *brm; sdp_attribute_t const *aa, *ba; if (a == b) return 0; if ((rv = (a != NULL) - (b != NULL))) return rv; if (a->m_type != b->m_type) return a->m_type < b->m_type ? -1 : 1; if (a->m_type == sdp_media_x) if ((rv = su_strcmp(a->m_type_name, b->m_type_name))) return rv; if (a->m_port != b->m_port) return a->m_port < b->m_port ? -1 : 1; if (a->m_port == 0 /* && b->m_port == 0 */) /* Ignore transport protocol and media list if media has been rejected */ return 0; if (a->m_number_of_ports != b->m_number_of_ports) return a->m_number_of_ports < b->m_number_of_ports ? -1 : 1; if (a->m_proto != b->m_proto) return a->m_proto < b->m_proto ? -1 : 1; if (a->m_proto == sdp_proto_x) if ((rv = su_strcmp(a->m_proto_name, b->m_proto_name))) return rv; if (a->m_mode != b->m_mode) return a->m_mode < b->m_mode ? -1 : 1; for (arm = a->m_rtpmaps, brm = b->m_rtpmaps; arm || brm; arm = arm->rm_next, brm = brm->rm_next) if ((rv = sdp_rtpmap_cmp(arm, brm))) return rv; if ((rv = sdp_list_cmp(a->m_format, b->m_format))) return rv; if ((rv = su_strcmp(a->m_information, b->m_information))) return rv; for (ac = a->m_connections, bc = b->m_connections; ac || bc; ac = ac->c_next, bc = bc->c_next) if ((rv = sdp_connection_cmp(ac, bc))) return rv; for (ab = a->m_bandwidths, bb = b->m_bandwidths; ab || bb; ab = ab->b_next, bb = bb->b_next) if ((rv = sdp_bandwidth_cmp(a->m_bandwidths, b->m_bandwidths))) return rv; if ((rv = sdp_key_cmp(a->m_key, b->m_key))) return rv; for (aa = a->m_attributes, ba = b->m_attributes; aa || bb; aa = aa->a_next, ba = ba->a_next) if ((rv = sdp_attribute_cmp(aa, ba))) return rv; return 0; }