aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt2
-rw-r--r--test/InetAddress.cpp171
-rw-r--r--test/InetAddress.h80
-rw-r--r--test/UdpSocket.cpp256
-rw-r--r--test/UdpSocket.h180
-rw-r--r--test/main.cpp54
6 files changed, 732 insertions, 11 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ee1adb6..04cea23 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -46,6 +46,8 @@ list(APPEND edilib_cpp_sources
decoder/PFT.cpp
decoder/eti.cpp
test/main.cpp
+ test/InetAddress.cpp
+ test/UdpSocket.cpp
)
list(APPEND edilib_c_sources
diff --git a/test/InetAddress.cpp b/test/InetAddress.cpp
new file mode 100644
index 0000000..d92e1b9
--- /dev/null
+++ b/test/InetAddress.cpp
@@ -0,0 +1,171 @@
+/*
+ Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Her Majesty the
+ Queen in Right of Canada (Communications Research Center Canada)
+
+ Copyright (C) 2016
+ Matthias P. Braendli, matthias.braendli@mpb.li
+
+ http://www.opendigitalradio.org
+ */
+/*
+ This file is part of ODR-DabMux.
+
+ ODR-DabMux is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ ODR-DabMux is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with ODR-DabMux. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "InetAddress.h"
+#include <iostream>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+
+#ifdef TRACE_ON
+# ifndef TRACE_CLASS
+# define TRACE_CLASS(clas, func) cout <<"-" <<(clas) <<"\t(" <<this <<")::" <<(func) <<endl
+# define TRACE_STATIC(clas, func) cout <<"-" <<(clas) <<"\t(static)::" <<(func) <<endl
+# endif
+#else
+# ifndef TRACE_CLASS
+# define TRACE_CLASS(clas, func)
+# define TRACE_STATIC(clas, func)
+# endif
+#endif
+
+
+int inetErrNo = 0;
+const char *inetErrMsg = nullptr;
+const char *inetErrDesc = nullptr;
+
+
+/**
+ * Constructs an IP address.
+ * @param port The port of this address
+ * @param name The name of this address
+ */
+InetAddress::InetAddress(int port, const char* name) {
+ TRACE_CLASS("InetAddress", "InetAddress(int, char)");
+ addr.sin_family = PF_INET;
+ addr.sin_addr.s_addr = htons(INADDR_ANY);
+ addr.sin_port = htons(port);
+ if (name)
+ setAddress(name);
+}
+
+
+/**
+ * Constructs a copy of inet
+ * @param inet The address to be copied
+ */
+InetAddress::InetAddress(const InetAddress &inet) {
+ TRACE_CLASS("InetAddress", "InetAddress(InetAddress)");
+ memcpy(&addr, &inet.addr, sizeof(addr));
+}
+
+
+/// Destructor
+InetAddress::~InetAddress() {
+ TRACE_CLASS("InetAddress" ,"~InetAddress()");
+}
+
+
+/// Returns the raw IP address of this InetAddress object.
+sockaddr *InetAddress::getAddress() {
+ TRACE_CLASS("InetAddress", "getAddress()");
+ return (sockaddr *)&addr;
+}
+
+
+/// Return the port of this address.
+int InetAddress::getPort()
+{
+ TRACE_CLASS("InetAddress", "getPort()");
+ return ntohs(addr.sin_port);
+}
+
+
+/**
+ * Returns the IP address string "%d.%d.%d.%d".
+ * @return IP address
+ */
+const char *InetAddress::getHostAddress() {
+ TRACE_CLASS("InetAddress", "getHostAddress()");
+ return inet_ntoa(addr.sin_addr);
+}
+
+
+/// Returns true if this address is multicast
+bool InetAddress::isMulticastAddress() {
+ TRACE_CLASS("InetAddress", "isMulticastAddress()");
+ return IN_MULTICAST(ntohl(addr.sin_addr.s_addr)); // a modifier
+}
+
+
+/**
+ * Set the port number
+ * @param port The new port number
+ */
+void InetAddress::setPort(int port)
+{
+ TRACE_CLASS("InetAddress", "setPort(int)");
+ addr.sin_port = htons(port);
+}
+
+
+/**
+ * Set the address
+ * @param name The new address name
+ * @return 0 if ok
+ * -1 if error
+ */
+int InetAddress::setAddress(const std::string& name)
+{
+ TRACE_CLASS("InetAddress", "setAddress(string)");
+ if (!name.empty()) {
+ if (atoi(name.c_str())) { // If it start with a number
+ if ((addr.sin_addr.s_addr = inet_addr(name.c_str())) == INADDR_NONE) {
+ addr.sin_addr.s_addr = htons(INADDR_ANY);
+ inetErrNo = 0;
+ inetErrMsg = "Invalid address";
+ inetErrDesc = name.c_str();
+ return -1;
+ }
+ }
+ else { // Assume it's a real name
+ hostent *host = gethostbyname(name.c_str());
+ if (host) {
+ addr.sin_addr = *(in_addr *)(host->h_addr);
+ } else {
+ addr.sin_addr.s_addr = htons(INADDR_ANY);
+ inetErrNo = 0;
+ inetErrMsg = "Could not find address";
+ inetErrDesc = name.c_str();
+ return -1;
+ }
+ }
+ }
+ else {
+ addr.sin_addr.s_addr = INADDR_ANY;
+ }
+ return 0;
+}
+
+
+void setInetError(const char* description)
+{
+ inetErrNo = 0;
+ inetErrNo = errno;
+ inetErrMsg = strerror(inetErrNo);
+ inetErrDesc = description;
+}
+
diff --git a/test/InetAddress.h b/test/InetAddress.h
new file mode 100644
index 0000000..0ccc70b
--- /dev/null
+++ b/test/InetAddress.h
@@ -0,0 +1,80 @@
+/*
+ Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Her Majesty the
+ Queen in Right of Canada (Communications Research Center Canada)
+
+ Copyright (C) 2016
+ Matthias P. Braendli, matthias.braendli@mpb.li
+
+ http://www.opendigitalradio.org
+ */
+/*
+ This file is part of ODR-DabMux.
+
+ ODR-DabMux is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ ODR-DabMux is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with ODR-DabMux. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _InetAddress
+#define _InetAddress
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#include <pthread.h>
+#include <string>
+
+#define SOCKET int
+#define INVALID_SOCKET -1
+#define INVALID_PORT -1
+
+
+/// The last error number
+extern int inetErrNo;
+/// The last error message
+extern const char *inetErrMsg;
+/// The description of the last error
+extern const char *inetErrDesc;
+/// Set the number, message and description of the last error
+void setInetError(const char* description);
+
+
+/**
+ * This class represents an Internet Protocol (IP) address.
+ * @author Pascal Charest pascal.charest@crc.ca
+ */
+class InetAddress {
+ public:
+ InetAddress(int port = 0, const char* name = NULL);
+ InetAddress(const InetAddress &addr);
+ ~InetAddress();
+
+ sockaddr *getAddress();
+ const char *getHostAddress();
+ int getPort();
+ int setAddress(const std::string& name);
+ void setPort(int port);
+ bool isMulticastAddress();
+
+ private:
+ sockaddr_in addr;
+};
+
+
+#endif
diff --git a/test/UdpSocket.cpp b/test/UdpSocket.cpp
new file mode 100644
index 0000000..ccdd7ed
--- /dev/null
+++ b/test/UdpSocket.cpp
@@ -0,0 +1,256 @@
+/*
+ Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Her Majesty the
+ Queen in Right of Canada (Communications Research Center Canada)
+
+ Copyright (C) 2016
+ Matthias P. Braendli, matthias.braendli@mpb.li
+
+ http://www.opendigitalradio.org
+ */
+/*
+ This file is part of ODR-DabMux.
+
+ ODR-DabMux is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ ODR-DabMux is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with ODR-DabMux. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "UdpSocket.h"
+
+#include <iostream>
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+
+using namespace std;
+
+UdpSocket::UdpSocket() :
+ listenSocket(INVALID_SOCKET)
+{
+ reinit(0, "");
+}
+
+UdpSocket::UdpSocket(int port) :
+ listenSocket(INVALID_SOCKET)
+{
+ reinit(port, "");
+}
+
+UdpSocket::UdpSocket(int port, const std::string& name) :
+ listenSocket(INVALID_SOCKET)
+{
+ reinit(port, name);
+}
+
+
+int UdpSocket::setBlocking(bool block)
+{
+ int res;
+ if (block)
+ res = fcntl(listenSocket, F_SETFL, 0);
+ else
+ res = fcntl(listenSocket, F_SETFL, O_NONBLOCK);
+ if (res == SOCKET_ERROR) {
+ setInetError("Can't change blocking state of socket");
+ return -1;
+ }
+ return 0;
+}
+
+int UdpSocket::reinit(int port, const std::string& name)
+{
+ if (listenSocket != INVALID_SOCKET) {
+ ::close(listenSocket);
+ }
+
+ if ((listenSocket = socket(PF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET) {
+ setInetError("Can't create socket");
+ return -1;
+ }
+ reuseopt_t reuse = 1;
+ if (setsockopt(listenSocket, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse))
+ == SOCKET_ERROR) {
+ setInetError("Can't reuse address");
+ return -1;
+ }
+
+ if (port) {
+ address.setAddress(name);
+ address.setPort(port);
+
+ if (bind(listenSocket, address.getAddress(), sizeof(sockaddr_in)) == SOCKET_ERROR) {
+ setInetError("Can't bind socket");
+ ::close(listenSocket);
+ listenSocket = INVALID_SOCKET;
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int UdpSocket::close()
+{
+ if (listenSocket != INVALID_SOCKET) {
+ ::close(listenSocket);
+ }
+
+ listenSocket = INVALID_SOCKET;
+
+ return 0;
+}
+
+UdpSocket::~UdpSocket()
+{
+ if (listenSocket != INVALID_SOCKET) {
+ ::close(listenSocket);
+ }
+}
+
+
+int UdpSocket::receive(UdpPacket& packet)
+{
+ socklen_t addrSize;
+ addrSize = sizeof(*packet.getAddress().getAddress());
+ ssize_t ret = recvfrom(listenSocket,
+ packet.getData(),
+ packet.getSize(),
+ 0,
+ packet.getAddress().getAddress(),
+ &addrSize);
+
+ if (ret == SOCKET_ERROR) {
+ packet.setSize(0);
+ if (errno == EAGAIN) {
+ return 0;
+ }
+ setInetError("Can't receive UDP packet");
+ return -1;
+ }
+
+ packet.setSize(ret);
+ return 0;
+}
+
+int UdpSocket::send(UdpPacket& packet)
+{
+ int ret = sendto(listenSocket, packet.getData(), packet.getSize(), 0,
+ packet.getAddress().getAddress(), sizeof(*packet.getAddress().getAddress()));
+ if (ret == SOCKET_ERROR && errno != ECONNREFUSED) {
+ setInetError("Can't send UDP packet");
+ return -1;
+ }
+ return 0;
+}
+
+
+int UdpSocket::send(const std::vector<uint8_t>& data, InetAddress destination)
+{
+ int ret = sendto(listenSocket, &data[0], data.size(), 0,
+ destination.getAddress(), sizeof(*destination.getAddress()));
+ if (ret == SOCKET_ERROR && errno != ECONNREFUSED) {
+ setInetError("Can't send UDP packet");
+ return -1;
+ }
+ return 0;
+}
+
+
+/**
+ * Must be called to receive data on a multicast address.
+ * @param groupname The multica
+st address to join.
+ * @return 0 if ok, -1 if error
+ */
+int UdpSocket::joinGroup(char* groupname)
+{
+ ip_mreqn group;
+ if ((group.imr_multiaddr.s_addr = inet_addr(groupname)) == INADDR_NONE) {
+ setInetError(groupname);
+ return -1;
+ }
+ if (!IN_MULTICAST(ntohl(group.imr_multiaddr.s_addr))) {
+ setInetError("Not a multicast address");
+ return -1;
+ }
+ group.imr_address.s_addr = htons(INADDR_ANY);;
+ group.imr_ifindex = 0;
+ if (setsockopt(listenSocket, IPPROTO_IP, IP_ADD_MEMBERSHIP, &group, sizeof(group))
+ == SOCKET_ERROR) {
+ setInetError("Can't join multicast group");
+ }
+ return 0;
+}
+
+int UdpSocket::setMulticastTTL(int ttl)
+{
+ if (setsockopt(listenSocket, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl))
+ == SOCKET_ERROR) {
+ setInetError("Can't set ttl");
+ return -1;
+ }
+
+ return 0;
+}
+
+int UdpSocket::setMulticastSource(const char* source_addr)
+{
+ struct in_addr addr;
+ if (inet_aton(source_addr, &addr) == 0) {
+ setInetError("Can't parse source address");
+ return -1;
+ }
+
+ if (setsockopt(listenSocket, IPPROTO_IP, IP_MULTICAST_IF, &addr, sizeof(addr))
+ == SOCKET_ERROR) {
+ setInetError("Can't set source address");
+ return -1;
+ }
+
+ return 0;
+}
+
+UdpPacket::UdpPacket() { }
+
+UdpPacket::UdpPacket(size_t initSize) :
+ m_buffer(initSize)
+{ }
+
+
+void UdpPacket::setSize(size_t newSize)
+{
+ m_buffer.resize(newSize);
+}
+
+
+uint8_t* UdpPacket::getData()
+{
+ return &m_buffer[0];
+}
+
+
+void UdpPacket::addData(const void *data, size_t size)
+{
+ uint8_t *d = (uint8_t*)data;
+ std::copy(d, d + size, std::back_inserter(m_buffer));
+}
+
+size_t UdpPacket::getSize()
+{
+ return m_buffer.size();
+}
+
+InetAddress UdpPacket::getAddress()
+{
+ return address;
+}
+
diff --git a/test/UdpSocket.h b/test/UdpSocket.h
new file mode 100644
index 0000000..f62db1d
--- /dev/null
+++ b/test/UdpSocket.h
@@ -0,0 +1,180 @@
+/*
+ Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Her Majesty the
+ Queen in Right of Canada (Communications Research Center Canada)
+
+ Copyright (C) 2016
+ Matthias P. Braendli, matthias.braendli@mpb.li
+
+ http://www.opendigitalradio.org
+ */
+/*
+ This file is part of ODR-DabMux.
+
+ ODR-DabMux is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ ODR-DabMux is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with ODR-DabMux. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _UDPSOCKET
+#define _UDPSOCKET
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "InetAddress.h"
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#include <pthread.h>
+#define SOCKET int
+#define INVALID_SOCKET -1
+#define SOCKET_ERROR -1
+#define reuseopt_t int
+
+#include <stdlib.h>
+#include <iostream>
+#include <vector>
+
+class UdpPacket;
+
+
+/**
+ * This class represents a socket for sending and receiving UDP packets.
+ *
+ * A UDP socket is the sending or receiving point for a packet delivery service.
+ * Each packet sent or received on a datagram socket is individually
+ * addressed and routed. Multiple packets sent from one machine to another may
+ * be routed differently, and may arrive in any order.
+ */
+class UdpSocket
+{
+ public:
+ /** Create a new socket that will not be bound to any port. To be used
+ * for data output.
+ */
+ UdpSocket();
+ /** Create a new socket.
+ * @param port The port number on which the socket will be bound
+ */
+ UdpSocket(int port);
+ /** Create a new socket.
+ * @param port The port number on which the socket will be bound
+ * @param name The IP address on which the socket will be bound.
+ * It is used to bind the socket on a specific interface if
+ * the computer have many NICs.
+ */
+ UdpSocket(int port, const std::string& name);
+ ~UdpSocket();
+ UdpSocket(const UdpSocket& other) = delete;
+ const UdpSocket& operator=(const UdpSocket& other) = delete;
+
+ /** reinitialise socket. Close the already open socket, and
+ * create a new one
+ */
+ int reinit(int port, const std::string& name);
+
+ /** Close the socket
+ */
+ int close(void);
+
+ /** Send an UDP packet.
+ * @param packet The UDP packet to be sent. It includes the data and the
+ * destination address
+ * return 0 if ok, -1 if error
+ */
+ int send(UdpPacket& packet);
+
+ /** Send an UDP packet
+ *
+ * return 0 if ok, -1 if error
+ */
+ int send(const std::vector<uint8_t>& data, InetAddress destination);
+
+ /** Receive an UDP packet.
+ * @param packet The packet that will receive the data. The address will be set
+ * to the source address.
+ * @return 0 if ok, -1 if error
+ */
+ int receive(UdpPacket& packet);
+
+ int joinGroup(char* groupname);
+ int setMulticastSource(const char* source_addr);
+ int setMulticastTTL(int ttl);
+
+ /** Set blocking mode. By default, the socket is blocking.
+ * @return 0 if ok
+ * -1 if error
+ */
+ int setBlocking(bool block);
+
+ protected:
+
+ /// The address on which the socket is bound.
+ InetAddress address;
+ /// The low-level socket used by system functions.
+ SOCKET listenSocket;
+};
+
+/** This class represents a UDP packet.
+ *
+ * A UDP packet contains a payload (sequence of bytes) and an address. For
+ * outgoing packets, the address is the destination address. For incoming
+ * packets, the address tells the user from what source the packet arrived from.
+ */
+class UdpPacket
+{
+ public:
+ /** Construct an empty UDP packet.
+ */
+ UdpPacket();
+ UdpPacket(size_t initSize);
+ UdpPacket(const UdpPacket& packet) = delete;
+ const UdpPacket& operator=(const UdpPacket&) = delete;
+ UdpPacket(const UdpPacket&& packet) = delete;
+ const UdpPacket& operator=(const UdpPacket&&) = delete;
+
+ /** Give the pointer to data.
+ * @return The pointer
+ */
+ uint8_t* getData(void);
+
+ /** Append some data at the end of data buffer and adjust size.
+ * @param data Pointer to the data to add
+ * @param size Size in bytes of new data
+ */
+ void addData(const void *data, size_t size);
+
+ size_t getSize(void);
+
+ /** Changes size of the data buffer size. Keeps data intact unless
+ * truncated.
+ */
+ void setSize(size_t newSize);
+
+ /** Returns the UDP address of the packet.
+ */
+ InetAddress getAddress(void);
+
+ const std::vector<uint8_t>& getBuffer(void) const {
+ return m_buffer;
+ }
+
+
+ private:
+ std::vector<uint8_t> m_buffer;
+ InetAddress address;
+};
+
+#endif // _UDPSOCKET
diff --git a/test/main.cpp b/test/main.cpp
index 66be109..a060314 100644
--- a/test/main.cpp
+++ b/test/main.cpp
@@ -22,18 +22,21 @@
#include <stdint.h>
#include <stdio.h>
#include <vector>
+#include <string>
+#include <regex>
#include "EDIDecoder.hpp"
+#include "UdpSocket.h"
int main(int argc, char **argv)
{
if (argc == 1) {
- fprintf(stderr, "Usage:\n %s <edi_file> [eti_file]\n", argv[0]);
+ fprintf(stderr, "Usage:\n %s <edi_source> [eti_file]\n", argv[0]);
+ fprintf(stderr, "edi_source is either a file, or udp://:port\n");
return 1;
}
- const char* fname = argv[1];
- FILE* fd = fopen(fname, "r");
+ const std::string source = argv[1];
FILE* fd_eti = nullptr;
if (argc == 3) {
@@ -48,16 +51,45 @@ int main(int argc, char **argv)
EDIDecoder decoder(writer);
- while (not feof(fd)) {
- const size_t bufsize = 1024;
- std::vector<uint8_t> buf(bufsize);
- int bytes_read = fread(buf.data(), bufsize, 1, fd);
+ const std::regex re_udp("udp://:([0-9]+)");
+ std::smatch m;
+ if (std::regex_match(source, m, re_udp)) {
+ const int udp_port = std::stoi(m[1].str());
- if (bytes_read) {
- decoder.push_bytes(buf);
+ printf("Receiving from udp port %d\n", udp_port);
+ UdpSocket sock(udp_port);
+
+ const size_t packsize = 8192;
+ UdpPacket packet(packsize);
+ while (true) {
+ int ret = sock.receive(packet);
+ if (ret == 0) {
+ const auto &buf = packet.getBuffer();
+ if (packet.getSize() == packsize) {
+ printf("Warning, possible UDP truncation\n");
+ }
+ decoder.push_bytes(buf);
+ }
+ else {
+ printf("Socket error: %s\n", inetErrMsg);
+ break;
+ }
}
- else {
- break;
+ }
+ else {
+ FILE* fd = fopen(source.c_str(), "r");
+
+ while (not feof(fd)) {
+ const size_t bufsize = 1024;
+ std::vector<uint8_t> buf(bufsize);
+ int bytes_read = fread(buf.data(), bufsize, 1, fd);
+
+ if (bytes_read) {
+ decoder.push_bytes(buf);
+ }
+ else {
+ break;
+ }
}
}