diff options
author | Martin Storsjö <martin@martin.st> | 2013-07-18 21:12:14 +0300 |
---|---|---|
committer | Martin Storsjö <martin@martin.st> | 2013-07-19 12:02:09 +0300 |
commit | 4d97ca040b40eb4771d1a6cacdb611f61a53afd8 (patch) | |
tree | afc7868d87fdcf641953777f45b83fc0285a0e1b | |
parent | 336353deaad6e4da28665f50a36988b02b55c20c (diff) | |
download | ffmpeg-4d97ca040b40eb4771d1a6cacdb611f61a53afd8.tar.gz |
rtpproto: Check the source IP if one single source has been specified
If another peer is sending unicast packets to the same port that
we are listening on, those packets can end up being received despite
using source specific multicast. For those cases, manually check the
source address of received packets against the intended source address.
This only handles the case when the source list is one single IP
address for now, which probably is the most common case.
Based on a patch by Ed Torbett.
Signed-off-by: Martin Storsjö <martin@martin.st>
-rw-r--r-- | libavformat/rtpproto.c | 58 |
1 files changed, 57 insertions, 1 deletions
diff --git a/libavformat/rtpproto.c b/libavformat/rtpproto.c index 20ffdab491..b050382301 100644 --- a/libavformat/rtpproto.c +++ b/libavformat/rtpproto.c @@ -42,7 +42,8 @@ typedef struct RTPContext { URLContext *rtp_hd, *rtcp_hd; - int rtp_fd, rtcp_fd; + int rtp_fd, rtcp_fd, ssm; + struct sockaddr_storage ssm_addr; } RTPContext; /** @@ -75,6 +76,44 @@ int ff_rtp_set_remote_url(URLContext *h, const char *uri) return 0; } +static struct addrinfo* rtp_resolve_host(const char *hostname, int port, + int type, int family, int flags) +{ + struct addrinfo hints = { 0 }, *res = 0; + int error; + char service[16]; + + snprintf(service, sizeof(service), "%d", port); + hints.ai_socktype = type; + hints.ai_family = family; + hints.ai_flags = flags; + if ((error = getaddrinfo(hostname, service, &hints, &res))) { + res = NULL; + av_log(NULL, AV_LOG_ERROR, "rtp_resolve_host: %s\n", gai_strerror(error)); + } + + return res; +} + +static int compare_addr(const struct sockaddr_storage *a, + const struct sockaddr_storage *b) +{ + if (a->ss_family != b->ss_family) + return 1; + if (a->ss_family == AF_INET) { + return (((const struct sockaddr_in *)a)->sin_addr.s_addr != + ((const struct sockaddr_in *)b)->sin_addr.s_addr); + } + +#if defined(IPPROTO_IPV6) + if (a->ss_family == AF_INET6) { + const uint8_t *s6_addr_a = ((const struct sockaddr_in6 *)a)->sin6_addr.s6_addr; + const uint8_t *s6_addr_b = ((const struct sockaddr_in6 *)b)->sin6_addr.s6_addr; + return memcmp(s6_addr_a, s6_addr_b, 16); + } +#endif + return 1; +} /** * add option to url of the form: @@ -178,7 +217,20 @@ static int rtp_open(URLContext *h, const char *uri, int flags) connect = strtol(buf, NULL, 10); } if (av_find_info_tag(buf, sizeof(buf), "sources", p)) { + struct addrinfo *sourceaddr = NULL; av_strlcpy(sources, buf, sizeof(sources)); + + /* Try resolving the IP if only one IP is specified - we don't + * support manually checking more than one IP. */ + if (!strchr(sources, ',')) + sourceaddr = rtp_resolve_host(sources, 0, + SOCK_DGRAM, AF_UNSPEC, + AI_NUMERICHOST); + if (sourceaddr) { + s->ssm = 1; + memcpy(&s->ssm_addr, sourceaddr->ai_addr, sourceaddr->ai_addrlen); + freeaddrinfo(sourceaddr); + } } } @@ -238,6 +290,8 @@ static int rtp_read(URLContext *h, uint8_t *buf, int size) continue; return AVERROR(EIO); } + if (s->ssm && compare_addr(&from, &s->ssm_addr)) + continue; break; } /* then RTP */ @@ -251,6 +305,8 @@ static int rtp_read(URLContext *h, uint8_t *buf, int size) continue; return AVERROR(EIO); } + if (s->ssm && compare_addr(&from, &s->ssm_addr)) + continue; break; } } else if (n < 0) { |