From 7a91333f7387d4f08eeb6464db93f51be80c33b5 Mon Sep 17 00:00:00 2001 From: Hans Zandbelt Date: Tue, 9 Nov 2004 00:27:16 +0000 Subject: [PATCH] IPv6 support patch by ("Hans Zandbelt" telin {dot} nl>) Originally committed as revision 3663 to svn://svn.ffmpeg.org/ffmpeg/trunk --- configure | 32 +++++++ libavformat/udp.c | 210 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 242 insertions(+) diff --git a/configure b/configure index 664825782f..289a327291 100755 --- a/configure +++ b/configure @@ -958,6 +958,30 @@ if (texi2html -version) >/dev/null 2>&1; then texi2html=yes fi +if test "$network" = "yes" ; then +########################################## +# IPv6 probe + +cat > $TMPC << EOF +#include +#include +#include +#include +int main( void ) { + struct sockaddr_storage saddr; + struct ipv6_mreq mreq6; + getaddrinfo(0,0,0,0); + getnameinfo(0,0,0,0,0,0,0); + IN6_IS_ADDR_MULTICAST(0); +} +EOF + +ipv6=no +if $cc -o $TMPE $TMPC > /dev/null 2>&1 ; then +ipv6=yes +fi +fi + case "`$cc -v 2>&1 | grep version`" in *gcc*) CFLAGS="-Wall $CFLAGS" @@ -1046,6 +1070,10 @@ echo "pthreads support" $pthreads echo "AMR-NB float support" $amr_nb echo "AMR-NB fixed support" $amr_nb_fixed echo "AMR-WB float support" $amr_wb +echo "network support $network" +if test "$network" = "yes" ; then +echo "IPv6 support $ipv6" +fi if test "$gpl" = "no" ; then echo "License: LGPL" else @@ -1294,6 +1322,10 @@ if test "$network" = "yes" ; then echo "CONFIG_NETWORK=yes" >> config.mak fi +if test "$ipv6" = "yes" ; then + echo "#define CONFIG_IPV6 1" >> $TMPH +fi + if test "$zlib" = "yes" ; then echo "#define CONFIG_ZLIB 1" >> $TMPH echo "CONFIG_ZLIB=yes" >> config.mak diff --git a/libavformat/udp.c b/libavformat/udp.c index 0ba76fa141..a90f36c280 100644 --- a/libavformat/udp.c +++ b/libavformat/udp.c @@ -33,12 +33,186 @@ typedef struct { int ttl; int is_multicast; int local_port; +#ifndef CONFIG_IPV6 struct ip_mreq mreq; struct sockaddr_in dest_addr; +#else + struct sockaddr_storage dest_addr; + size_t dest_addr_len; +#endif } UDPContext; #define UDP_TX_BUF_SIZE 32768 +#ifdef CONFIG_IPV6 + +int udp_ipv6_is_multicast_address(const struct sockaddr *addr) { + if (addr->sa_family == AF_INET) + return IN_MULTICAST(ntohl(((struct sockaddr_in *)addr)->sin_addr.s_addr)); + if (addr->sa_family == AF_INET6) + return IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6 *)addr)->sin6_addr); + return -1; +} + +int udp_ipv6_set_multicast_ttl(int sockfd, int mcastTTL, struct sockaddr *addr) { + if (addr->sa_family == AF_INET) { + if (setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_TTL, &mcastTTL, sizeof(mcastTTL)) < 0) { + perror("setsockopt(IP_MULTICAST_TTL)"); + return -1; + } + } + if (addr->sa_family == AF_INET6) { + if (setsockopt(sockfd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &mcastTTL, sizeof(mcastTTL)) < 0) { + perror("setsockopt(IPV6_MULTICAST_HOPS)"); + return -1; + } + } + return 0; +} + +int udp_ipv6_join_multicast_group(int sockfd, struct sockaddr *addr) { + struct ip_mreq mreq; + struct ipv6_mreq mreq6; + if (addr->sa_family == AF_INET) { + mreq.imr_multiaddr.s_addr = ((struct sockaddr_in *)addr)->sin_addr.s_addr; + mreq.imr_interface.s_addr= INADDR_ANY; + if (setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const void *)&mreq, sizeof(mreq)) < 0) { + perror("setsockopt(IP_ADD_MEMBERSHIP)"); + return -1; + } + } + if (addr->sa_family == AF_INET6) { + memcpy(&mreq6.ipv6mr_multiaddr, &(((struct sockaddr_in6 *)addr)->sin6_addr), sizeof(struct in6_addr)); + mreq6.ipv6mr_interface= 0; + if (setsockopt(sockfd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq6, sizeof(mreq6)) < 0) { + perror("setsockopt(IPV6_ADD_MEMBERSHIP)"); + return -1; + } + } + return 0; +} + +int udp_ipv6_leave_multicast_group(int sockfd, struct sockaddr *addr) { + struct ip_mreq mreq; + struct ipv6_mreq mreq6; + if (addr->sa_family == AF_INET) { + mreq.imr_multiaddr.s_addr = ((struct sockaddr_in *)addr)->sin_addr.s_addr; + mreq.imr_interface.s_addr= INADDR_ANY; + if (setsockopt(sockfd, IPPROTO_IP, IP_DROP_MEMBERSHIP, (const void *)&mreq, sizeof(mreq)) < 0) { + perror("setsockopt(IP_DROP_MEMBERSHIP)"); + return -1; + } + } + if (addr->sa_family == AF_INET6) { + memcpy(&mreq6.ipv6mr_multiaddr, &(((struct sockaddr_in6 *)addr)->sin6_addr), sizeof(struct in6_addr)); + mreq6.ipv6mr_interface= 0; + if (setsockopt(sockfd, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, &mreq6, sizeof(mreq6)) < 0) { + perror("setsockopt(IPV6_DROP_MEMBERSHIP)"); + return -1; + } + } + return 0; +} + +struct addrinfo* udp_ipv6_resolve_host(const char *hostname, int port, int type, int family, int flags) { + struct addrinfo hints, *res = 0; + int error; + char sport[16]; + const char *node = 0, *service = 0; + + if (port > 0) { + sprintf(sport, "%d", port); + service = sport; + } + if ((hostname) && (hostname[0] != '\0') && (hostname[0] != '?')) { + node = hostname; + } + if ((node) || (service)) { + memset(&hints, 0, sizeof(hints)); + hints.ai_socktype = type; + hints.ai_family = family; + hints.ai_flags = flags; + if ((error = getaddrinfo(node, service, &hints, &res))) { + fprintf(stderr, "udp_ipv6_resolve_host: %s\n", gai_strerror(error)); + } + } + return res; +} + +int udp_ipv6_set_remote_url(URLContext *h, const char *uri) { + UDPContext *s = h->priv_data; + char hostname[256]; + int port; + struct addrinfo *res0; + url_split(NULL, 0, NULL, 0, hostname, sizeof(hostname), &port, NULL, 0, uri); + res0 = udp_ipv6_resolve_host(hostname, port, SOCK_DGRAM, AF_UNSPEC, 0); + if (res0 == 0) return AVERROR_IO; + memcpy(&s->dest_addr, res0->ai_addr, res0->ai_addrlen); + s->dest_addr_len = res0->ai_addrlen; + freeaddrinfo(res0); + return 0; +} + +int udp_ipv6_set_local(URLContext *h) { + UDPContext *s = h->priv_data; + int udp_fd = -1; + struct sockaddr_storage clientaddr; + socklen_t addrlen; + char sbuf[NI_MAXSERV]; + char hbuf[NI_MAXHOST]; + struct addrinfo *res0; + int family; + + if (s->local_port != 0) { + res0 = udp_ipv6_resolve_host(0, s->local_port, SOCK_DGRAM, AF_UNSPEC, AI_PASSIVE); + if (res0 == 0) return -1; + family = res0->ai_family; + freeaddrinfo(res0); + } else { + family = s->dest_addr.ss_family; + } + + udp_fd = socket(family, SOCK_DGRAM, 0); + if (udp_fd < 0) { + perror("socket"); + goto fail; + } + + if (s->local_port != 0) { + if (bind(udp_fd, res0->ai_addr, res0->ai_addrlen) < 0) { + perror("bind"); + goto fail; + } + } + + addrlen = sizeof(clientaddr); + if (getsockname(udp_fd, (struct sockaddr *)&clientaddr, &addrlen) < 0) { + perror("getsockname"); + goto fail; + } + + if (getnameinfo((struct sockaddr *)&clientaddr, addrlen, hbuf, sizeof(hbuf), sbuf, sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV) != 0) { + perror("getnameinfo"); + goto fail; + } + + s->local_port = strtol(sbuf, NULL, 10); + + return udp_fd; + + fail: + if (udp_fd >= 0) +#ifdef CONFIG_BEOS_NETSERVER + closesocket(udp_fd); +#else + close(udp_fd); +#endif + return -1; +} + +#endif + + /** * If no filename is given to av_open_input_file because you want to * get the local port first, then you must call this function to set @@ -56,6 +230,9 @@ typedef struct { */ int udp_set_remote_url(URLContext *h, const char *uri) { +#ifdef CONFIG_IPV6 + return udp_ipv6_set_remote_url(h, uri); +#else UDPContext *s = h->priv_data; char hostname[256]; int port; @@ -68,6 +245,7 @@ int udp_set_remote_url(URLContext *h, const char *uri) s->dest_addr.sin_family = AF_INET; s->dest_addr.sin_port = htons(port); return 0; +#endif } /** @@ -143,6 +321,7 @@ static int udp_open(URLContext *h, const char *uri, int flags) udp_set_remote_url(h, uri); } +#ifndef CONFIG_IPV6 udp_fd = socket(PF_INET, SOCK_DGRAM, 0); if (udp_fd < 0) goto fail; @@ -185,6 +364,24 @@ static int udp_open(URLContext *h, const char *uri, int flags) } } } +#endif +#else + if (s->is_multicast && !(h->flags & URL_WRONLY)) + s->local_port = port; + udp_fd = udp_ipv6_set_local(h); + if (udp_fd < 0) + goto fail; +#ifndef CONFIG_BEOS_NETSERVER + if (s->is_multicast) { + if (h->flags & URL_WRONLY) { + if (udp_ipv6_set_multicast_ttl(udp_fd, s->ttl, (struct sockaddr *)&s->dest_addr) < 0) + goto fail; + } else { + if (udp_ipv6_join_multicast_group(udp_fd, (struct sockaddr *)&s->dest_addr) < 0) + goto fail; + } + } +#endif #endif if (is_output) { @@ -212,7 +409,11 @@ static int udp_open(URLContext *h, const char *uri, int flags) static int udp_read(URLContext *h, uint8_t *buf, int size) { UDPContext *s = h->priv_data; +#ifndef CONFIG_IPV6 struct sockaddr_in from; +#else + struct sockaddr_storage from; +#endif int from_len, len; for(;;) { @@ -237,7 +438,11 @@ static int udp_write(URLContext *h, uint8_t *buf, int size) for(;;) { ret = sendto (s->udp_fd, buf, size, 0, (struct sockaddr *) &s->dest_addr, +#ifndef CONFIG_IPV6 sizeof (s->dest_addr)); +#else + s->dest_addr_len); +#endif if (ret < 0) { if (errno != EINTR && errno != EAGAIN) return AVERROR_IO; @@ -253,12 +458,17 @@ static int udp_close(URLContext *h) UDPContext *s = h->priv_data; #ifndef CONFIG_BEOS_NETSERVER +#ifndef CONFIG_IPV6 if (s->is_multicast && !(h->flags & URL_WRONLY)) { if (setsockopt(s->udp_fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &s->mreq, sizeof(s->mreq)) < 0) { perror("IP_DROP_MEMBERSHIP"); } } +#else + if (s->is_multicast && !(h->flags & URL_WRONLY)) + udp_ipv6_leave_multicast_group(s->udp_fd, (struct sockaddr *)&s->dest_addr); +#endif close(s->udp_fd); #else closesocket(s->udp_fd); -- GitLab