From 4c773472bdc8cc9d0e78d08b125aa04a4ec9fc34 Mon Sep 17 00:00:00 2001 From: Robin Daermann Date: Wed, 4 Nov 2015 16:41:47 +0100 Subject: [PATCH] Add support for IPv6 in RTSP code Monitors with source type 'remote' can now be accessed over IPv6. This code uses getaddrinfo(3) now instead of gethostbyname(3) - and changes a lot of networking stuff which should be tested thoroughly. --- src/zm_comms.cpp | 162 ++++++++++++++++++++++++++++++++++++++++++++ src/zm_comms.h | 146 +++------------------------------------ src/zm_rtp_ctrl.cpp | 9 +-- src/zm_rtp_data.cpp | 16 +++-- src/zm_rtsp.cpp | 6 +- src/zm_rtsp.h | 4 ++ 6 files changed, 192 insertions(+), 151 deletions(-) diff --git a/src/zm_comms.cpp b/src/zm_comms.cpp index a109019bd..beac7821e 100644 --- a/src/zm_comms.cpp +++ b/src/zm_comms.cpp @@ -35,6 +35,8 @@ #include #include #include +#include // for debug output +#include // for snprintf #ifdef SOLARIS #include // define FIONREAD @@ -516,6 +518,166 @@ bool Socket::setNoDelay( bool nodelay ) return( true ); } +bool InetSocket::connect( const char *host, const char *serv ) +{ + struct addrinfo hints; + struct addrinfo *result, *rp; + int s; + char buf[255]; + + mAddressFamily = AF_UNSPEC; + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ + hints.ai_socktype = getType(); + hints.ai_flags = 0; + hints.ai_protocol = 0; /* Any protocol */ + + s = getaddrinfo(host, serv, &hints, &result); + if (s != 0) { + Error( "connect(): getaddrinfo: %s", gai_strerror(s) ); + return( false ); + } + + /* getaddrinfo() returns a list of address structures. + * Try each address until we successfully connect(2). + * If socket(2) (or connect(2)) fails, we (close the socket + * and) try the next address. */ + + for (rp = result; rp != NULL; rp = rp->ai_next) { + if (mSd != -1) { + if (::connect(mSd, rp->ai_addr, rp->ai_addrlen) != -1) + break; /* Success */ + continue; + } + memset(&buf, 0, sizeof(buf)); + if (rp->ai_family == AF_INET) { + inet_ntop(AF_INET, &((struct sockaddr_in *)rp->ai_addr)->sin_addr, buf, sizeof(buf)-1); + } + else if (rp->ai_family == AF_INET6) { + inet_ntop(AF_INET6, &((struct sockaddr_in6 *)rp->ai_addr)->sin6_addr, buf, sizeof(buf)-1); + } + else { + strncpy(buf, "n/a", sizeof(buf)-1); + } + Debug( 1, "connect(): Trying '%s', family '%d', proto '%d'", buf, rp->ai_family, rp->ai_protocol); + mSd = ::socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (mSd == -1) + continue; + + int val = 1; + + (void)::setsockopt( mSd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val) ); + (void)::setsockopt( mSd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val) ); + mAddressFamily = rp->ai_family; /* save AF_ for ctrl and data connections */ + + if (::connect(mSd, rp->ai_addr, rp->ai_addrlen) != -1) + break; /* Success */ + + ::close(mSd); + } + + if (rp == NULL) { /* No address succeeded */ + Error( "connect(), Could not connect" ); + mAddressFamily = AF_UNSPEC; + return( false ); + } + + freeaddrinfo(result); /* No longer needed */ + + mState = CONNECTED; + + return( true ); +} + +bool InetSocket::connect( const char *host, int port ) +{ + char serv[8]; + snprintf(serv, sizeof(serv), "%d", port); + + return connect( host, serv ); +} + +bool InetSocket::bind( const char * host, const char * serv ) +{ + struct addrinfo hints; + struct addrinfo *result, *rp; + int s; + char buf[255]; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ + hints.ai_socktype = getType(); + hints.ai_flags = AI_PASSIVE; /* For wildcard IP address */ + hints.ai_protocol = 0; /* Any protocol */ + hints.ai_canonname = NULL; + hints.ai_addr = NULL; + hints.ai_next = NULL; + + s = getaddrinfo(host, serv, &hints, &result); + if (s != 0) { + Error( "bind(): getaddrinfo: %s", gai_strerror(s) ); + return( false ); + } + + /* getaddrinfo() returns a list of address structures. + * Try each address until we successfully bind(2). + * If socket(2) (or bind(2)) fails, we (close the socket + * and) try the next address. */ + for (rp = result; rp != NULL; rp = rp->ai_next) { + memset(&buf, 0, sizeof(buf)); + if (rp->ai_family == AF_INET) { + inet_ntop(AF_INET, &((struct sockaddr_in *)rp->ai_addr)->sin_addr, buf, sizeof(buf)-1); + } + else if (rp->ai_family == AF_INET6) { + inet_ntop(AF_INET6, &((struct sockaddr_in6 *)rp->ai_addr)->sin6_addr, buf, sizeof(buf)-1); + } + else { + strncpy(buf, "n/a", sizeof(buf)-1); + } + Debug( 1, "bind(): Trying '%s', family '%d', proto '%d'", buf, rp->ai_family, rp->ai_protocol); + mSd = ::socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (mSd == -1) + continue; + + mState = DISCONNECTED; + if (::bind(mSd, rp->ai_addr, rp->ai_addrlen) == 0) + break; /* Success */ + + ::close(mSd); + mSd = -1; + } + + if (rp == NULL) { /* No address succeeded */ + Error( "bind(), Could not bind" ); + return( false ); + } + + freeaddrinfo(result); /* No longer needed */ + + return( true ); +} + +bool InetSocket::bind( const char * serv ) +{ + return bind( NULL, serv); +} + +bool InetSocket::bind( const char * host, int port ) +{ + char serv[8]; + snprintf(serv, sizeof(serv), "%d", port); + + return bind( host, serv ); +} + +bool InetSocket::bind( int port ) +{ + char serv[8]; + snprintf(serv, sizeof(serv), "%d", port); + + return bind( NULL, serv ); +} + bool TcpInetServer::listen() { return( Socket::listen() ); diff --git a/src/zm_comms.h b/src/zm_comms.h index ef1dda833..926a1148c 100644 --- a/src/zm_comms.h +++ b/src/zm_comms.h @@ -399,10 +399,13 @@ public: class InetSocket : virtual public Socket { +protected: + int mAddressFamily; + public: int getDomain() const { - return( AF_INET ); + return( mAddressFamily ); } virtual socklen_t getAddrSize() const { @@ -410,92 +413,13 @@ public: } protected: - bool resolveLocal( const char *host, const char *serv, const char *proto ) - { - SockAddrInet *addr = new SockAddrInet; - mLocalAddr = addr; - return( addr->resolve( host, serv, proto ) ); - } - bool resolveLocal( const char *host, int port, const char *proto ) - { - SockAddrInet *addr = new SockAddrInet; - mLocalAddr = addr; - return( addr->resolve( host, port, proto ) ); - } - bool resolveLocal( const char *serv, const char *proto ) - { - SockAddrInet *addr = new SockAddrInet; - mLocalAddr = addr; - return( addr->resolve( serv, proto ) ); - } - bool resolveLocal( int port, const char *proto ) - { - SockAddrInet *addr = new SockAddrInet; - mLocalAddr = addr; - return( addr->resolve( port, proto ) ); - } + bool connect( const char *host, const char *serv ); + bool connect( const char *host, int port ); - bool resolveRemote( const char *host, const char *serv, const char *proto ) - { - SockAddrInet *addr = new SockAddrInet; - mRemoteAddr = addr; - return( addr->resolve( host, serv, proto ) ); - } - bool resolveRemote( const char *host, int port, const char *proto ) - { - SockAddrInet *addr = new SockAddrInet; - mRemoteAddr = addr; - return( addr->resolve( host, port, proto ) ); - } - -protected: - bool bind( const SockAddrInet &addr ) - { - mLocalAddr = new SockAddrInet( addr ); - return( Socket::bind() ); - } - bool bind( const char *host, const char *serv ) - { - if ( !resolveLocal( host, serv, getProtocol() ) ) - return( false ); - return( Socket::bind() ); - } - bool bind( const char *host, int port ) - { - if ( !resolveLocal( host, port, getProtocol() ) ) - return( false ); - return( Socket::bind() ); - } - bool bind( const char *serv ) - { - if ( !resolveLocal( serv, getProtocol() ) ) - return( false ); - return( Socket::bind() ); - } - bool bind( int port ) - { - if ( !resolveLocal( port, getProtocol() ) ) - return( false ); - return( Socket::bind() ); - } - - bool connect( const SockAddrInet &addr ) - { - mRemoteAddr = new SockAddrInet( addr ); - return( Socket::connect() ); - } - bool connect( const char *host, const char *serv ) - { - if ( !resolveRemote( host, serv, getProtocol() ) ) - return( false ); - return( Socket::connect() ); - } - bool connect( const char *host, int port ) - { - if ( !resolveRemote( host, port, getProtocol() ) ) - return( false ); - return( Socket::connect() ); - } + bool bind( const char *host, const char *serv ); + bool bind( const char *host, int port ); + bool bind( const char *serv ); + bool bind( int port ); }; class UnixSocket : virtual public Socket @@ -591,10 +515,6 @@ public: class UdpInetSocket : virtual public UdpSocket, virtual public InetSocket { public: - bool bind( const SockAddrInet &addr ) - { - return( InetSocket::bind( addr ) ); - } bool bind( const char *host, const char *serv ) { return( InetSocket::bind( host, serv ) ); @@ -612,10 +532,6 @@ public: return( InetSocket::bind( port ) ); } - bool connect( const SockAddrInet &addr ) - { - return( InetSocket::connect( addr ) ); - } bool connect( const char *host, const char *serv ) { return( InetSocket::connect( host, serv ) ); @@ -642,33 +558,7 @@ public: class UdpInetClient : public UdpInetSocket { -protected: - bool bind( const SockAddrInet &addr ) - { - return( UdpInetSocket::bind( addr ) ); - } - bool bind( const char *host, const char *serv ) - { - return( UdpInetSocket::bind( host, serv ) ); - } - bool bind( const char *host, int port ) - { - return( UdpInetSocket::bind( host, port ) ); - } - bool bind( const char *serv ) - { - return( UdpInetSocket::bind( serv ) ); - } - bool bind( int port ) - { - return( UdpInetSocket::bind( port ) ); - } - public: - bool connect( const SockAddrInet &addr ) - { - return( UdpInetSocket::connect( addr ) ); - } bool connect( const char *host, const char *serv ) { return( UdpInetSocket::connect( host, serv ) ); @@ -697,10 +587,6 @@ public: class UdpInetServer : public UdpInetSocket { public: - bool bind( const SockAddrInet &addr ) - { - return( UdpInetSocket::bind( addr ) ); - } bool bind( const char *host, const char *serv ) { return( UdpInetSocket::bind( host, serv ) ); @@ -812,18 +698,6 @@ public: class TcpInetServer : public TcpInetSocket { public: - bool bind( const char *host, const char *serv ) - { - return( TcpInetSocket::bind( host, serv ) ); - } - bool bind( const char *host, int port ) - { - return( TcpInetSocket::bind( host, port ) ); - } - bool bind( const char *serv ) - { - return( TcpInetSocket::bind( serv ) ); - } bool bind( int port ) { return( TcpInetSocket::bind( port ) ); diff --git a/src/zm_rtp_ctrl.cpp b/src/zm_rtp_ctrl.cpp index 1a0604137..0870670a8 100644 --- a/src/zm_rtp_ctrl.cpp +++ b/src/zm_rtp_ctrl.cpp @@ -279,20 +279,17 @@ int RtpCtrlThread::run() UdpInetSocket rtpCtrlServer; if ( mRtpSource.getLocalHost() != "" ) { - localAddr.resolve( mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort(), "udp" ); - if ( !rtpCtrlServer.bind( localAddr ) ) + if ( !rtpCtrlServer.bind( mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort() ) ) Fatal( "Failed to bind RTCP server" ); sendReports = false; Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort() ); } else { - localAddr.resolve( mRtpSource.getLocalCtrlPort(), "udp" ); - if ( !rtpCtrlServer.bind( localAddr ) ) + if ( !rtpCtrlServer.bind( mRtspThread.getAddressFamily() == AF_INET6 ? "::" : "0.0.0.0", mRtpSource.getLocalCtrlPort() ) ) Fatal( "Failed to bind RTCP server" ); Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort() ); - remoteAddr.resolve( mRtpSource.getRemoteHost().c_str(), mRtpSource.getRemoteCtrlPort(), "udp" ); - if ( !rtpCtrlServer.connect( remoteAddr ) ) + if ( !rtpCtrlServer.connect( mRtpSource.getRemoteHost().c_str(), mRtpSource.getRemoteCtrlPort() ) ) Fatal( "Failed to connect RTCP server" ); Debug( 3, "Connected to %s:%d", mRtpSource.getRemoteHost().c_str(), mRtpSource.getRemoteCtrlPort() ); sendReports = true; diff --git a/src/zm_rtp_data.cpp b/src/zm_rtp_data.cpp index 257f46947..554dfbcca 100644 --- a/src/zm_rtp_data.cpp +++ b/src/zm_rtp_data.cpp @@ -67,13 +67,17 @@ int RtpDataThread::run() SockAddrInet localAddr; UdpInetServer rtpDataSocket; - if ( mRtpSource.getLocalHost() != "" ) - localAddr.resolve( mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalDataPort(), "udp" ); + if ( mRtpSource.getLocalHost() != "" ) { + if ( !rtpDataSocket.bind( mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalDataPort() ) ) + Fatal( "Failed to bind RTP server" ); + Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalDataPort() ); + } else - localAddr.resolve( mRtpSource.getLocalDataPort(), "udp" ); - if ( !rtpDataSocket.bind( localAddr ) ) - Fatal( "Failed to bind RTP server" ); - Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalDataPort() ); + { + if ( !rtpDataSocket.bind( mRtspThread.getAddressFamily() == AF_INET6 ? "::" : "0.0.0.0", mRtpSource.getLocalDataPort() ) ) + Fatal( "Failed to bind RTP server" ); + Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalDataPort() ); + } Select select( 3 ); select.addReader( &rtpDataSocket ); diff --git a/src/zm_rtsp.cpp b/src/zm_rtsp.cpp index 0dbcb7329..79c71212f 100644 --- a/src/zm_rtsp.cpp +++ b/src/zm_rtsp.cpp @@ -234,7 +234,7 @@ int RtspThread::run() response.reserve( ZM_NETWORK_BUFSIZ ); - if ( !mRtspSocket.connect( mHost.c_str(), strtol( mPort.c_str(), NULL, 10 ) ) ) + if ( !mRtspSocket.connect( mHost.c_str(), mPort.c_str() ) ) Fatal( "Unable to connect RTSP socket" ); //Select select( 0.25 ); //select.addReader( &mRtspSocket ); @@ -248,7 +248,7 @@ int RtspThread::run() bool authTried = false; if ( mMethod == RTP_RTSP_HTTP ) { - if ( !mRtspSocket2.connect( mHost.c_str(), strtol( mPort.c_str(), NULL, 10 ) ) ) + if ( !mRtspSocket2.connect( mHost.c_str(), mPort.c_str() ) ) Fatal( "Unable to connect auxiliary RTSP/HTTP socket" ); //Select select( 0.25 ); //select.addReader( &mRtspSocket2 ); @@ -306,7 +306,7 @@ int RtspThread::run() mAuthenticator->checkAuthResponse(response); Debug(2, "Processed 401 response"); mRtspSocket.close(); - if ( !mRtspSocket.connect( mHost.c_str(), strtol( mPort.c_str(), NULL, 10 ) ) ) + if ( !mRtspSocket.connect( mHost.c_str(), mPort.c_str() ) ) Fatal( "Unable to reconnect RTSP socket" ); Debug(2, "connection should be reopened now"); } diff --git a/src/zm_rtsp.h b/src/zm_rtsp.h index f5dcb9552..2fb5ee3bf 100644 --- a/src/zm_rtsp.h +++ b/src/zm_rtsp.h @@ -137,6 +137,10 @@ public: { return( mStop ); } + int getAddressFamily () + { + return mRtspSocket.getDomain(); + } }; #endif // ZM_RTSP_H