#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include "alias_local.h"
#define RTSP_CONTROL_PORT_NUMBER_1 554
#define RTSP_CONTROL_PORT_NUMBER_2 7070
#define RTSP_PORT_GROUP 2
#define ISDIGIT(a) (((a) >= '0') && ((a) <= '9'))
static int
search_string(char *data, int dlen, const char *search_str)
{
int i, j, k;
int search_str_len;
search_str_len = strlen(search_str);
for (i = 0; i < dlen - search_str_len; i++) {
for (j = i, k = 0; j < dlen - search_str_len; j++, k++) {
if (data[j] != search_str[k] &&
data[j] != search_str[k] - ('a' - 'A')) {
break;
}
if (k == search_str_len - 1) {
return j + 1;
}
}
}
return -1;
}
static int
alias_rtsp_out(struct ip *pip,
struct alias_link *link,
char *data,
const char *port_str)
{
int hlen, tlen, dlen;
struct tcphdr *tc;
int i, j, pos, state, port_dlen, new_dlen, delta;
u_short p[2], new_len;
u_short sport, eport, base_port;
u_short salias = 0, ealias = 0, base_alias = 0;
const char *transport_str = "transport:";
char newdata[2048], *port_data, *port_newdata, stemp[80];
int links_created = 0, pkt_updated = 0;
struct alias_link *rtsp_link = NULL;
struct in_addr null_addr;
tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
hlen = (pip->ip_hl + tc->th_off) << 2;
tlen = ntohs(pip->ip_len);
dlen = tlen - hlen;
if (dlen > sizeof(newdata)) {
#if DEBUG
fprintf(stderr, "alias_rtsp_out: data len too big");
#endif
return -1;
}
pos = search_string(data, dlen, transport_str);
if (pos < 0) {
#if DEBUG
fprintf(stderr, "alias_rtsp_out: Transport not found");
#endif
return -1;
}
if (pos >= sizeof(newdata)) {
#if DEBUG
fprintf(stderr, "alias_rtsp_out: Transport too far");
#endif
return -1;
}
port_data = data + pos;
port_dlen = dlen - pos;
memcpy(newdata, data, pos);
port_newdata = newdata + pos;
while (port_dlen > strlen(port_str)) {
pos = search_string(port_data, port_dlen, port_str);
if (pos < 0 || port_data + pos + 1 > newdata + sizeof(newdata)) {
#if DEBUG
fprintf(stderr, "alias_rtsp_out: port_str too far\n");
#endif
break;
}
memcpy (port_newdata, port_data, pos + 1);
port_newdata += (pos + 1);
p[0] = p[1] = 0;
sport = eport = 0;
state = 0;
for (i = pos; i < port_dlen; i++) {
switch(state) {
case 0:
if (port_data[i] == '=') {
state++;
}
break;
case 1:
if (ISDIGIT(port_data[i])) {
p[0] = p[0] * 10 + port_data[i] - '0';
} else {
if (port_data[i] == ';') {
state = 3;
}
if (port_data[i] == '-') {
state++;
}
}
break;
case 2:
if (ISDIGIT(port_data[i])) {
p[1] = p[1] * 10 + port_data[i] - '0';
} else {
state++;
}
break;
case 3:
base_port = p[0];
sport = htons(p[0]);
eport = htons(p[1]);
if (!links_created) {
links_created = 1;
null_addr.s_addr = 0;
if (0 == (salias = FindNewPortGroup(null_addr,
FindAliasAddress(pip->ip_src),
sport, 0,
RTSP_PORT_GROUP,
IPPROTO_UDP, 1))) {
#ifdef DEBUG
fprintf(stderr,
"PacketAlias/RTSP: Cannot find contiguous RTSP data ports\n");
#endif
} else {
base_alias = ntohs(salias);
for (j = 0; j < RTSP_PORT_GROUP; j++) {
rtsp_link = FindRtspOut(GetOriginalAddress(link), null_addr,
htons(base_port + j), htons(base_alias + j),
IPPROTO_UDP);
if (rtsp_link != NULL) {
#ifndef NO_FW_PUNCH
PunchFWHole(rtsp_link);
#endif
} else {
#ifdef DEBUG
fprintf(stderr,
"PacketAlias/RTSP: Cannot allocate RTSP data ports\n");
#endif
break;
}
}
}
ealias = htons(base_alias + (RTSP_PORT_GROUP - 1));
}
if (salias && rtsp_link) {
size_t lentmp;
sprintf(stemp, "%d", ntohs(salias));
lentmp = strlen(stemp);
if (port_newdata + lentmp + 1 > newdata + sizeof(newdata)) {
#if DEBUG
fprintf(stderr, "PacketAlias/RTSP: salias too far\n");
#endif
break;
}
memcpy(port_newdata, stemp, lentmp);
port_newdata += lentmp;
if (eport != 0) {
sprintf(stemp, "%d", ntohs(ealias));
lentmp = strlen(stemp);
if (port_newdata + lentmp + 2 > newdata + sizeof(newdata)) {
#if DEBUG
fprintf(stderr, "PacketAlias/RTSP: ealias too far\n");
#endif
break;
}
*port_newdata = '-';
port_newdata++;
memcpy(port_newdata, stemp, lentmp);
port_newdata += lentmp;
}
pkt_updated = 1;
*port_newdata = ';';
port_newdata++;
}
state++;
break;
}
if (state > 3) {
break;
}
}
port_data += i;
port_dlen -= i;
}
if (!pkt_updated) {
#if DEBUG
fprintf(stderr, "PacketAlias/RTSP: Packet not updated\n");
#endif
return -1;
}
memcpy (port_newdata, port_data, port_dlen);
port_newdata += port_dlen;
*port_newdata = '\0';
new_dlen = port_newdata - newdata;
memcpy (data, newdata, new_dlen);
SetAckModified(link);
delta = GetDeltaSeqOut(pip, link);
AddSeq(pip, link, delta + new_dlen - dlen);
new_len = htons(hlen + new_dlen);
DifferentialChecksum(&pip->ip_sum,
&new_len,
&pip->ip_len,
1);
pip->ip_len = new_len;
tc->th_sum = 0;
tc->th_sum = TcpChecksum(pip);
return 0;
}
static int
alias_pna_out(struct ip *pip,
struct alias_link *link,
char *data,
int dlen)
{
struct alias_link *pna_links;
u_short msg_id, msg_len;
char *work;
u_short alias_port, port;
struct tcphdr *tc;
work = data;
work += 5;
while (work + 4 < data + dlen) {
memcpy(&msg_id, work, 2);
work += 2;
memcpy(&msg_len, work, 2);
work += 2;
if (ntohs(msg_id) == 0) {
return 0;
}
if ((ntohs(msg_id) == 1) || (ntohs(msg_id) == 7)) {
memcpy(&port, work, 2);
pna_links = FindUdpTcpOut(pip->ip_src, GetDestAddress(link),
port, 0, IPPROTO_UDP, 1);
if (pna_links != NULL) {
#ifndef NO_FW_PUNCH
PunchFWHole(pna_links);
#endif
tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
alias_port = GetAliasPort(pna_links);
memcpy(work, &alias_port, 2);
tc->th_sum = 0;
tc->th_sum = TcpChecksum(pip);
}
}
work += ntohs(msg_len);
}
return 0;
}
void
AliasHandleRtspOut(struct ip *pip, struct alias_link *link, int maxpacketsize)
{
int hlen, tlen, dlen;
struct tcphdr *tc;
char *data;
const char *setup = "SETUP", *pna = "PNA", *str200 = "200";
const char *okstr = "OK", *client_port_str = "client_port";
const char *server_port_str = "server_port";
int i, parseOk;
tc = (struct tcphdr *)((char *)pip + (pip->ip_hl << 2));
hlen = (pip->ip_hl + tc->th_off) << 2;
tlen = ntohs(pip->ip_len);
dlen = tlen - hlen;
data = (char*)pip;
data += hlen;
if ((ntohs(tc->th_dport) == RTSP_CONTROL_PORT_NUMBER_1) ||
(ntohs(tc->th_dport) == RTSP_CONTROL_PORT_NUMBER_2)) {
if (dlen >= strlen(setup)) {
if (memcmp(data, setup, strlen(setup)) == 0) {
alias_rtsp_out(pip, link, data, client_port_str);
return;
}
}
if (dlen >= strlen(pna)) {
if (memcmp(data, pna, strlen(pna)) == 0) {
alias_pna_out(pip, link, data, dlen);
}
}
} else {
if (dlen >= strlen(str200)) {
for (parseOk = 0, i = 0;
i <= dlen - strlen(str200);
i++) {
if (memcmp(&data[i], str200, strlen(str200)) == 0) {
parseOk = 1;
break;
}
}
if (parseOk) {
i += strlen(str200);
while(data[i] == ' ')
i++;
if ((dlen - i) >= strlen(okstr)) {
if (memcmp(&data[i], okstr, strlen(okstr)) == 0)
alias_rtsp_out(pip, link, data, server_port_str);
}
}
}
}
}