aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--channels/chan_sip.c72
-rw-r--r--include/asterisk/rtp.h17
-rw-r--r--main/rtp.c24
3 files changed, 109 insertions, 4 deletions
diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index f40a4302a..e198ca99a 100644
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -7403,6 +7403,63 @@ static int find_sdp(struct sip_request *req)
return FALSE;
}
+enum media_type {
+ SDP_AUDIO,
+ SDP_VIDEO,
+};
+
+static int get_ip_and_port_from_sdp(struct sip_request *req, const enum media_type media, struct sockaddr_in *sin)
+{
+ const char *m;
+ const char *c;
+ int miterator = req->sdp_start;
+ int citerator = req->sdp_start;
+ int x = 0;
+ int numberofports;
+ int len;
+ char host[258] = ""; /*Initialize to empty so we will know if we have any input */
+ struct ast_hostent audiohp;
+ struct hostent *hp;
+
+ c = get_sdp_iterate(&citerator, req, "c");
+ if (sscanf(c, "IN IP4 %256s", host) != 1) {
+ ast_log(LOG_WARNING, "Invalid host in c= line, '%s'\n", c);
+ /* Continue since there may be a valid host in a c= line specific to the audio stream */
+ }
+ /* We only want the m and c lines for audio */
+ while ((m = get_sdp_iterate(&miterator, req, "m"))) {
+ if ((media == SDP_AUDIO && ((sscanf(m, "audio %d/%d RTP/AVP %n", &x, &numberofports, &len) == 2 && len > 0) ||
+ (sscanf(m, "audio %d RTP/AVP %n", &x, &len) == 1 && len > 0))) ||
+ (media == SDP_VIDEO && ((sscanf(m, "video %d/%d RTP/AVP %n", &x, &numberofports, &len) == 2 && len > 0) ||
+ (sscanf(m, "video %d RTP/AVP %n", &x, &len) == 1 && len > 0)))) {
+ /* See if there's a c= line for this media stream.
+ * XXX There is no guarantee that we'll be grabbing the c= line for this
+ * particular media stream here. However, this is the same logic used in process_sdp.
+ */
+ c = get_sdp_iterate(&citerator, req, "c");
+ if (!ast_strlen_zero(c)) {
+ sscanf(c, "IN IP4 %256s", host);
+ }
+ break;
+ }
+ }
+
+ if (ast_strlen_zero(host) || x == 0) {
+ ast_log(LOG_WARNING, "Failed to read an alternate host or port in SDP. Expect %s problems\n", media == SDP_AUDIO ? "audio" : "video");
+ return -1;
+ }
+
+ hp = ast_gethostbyname(host, &audiohp);
+ if (!hp) {
+ ast_log(LOG_WARNING, "Could not look up IP address of alternate hostname. Expect %s problems\n", media == SDP_AUDIO? "audio" : "video");
+ return -1;
+ }
+
+ memcpy(&sin->sin_addr, hp->h_addr, sizeof(sin->sin_addr));
+ sin->sin_port = htons(x);
+ return 0;
+}
+
/*! \brief Process SIP SDP offer, select formats and activate RTP channels
If offer is rejected, we will not change any properties of the call
Return 0 on success, a negative value on errors.
@@ -18882,6 +18939,21 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int
} else {
/* We already have a pending invite. Sorry. You are on hold. */
p->glareinvite = seqno; /* must hold on to this seqno to process ack and retransmit correctly */
+ if (p->rtp && find_sdp(req)) {
+ struct sockaddr_in sin;
+ if (get_ip_and_port_from_sdp(req, SDP_AUDIO, &sin)) {
+ ast_log(LOG_WARNING, "Failed to set an alternate media source on glared reinvite. Audio may not work properly on this call.\n");
+ } else {
+ ast_rtp_set_alt_peer(p->rtp, &sin);
+ }
+ if (p->vrtp) {
+ if (get_ip_and_port_from_sdp(req, SDP_VIDEO, &sin)) {
+ ast_log(LOG_WARNING, "Failed to set an alternate media source on glared reinvite. Video may not work properly on this call.\n");
+ } else {
+ ast_rtp_set_alt_peer(p->vrtp, &sin);
+ }
+ }
+ }
transmit_response_reliable(p, "491 Request Pending", req);
ast_debug(1, "Got INVITE on call where we already have pending INVITE, deferring that - %s\n", p->callid);
/* Don't destroy dialog here */
diff --git a/include/asterisk/rtp.h b/include/asterisk/rtp.h
index 19b98c30d..842b01d61 100644
--- a/include/asterisk/rtp.h
+++ b/include/asterisk/rtp.h
@@ -162,6 +162,23 @@ struct ast_rtp *ast_rtp_new_with_bindaddr(struct sched_context *sched, struct io
void ast_rtp_set_peer(struct ast_rtp *rtp, struct sockaddr_in *them);
+/*!
+ * \since 1.4.26
+ * \brief set potential alternate source for RTP media
+ *
+ * This function may be used to give the RTP stack a hint that there is a potential
+ * second source of media. One case where this is used is when the SIP stack receives
+ * a REINVITE to which it will be replying with a 491. In such a scenario, the IP and
+ * port information in the SDP of that REINVITE lets us know that we may receive media
+ * from that source/those sources even though the SIP transaction was unable to be completed
+ * successfully
+ *
+ * \param rtp The RTP structure we wish to set up an alternate host/port on
+ * \param alt The address information for the alternate media source
+ * \retval void
+ */
+void ast_rtp_set_alt_peer(struct ast_rtp *rtp, struct sockaddr_in *alt);
+
/* Copies from rtp to them and returns 1 if there was a change or 0 if it was already the same */
int ast_rtp_get_peer(struct ast_rtp *rtp, struct sockaddr_in *them);
diff --git a/main/rtp.c b/main/rtp.c
index 6a806911c..1e6a438e0 100644
--- a/main/rtp.c
+++ b/main/rtp.c
@@ -146,6 +146,7 @@ struct ast_rtp {
unsigned int flags;
struct sockaddr_in us; /*!< Socket representation of the local endpoint. */
struct sockaddr_in them; /*!< Socket representation of the remote endpoint. */
+ struct sockaddr_in altthem; /*!< Alternate source of remote media */
struct timeval rxcore;
struct timeval txcore;
double drxcore; /*!< The double representation of the first received packet */
@@ -230,6 +231,7 @@ struct ast_rtcp {
int s; /*!< Socket */
struct sockaddr_in us; /*!< Socket representation of the local endpoint. */
struct sockaddr_in them; /*!< Socket representation of the remote endpoint. */
+ struct sockaddr_in altthem; /*!< Alternate source for RTCP */
unsigned int soc; /*!< What they told us */
unsigned int spc; /*!< What they told us */
unsigned int themrxlsr; /*!< The middle 32 bits of the NTP timestamp in the last received SR*/
@@ -1207,8 +1209,10 @@ struct ast_frame *ast_rtcp_read(struct ast_rtp *rtp)
if (rtp->nat) {
/* Send to whoever sent to us */
- if ((rtp->rtcp->them.sin_addr.s_addr != sock_in.sin_addr.s_addr) ||
- (rtp->rtcp->them.sin_port != sock_in.sin_port)) {
+ if (((rtp->rtcp->them.sin_addr.s_addr != sock_in.sin_addr.s_addr) ||
+ (rtp->rtcp->them.sin_port != sock_in.sin_port)) &&
+ ((rtp->rtcp->altthem.sin_addr.s_addr != sock_in.sin_addr.s_addr) ||
+ (rtp->rtcp->altthem.sin_port != sock_in.sin_port))) {
memcpy(&rtp->rtcp->them, &sock_in, sizeof(rtp->rtcp->them));
if (option_debug || rtpdebug)
ast_debug(0, "RTCP NAT: Got RTCP from other end. Now sending to address %s:%d\n", ast_inet_ntoa(rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port));
@@ -1639,8 +1643,10 @@ struct ast_frame *ast_rtp_read(struct ast_rtp *rtp)
/* Send to whoever send to us if NAT is turned on */
if (rtp->nat) {
- if ((rtp->them.sin_addr.s_addr != sock_in.sin_addr.s_addr) ||
- (rtp->them.sin_port != sock_in.sin_port)) {
+ if (((rtp->them.sin_addr.s_addr != sock_in.sin_addr.s_addr) ||
+ (rtp->them.sin_port != sock_in.sin_port)) &&
+ ((rtp->altthem.sin_addr.s_addr != sock_in.sin_addr.s_addr) ||
+ (rtp->altthem.sin_port != sock_in.sin_port))) {
rtp->them = sock_in;
if (rtp->rtcp) {
int h = 0;
@@ -2650,6 +2656,16 @@ void ast_rtp_set_peer(struct ast_rtp *rtp, struct sockaddr_in *them)
rtp->strict_rtp_state = STRICT_RTP_LEARN;
}
+void ast_rtp_set_alt_peer(struct ast_rtp *rtp, struct sockaddr_in *alt)
+{
+ rtp->altthem.sin_port = alt->sin_port;
+ rtp->altthem.sin_addr = alt->sin_addr;
+ if (rtp->rtcp) {
+ rtp->rtcp->altthem.sin_port = htons(ntohs(alt->sin_port) + 1);
+ rtp->rtcp->altthem.sin_addr = alt->sin_addr;
+ }
+}
+
int ast_rtp_get_peer(struct ast_rtp *rtp, struct sockaddr_in *them)
{
if ((them->sin_family != AF_INET) ||