diff options
-rw-r--r-- | doc/advanced.mux | 6 | ||||
-rw-r--r-- | src/dabOutput/dabOutputUdp.cpp | 121 |
2 files changed, 80 insertions, 47 deletions
diff --git a/doc/advanced.mux b/doc/advanced.mux index b6d6ef3..f573e02 100644 --- a/doc/advanced.mux +++ b/doc/advanced.mux @@ -388,8 +388,10 @@ outputs { ; Other outputs: ; TCP listen on port ;net "tcp://host:port" - ; UDP send to host:port - ;net "ucp://host:port" + ; UDP send to host:port, simple example for unicast + ;net_udp "udp://host:port" + ; example with source and TTL specification for multicast + ;net_udp "udp://237.10.0.230:7000?src=10.0.1.125,ttl=1" ; RAW (for farsync ETI card) ;farsync "raw://device" } diff --git a/src/dabOutput/dabOutputUdp.cpp b/src/dabOutput/dabOutputUdp.cpp index 82abdea..392cdb2 100644 --- a/src/dabOutput/dabOutputUdp.cpp +++ b/src/dabOutput/dabOutputUdp.cpp @@ -30,6 +30,9 @@ #if defined(HAVE_OUTPUT_UDP) #include <cstring> +#include <cstdlib> +#include <boost/regex.hpp> +#include <string> #include <cstdio> #include <limits.h> #include "dabOutput.h" @@ -48,58 +51,86 @@ int DabOutputUdp::Open(const char* name) { - // we are going to modify it - char* hostport = strdup(name); // the name is actually an tuple host:port - - char* address; - long port; - address = strchr(hostport, ':'); - if (address == NULL) { - etiLog.log(error, - "\"%s\" is an invalid format for UDP address: " - "should be [address]:port - > aborting\n", - hostport); - goto udp_open_fail; - } + using namespace std; + using namespace boost; + + const string uri_without_proto(name); + + regex re_url("([^:]+):([0-9]+)(.*)"); + regex re_query("[?](?:src=([^,]+))(?:,ttl=([0-9]+))?"); + smatch what; + if (regex_match(uri_without_proto, what, re_url, match_default)) { + string address = what[1]; + etiLog.level(debug) << "***** UDP ADDR " << address; + if (this->packet_->getAddress().setAddress(address.c_str()) == -1) { + etiLog.level(error) << "can't set address " << + address << "(" << inetErrDesc << ": " << inetErrMsg << ")"; + return -1; + } - // terminate string hostport after the host, and advance address to the port number - *(address++) = 0; + string port_str = what[2]; + long port = std::strtol(port_str.c_str(), nullptr, 0); + etiLog.level(debug) << "***** UDP PORT " << port_str << " -> " << port; - port = strtol(address, (char **)NULL, 10); - if ((port == LONG_MIN) || (port == LONG_MAX)) { - etiLog.log(error, - "can't convert port number in UDP address %s\n", address); - goto udp_open_fail; - } - if (port == 0) { - etiLog.log(error, - "can't use port number 0 in UDP address\n"); - goto udp_open_fail; - } - address = hostport; - if (strlen(address) > 0) { - if (this->packet_->getAddress().setAddress(address) == -1) { - etiLog.log(error, "can't set address %s (%s: %s) " - "-> aborting\n", address, inetErrDesc, inetErrMsg); - goto udp_open_fail; + if ((port <= 0) || (port >= 65536)) { + etiLog.level(error) << + "can't convert port number in UDP address " << uri_without_proto; + return -1; } - } - this->packet_->getAddress().setPort(port); - if (this->socket_->create() == -1) { - etiLog.log(error, "can't create UDP socket (%s: %s) " - "-> aborting\n)", inetErrDesc, inetErrMsg); - goto udp_open_fail; + this->packet_->getAddress().setPort(port); + + if (this->socket_->create() == -1) { + etiLog.level(error) << "can't create UDP socket (" << + inetErrDesc << ": " << inetErrMsg << ")"; + return -1; + } + + string query_params = what[3]; + etiLog.level(debug) << "***** UDP Q " << query_params; + smatch query_what; + if (regex_match(query_params, query_what, re_query, match_default)) { + string src = query_what[1]; + etiLog.level(debug) << "***** UDP Q SRC " << src; + int err = socket_->setMulticastSource(src.c_str()); + if (err) { + etiLog.level(error) << "UDP output socket set source failed!"; + return -1; + } + + string ttl_str = query_what[2]; + etiLog.level(debug) << "***** UDP Q TTL " << ttl_str; + + if (not ttl_str.empty()) { + long ttl = std::strtol(ttl_str.c_str(), nullptr, 0); + etiLog.level(debug) << "***** UDP Q TTL# " << ttl; + if ((ttl <= 0) || (ttl >= 255)) { + etiLog.level(error) << "Invalid TTL setting in " << + uri_without_proto; + return -1; + } + + err = socket_->setMulticastTTL(ttl); + if (err) { + etiLog.level(error) << "UDP output socket set TTL failed!"; + return -1; + } + } + } + else if (not query_params.empty()) { + etiLog.level(error) << "UDP output: could not parse parameters " << + query_params; + return -1; + } + } + else { + etiLog.level(error) << uri_without_proto << + " is an invalid format for UDP address: " + "expected ADDRESS:PORT[?src=SOURCE,ttl=TTL]"; + return -1; } - //sprintf(hostport, "%s:%i", this->packet_->getAddress().getHostAddress(), - // this->packet_->getAddress().getPort()); return 0; - -udp_open_fail: - // strdup forces us to free - free(hostport); - return -1; } |