Commit 0bf41116 authored by Rémi Denis-Courmont's avatar Rémi Denis-Courmont

miniSAPserver 0.3.6

parent 9ceb3fba
# The format of this file was inspired by the Linux kernel CREDITS file.
#
# Authors and contributors are listed alphabetically.
#
# The fields are: name (N), email (E), web-address (W), CVS account login (C),
# PGP key ID and fingerprint (P), description (D), and snail-mail address (S).
N: Damien Lucas
E: nitrox@videolan.org
C: nitrox
D: Core
S: France
N: Arnaud Schauly
E: gitan@via.ecp.fr
C: gitan
D: Core
S: France
N: Clément Stenac
E: zorglub@via.ecp.fr
C: zorglub
D: Core
S: France
N: Derk-Jan Hartman
E: hartman at videolan dot org
C: hartman
D: Delay option
D: Standards compliance fixes
S: Netherlands
N: Rémi Denis-Courmont
E: rem at videolan dot org
C: courmisch
D: Unicode support, various fixes
S: Finland
This diff is collapsed.
This diff is collapsed.
ACLOCAL_AMFLAGS = -I m4
bin_PROGRAMS = sapserver
AM_CPPFLAGS= -DSYSCONFDIR=\"@sysconfdir@\" $(INCICONV)
sapserver_SOURCES = broadcast.cpp broadcast.h \
message.cpp message.h \
parser.cpp parser.h \
program.cpp program.h \
sapserver.cpp sapserver.h
sapserver_LDADD=$(LIBICONV)
if CONFIG_SLP
sapserver_SOURCES += slp.cpp lslp.h
sapserver_LDADD += -lslp
endif
man1_MANS=sapserver.1
dist_sysconf_DATA=sap.cfg
EXTRA_DIST = $(man1_MANS) m4/getopt.m4 m4/sockaddr.m4 admin/config.rpath
-- Changes from version 0.3.5 to version 0.3.6
* Add proper a=rtcp parameter when the RTP port is odd
(this fixes compatibility with VLC 0.9.9 and up).
-- Changes from version 0.3.4 to version 0.3.5
* Fix announce timer (again)
* Fix raw UDP media type
-- Changes from version 0.3.3 to version 0.3.4
* Fix announce timer
* Set sockaddr.sa_len on BSDs
-- Changes from version 0.3.2 to version 0.3.3
* Avoid some useless memory allocations
* Interface selection support for IPv6 announces
* Set default program TTL to 255 similarly
* Include charset session-level attribute
* Lots of conformance fixes:
- tool is a session-level attribute
- default SAP TTL is 255
- IPv6 announces must not include a TTL
- mux and packetformat attributes do not exists
- use CRLF rather than just LF
* Move playlist group and connection infos to session-level
-- Changes from version 0.3.1 to version 0.3.2
* Fix broken multicast TTL setting on IPv4 packets
-- Changes from version 0.3.0 to version 0.3.1
* Automatic selection of IP version protocol
* Fix sending of IPv6 addresses in announces
* Fix SLP configure option
* Fix compilation of SLP support
-- Changes from version 0.2.4 to version 0.3.0
* Interface selection (patch by John Wehle <john at feith dot com>)
* On-the-fly characters encoding conversion from user's locale to UTF-8
* Automatic selection of IPv4/IPv6 address to send SAP announce to
* Removed HTTP non-standard announces support
* Removed ipversion and ipv6_scope option (now it's automatic)
* Added proper RTP announces support (with type=rtp)
* Lots of cleanups
-- Changes from version 0.2.3 to version 0.2.4
* Bugfix in RTP payloadformat announce
* Added a=tool parameter so miniSAPserver can be more easily recognized
* Added a config option to set the SAP delay
* Added a=mux:m2t and a=packetformat:RAW parameters
* Changed a=plgroup to a=x-plgroup because it is a non standardized parameters
* Updated the manpage
* Clarified that miniSAPserver is only supposed to work for MPEG TS streams.
-- Changes from version 0.2.2 to version 0.2.3
* Support for VLC's playlist groups
* Support for announcing of HTTP streams
* Bugfix in IPv6 multicast support
* Bugfix in the autoconf/automake system
* Created a manpage
* Support for the SLP announcing protocol
* Improved build system
-- Changes from version 0.2.1 to version 0.2.2
* Bugfixes
-- Changes from version 0.2.0 to version 0.2.1
* IPv6 support -both for announces and announced streams-
-- Changes from version 0.1.2 to version 0.2.0
* Major structural improvements
* The miniSAPserver now sends packets that comply with the RFC
-- Changes from version 0.1.1 to version 0.1.2
* Bugfixes
-- Changes from version 0.1.0 to version 0.1.1
* Bugfixes
-- Version 0.1.0: initial release
This diff is collapsed.
#! /bin/sh
## Minimalistic boostrap file for the mini-SAP-server
## $Id$
exec autoreconf -fi
/*****************************************************************************
* broadcast.cpp : SAP Broadcast class
****************************************************************************
* Copyright (C) 1998-2006 VideoLAN
* $Id$
*
* Authors: Damien Lucas <nitrox@videolan.org>
* Rémi Denis-Courmont <rem # videolan.org>
*
*
* This program 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 2 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
#include <sys/types.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <unistd.h>
using namespace std;
#include <vector>
#include <string>
#include "program.h"
#include "message.h"
#include "broadcast.h"
Broadcast::Broadcast(int i_ttl, const char *psz_iface) : fd4 (-1), fd6 (-1),
scope_id (0)
{
/* Initializes IPv6 socket */
if (psz_iface != NULL)
scope_id = if_nametoindex (psz_iface);
fd6 = socket (AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
if (fd6 != -1)
{
struct sockaddr_in6 addr;
memset (&addr, 0, sizeof (addr));
addr.sin6_family = AF_INET6;
#ifdef HAVE_SA_LEN
addr.sin6_len = sizeof (addr);
#endif
if (psz_iface != NULL)
{
if (scope_id != 0)
{
if (setsockopt (fd6, IPPROTO_IPV6, IPV6_MULTICAST_IF,
&scope_id, sizeof (scope_id)) == 0)
scope_id = 0;
else
perror("setsockopt(IPV6_MULTICAST_IF)");
}
else
perror (psz_iface);
if (scope_id == 0)
{
close (fd6);
fd6 = -1;
}
}
if (i_ttl != 0)
setsockopt (fd6, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
&i_ttl, sizeof(i_ttl));
if (bind (fd6, (struct sockaddr *)&addr, sizeof (addr)) == 0)
shutdown (fd6, SHUT_RD);
else
{
close (fd6);
fd6 = -1;
}
}
/* Initializes IPv4 socket */
fd4 = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (fd4 != -1)
{
struct sockaddr_in addr;
memset (&addr, 0, sizeof (addr));
addr.sin_family = AF_INET;
#ifdef HAVE_SA_LEN
addr.sin_len = sizeof (addr);
#endif
if (psz_iface != NULL)
{
struct ifreq req;
strncpy (req.ifr_name, psz_iface, IFNAMSIZ - 1);
req.ifr_name[IFNAMSIZ - 1] = '\0';
if (ioctl (fd4, SIOCGIFADDR, &req) < 0)
{
perror("ioctl(SIOCGIFADDR)");
close (fd4);
fd4 = -1;
}
else
{
struct in_addr *ip;
ip = &((struct sockaddr_in *)(&req.ifr_addr))->sin_addr;
if (setsockopt(fd4, IPPROTO_IP, IP_MULTICAST_IF,
(char *)ip, sizeof (*ip)))
{
perror("setsockopt(IP_MULTICAST_IF)");
close (fd4);
fd4 = -1;
}
}
}
if (i_ttl != 0)
setsockopt (fd4, IPPROTO_IP, IP_MULTICAST_TTL,
&i_ttl, sizeof(i_ttl));
if (bind (fd4, (struct sockaddr *)&addr, sizeof (addr)) == 0)
shutdown (fd4, SHUT_RD);
else
{
close (fd4);
fd4 = -1;
}
}
}
Broadcast::~Broadcast()
{
if (fd4 != -1)
close (fd4);
if (fd6 != -1)
close (fd6);
}
int Broadcast::Send(Message* m, const struct sockaddr *dst, socklen_t len)
{
/* Get the message and the length */
const uint8_t* message = m->GetFinalMessage();
size_t length = m->GetFinalMessageLen();
if(message==NULL)
{
fprintf(stderr, "Bad message, skipping\n");
return (-1);
}
int fd;
switch (dst->sa_family)
{
case AF_INET6:
fd = fd6;
break;
case AF_INET:
fd = fd4;
break;
default:
fd = -1;
}
if (fd == -1)
return -1;
if (sendto (fd, message, length, 0, dst, len) < 0)
{
perror ("sendto");
return -1;
}
return 0;
}
int Broadcast::GuessDestination (const char *str,
struct sockaddr_storage *addr,
socklen_t *addrlen) const
{
union
{
struct in6_addr in6;
struct in_addr in;
} n;
if (inet_pton (AF_INET6, str, &n.in6) <= 0)
{
if (inet_pton (AF_INET, str, &n.in) <= 0)
{
fprintf (stderr, "%s: invalid IP address\n", str);
return -1;
}
/* str is an IPv4 address */
in_addr_t ip = ntohl (n.in.s_addr);
// 224.0.0.0/24 => 224.0.0.255
if ((ip & 0xffffff00) == 0xe0000000)
ip = 0xe00000ff;
else
// 239.255.0.0/16 => 239.255.255.255
if ((ip & 0xffff0000) == 0xefff0000)
ip = 0xefffffff;
else
// 239.192.0.0/14 => 239.195.255.255
if ((ip & 0xfffc0000) == 0xefc00000)
ip = 0xefc3ffff;
else
// other multicast address => 224.2.127.254
if ((ip & 0xf0000000) == 0xe0000000)
ip = 0xe0027ffe;
else
{
fprintf (stderr, "%s: not a multicast IPv4 address\n", str);
return -1;
}
ip = htonl (ip);
struct sockaddr_in *a4 = (struct sockaddr_in *)addr;
memset (a4, 0, sizeof (*a4));
a4->sin_family = AF_INET;
#ifdef HAVE_SA_LEN
a4->sin_len = sizeof (*a4);
#endif
a4->sin_port = htons (HELLO_PORT);
memcpy (&a4->sin_addr.s_addr, &ip, sizeof (ip));
*addrlen = sizeof (*a4);
return 0;
}
/* str is an IPv6 address */
if (n.in6.s6_addr[0] == 0xff)
{
/* multicast address */
// ff0x::2:7ffe with x = scope
n.in6.s6_addr[1] = n.in6.s6_addr[1] & 0xf;
// (ff0x):0000:0000:0000:0000:0000:0002:7ffe
memcpy (n.in6.s6_addr + 2,
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x7f\xfe",
14);
}
else
{
fprintf (stderr, "%s: not a multicast IPv6 address\n", str);
return -1;
}
struct sockaddr_in6 *a6 = (struct sockaddr_in6 *)addr;
memset (a6, 0, sizeof (*a6));
a6->sin6_family = AF_INET6;
#ifdef HAVE_SA_LEN
a6->sin_len = sizeof (*a6);
#endif
a6->sin6_scope_id = scope_id;
a6->sin6_port = htons (HELLO_PORT);
memcpy (&a6->sin6_addr, &n.in6, sizeof (n.in6));
*addrlen = sizeof (*a6);
return 0;
}
/*****************************************************************************
* broacast.h : SAP Broadcast Class definition
****************************************************************************
* Copyright (C) 1998-2005 VideoLAN
* $Id$
*
* Authors: Damien Lucas <nitrox@videolan.org>
*
* This program 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 2 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
#define HELLO_PORT 9875
#define HELLO_PORT_STR "9875"
#define SAP_IPV4_ADDR "224.2.127.254"
#define SAP_IPV6_ADDR_1 "ff0"
/* You must put the scope in between */
#define SAP_IPV6_ADDR_2 "::2:7ffe"
class Broadcast {
public:
Broadcast(int i_ttl = 0, const char *psz_iface = NULL);
~Broadcast(void);
int Send(Message*, const struct sockaddr *dst, socklen_t len);
int GuessDestination(const char *prgm, struct sockaddr_storage *addr,
socklen_t *len) const;
private:
int fd4, fd6; /* File descriptor on the socket */
int scope_id;
};
dnl Autoconf settings for mini-SAP-server
dnl $Id$
AC_COPYRIGHT([Copyright (C) the VideoLAN team 1998-2009])
AC_INIT(miniSAPserver,0.3.6,streaming@videolan.org)
AC_PREREQ(2.50)
AC_CONFIG_SRCDIR(configure.ac)
AC_CONFIG_AUX_DIR(admin)
AC_CONFIG_MACRO_DIR(m4)
AC_CONFIG_HEADERS(config.h)
dnl ****************************
dnl General checks
dnl ****************************
AC_PROG_CC
AC_PROG_CXX
AC_C_CONST
AC_HEADER_STDBOOL
AC_CHECK_FUNCS([clearenv])
RDC_REPLACE_FUNC_GETOPT_LONG
dnl ****************************
dnl Features
dnl ****************************
AM_INIT_AUTOMAKE([check-news dist-bzip2 no-dist-gzip std-options])
dnl On the fly charset transcoding
AM_ICONV
RDC_STRUCT_SOCKADDR_LEN
dnl Enable/Disable slp
AS_IF([test "x${enable_slp}" == "x"],
[enable_slp="no"])
AC_ARG_ENABLE(slp,
[AC_HELP_STRING([--enable-slp],
[enable SLP announcing (default disabled)])],
[AS_IF([test "${enable_slp}" != "no"],
[AC_CHECK_HEADERS(slp.h,,
[AC_MSG_ERROR(["Unable to find SLP headers"])])
AC_DEFINE(CONFIG_SLP,,[SLP announcing support])
])
])
AM_CONDITIONAL(CONFIG_SLP, [test "${enable_slp}" != "no"])
dnl Debug Mode
AC_ARG_ENABLE(debug,
AC_HELP_STRING([--enable-debug],
[enable debug mode (default enabled)]))
AS_IF([test "${enable_debug}" != "no"],[CFLAGS="${CFLAGS} -g"])
dnl ****************************
dnl Output Files
dnl ****************************
AC_SUBST(CONFIG_SLP)
AC_SUBST(CFLAGS)
AC_OUTPUT(Makefile)
/*****************************************************************************
* slp.h : SLP Class definition
****************************************************************************
* Copyright (C) 1998-2003 VideoLAN
* $Id$
*
* Authors: Clément Stenac <zorglub@via.ecp.fr>
*
* This program 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 2 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
void SLPReg ( SLPHandle , SLPError , void * );
class SLP {
public:
SLP();
~SLP();
int Register(Program *);
int UnRegister(Program *);
private:
SLPHandle slp_handle;
SLPError slp_result;
};
# getopt.m4
dnl Copyright (C) 2003-2004 Remi Denis-Courmont
dnl From Remi Denis-Courmont
AC_DEFUN([RDC_FUNC_GETOPT_LONG],
[AC_CHECK_HEADERS(getopt.h)
AH_TEMPLATE([HAVE_GETOPT_LONG], [Define to 1 if you have the `getopt_long' function.])
AC_SEARCH_LIBS(getopt_long, [gnugetopt], have_getopt_long=yes,
have_getopt_long=no)
if test $have_getopt_long = yes; then
AC_DEFINE(HAVE_GETOPT_LONG)
$1
else
$2
false
fi
])
AC_DEFUN([RDC_REPLACE_FUNC_GETOPT_LONG],
[AH_BOTTOM([/* Fallback replacement for GNU `getopt_long' */
#ifndef HAVE_GETOPT_LONG
# define getopt_long( argc, argv, optstring, longopts, longindex ) \
getopt (argc, argv, optstring)
# if !GETOPT_STRUCT_OPTION && !HAVE_GETOPT_H
struct option { const char *name; int has_arg; int *flag; int val; };
# define GETOPT_STRUCT_OPTION 1
# endif
# ifndef required_argument
# define no_argument 0
# define required_argument 1
# define optional_argument 2
# endif
#endif])
RDC_FUNC_GETOPT_LONG
])
# sockaddr.m4
# Copyright © 2003-2006 Rémi Denis-Courmont
# <rdenis (at) simphalempin (dot) com>.
# This file (sockaddr.m4) is free software; unlimited permission to
# copy and/or distribute it , with or without modifications, as long
# as this notice is preserved.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE.
dnl SHOULD check <sys/socket.h>, <winsock2.h> before that
AC_DEFUN([RDC_STRUCT_SOCKADDR_LEN],
[AC_LANG_ASSERT(C)
AH_TEMPLATE(HAVE_SA_LEN, [Define to 1 if `struct sockaddr' has a `sa_len' member.])
AC_CACHE_CHECK([if struct sockaddr has a sa_len member],
rdc_cv_struct_sockaddr_len,
[AC_COMPILE_IFELSE([AC_LANG_PROGRAM([
[#if HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
#include <sys/socket.h>]], [[struct sockaddr addr; addr.sa_len = 0;]])],
rdc_cv_struct_sockaddr_len=yes,
rdc_cv_struct_sockaddr_len=no)])
AS_IF([test $rdc_cv_struct_sockaddr_len = yes],
[AC_DEFINE(HAVE_SA_LEN)])
])
/*****************************************************************************
* message.cpp : SAP Message class
****************************************************************************
* Copyright (C) 1998-2006 the VideoLAN team
* $Id$
*
* Authors: Damien Lucas <nitrox@videolan.org>
* Philippe Van Hecke <philippe.vanhecke@belnet.be>
* Derk-Jan Hartman <hartman at videolan dot org>
* Rémi Denis-Courmont <rem # videolan dot org>
*
* This program 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 2 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
#include <arpa/inet.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <string>
#include <sstream>
#include <vector>
using namespace std;
#include "sapserver.h"
#include "program.h"
#include "message.h"
static const char mime_type[] = "application/sdp";
/*****************************************************************************
* Constructors
*****************************************************************************/
Message::Message(uint16_t v, const char* ip)
{
version = v;
u_long ip_server = inet_addr (ip); //TODO automaticaly get it ?
msg_len = 4
+ 4 /* (IPv4 specific) */
/*+ authentification length (N/A) */
+ sizeof (mime_type);
msg = (uint8_t *)realloc (NULL, msg_len);
if (msg == NULL)
return; // TODO: throw an exception
// Build the Message Header once initialized according to RFC 2974 (SAP)
/* Byte 0 : Version number V1 = 001 (3 bits)
* Address type IPv4/IPv6 = 0/1 (1 bit)
* Reserved 0 (1 bit)
* Message Type ann/del = 0/1 (1 bit)
* Encryption on/off = 0/1 (1 bit)
* Compressed on/off = 0/1 (1 bit) */
msg[0] = 0x20;
//if ( ip_version == SAP_IPV6 ) msg[0] |= 0x10;
//if ( type == SAP_DELETE ) msg[0] |= 0x04;
//if ( encrypted ) msg[0] |= 0x02;
//if ( compressed ) msg[0] |= 0x01;
/* Byte 1 : Authe