1984 lines
65 KiB
C
1984 lines
65 KiB
C
|
/*
|
||
|
*
|
||
|
* @APPLE_LICENSE_HEADER_START@
|
||
|
*
|
||
|
* Copyright (c) 1999-2008 Apple Inc. All Rights Reserved.
|
||
|
*
|
||
|
* This file contains Original Code and/or Modifications of Original Code
|
||
|
* as defined in and that are subject to the Apple Public Source License
|
||
|
* Version 2.0 (the 'License'). You may not use this file except in
|
||
|
* compliance with the License. Please obtain a copy of the License at
|
||
|
* http://www.opensource.apple.com/apsl/ and read it before using this
|
||
|
* file.
|
||
|
*
|
||
|
* The Original Code and all software distributed under the License are
|
||
|
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
||
|
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
|
||
|
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
|
||
|
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
|
||
|
* Please see the License for the specific language governing rights and
|
||
|
* limitations under the License.
|
||
|
*
|
||
|
* @APPLE_LICENSE_HEADER_END@
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
* proxy.c
|
||
|
*
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
#include <time.h>
|
||
|
#include <fcntl.h>
|
||
|
#include <errno.h>
|
||
|
#include <assert.h>
|
||
|
|
||
|
|
||
|
#include <sys/types.h>
|
||
|
#include <sys/time.h>
|
||
|
#include <arpa/inet.h>
|
||
|
#include <netinet/in.h>
|
||
|
#include <netdb.h>
|
||
|
#include <sys/resource.h>
|
||
|
#include <signal.h>
|
||
|
#include <sys/signal.h>
|
||
|
#include <sys/socket.h>
|
||
|
|
||
|
#include <ctype.h>
|
||
|
#include <regex.h>
|
||
|
#include <arpa/inet.h>
|
||
|
#include <netinet/in.h>
|
||
|
|
||
|
#include "./proxy_plat.h"
|
||
|
#include "util.h"
|
||
|
#include "proxy.h"
|
||
|
|
||
|
#include <unistd.h>
|
||
|
|
||
|
#ifndef __MacOSX__
|
||
|
#include "get_opt.h"
|
||
|
#endif
|
||
|
|
||
|
#include "../revision.h"
|
||
|
/**********************************************/
|
||
|
// Globals
|
||
|
int gQuitting = 0;
|
||
|
int gVerbose = 0;
|
||
|
int gDebug = 0;
|
||
|
#if defined(mac) || defined(WIN32)
|
||
|
int gStats = 1;
|
||
|
#else
|
||
|
int gStats = 0;
|
||
|
#ifndef unix
|
||
|
#define unix
|
||
|
#endif
|
||
|
|
||
|
#endif
|
||
|
|
||
|
#define ANY_ADDRESS -1
|
||
|
#define MAX_CMD_BUFF 512
|
||
|
#define MAX_CONFIG_LINE_LEN 512
|
||
|
#define MAX_LINE_BUFF 2048
|
||
|
|
||
|
int gNeedsService = 0;
|
||
|
|
||
|
#ifndef __MacOSX__
|
||
|
extern char *gConfigFilePath;
|
||
|
#endif
|
||
|
|
||
|
rtsp_session *gSessions = NULL;
|
||
|
|
||
|
subnet_allow *gAllowedNets = NULL;
|
||
|
rtsp_listener *gListeners = NULL;
|
||
|
int gUserLimit = 0;
|
||
|
int gNumUsers = 0;
|
||
|
|
||
|
//int gUDPPortMin = 4000;
|
||
|
//int gUDPPortMax = 65535;
|
||
|
|
||
|
int gProxyIP = -1;
|
||
|
int gRTSPIP = ANY_ADDRESS;
|
||
|
int gMaxPorts = 0;
|
||
|
|
||
|
unsigned long gBytesReceived = 0;
|
||
|
unsigned long gBytesSent = 0;
|
||
|
unsigned long gPacketsReceived = 0;
|
||
|
unsigned long gPacketsSent = 0;
|
||
|
unsigned long gLastPacketsReceived = 0;
|
||
|
unsigned long gLastPacketsSent = 0;
|
||
|
|
||
|
/**********************************************/
|
||
|
|
||
|
int gDropEnabled = 0;
|
||
|
float gDropPercent = 0.0;
|
||
|
time_t gStartDropOffset = 0;
|
||
|
|
||
|
/**********************************************/
|
||
|
#if defined(unix)
|
||
|
void sig_catcher(int sig)
|
||
|
{
|
||
|
if (gDropEnabled)
|
||
|
{
|
||
|
|
||
|
if (sig == SIGUSR1)
|
||
|
{ gDropPercent = 0;
|
||
|
#if __solaris__
|
||
|
signal(SIGUSR1, sig_catcher);
|
||
|
#endif
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (sig == SIGUSR2)
|
||
|
{ gDropPercent += 1.0;
|
||
|
if (gDropPercent > 100.0)
|
||
|
gDropPercent = 100.0;
|
||
|
|
||
|
#if __solaris__
|
||
|
signal(SIGUSR2, sig_catcher);
|
||
|
#endif
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (sig == SIGHUP)
|
||
|
{
|
||
|
FILE * dropParamFile = fopen("drop","r");
|
||
|
if (dropParamFile != NULL)
|
||
|
{ char inBuff[256];
|
||
|
while (fgets(inBuff, sizeof(inBuff), dropParamFile) != 0)
|
||
|
{ char *tag;
|
||
|
char *value;
|
||
|
tag = strtok(inBuff, " ");
|
||
|
if (tag != NULL && NULL != strstr(tag,"drop_percent"))
|
||
|
{ value = strtok(NULL, " \r\n");
|
||
|
printf("drop_percent value=%s\n",value);
|
||
|
if (value != NULL)
|
||
|
sscanf(value, "%f",&gDropPercent);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
fclose(dropParamFile);
|
||
|
}
|
||
|
#if __solaris__
|
||
|
signal(SIGHUP, sig_catcher);
|
||
|
#endif
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// do nothing cases
|
||
|
if ( (sig == SIGUSR1) || (sig == SIGUSR2) || (sig == SIGHUP) )
|
||
|
{
|
||
|
#if __solaris__
|
||
|
sigignore(sig);
|
||
|
#endif
|
||
|
return;
|
||
|
}
|
||
|
gQuitting = 1;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
/**********************************************/
|
||
|
static void print_usage(char *name)
|
||
|
{
|
||
|
printf("%s/%s Built on %s, %s: [-dDvh] [-p #] [-c <file>]\n", name, kVersionString, __DATE__, __TIME__ );
|
||
|
printf(" -d : debug\n");
|
||
|
printf(" -D : verbose debug\n");
|
||
|
printf(" -v : print usage\n");
|
||
|
printf(" -h : help (this message)\n");
|
||
|
printf(" -p # : listen on port # (defaults to 554)\n");
|
||
|
printf(" -c <file> : configuration file (defaults to %s)\n", gConfigFilePath);
|
||
|
printf(" -i <hostname/ip address> : RTP Hostname/IP Address bind address.\n");
|
||
|
printf(" -s : statistics\n");
|
||
|
printf(" -x : enable packet drop mode (defaults to 0). SIGUSR1 to reset to 0. SIGUSR2 to add 1 to drop percent\n");
|
||
|
printf(" use SIGHUP to read tag and value 'drop_percent 5' from the local file 'drop'. Use -s to see drop statistics\n");
|
||
|
|
||
|
}
|
||
|
|
||
|
/**********************************************/
|
||
|
int main(int argc, char *argv[])
|
||
|
{
|
||
|
int i, j;
|
||
|
int numOptions = 0; // num command line options spec'd
|
||
|
signed char ch;
|
||
|
int listening_port = 554, user_listener = false;
|
||
|
time_t time_zero, now, last;
|
||
|
time_t usnow, uslast;
|
||
|
char *hostname = NULL;
|
||
|
|
||
|
#ifndef __MacOSX__
|
||
|
extern char *optarg;
|
||
|
extern int optind;
|
||
|
#endif
|
||
|
|
||
|
time_zero = time(0);
|
||
|
last = time_zero;
|
||
|
|
||
|
#if defined(unix)
|
||
|
//
|
||
|
// increase file descriptor limit
|
||
|
{
|
||
|
struct rlimit rl;
|
||
|
rl.rlim_cur = 4096;
|
||
|
rl.rlim_max = 4096;
|
||
|
setrlimit(RLIMIT_NOFILE, &rl);
|
||
|
}
|
||
|
//
|
||
|
signal(SIGHUP, sig_catcher);
|
||
|
signal(SIGTERM, sig_catcher);
|
||
|
signal(SIGUSR1, sig_catcher);
|
||
|
signal(SIGUSR2, sig_catcher);
|
||
|
signal(SIGPIPE, SIG_IGN);
|
||
|
|
||
|
//
|
||
|
// boost our priority
|
||
|
#if defined( _sparc) || defined(HAVE_SETPRIORITY) || defined(__sgi__)
|
||
|
i = setpriority(PRIO_PROCESS, 0, -20);
|
||
|
if (i != 0)
|
||
|
fprintf(stderr, "schedctl failed %d\n", errno);
|
||
|
#endif
|
||
|
#endif // defined(unix)
|
||
|
//
|
||
|
// initialize the network stacks
|
||
|
init_network();
|
||
|
init_ui();
|
||
|
|
||
|
//
|
||
|
// read command line options
|
||
|
#if __MacOSX__
|
||
|
while ((ch = getopt(argc, argv, gOptionsString)) != -1)
|
||
|
#else
|
||
|
while ((ch = get_opt(argc, argv, gOptionsString)) != -1)
|
||
|
#endif
|
||
|
{
|
||
|
numOptions++;
|
||
|
switch (ch)
|
||
|
{
|
||
|
case 'p':
|
||
|
listening_port = atoi(optarg);
|
||
|
user_listener = true;
|
||
|
break;
|
||
|
|
||
|
case 'd':
|
||
|
gDebug = 1;
|
||
|
signal(SIGINT, sig_catcher); // try to catch the signal so we show what's still running.
|
||
|
break;
|
||
|
|
||
|
case 'D':
|
||
|
gVerbose = 1;
|
||
|
gDebug = 1;
|
||
|
signal(SIGINT, sig_catcher); // try to catch the signal so we show what's still running.
|
||
|
break;
|
||
|
|
||
|
case 'x':
|
||
|
gDropEnabled = 1;
|
||
|
break;
|
||
|
|
||
|
case 'v':
|
||
|
print_usage(argv[0]);
|
||
|
exit(0);
|
||
|
break;
|
||
|
case 's':
|
||
|
gStats = 1;
|
||
|
break;
|
||
|
case 'h':
|
||
|
print_usage(argv[0]);
|
||
|
numOptions -= 2; // not an option, if -h only, print usage and quit
|
||
|
break;
|
||
|
case 'i':
|
||
|
hostname = optarg;
|
||
|
break;
|
||
|
case 'c':
|
||
|
if ( optarg )
|
||
|
gConfigFilePath = str_dup(optarg);
|
||
|
else
|
||
|
{
|
||
|
printf("-c option requires a file path as an argument.\n");
|
||
|
exit(-1);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// will be less < 0 if asked for -h only, if -h with other options we'll
|
||
|
// continue to run, if no options at all we'll continute to run using
|
||
|
// the default config gile path
|
||
|
|
||
|
if ( numOptions < 0 )
|
||
|
exit(0);
|
||
|
|
||
|
argc -= optind;
|
||
|
argv += optind;
|
||
|
|
||
|
//
|
||
|
// read and deal with config
|
||
|
read_config();
|
||
|
|
||
|
//
|
||
|
// set up rtsp listener (if necessary)
|
||
|
// -- if it wasn't specified in the config file, or the user had
|
||
|
// one on the command line
|
||
|
if (!gListeners || user_listener)
|
||
|
add_rtsp_port_listener(ANY_ADDRESS,listening_port);
|
||
|
|
||
|
/* exit if we can't listen */
|
||
|
if (!gListeners) {
|
||
|
ErrorString("Can't set up any RTSP listeners. Exiting...\n");
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
/* if a hostname was specified, it over-writes whatever's in
|
||
|
* the config file. */
|
||
|
if (hostname)
|
||
|
name_to_ip_num(hostname, &gProxyIP, false);
|
||
|
|
||
|
//
|
||
|
if (gProxyIP == -1)
|
||
|
{ //gProxyIP = get_local_ip_address(); this returns the local loopback (127.0.0.1) and is useless
|
||
|
ErrorString("An rtp-bind-addr configuration line or -i command line option is required.\n");
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
if (gVerbose)
|
||
|
printf("rtp-bind-addr: %s\n", ip_to_string(gProxyIP));
|
||
|
|
||
|
if (!(gVerbose || gDebug || gStats))
|
||
|
daemonize();
|
||
|
//
|
||
|
|
||
|
//
|
||
|
// compile regular expressions for RTSP
|
||
|
uslast = microseconds();
|
||
|
usnow = uslast;
|
||
|
//
|
||
|
// main event loop
|
||
|
i = 0, j = 0;
|
||
|
while (!gQuitting) {
|
||
|
if ((i++ >= 50) || !gNeedsService) {
|
||
|
service_listeners();
|
||
|
i = 0;
|
||
|
}
|
||
|
if ((j++ > 25) || !gNeedsService) {
|
||
|
service_sessions();
|
||
|
j = 0;
|
||
|
}
|
||
|
service_shoks();
|
||
|
|
||
|
if (gStats) {
|
||
|
now = time(0);
|
||
|
if ((now - last) >= 2) {
|
||
|
time_t msElapsed;
|
||
|
static unsigned long lastBPSReceived = 0, lastBPSSent = 0;
|
||
|
unsigned long bpsReceived, bpsSent;
|
||
|
stats_chunk stats;
|
||
|
stats.numClients = gNumUsers;
|
||
|
stats.elapsedSeconds = now - time_zero;
|
||
|
usnow = microseconds();
|
||
|
msElapsed = (usnow - uslast) / USEC_PER_MSEC;
|
||
|
bpsReceived = ((gBytesReceived * USEC_PER_MSEC) / msElapsed) * 8;
|
||
|
bpsSent = ((gBytesSent * USEC_PER_MSEC) / msElapsed) * 8;
|
||
|
if (lastBPSReceived) {
|
||
|
stats.bpsReceived = (bpsReceived + lastBPSReceived) / 2;
|
||
|
stats.bpsSent = (bpsSent + lastBPSSent) / 2;
|
||
|
}
|
||
|
else {
|
||
|
stats.bpsReceived = bpsReceived;
|
||
|
stats.bpsSent = bpsSent;
|
||
|
}
|
||
|
stats.ppsReceived = ((gPacketsReceived - gLastPacketsReceived) * USEC_PER_MSEC) / msElapsed;
|
||
|
stats.ppsSent = ((gPacketsSent - gLastPacketsSent) * USEC_PER_MSEC) / msElapsed;
|
||
|
stats.totalPacketsReceived = gPacketsReceived;
|
||
|
stats.totalPacketsSent = gPacketsSent;
|
||
|
if (stats.ppsReceived > 0)
|
||
|
stats.percentLostPackets = 100.0 - ((float) stats.ppsSent / (float) stats.ppsReceived * (float)100.0);
|
||
|
else
|
||
|
stats.percentLostPackets = 0.0;
|
||
|
stats.numPorts = gMaxPorts;
|
||
|
DoStats(&stats);
|
||
|
gBytesReceived = 0;
|
||
|
gBytesSent = 0;
|
||
|
gLastPacketsReceived = gPacketsReceived;
|
||
|
gLastPacketsSent = gPacketsSent;
|
||
|
lastBPSReceived = bpsReceived;
|
||
|
lastBPSSent = bpsSent;
|
||
|
uslast = usnow;
|
||
|
last = now;
|
||
|
}
|
||
|
}
|
||
|
if (!gNeedsService) {
|
||
|
sleep_milliseconds(1);
|
||
|
}
|
||
|
else {
|
||
|
service_ui(0);
|
||
|
gNeedsService = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
cleanup_sessions();
|
||
|
cleanup_listeners();
|
||
|
|
||
|
//
|
||
|
// terminate the network stacks
|
||
|
term_network();
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/**********************************************/
|
||
|
int service_listeners()
|
||
|
{
|
||
|
rtsp_listener *listener;
|
||
|
|
||
|
listener = gListeners;
|
||
|
while (listener)
|
||
|
{
|
||
|
answer_new_connection(listener);
|
||
|
listener = listener->next;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/**********************************************/
|
||
|
void cleanup_listeners()
|
||
|
{
|
||
|
rtsp_listener *listener, *next;
|
||
|
|
||
|
listener = gListeners;
|
||
|
while (listener)
|
||
|
{
|
||
|
next = listener->next;
|
||
|
if (gDebug)
|
||
|
DebugString1("Closing listener socket %d", listener->skt);
|
||
|
close_socket(listener->skt);
|
||
|
free(listener);
|
||
|
listener = next;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**********************************************/
|
||
|
void add_rtsp_port_listener(int address,int port)
|
||
|
{
|
||
|
rtsp_listener *listener;
|
||
|
int skt;
|
||
|
|
||
|
if (gVerbose)
|
||
|
printf("Listening for RTSP messages on port %d\n", port);
|
||
|
|
||
|
if ((skt = new_socket_tcp(true)) == INVALID_SOCKET) {
|
||
|
ErrorString1("couldn't set up RTSP listening socket (%d): %m\n",
|
||
|
port);
|
||
|
return;
|
||
|
}
|
||
|
set_socket_reuse_address(skt);
|
||
|
|
||
|
if (bind_socket_to_address(skt, address, port, true) == SOCKET_ERROR)
|
||
|
{
|
||
|
close_socket(skt);
|
||
|
perror("binding RTSP listening socket");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
make_socket_nonblocking(skt);
|
||
|
|
||
|
if (listen_to_socket(skt) == SOCKET_ERROR)
|
||
|
{
|
||
|
close_socket(skt);
|
||
|
ErrorString1("listening on RTSP socket (%d): %m\n", port);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
listener = (rtsp_listener *)malloc(sizeof(rtsp_listener));
|
||
|
if (!listener) {
|
||
|
ErrorString("Couldn't allocate memory for listener.\n");
|
||
|
exit(1);
|
||
|
}
|
||
|
listener->skt = skt;
|
||
|
listener->port = port;
|
||
|
listener->next = gListeners;
|
||
|
gListeners = listener;
|
||
|
}
|
||
|
|
||
|
/**********************************************/
|
||
|
void answer_new_connection(rtsp_listener *listener)
|
||
|
{
|
||
|
int skt;
|
||
|
rtsp_session *session;
|
||
|
|
||
|
if ( !call_is_waiting(listener->skt, &skt))
|
||
|
return;
|
||
|
|
||
|
session = new_session();
|
||
|
if (!session) {
|
||
|
ErrorString("Couldn't create a new session\n");
|
||
|
close_socket(skt);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
session->client_skt = skt;
|
||
|
make_socket_nonblocking(session->client_skt);
|
||
|
session->client_ip = get_remote_address(session->client_skt, NULL);
|
||
|
session->client_interface_addr = get_interface_addr(session->client_skt);
|
||
|
session->newSession = true;
|
||
|
|
||
|
//
|
||
|
// add him to our session list
|
||
|
add_session(session);
|
||
|
|
||
|
if (gVerbose)
|
||
|
printf("Added connection for client %s.\n", ip_to_string(session->client_ip));
|
||
|
|
||
|
gNeedsService++;
|
||
|
}
|
||
|
|
||
|
/**********************************************/
|
||
|
void add_session(rtsp_session *session)
|
||
|
{
|
||
|
if (gSessions == NULL)
|
||
|
{
|
||
|
gSessions = session;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
session->next = gSessions;
|
||
|
gSessions = session;
|
||
|
}
|
||
|
gNumUsers++;
|
||
|
}
|
||
|
|
||
|
/**********************************************/
|
||
|
void remove_session(rtsp_session *session)
|
||
|
{
|
||
|
if (gSessions == NULL)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (gSessions == session)
|
||
|
{
|
||
|
gSessions = session->next;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
rtsp_session *cur, *last = gSessions;
|
||
|
cur = last->next;
|
||
|
while (cur != NULL)
|
||
|
{
|
||
|
if (cur == session)
|
||
|
{
|
||
|
last->next = cur->next;
|
||
|
break;
|
||
|
}
|
||
|
last = cur;
|
||
|
cur = cur->next;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**********************************************/
|
||
|
rtsp_session *new_session(void)
|
||
|
{
|
||
|
rtsp_session *s;
|
||
|
int i;
|
||
|
|
||
|
s = (rtsp_session*)calloc(1, sizeof(rtsp_session));
|
||
|
if (s != NULL)
|
||
|
{
|
||
|
s->next = NULL;
|
||
|
s->die = false;
|
||
|
s->client_skt = INVALID_SOCKET;
|
||
|
s->client_ip = -1;
|
||
|
s->server_address = NULL;
|
||
|
s->server_skt = INVALID_SOCKET;
|
||
|
s->server_interface_addr = 0;
|
||
|
s->client_interface_addr = 0;
|
||
|
s->server_ip = -1;
|
||
|
s->server_port = 554;
|
||
|
s->server_skt_pending_connection = false;
|
||
|
s->state = stRecvClientCommand;
|
||
|
s->transaction_type = ttNone;
|
||
|
s->sessionID = NULL;
|
||
|
|
||
|
s->cur_trk = 0;
|
||
|
for (i=0; i<MAX_TRACKS; i++)
|
||
|
{
|
||
|
s->trk[i].ID = 0;
|
||
|
s->trk[i].ClientRTPPort = -1;
|
||
|
s->trk[i].ServerRTPPort = -1;
|
||
|
|
||
|
s->trk[i].RTP_S2P = NULL;
|
||
|
s->trk[i].RTCP_S2P = NULL;
|
||
|
s->trk[i].RTP_P2C = NULL;
|
||
|
s->trk[i].RTCP_P2C = NULL;
|
||
|
|
||
|
s->trk[i].RTP_S2C_tpb.status = NULL;
|
||
|
s->trk[i].RTP_S2C_tpb.send_from = NULL;
|
||
|
s->trk[i].RTP_S2C_tpb.send_to_ip = -1;
|
||
|
s->trk[i].RTP_S2C_tpb.send_to_port = -1;
|
||
|
s->trk[i].RTP_S2C_tpb.packetSendCount = 0;
|
||
|
s->trk[i].RTP_S2C_tpb.nextDropPacket = 0;
|
||
|
s->trk[i].RTP_S2C_tpb.droppedPacketCount = 0;
|
||
|
|
||
|
s->trk[i].RTCP_S2C_tpb.status = NULL;
|
||
|
s->trk[i].RTCP_S2C_tpb.send_from = NULL;
|
||
|
s->trk[i].RTCP_S2C_tpb.send_to_ip = -1;
|
||
|
s->trk[i].RTCP_S2C_tpb.send_to_port = -1;
|
||
|
s->trk[i].RTCP_S2C_tpb.packetSendCount = 0;
|
||
|
s->trk[i].RTCP_S2C_tpb.nextDropPacket = 0;
|
||
|
s->trk[i].RTCP_S2C_tpb.droppedPacketCount = 0;
|
||
|
|
||
|
s->trk[i].RTCP_C2S_tpb.status = NULL;
|
||
|
s->trk[i].RTCP_C2S_tpb.send_from = NULL;
|
||
|
s->trk[i].RTCP_C2S_tpb.send_to_ip = -1;
|
||
|
s->trk[i].RTCP_C2S_tpb.send_to_port = -1;
|
||
|
s->trk[i].RTCP_C2S_tpb.packetSendCount = 0;
|
||
|
s->trk[i].RTCP_C2S_tpb.nextDropPacket = 0;
|
||
|
s->trk[i].RTCP_C2S_tpb.droppedPacketCount = 0;
|
||
|
}
|
||
|
s->numTracks = 0;
|
||
|
|
||
|
s->amtInClientInBuffer = 0;
|
||
|
s->amtInClientOutBuffer = 0;
|
||
|
s->amtInServerInBuffer = 0;
|
||
|
s->amtInServerOutBuffer = 0;
|
||
|
s->totalContentLength = 0; // headers + body
|
||
|
s->contentLength = 0; // just body
|
||
|
s->haveParsedServerReplyHeaders = 0;
|
||
|
}
|
||
|
return s;
|
||
|
}
|
||
|
|
||
|
/**********************************************/
|
||
|
void cleanup_session(rtsp_session *s)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
if (s->client_skt != INVALID_SOCKET)
|
||
|
{
|
||
|
if (gDebug)
|
||
|
printf("Closing client rtsp socket %d (ip %s)\n", s->client_skt, ip_to_string(s->client_ip));
|
||
|
close_socket(s->client_skt);
|
||
|
s->client_skt = INVALID_SOCKET;
|
||
|
}
|
||
|
|
||
|
if (s->server_skt != INVALID_SOCKET)
|
||
|
{
|
||
|
if (gDebug)
|
||
|
printf("Closing server rtsp socket %d (ip %s)\n", s->server_skt, ip_to_string(s->server_ip));
|
||
|
close_socket(s->server_skt);
|
||
|
s->server_skt = INVALID_SOCKET;
|
||
|
|
||
|
}
|
||
|
|
||
|
if (s->server_address)
|
||
|
{
|
||
|
free(s->server_address);
|
||
|
s->server_address = NULL;
|
||
|
}
|
||
|
|
||
|
if (s->sessionID)
|
||
|
{
|
||
|
free(s->sessionID);
|
||
|
s->sessionID = NULL;
|
||
|
}
|
||
|
|
||
|
for (i=0; i<s->numTracks; i++)
|
||
|
{
|
||
|
if (s->trk[i].RTP_S2P)
|
||
|
remove_shok_ref(s->trk[i].RTP_S2P, s->server_interface_addr, s->server_ip, true);
|
||
|
if (s->trk[i].RTP_P2C)
|
||
|
remove_shok_ref(s->trk[i].RTP_P2C, s->client_interface_addr, s->client_ip, true);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**********************************************/
|
||
|
void cleanup_sessions(void)
|
||
|
{
|
||
|
rtsp_session *cur, *next;
|
||
|
|
||
|
cur = gSessions;
|
||
|
while (cur)
|
||
|
{
|
||
|
next = cur->next;
|
||
|
cleanup_session(cur);
|
||
|
free(cur);
|
||
|
cur = next;
|
||
|
}
|
||
|
gSessions = NULL;
|
||
|
}
|
||
|
|
||
|
/**********************************************/
|
||
|
int service_sessions()
|
||
|
{
|
||
|
rtsp_session *cur, *next;
|
||
|
|
||
|
cur = gSessions;
|
||
|
while (cur)
|
||
|
{
|
||
|
next = cur->next;
|
||
|
if (cur->newSession)
|
||
|
{
|
||
|
cur->newSession = false;
|
||
|
//
|
||
|
// check to see if this user is allowed
|
||
|
if (! allow_ip(cur->client_ip))
|
||
|
{
|
||
|
cur->die = true;
|
||
|
send_rtsp_error(cur->client_skt, kPermissionDenied);
|
||
|
if (gVerbose)
|
||
|
printf("Refusing connection for client %s - not allowed\n",
|
||
|
ip_to_string(cur->client_ip));
|
||
|
}
|
||
|
//
|
||
|
// see if we're going beyond our user limit
|
||
|
else if (gUserLimit && (gNumUsers > gUserLimit))
|
||
|
{
|
||
|
cur->die = true;
|
||
|
send_rtsp_error(cur->client_skt, kTooManyUsers);
|
||
|
if (gVerbose)
|
||
|
printf("Refusing connection for client %s - too many users\n",
|
||
|
ip_to_string(cur->client_ip));
|
||
|
}
|
||
|
}
|
||
|
if (cur->die)
|
||
|
{
|
||
|
gNumUsers--;
|
||
|
remove_session(cur);
|
||
|
cleanup_session(cur);
|
||
|
free(cur);
|
||
|
cur = NULL;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (cur->client_skt != INVALID_SOCKET || cur->server_skt != INVALID_SOCKET)
|
||
|
service_session(cur);
|
||
|
}
|
||
|
cur = next;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/**********************************************/
|
||
|
static const t_cmd_map cmd_map[] = {
|
||
|
{"DESCRIBE", ttDescribe},
|
||
|
{"SETUP", ttSetup},
|
||
|
{"PLAY", ttPlay},
|
||
|
{"PAUSE", ttPause},
|
||
|
{"STOP", ttStop},
|
||
|
{"TEARDOWN", ttTeardown},
|
||
|
{"OPTIONS", ttOptions},
|
||
|
{"ANNOUNCE", ttAnnounce},
|
||
|
{"REDIRECT", ttRedirect},
|
||
|
{"GET_PARAMETER", ttGetParameter},
|
||
|
{"SET_PARAMETER", ttSetParameter},
|
||
|
{NULL, ttNone}
|
||
|
};
|
||
|
|
||
|
static int cmd_to_transaction_type(char *cmd)
|
||
|
{
|
||
|
const t_cmd_map *map;
|
||
|
map = cmd_map;
|
||
|
while (map->cmd != NULL) {
|
||
|
if (str_casecmp(map->cmd, cmd) == 0)
|
||
|
return map->type;
|
||
|
map++;
|
||
|
}
|
||
|
return ttNone;
|
||
|
}
|
||
|
|
||
|
/**********************************************/
|
||
|
static int track_id_to_idx(rtsp_session *s, int id)
|
||
|
{
|
||
|
int i;
|
||
|
for (i=0; i<s->numTracks; i++) {
|
||
|
if (s->trk[i].ID == id)
|
||
|
return i;
|
||
|
}
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
/**********************************************/
|
||
|
static int has_two_crlfs(char *s)
|
||
|
{
|
||
|
int l, n;
|
||
|
char *p;
|
||
|
l = strlen(s);
|
||
|
if (l < 4)
|
||
|
return 0;
|
||
|
n = 3;
|
||
|
p = s + n;
|
||
|
while (n < l) {
|
||
|
if (s[n] != '\n')
|
||
|
n += 1;
|
||
|
else if (s[n-1] != '\r' || s[n-2] != '\n' || s[n-3] != '\r')
|
||
|
n += 2;
|
||
|
else
|
||
|
return n+1;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**********************************************/
|
||
|
static int is_command(char *inp, char *cmd, int cmdLen, char *server, int serverLen)
|
||
|
{
|
||
|
int l;
|
||
|
char *p;
|
||
|
char *firstCmdChar = cmd;
|
||
|
char *firstServerChar = server;
|
||
|
|
||
|
char *lastCmdChar = NULL;
|
||
|
if ( cmd && cmdLen > 2)
|
||
|
lastCmdChar = &cmd[cmdLen -1];
|
||
|
|
||
|
char *lastServerChar = NULL;
|
||
|
if ( server && serverLen > 2)
|
||
|
lastServerChar = &server[serverLen -1];
|
||
|
|
||
|
if (lastServerChar == NULL || lastCmdChar == NULL)
|
||
|
return 0;
|
||
|
|
||
|
l = strlen(inp);
|
||
|
|
||
|
if (l < 17) /* "RTSP/1.0" (8) + " rtsp:// " (9) */
|
||
|
return 0;
|
||
|
|
||
|
if (strn_casecmp(inp + l - 8, "RTSP/1.0", 8) != 0)
|
||
|
return 0;
|
||
|
|
||
|
if (gVerbose)
|
||
|
printf("command hex=");
|
||
|
|
||
|
p = inp;
|
||
|
while (*p && (*p != ' ') && (cmd < lastCmdChar) )
|
||
|
{ *cmd++ = *p++;
|
||
|
if (gVerbose)
|
||
|
printf("%x",*cmd);
|
||
|
}
|
||
|
*cmd = '\0';
|
||
|
if (gVerbose)
|
||
|
printf("%x\ncommand str=%s\ncommand count with term=%d\n",*cmd, firstCmdChar, (cmd - firstCmdChar) + 1);
|
||
|
|
||
|
if (strn_casecmp(p, " rtsp://", 8) != 0)
|
||
|
return 0;
|
||
|
|
||
|
if (gVerbose)
|
||
|
printf("server hex=");
|
||
|
p += 8;
|
||
|
while (*p && (*p != '/') && (server < lastServerChar) )
|
||
|
{ *server++ = *p++;
|
||
|
if (gVerbose)
|
||
|
printf("%x",*server);
|
||
|
}
|
||
|
*server = '\0';
|
||
|
if (gVerbose)
|
||
|
printf("%x\nserver str=%s\nserver count with term=%d\n",*server, firstServerChar, (server - firstServerChar) + 1);
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
/**********************************************/
|
||
|
static int has_trackID(char *inp, int *trackID)
|
||
|
{
|
||
|
int l;
|
||
|
char *p;
|
||
|
l = strlen(inp);
|
||
|
|
||
|
if (l < 18) /* "RTSP/1.0" (8) + "trackID=n " (10) */
|
||
|
return 0;
|
||
|
if (strn_casecmp(inp + l - 8, "RTSP/1.0", 8) != 0)
|
||
|
return 0;
|
||
|
|
||
|
p = inp;
|
||
|
while (p) {
|
||
|
p = strchr(p, '=');
|
||
|
if (p - 7 < inp) {
|
||
|
p++;
|
||
|
continue;
|
||
|
}
|
||
|
if (strn_casecmp(p - 7, "trackid=", 8) != 0) {
|
||
|
p++;
|
||
|
continue;
|
||
|
}
|
||
|
*trackID = atoi(p + 1);
|
||
|
return 1;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/**********************************************/
|
||
|
static int has_content_length(char *inp, int *len)
|
||
|
{
|
||
|
int l;
|
||
|
char *p;
|
||
|
l = strlen(inp);
|
||
|
|
||
|
if (l < 16) /* "Content-Length:n" (16) */
|
||
|
return 0;
|
||
|
if (strn_casecmp(inp, "content-length", 14) != 0)
|
||
|
return 0;
|
||
|
p = strchr(inp, ':');
|
||
|
p++;
|
||
|
while (*p && (*p == ' '))
|
||
|
p++;
|
||
|
if (p) {
|
||
|
*len = atoi(p);
|
||
|
return 1;
|
||
|
}
|
||
|
else
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/**********************************************/
|
||
|
static int has_IN_IP(char *inp, char *str)
|
||
|
{
|
||
|
int l;
|
||
|
char *p;
|
||
|
l = strlen(inp);
|
||
|
|
||
|
if (l < 10) /* "c=IN IP4 n" (10) */
|
||
|
return 0;
|
||
|
if (strn_casecmp(inp, "c=IN IP4 ", 9) != 0)
|
||
|
return 0;
|
||
|
p = inp + 9;
|
||
|
while (*p && (*p == ' '))
|
||
|
p++;
|
||
|
|
||
|
while (*p && ((*p >= '0' && *p <= '9') || *p == '.'))
|
||
|
*str++ = *p++;
|
||
|
*str = '\0';
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
/**********************************************/
|
||
|
static int has_sessionID(char *inp, char *sessionID)
|
||
|
{
|
||
|
int l;
|
||
|
char *p;
|
||
|
l = strlen(inp);
|
||
|
|
||
|
if (l < 9) /* "Session:x" (9) */
|
||
|
return 0;
|
||
|
if (strn_casecmp(inp, "session", 7) != 0)
|
||
|
return 0;
|
||
|
p = strchr(inp, ':');
|
||
|
p++;
|
||
|
while (*p && (*p == ' '))
|
||
|
p++;
|
||
|
if (p) {
|
||
|
strcpy(sessionID, p);
|
||
|
return 1;
|
||
|
}
|
||
|
else
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/**********************************************/
|
||
|
static int has_client_port(char *inp, int *port)
|
||
|
{
|
||
|
int l;
|
||
|
char *p;
|
||
|
l = strlen(inp);
|
||
|
|
||
|
if (l < 23) /* "Transport:<>client_port=n" (23) */
|
||
|
return 0;
|
||
|
if (strn_casecmp(inp, "transport", 9) != 0)
|
||
|
return 0;
|
||
|
|
||
|
p = inp;
|
||
|
while (p) {
|
||
|
p = strchr(p, '=');
|
||
|
if (p - 11 < inp) {
|
||
|
p++;
|
||
|
continue;
|
||
|
}
|
||
|
if (strn_casecmp(p - 11, "client_port=", 12) != 0) {
|
||
|
p++;
|
||
|
continue;
|
||
|
}
|
||
|
*port = atoi(p + 1);
|
||
|
*++p = '\0';
|
||
|
return 1;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/**********************************************/
|
||
|
static char *find_transport_header(char *inp)
|
||
|
{
|
||
|
inp = strchr(inp, ';');
|
||
|
if (inp != NULL)
|
||
|
inp++;
|
||
|
return inp;
|
||
|
}
|
||
|
|
||
|
/**********************************************/
|
||
|
static int has_ports(char *inp, int *client_port, int *server_port)
|
||
|
{
|
||
|
int l, got_server = 0, got_client = 0;
|
||
|
char *p;
|
||
|
l = strlen(inp);
|
||
|
|
||
|
if (l < 40) /* "Transport:<>client_port=n-nserver_port=n-n" (40) */
|
||
|
return 0;
|
||
|
if (strn_casecmp(inp, "transport", 9) != 0)
|
||
|
return 0;
|
||
|
|
||
|
p = inp;
|
||
|
while (p && !(got_client && got_server)) {
|
||
|
p = strchr(p, '=');
|
||
|
if (p - 11 < inp) {
|
||
|
}
|
||
|
else if (p == NULL)
|
||
|
{
|
||
|
}
|
||
|
else if (strn_casecmp(p - 11, "client_port=", 12) == 0) {
|
||
|
got_client = 1;
|
||
|
*client_port = atoi(p + 1);
|
||
|
}
|
||
|
else if (strn_casecmp(p - 11, "server_port=", 12) == 0) {
|
||
|
got_server = 1;
|
||
|
*server_port = atoi(p + 1);
|
||
|
}
|
||
|
p++;
|
||
|
}
|
||
|
if (got_client && got_server)
|
||
|
return 1;
|
||
|
else
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/**********************************************/
|
||
|
void service_session(rtsp_session *s)
|
||
|
{
|
||
|
int i, canRead, numDelims = 0;
|
||
|
int num = SOCKET_ERROR;
|
||
|
char *pBuf, *p;
|
||
|
char temp[RTSP_SESSION_BUF_SIZE + 1];
|
||
|
char lineBuff[MAX_LINE_BUFF + 1];
|
||
|
track_info *t;
|
||
|
char cmd[MAX_CMD_BUFF], *w;
|
||
|
int responseHeaderLen = 0;
|
||
|
char *startBuff;
|
||
|
|
||
|
/* see if we have any commands coming in */
|
||
|
pBuf = s->cinbuf + s->amtInClientInBuffer;
|
||
|
canRead = sizeof(s->cinbuf) - s->amtInClientInBuffer - 1;
|
||
|
if (canRead > 0) {
|
||
|
if ((num = recv_tcp(s->client_skt, pBuf, canRead)) == SOCKET_ERROR) {
|
||
|
switch (GetLastSocketError(s->client_skt)) {
|
||
|
case EAGAIN:
|
||
|
/* do nothing, no data to be read. */
|
||
|
break;
|
||
|
case EPIPE: // connection broke
|
||
|
case ENOTCONN: // shut down
|
||
|
case ECONNRESET:
|
||
|
s->state = stClientShutdown;
|
||
|
break;
|
||
|
default:
|
||
|
ErrorString1("problems reading from session socket (%d)\n", GetLastSocketError(s->client_skt));
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
else if (num == 0) {
|
||
|
// if readable and # of bytes is 0, then the client has shut down
|
||
|
s->state = stClientShutdown;
|
||
|
}
|
||
|
else {
|
||
|
pBuf[num] = '\0';
|
||
|
s->amtInClientInBuffer += num;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* see what we have to do as a result of this or previous commands */
|
||
|
switch (s->state)
|
||
|
{
|
||
|
case stIdle:
|
||
|
break;
|
||
|
|
||
|
case stRecvClientCommand:
|
||
|
//
|
||
|
// have we read a full command yet?
|
||
|
if (s->amtInClientInBuffer == 0 || ! has_two_crlfs(s->cinbuf))
|
||
|
break;
|
||
|
memset(temp, 0, sizeof(temp));
|
||
|
|
||
|
#if __MacOSX__
|
||
|
strlcpy(temp, s->cinbuf, sizeof(temp));
|
||
|
#else
|
||
|
strncpy(temp, s->cinbuf, sizeof(temp) -1); //relies on memset above to 0 term last char.
|
||
|
#endif
|
||
|
|
||
|
pBuf = temp;
|
||
|
if ( (p = str_sep(&pBuf, "\r\n")) != NULL )
|
||
|
{
|
||
|
if (is_command(p, cmd, sizeof(cmd), temp, sizeof(temp)))
|
||
|
{
|
||
|
if (s->server_address != NULL)
|
||
|
free(s->server_address);
|
||
|
s->server_address = malloc(strlen(temp) + 1);
|
||
|
assert(s->server_address != NULL);
|
||
|
strcpy(s->server_address, temp);
|
||
|
//
|
||
|
// take port off address (if any)
|
||
|
if ((w = strchr(s->server_address, ':')) != NULL)
|
||
|
*w++ = '\0';
|
||
|
//
|
||
|
// make an async request for the IP number
|
||
|
#if USE_THREAD
|
||
|
name_to_ip_num(s->server_address, &s->tempIP, true);
|
||
|
#else
|
||
|
name_to_ip_num(s->server_address, &s->tempIP, false);
|
||
|
#endif
|
||
|
s->state = stWaitingForIPAddress;
|
||
|
}
|
||
|
else {
|
||
|
ErrorStringS("Couldn't make sense of client command [%s]\n", temp);
|
||
|
s->state = stError;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
ErrorStringS("Couldn't make sense of client command [%s]\n", temp);
|
||
|
s->state = stError;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case stWaitingForIPAddress:
|
||
|
if (s->tempIP != kPENDING_ADDRESS)
|
||
|
{
|
||
|
add_to_IP_cache(s->server_address, s->tempIP);
|
||
|
s->state = stParseClientCommand;
|
||
|
}
|
||
|
if (s->tempIP == -1)
|
||
|
{
|
||
|
send_rtsp_error(s->client_skt, kServerNotFound);
|
||
|
s->state = stBadServerName;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case stParseClientCommand:
|
||
|
if (gDebug)
|
||
|
DebugStringS("service_session stParseClientCommand start=%s", s->cinbuf);
|
||
|
startBuff = s->soutbuf;
|
||
|
//
|
||
|
// see what the command and server address is
|
||
|
//
|
||
|
// munge the data and snarf what we need
|
||
|
pBuf = s->cinbuf;
|
||
|
while ((p = str_sep(&pBuf, "\r\n")) != NULL)
|
||
|
{
|
||
|
//
|
||
|
// Count the empty fields; three in a row is the end of the header
|
||
|
if (*p == '\0') {
|
||
|
if (++numDelims == 3)
|
||
|
break;
|
||
|
continue;
|
||
|
}
|
||
|
else
|
||
|
numDelims = 0;
|
||
|
//
|
||
|
// see if we can snarf our data out of the headers
|
||
|
if (is_command(p, cmd,sizeof(cmd), temp, sizeof(temp) ))
|
||
|
{
|
||
|
int ip;
|
||
|
//
|
||
|
// get server address
|
||
|
if (s->server_address != NULL)
|
||
|
free(s->server_address);
|
||
|
s->server_address = malloc(strlen(temp) + 1);
|
||
|
assert(s->server_address != NULL);
|
||
|
strcpy(s->server_address, temp);
|
||
|
//
|
||
|
// get server port (if any)
|
||
|
if ((w = strchr(s->server_address, ':')) != NULL)
|
||
|
{
|
||
|
*w++ = '\0';
|
||
|
s->server_port = atoi(w);
|
||
|
}
|
||
|
//
|
||
|
// check to see if command is pointing to the same server that
|
||
|
// we're already connected to.
|
||
|
name_to_ip_num(s->server_address, &ip, false);
|
||
|
if ((ip != s->server_ip) && (s->server_skt != INVALID_SOCKET)) {
|
||
|
close_socket(s->server_skt);
|
||
|
s->server_skt = INVALID_SOCKET;
|
||
|
s->server_ip = ip;
|
||
|
}
|
||
|
|
||
|
if (ip == -1) {
|
||
|
s->state = stBadServerName;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (gProxyIP == ip ) // don't connect to yourself
|
||
|
{
|
||
|
ErrorStringS("Invalid session: destination IP is the same as this proxy IP (%s).\n",ip_to_string(ip));
|
||
|
close_socket(s->server_skt);
|
||
|
s->server_skt = INVALID_SOCKET;
|
||
|
s->state = stBadServerName;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
s->server_ip = ip;
|
||
|
if (gVerbose)
|
||
|
printf("%s command for server %s:%d (ip %s)\n",
|
||
|
cmd, s->server_address, s->server_port,
|
||
|
ip_to_string(s->server_ip));
|
||
|
s->transaction_type = cmd_to_transaction_type(cmd);
|
||
|
|
||
|
if (s->transaction_type == ttSetup && has_trackID(p, &i))
|
||
|
{
|
||
|
|
||
|
num = track_id_to_idx(s, i);
|
||
|
if (num == -1 )
|
||
|
{
|
||
|
if (s->numTracks == MAX_TRACKS) //stop before indexing the track array out of bounds
|
||
|
{
|
||
|
ErrorString1("Invalid session: The number of tracks are greater than the allowed maximum = (%d).\n",MAX_TRACKS);
|
||
|
close_socket(s->server_skt);
|
||
|
s->server_skt = INVALID_SOCKET;
|
||
|
s->state = stError;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
num = s->numTracks;
|
||
|
s->trk[s->numTracks++].ID = i;
|
||
|
}
|
||
|
s->cur_trk = num;
|
||
|
}
|
||
|
}
|
||
|
else if (s->transaction_type == ttSetup
|
||
|
&& has_client_port(p, &(s->trk[s->cur_trk].ClientRTPPort)))
|
||
|
{
|
||
|
t = s->trk + s->cur_trk;
|
||
|
if (gDebug)
|
||
|
printf("Client ports for track %d are %d-%d\n",s->cur_trk, t->ClientRTPPort, t->ClientRTPPort + 1);
|
||
|
//
|
||
|
// make rtp/rtcp port pair for proxy=>server
|
||
|
if (make_udp_port_pair(s->server_interface_addr, s->server_ip, &t->RTP_S2P, &t->RTCP_S2P) == -1)
|
||
|
{ s->server_skt = INVALID_SOCKET;
|
||
|
ErrorString1("Couldn't create udp port pair for proxy=>server\n", GetLastSocketError(s->server_skt));
|
||
|
s->state = stError;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (gDebug)
|
||
|
printf("Created ports for server to proxy on track %d are %d-%d sockets %d-%d\n",
|
||
|
s->cur_trk, t->RTP_S2P->port, t->RTCP_S2P->port,
|
||
|
t->RTP_S2P->socket, t->RTCP_S2P->socket);
|
||
|
//
|
||
|
// reconstruct the client port string
|
||
|
sprintf(temp, "%s%d-%d", p, t->RTP_S2P->port, t->RTCP_S2P->port);
|
||
|
p = temp;
|
||
|
}
|
||
|
|
||
|
if (0 == strn_casecmp(p, "x-dynamic-rate", 14))// don't send to server not supported.
|
||
|
p = "x-dynamic-rate: 0";
|
||
|
|
||
|
//
|
||
|
// put the line in the outgoing buffer
|
||
|
num = strlen(p);
|
||
|
memcpy(s->soutbuf + s->amtInServerOutBuffer, p, (size_t)num);
|
||
|
s->amtInServerOutBuffer += num;
|
||
|
|
||
|
s->soutbuf[s->amtInServerOutBuffer++] = '\r';
|
||
|
s->soutbuf[s->amtInServerOutBuffer++] = '\n';
|
||
|
}
|
||
|
|
||
|
if (*(s->cinbuf + s->amtInServerOutBuffer) == 0)
|
||
|
{
|
||
|
s->soutbuf[s->amtInServerOutBuffer++] = '\r';
|
||
|
s->soutbuf[s->amtInServerOutBuffer++] = '\n';
|
||
|
}
|
||
|
|
||
|
s->amtInClientInBuffer -= s->amtInServerOutBuffer;
|
||
|
if (s->amtInClientInBuffer > 0)
|
||
|
{
|
||
|
memcpy(s->cinbuf, s->cinbuf + s->amtInServerOutBuffer, (size_t) s->amtInClientInBuffer);
|
||
|
s->cinbuf[s->amtInClientInBuffer] = 0;
|
||
|
if (gDebug)
|
||
|
DebugStringS("service_session stParseClientCommand memcpy to s->cinbuf=%s", s->cinbuf);
|
||
|
}
|
||
|
else if (s->amtInClientInBuffer < 0)
|
||
|
s->amtInClientInBuffer = 0;
|
||
|
s->state = stServerTransactionSend;
|
||
|
|
||
|
if (s->soutbuf[s->amtInServerOutBuffer - 4] != '\r')
|
||
|
{
|
||
|
s->soutbuf[s->amtInServerOutBuffer++] = '\r';
|
||
|
s->soutbuf[s->amtInServerOutBuffer++] = '\n';
|
||
|
}
|
||
|
|
||
|
if (gDebug)
|
||
|
DebugStringS("service_session stParseClientCommand SEND TO CLIENT=%s", s->soutbuf);
|
||
|
gNeedsService++;
|
||
|
break;
|
||
|
|
||
|
case stServerTransactionSend:
|
||
|
//
|
||
|
// check to see if we've got a connection to the server open
|
||
|
if (s->server_skt == INVALID_SOCKET)
|
||
|
{
|
||
|
//
|
||
|
// create a connection if we don't have one
|
||
|
if ((s->server_skt = new_socket_tcp(false)) == INVALID_SOCKET) {
|
||
|
ErrorString("Couldn't open a socket to connect to server.\n");
|
||
|
s->state = stError;
|
||
|
return;
|
||
|
}
|
||
|
set_socket_reuse_address(s->server_skt);
|
||
|
make_socket_nonblocking(s->server_skt);
|
||
|
s->server_skt_pending_connection = true;
|
||
|
#if DO_ASYNC
|
||
|
if ((i = connect_to_address(s, conn_finished_proc, s->server_skt, s->server_ip, s->server_port)) == SOCKET_ERROR) {
|
||
|
#else
|
||
|
if ((i = connect_to_address(s->server_skt, s->server_ip, s->server_port)) == SOCKET_ERROR) {
|
||
|
#endif
|
||
|
num = GetLastSocketError(s->server_skt);
|
||
|
switch (GetLastSocketError(s->server_skt)) {
|
||
|
case EISCONN: /* already connected */
|
||
|
break;
|
||
|
case EINPROGRESS: /* connection can't be completed immediately */
|
||
|
case EAGAIN: /* connection can't be completed immediately */
|
||
|
case EALREADY: /* previous connection attempt hasn't been completed */
|
||
|
return;
|
||
|
default:
|
||
|
close_socket(s->server_skt);
|
||
|
s->server_skt = INVALID_SOCKET;
|
||
|
ErrorString1("Couldn't connect to server %d\n", GetLastSocketError(s->server_skt));
|
||
|
s->state = stCantConnectToServer;
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
s->server_interface_addr = get_interface_addr(s->server_skt);
|
||
|
s->server_skt_pending_connection = false;
|
||
|
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// check to see if we're connected (writable) and send the command
|
||
|
if (s->amtInServerOutBuffer) {
|
||
|
if ((num = send_tcp(s->server_skt, s->soutbuf, s->amtInServerOutBuffer)) == SOCKET_ERROR) {
|
||
|
switch (GetLastSocketError(s->server_skt)) {
|
||
|
case EPIPE: // connection broke
|
||
|
case ENOTCONN: // shut down
|
||
|
case ECONNRESET:
|
||
|
s->state = stServerShutdown;
|
||
|
break;
|
||
|
case EAGAIN: // was busy - try again
|
||
|
case EINTR: // got interrupted - try again
|
||
|
break;
|
||
|
default:
|
||
|
ErrorString1("writing to server error (%d)\n", GetLastSocketError(s->server_skt));
|
||
|
s->state = stError;
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
else if (num == 0)
|
||
|
s->state = stServerShutdown;
|
||
|
else {
|
||
|
s->amtInServerOutBuffer -= num;
|
||
|
if (s->amtInServerOutBuffer == 0)
|
||
|
s->state = stServerTransactionRecv;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
gNeedsService++;
|
||
|
break;
|
||
|
|
||
|
case stServerTransactionRecv:
|
||
|
//
|
||
|
// check to see if we've got a response from the server
|
||
|
if (s->server_skt == INVALID_SOCKET)
|
||
|
{
|
||
|
if (gDebug) printf("s->server_skt == INVALID_SOCKET\n");
|
||
|
s->state = stServerShutdown;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
pBuf = s->sinbuf + s->amtInServerInBuffer;
|
||
|
canRead = sizeof(s->sinbuf) - s->amtInServerInBuffer - 1;
|
||
|
|
||
|
if (canRead > 0)
|
||
|
{
|
||
|
if ((num = recv_tcp(s->server_skt, pBuf, canRead)) == SOCKET_ERROR)
|
||
|
{
|
||
|
//if (gDebug) printf("problems reading from server (%d)",
|
||
|
// GetLastSocketError(s->server_skt));
|
||
|
|
||
|
switch (GetLastSocketError(s->server_skt))
|
||
|
{
|
||
|
case EAGAIN:
|
||
|
/* do nothing, no data to be read. */
|
||
|
gNeedsService++;
|
||
|
break;
|
||
|
case EPIPE: // connection broke
|
||
|
case ENOTCONN: // shut down
|
||
|
case ECONNRESET:
|
||
|
s->state = stServerShutdown;
|
||
|
break;
|
||
|
default:
|
||
|
ErrorString1("problems reading from server (%d)\n", GetLastSocketError(s->server_skt));
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pBuf[num] = '\0';
|
||
|
if (gDebug)
|
||
|
printf("\nread %d bytes from server:%s\n", num, pBuf);
|
||
|
if (0 == num)
|
||
|
sleep_milliseconds(1);
|
||
|
s->amtInServerInBuffer += num;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
if (gDebug) printf("can't read now!\n");
|
||
|
|
||
|
|
||
|
// DMS - if there is a content-length, make sure we've gotten all that data too.
|
||
|
if ((s->totalContentLength > 0) && (s->amtInServerInBuffer < s->totalContentLength))
|
||
|
{
|
||
|
//if (gDebug) printf("1-not enough in buffer content length %li, amt in buffer %li\n",
|
||
|
// (long)s->totalContentLength, (long)s->amtInServerInBuffer);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if ( s->totalContentLength == 0 )
|
||
|
{ //-rt if totalContentLength != 0,then we must have seen "has_two_crlfs"
|
||
|
// and already parsed out the content-length header.
|
||
|
// now we won't be able to find it again becuase str_sep
|
||
|
// in the parsing code below has already replaced the CRLFs with \0's.
|
||
|
|
||
|
|
||
|
//
|
||
|
// did we get complete response headers yet?
|
||
|
responseHeaderLen = has_two_crlfs(s->sinbuf);
|
||
|
|
||
|
if (responseHeaderLen == 0)
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
if ( !s->haveParsedServerReplyHeaders )
|
||
|
{
|
||
|
// we can only do this one time!
|
||
|
|
||
|
//
|
||
|
// munge the data for the client, while snarfing what we need
|
||
|
|
||
|
for (pBuf = s->sinbuf; (p = str_sep(&pBuf, "\r\n")) != NULL; )
|
||
|
{
|
||
|
//
|
||
|
// Count the empty fields; three in a row is end of the header
|
||
|
if (*p == '\0')
|
||
|
{
|
||
|
if (++numDelims == 3)
|
||
|
break;
|
||
|
continue;
|
||
|
}
|
||
|
else
|
||
|
numDelims = 0;
|
||
|
|
||
|
if (0 == strn_casecmp(p, "x-Accept-Dynamic-Rate", 21))
|
||
|
p = "x-Accept-Dynamic-Rate: 0";
|
||
|
|
||
|
// see if we can snarf any data out of the headers
|
||
|
if (has_content_length(p, &s->contentLength))
|
||
|
{
|
||
|
// DMS - Set the total content length, if applicable
|
||
|
s->totalContentLength = s->contentLength + responseHeaderLen;
|
||
|
}
|
||
|
else if (has_sessionID(p, temp))
|
||
|
{
|
||
|
if (!s->sessionID)
|
||
|
{
|
||
|
s->sessionID = malloc(strlen(temp) + 1);
|
||
|
if(s->sessionID == NULL)
|
||
|
{
|
||
|
ErrorString("Can't allocate session ID");
|
||
|
exit(1);
|
||
|
}
|
||
|
strcpy(s->sessionID, temp);
|
||
|
}
|
||
|
else if (str_casecmp(s->sessionID, temp) != 0)
|
||
|
ErrorString("Bad session ID in response from server\n");
|
||
|
|
||
|
}
|
||
|
else if (s->transaction_type == ttSetup && has_ports(p, &i, &num))
|
||
|
{
|
||
|
t = s->trk + s->cur_trk;
|
||
|
t->ServerRTPPort = num;
|
||
|
|
||
|
if (gDebug)
|
||
|
printf("Server ports for track %d are %d-%d \n",
|
||
|
s->cur_trk, t->ServerRTPPort, t->ServerRTPPort + 1);
|
||
|
//
|
||
|
// make rtp/rtcp port pair here proxy=>client
|
||
|
if (make_udp_port_pair(s->client_interface_addr, s->client_ip, &t->RTP_P2C, &t->RTCP_P2C) == -1)
|
||
|
{ s->server_skt = INVALID_SOCKET;
|
||
|
ErrorString1("Couldn't create udp port pair for proxy=>client\n", GetLastSocketError(s->server_skt));
|
||
|
s->state = stError;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// set up transfer param blocks
|
||
|
t->RTP_S2C_tpb.status = &s->die;
|
||
|
t->RTP_S2C_tpb.send_from = t->RTP_P2C;
|
||
|
t->RTP_S2C_tpb.send_to_ip = s->client_ip;
|
||
|
t->RTP_S2C_tpb.send_to_port = t->ClientRTPPort;
|
||
|
strcpy(t->RTP_S2C_tpb.socketName, "RTP Server to Client");
|
||
|
upon_receipt_from(t->RTP_S2P, s->server_ip,
|
||
|
transfer_data, &(t->RTP_S2C_tpb));
|
||
|
|
||
|
t->RTCP_S2C_tpb.status = &s->die;
|
||
|
t->RTCP_S2C_tpb.send_from = t->RTCP_P2C;
|
||
|
t->RTCP_S2C_tpb.send_to_ip = s->client_ip;
|
||
|
t->RTCP_S2C_tpb.send_to_port = t->ClientRTPPort + 1;
|
||
|
strcpy(t->RTCP_S2C_tpb.socketName,"RTCP Server to Client");
|
||
|
upon_receipt_from(t->RTCP_S2P, s->server_ip,
|
||
|
transfer_data, &(t->RTCP_S2C_tpb));
|
||
|
|
||
|
t->RTCP_C2S_tpb.status = &s->die;
|
||
|
t->RTCP_C2S_tpb.send_from = t->RTCP_S2P;
|
||
|
t->RTCP_C2S_tpb.send_to_ip = s->server_ip;
|
||
|
t->RTCP_C2S_tpb.send_to_port = t->ServerRTPPort + 1;
|
||
|
strcpy(t->RTCP_C2S_tpb.socketName,"RTCP Client to Server");
|
||
|
upon_receipt_from(t->RTCP_P2C, s->client_ip,
|
||
|
transfer_data, &(t->RTCP_C2S_tpb));
|
||
|
|
||
|
if (gDebug)
|
||
|
printf( "Created ports for proxy to client on track %d are %d-%d sockets %d-%d\n",
|
||
|
s->cur_trk,
|
||
|
t->RTP_P2C->port, t->RTCP_P2C->port,
|
||
|
t->RTP_P2C->socket, t->RTCP_P2C->socket
|
||
|
);
|
||
|
|
||
|
//
|
||
|
// reconstruct the client;server string
|
||
|
w = find_transport_header(p);
|
||
|
if (w != NULL)
|
||
|
*w = '\0';
|
||
|
sprintf(temp, "%sclient_port=%d-%d;server_port=%d-%d;source=%s", p,
|
||
|
t->ClientRTPPort, t->ClientRTPPort+1,
|
||
|
t->RTP_P2C->port, t->RTCP_P2C->port,
|
||
|
ip_to_string(gProxyIP));
|
||
|
p = temp;
|
||
|
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// put the line in the outgoing buffer
|
||
|
num = strlen(p);
|
||
|
|
||
|
assert( num + s->amtInClientOutBuffer + 2 <= RTSP_SESSION_BUF_SIZE );
|
||
|
|
||
|
memcpy(s->coutbuf + s->amtInClientOutBuffer, p, (size_t) num);
|
||
|
s->amtInClientOutBuffer += num;
|
||
|
s->coutbuf[s->amtInClientOutBuffer++] = '\r';
|
||
|
s->coutbuf[s->amtInClientOutBuffer++] = '\n';
|
||
|
|
||
|
// Drop a pointer to where the content body might begin,
|
||
|
// if this is in fact the last line
|
||
|
s->responseBodyP = pBuf + 3;
|
||
|
}
|
||
|
|
||
|
assert( s->amtInClientOutBuffer + 2 <= RTSP_SESSION_BUF_SIZE );
|
||
|
s->coutbuf[s->amtInClientOutBuffer++] = '\r';
|
||
|
s->coutbuf[s->amtInClientOutBuffer++] = '\n';
|
||
|
}
|
||
|
|
||
|
// the headers are done now.
|
||
|
s->haveParsedServerReplyHeaders = 1;
|
||
|
|
||
|
// DMS - if there is a content-length, make sure we've gotten all that data too.
|
||
|
if ((s->totalContentLength > 0) && (s->amtInServerInBuffer < s->totalContentLength))
|
||
|
{
|
||
|
//if (gDebug)
|
||
|
// printf("2- not enough in buffer content length %li, amt in buffer %li\n",
|
||
|
// (long)s->totalContentLength, (long)s->amtInServerInBuffer);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
//if (gDebug) printf("have complete response\n" );
|
||
|
|
||
|
pBuf = s->responseBodyP;
|
||
|
|
||
|
//
|
||
|
// munge and add the content if there is any
|
||
|
if (s->contentLength)
|
||
|
{
|
||
|
if (s->transaction_type == ttDescribe)
|
||
|
{
|
||
|
char *nextBuffPos = pBuf;
|
||
|
|
||
|
// use "get_line_str" so that we preserve the EOL format
|
||
|
// of the describe resonse *exactly*
|
||
|
nextBuffPos = get_line_str( lineBuff, nextBuffPos, MAX_LINE_BUFF );
|
||
|
|
||
|
while ( *lineBuff )
|
||
|
{
|
||
|
// c=IN IP0 ?
|
||
|
if (has_IN_IP(lineBuff, temp))
|
||
|
{
|
||
|
//char *nextChar = lineBuff;
|
||
|
|
||
|
//
|
||
|
// reconstruct the IN IP string, but
|
||
|
// make sure our replacement string is the
|
||
|
// same length as the original string
|
||
|
// so as not to change the Content-Length string
|
||
|
// while ( *nextChar )
|
||
|
// {
|
||
|
// replace all digits on the line with zeros...
|
||
|
// if ( isdigit( *nextChar ) )
|
||
|
// *nextChar = '0';
|
||
|
// nextChar++;
|
||
|
//
|
||
|
// }
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// put the line in the outgoing buffer
|
||
|
num = strlen(lineBuff);
|
||
|
|
||
|
assert( num + s->amtInClientOutBuffer <= RTSP_SESSION_BUF_SIZE );
|
||
|
|
||
|
memcpy(s->coutbuf + s->amtInClientOutBuffer, lineBuff, (size_t)num);
|
||
|
s->amtInClientOutBuffer += num;
|
||
|
|
||
|
nextBuffPos = get_line_str( lineBuff, nextBuffPos, MAX_LINE_BUFF );
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
assert( s->contentLength + s->amtInClientOutBuffer <= RTSP_SESSION_BUF_SIZE );
|
||
|
|
||
|
memcpy(&s->coutbuf[s->amtInClientOutBuffer], pBuf, (size_t) s->contentLength);
|
||
|
s->amtInClientOutBuffer += s->contentLength;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
s->state = stSendClientResponse;
|
||
|
s->amtInServerInBuffer = 0;
|
||
|
s->totalContentLength = 0;
|
||
|
s->haveParsedServerReplyHeaders = 0;
|
||
|
s->contentLength = 0;
|
||
|
//printf("NEXT: stSendClientResponse\n" );
|
||
|
gNeedsService++;
|
||
|
break;
|
||
|
|
||
|
case stSendClientResponse:
|
||
|
//printf("stSendClientResponse\n" );
|
||
|
//
|
||
|
// check to see that we're still connected (writable) and send the response
|
||
|
if (s->amtInClientOutBuffer && isWritable(s->client_skt))
|
||
|
{
|
||
|
if ((num = send_tcp(s->client_skt, s->coutbuf, s->amtInClientOutBuffer)) == SOCKET_ERROR)
|
||
|
{
|
||
|
switch (GetLastSocketError(s->client_skt))
|
||
|
{
|
||
|
case EPIPE: // connection broke
|
||
|
case ENOTCONN: // shut down
|
||
|
case ECONNRESET:
|
||
|
s->state = stClientShutdown;
|
||
|
break;
|
||
|
case EAGAIN: // was busy - try again
|
||
|
case EINTR: // got interrupted - try again
|
||
|
break;
|
||
|
default:
|
||
|
ErrorString1("writing to client error (%d)\n", GetLastSocketError(s->client_skt));
|
||
|
s->state = stError;
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
s->amtInClientOutBuffer -= num;
|
||
|
if (s->amtInClientOutBuffer == 0) {
|
||
|
if (s->transaction_type == ttTeardown)
|
||
|
s->state = stClientShutdown;
|
||
|
else
|
||
|
s->state = stRecvClientCommand;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
gNeedsService++;
|
||
|
break;
|
||
|
|
||
|
|
||
|
case stClientShutdown:
|
||
|
if (gDebug && s->client_ip != -1) DebugString1("Client shutdown (ip %s)", ip_to_string(s->client_ip));
|
||
|
s->die = true;
|
||
|
gNeedsService++;
|
||
|
break;
|
||
|
|
||
|
case stBadServerName:
|
||
|
send_rtsp_error(s->client_skt, kServerNotFound);
|
||
|
s->state = stServerShutdown;
|
||
|
break;
|
||
|
|
||
|
case stCantConnectToServer:
|
||
|
send_rtsp_error(s->client_skt, kServerNotFound);
|
||
|
s->state = stServerShutdown;
|
||
|
break;
|
||
|
|
||
|
case stServerShutdown:
|
||
|
if (gDebug && s->server_ip != -1) DebugString1("Server shutdown (ip %s)", ip_to_string(s->server_ip));
|
||
|
s->die = true;
|
||
|
gNeedsService++;
|
||
|
break;
|
||
|
|
||
|
case stError:
|
||
|
send_rtsp_error(s->client_skt, kUnknownError);
|
||
|
if (gDebug) DebugString("error condition.\n");
|
||
|
s->die = true;
|
||
|
gNeedsService++;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**********************************************/
|
||
|
|
||
|
void read_config() {
|
||
|
int fd, eof, num;
|
||
|
char buf, line[MAX_CONFIG_LINE_LEN], temp[MAX_CONFIG_LINE_LEN];
|
||
|
int line_pos;
|
||
|
regmatch_t pmatch[3];
|
||
|
regex_t regexpAllow, regexpComment, regexpUsers;
|
||
|
regex_t regexpListen, regexpPortRange;
|
||
|
regex_t regexpRTPAddr;
|
||
|
|
||
|
if ((fd = open(gConfigFilePath, O_RDONLY)) == -1) {
|
||
|
switch (errno) {
|
||
|
case EACCES:
|
||
|
ErrorStringS("Config file %s inaccessible\n", gConfigFilePath);
|
||
|
break;
|
||
|
default:
|
||
|
ErrorString1("Problems opening config file (%d)\n", errno);
|
||
|
break;
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (gVerbose)
|
||
|
printf("Opened config file %s\n", gConfigFilePath);
|
||
|
|
||
|
regcomp(®expAllow, "^[ \t]*allow[ \t]+([0-9\\.]+)/([0-9]+).*$",
|
||
|
REG_EXTENDED | REG_ICASE);
|
||
|
regcomp(®expUsers, "^[ \t]*users[ \t]+([0-9]+).*$",
|
||
|
REG_EXTENDED | REG_ICASE);
|
||
|
regcomp(®expListen, "^[ \t]*listen[ \t]+([0-9\\.]+[ \t]+)?([0-9]+).*$",
|
||
|
REG_EXTENDED | REG_ICASE);
|
||
|
|
||
|
regcomp(®expPortRange, "^[ \t]*port-range[ \t]+([0-9]+)-([0-9]+).*$",
|
||
|
REG_EXTENDED | REG_ICASE);
|
||
|
regcomp(®expComment, "^[ \t]*#.*$", REG_EXTENDED | REG_ICASE);
|
||
|
|
||
|
regcomp(®expRTPAddr, "^[ \t]*rtp-bind-addr[ \t]+([a-z0-9\\.\\-]+).*$",REG_EXTENDED | REG_ICASE);
|
||
|
|
||
|
eof = false;
|
||
|
while (!eof) {
|
||
|
//
|
||
|
// read a line from the configuration file
|
||
|
line_pos = 0;
|
||
|
while (1) {
|
||
|
num = read(fd, &buf, 1);
|
||
|
if (num <= 0) {
|
||
|
eof = true;
|
||
|
break;
|
||
|
}
|
||
|
if (buf == '\n' || buf == '\r')
|
||
|
break;
|
||
|
if (line_pos < MAX_CONFIG_LINE_LEN)
|
||
|
line[line_pos++] = buf;
|
||
|
}
|
||
|
line[line_pos] = '\0';
|
||
|
if (gDebug) DebugStringS("Read config line: %s", line);
|
||
|
|
||
|
//
|
||
|
// if the line has anything, try to parse it
|
||
|
if (line_pos > 0) {
|
||
|
if (regexec(®expAllow, line, 3, pmatch, 0) == 0) {
|
||
|
int ip, range;
|
||
|
|
||
|
num = pmatch[1].rm_eo - pmatch[1].rm_so;
|
||
|
memcpy(temp, line + pmatch[1].rm_so, (size_t) num);
|
||
|
temp[num] = '\0';
|
||
|
range = atoi(line + pmatch[2].rm_so);
|
||
|
//name_to_ip_num(temp, &ip, false);
|
||
|
ip = ntohl(inet_addr(temp));
|
||
|
if (gVerbose)
|
||
|
printf("Allow connections from %s/%d\n", ip_to_string(ip), range);
|
||
|
add_allow_subnet(ip, range);
|
||
|
}
|
||
|
else if (regexec(®expUsers, line, 3, pmatch, 0) == 0) {
|
||
|
gUserLimit = atoi(line + pmatch[1].rm_so);
|
||
|
if (gVerbose)
|
||
|
printf("Limit number of users to %d\n", gUserLimit);
|
||
|
}
|
||
|
else if (regexec(®expListen, line, 3, pmatch, 0) == 0) {
|
||
|
struct in_addr bindaddr;
|
||
|
int port = atoi(line + pmatch[2].rm_so);
|
||
|
|
||
|
if(pmatch[1].rm_so==-1)
|
||
|
bindaddr.s_addr= (in_addr_t) htonl(ANY_ADDRESS);
|
||
|
else
|
||
|
{
|
||
|
*(line+pmatch[1].rm_eo)='\0';
|
||
|
#if __solaris__
|
||
|
bindaddr.s_addr = inet_addr(line+pmatch[1].rm_so);
|
||
|
if( 0 == bindaddr.s_addr)
|
||
|
#else
|
||
|
if( inet_aton(line+pmatch[1].rm_so, (void *) &bindaddr)==0 )
|
||
|
#endif
|
||
|
printf("listen: failed to parse IP address %s\n",line+pmatch[1].rm_so);
|
||
|
}
|
||
|
|
||
|
add_rtsp_port_listener( (int) ntohl(bindaddr.s_addr),port);
|
||
|
}
|
||
|
else if (regexec(®expPortRange, line, 3, pmatch, 0) == 0) {
|
||
|
int minPort, maxPort;
|
||
|
minPort = atoi(line + pmatch[1].rm_so);
|
||
|
maxPort = atoi(line + pmatch[2].rm_so);
|
||
|
set_udp_port_min_and_max(minPort, maxPort);
|
||
|
if (gVerbose)
|
||
|
printf("Use UDP ports %d through %d\n", minPort, maxPort);
|
||
|
}
|
||
|
else if (regexec(®expComment, line, 0, NULL, 0) == 0) {
|
||
|
// comment - do nothing
|
||
|
}
|
||
|
else if (regexec(®expRTPAddr,line,3,pmatch,0)==0) {
|
||
|
num = pmatch[1].rm_eo - pmatch[1].rm_so;
|
||
|
memcpy(temp, line + pmatch[1].rm_so, (size_t) num);
|
||
|
temp[num] = '\0';
|
||
|
name_to_ip_num(temp, &gProxyIP, false);
|
||
|
if (gProxyIP == -1) {
|
||
|
printf("unable to configure rtp-bind-addr: %s\n", temp);
|
||
|
} else if (gVerbose) {
|
||
|
printf("configured rtp-bind-addr: %s (%s)\n", temp, ip_to_string(gProxyIP));
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
ErrorStringS("invalid configuration line [%s]\n", line);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
regfree(®expAllow);
|
||
|
regfree(®expComment);
|
||
|
regfree(®expUsers);
|
||
|
regfree(®expListen);
|
||
|
regfree(®expPortRange);
|
||
|
regfree(®expRTPAddr);
|
||
|
|
||
|
close(fd);
|
||
|
}
|
||
|
|
||
|
/**********************************************/
|
||
|
void add_allow_subnet(int ip, int range)
|
||
|
{
|
||
|
subnet_allow *allow;
|
||
|
|
||
|
allow = (subnet_allow*)malloc(sizeof(subnet_allow));
|
||
|
assert(allow != NULL);
|
||
|
allow->ip = ip;
|
||
|
allow->range = range;
|
||
|
allow->next = gAllowedNets;
|
||
|
gAllowedNets = allow;
|
||
|
}
|
||
|
|
||
|
/**********************************************/
|
||
|
static int bitfill(int bits)
|
||
|
{
|
||
|
int mask, i;
|
||
|
|
||
|
/* unroll the bit fill and deal with common subnet masks. */
|
||
|
if (bits >= 24) {
|
||
|
mask = 0xffffff00;
|
||
|
bits -= 24;
|
||
|
i = 24;
|
||
|
} else if (bits >= 16) {
|
||
|
mask = 0xffff0000;
|
||
|
bits -= 16;
|
||
|
i = 16;
|
||
|
} else if (bits >= 8) {
|
||
|
mask = 0xff000000;
|
||
|
bits -= 8;
|
||
|
i = 8;
|
||
|
} else {
|
||
|
mask = i = 0;
|
||
|
}
|
||
|
|
||
|
/* only 7 more bits to go! */
|
||
|
while (bits) {
|
||
|
mask |= (1 << (32 - i - bits));
|
||
|
bits--;
|
||
|
}
|
||
|
return mask;
|
||
|
}
|
||
|
|
||
|
/**********************************************/
|
||
|
bool allow_ip(int ip)
|
||
|
{
|
||
|
int mask;
|
||
|
subnet_allow *cur;
|
||
|
|
||
|
cur = gAllowedNets;
|
||
|
if (cur == NULL)
|
||
|
return true;
|
||
|
|
||
|
while (cur) {
|
||
|
mask = bitfill(cur->range);
|
||
|
if ((cur->ip & mask) == (ip & mask))
|
||
|
return true;
|
||
|
cur = cur->next;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/**********************************************/
|
||
|
void send_rtsp_error(int skt, int refusal)
|
||
|
{
|
||
|
char *refusal_string;
|
||
|
|
||
|
switch (refusal) {
|
||
|
case kServerNotFound:
|
||
|
refusal_string = "RTSP/1.0 462 Destination unreachable\r\n";
|
||
|
break;
|
||
|
case kUnknownError:
|
||
|
refusal_string = "RTSP/1.0 500 Unknown proxy error\r\n";
|
||
|
break;
|
||
|
case kPermissionDenied:
|
||
|
refusal_string = "RTSP/1.0 403 Proxy denied\r\n";
|
||
|
break;
|
||
|
case kTooManyUsers:
|
||
|
refusal_string = "RTSP/1.0 503 Too many proxy users\r\n";
|
||
|
break;
|
||
|
default:
|
||
|
refusal_string = "RTSP/1.0 500 Unknown proxy error\r\n";
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
ErrorStringS("RTSP error: %s", refusal_string);
|
||
|
send_tcp(skt, refusal_string, (int) strlen(refusal_string));
|
||
|
}
|
||
|
|
||
|
/**********************************************/
|
||
|
/**********************************************/
|