#include <sys/cdefs.h>
#ifdef _KERNEL
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/module.h>
#else
#include <sys/types.h>
#include <errno.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#endif
#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#ifdef _KERNEL
#include <netinet/libalias/alias.h>
#include <netinet/libalias/alias_local.h>
#include <netinet/libalias/alias_mod.h>
#else
#include "alias_local.h"
#include "alias_mod.h"
#endif
#define RTSP_CONTROL_PORT_NUMBER_1 554
#define RTSP_CONTROL_PORT_NUMBER_2 7070
#define TFTP_PORT_NUMBER 69
static void AliasHandleRtspOut(struct libalias *, struct ip *,
struct alias_link *, size_t);
static int
fingerprint(struct libalias *la, struct alias_data *ah)
{
if (ah->dport != NULL && ah->aport != NULL && ah->sport != NULL &&
ntohs(*ah->dport) == TFTP_PORT_NUMBER)
return (0);
if (ah->dport == NULL || ah->sport == NULL || ah->lnk == NULL ||
ah->maxpktsize == 0)
return (-1);
if (ntohs(*ah->dport) == RTSP_CONTROL_PORT_NUMBER_1
|| ntohs(*ah->sport) == RTSP_CONTROL_PORT_NUMBER_1
|| ntohs(*ah->dport) == RTSP_CONTROL_PORT_NUMBER_2
|| ntohs(*ah->sport) == RTSP_CONTROL_PORT_NUMBER_2)
return (0);
return (-1);
}
static int
protohandler(struct libalias *la, struct ip *pip, struct alias_data *ah)
{
if (ntohs(*ah->dport) == TFTP_PORT_NUMBER)
FindRtspOut(la, pip->ip_src, pip->ip_dst,
*ah->sport, *ah->aport, IPPROTO_UDP);
else
AliasHandleRtspOut(la, pip, ah->lnk, ah->maxpktsize);
return (0);
}
struct proto_handler handlers[] = {
{
.pri = 100,
.dir = OUT,
.proto = TCP|UDP,
.fingerprint = &fingerprint,
.protohandler = &protohandler
},
{ EOH }
};
static int
mod_handler(module_t mod, int type, void *data)
{
int error;
switch (type) {
case MOD_LOAD:
error = 0;
LibAliasAttachHandlers(handlers);
break;
case MOD_UNLOAD:
error = 0;
LibAliasDetachHandlers(handlers);
break;
default:
error = EINVAL;
}
return (error);
}
#ifdef _KERNEL
static
#endif
moduledata_t alias_mod = {
"alias_smedia", mod_handler, NULL
};
#ifdef _KERNEL
DECLARE_MODULE(alias_smedia, alias_mod, SI_SUB_DRIVERS, SI_ORDER_SECOND);
MODULE_VERSION(alias_smedia, 1);
MODULE_DEPEND(alias_smedia, libalias, 1, 1, 1);
#endif
#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 ssize_t
search_string(char *data, size_t dlen, const char *search_str)
{
size_t i, j, k;
size_t search_str_len;
search_str_len = strlen(search_str);
if (search_str_len > dlen)
return (-1);
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 libalias *la, struct ip *pip,
struct alias_link *lnk,
char *data, size_t maxlen,
const char *port_str)
{
size_t hlen, tlen, dlen;
size_t i, j;
struct tcphdr *tc;
int delta, state;
ssize_t pos, slen;
size_t new_dlen, port_dlen, port_slen;
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, *port_data;
bool links_created = false, pkt_updated = false;
struct alias_link *rtsp_lnk = NULL;
struct in_addr null_addr;
tc = (struct tcphdr *)ip_next(pip);
hlen = (pip->ip_hl + tc->th_off) << 2;
tlen = ntohs(pip->ip_len);
dlen = tlen - hlen;
if (hlen > tlen || tlen > maxlen)
return (-1);
port_slen = strlen(port_str);
pos = search_string(data, dlen, transport_str);
if (pos < 0)
return (-1);
port_data = data + pos;
port_dlen = dlen - pos;
maxlen -= hlen;
if ((newdata = malloc(maxlen)) == NULL)
return (-1);
memcpy(newdata, data, pos);
new_dlen = pos;
while (port_dlen > port_slen) {
pos = search_string(port_data, port_dlen, port_str);
if (pos < 0 || (size_t)pos + 1 > maxlen - new_dlen)
break;
memcpy(newdata + new_dlen, port_data, pos + 1);
new_dlen += 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;
else 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 = true;
null_addr.s_addr = 0;
if (0 == (salias = FindNewPortGroup(la, null_addr,
FindAliasAddress(la, pip->ip_src),
sport, 0,
RTSP_PORT_GROUP,
IPPROTO_UDP, 1))) {
#ifdef LIBALIAS_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_lnk = FindRtspOut(la, GetOriginalAddress(lnk), null_addr,
htons(base_port + j), htons(base_alias + j),
IPPROTO_UDP);
if (rtsp_lnk != NULL) {
#ifndef NO_FW_PUNCH
PunchFWHole(rtsp_lnk);
#endif
} else {
#ifdef LIBALIAS_DEBUG
fprintf(stderr,
"PacketAlias/RTSP: Cannot allocate RTSP data ports\n");
#endif
break;
}
}
}
ealias = htons(base_alias + (RTSP_PORT_GROUP - 1));
}
if (salias && rtsp_lnk) {
pkt_updated = true;
slen = snprintf(newdata + new_dlen,
maxlen - new_dlen, "%d", ntohs(salias));
if (slen < 0 || slen >= maxlen - new_dlen)
goto fail;
new_dlen += slen;
if (eport != 0) {
if (new_dlen == maxlen)
goto fail;
newdata[new_dlen++] = '-';
slen = snprintf(newdata + new_dlen,
maxlen - new_dlen, "%d", ntohs(ealias));
if (slen < 0 || slen >= maxlen - new_dlen)
goto fail;
new_dlen += slen;
}
if (new_dlen == maxlen)
goto fail;
newdata[new_dlen++] = ';';
}
state++;
break;
}
if (state > 3) {
break;
}
}
port_data += i;
port_dlen -= i;
}
if (!pkt_updated)
goto fail;
if (new_dlen + port_dlen > maxlen)
goto fail;
memmove(data + new_dlen, port_data, port_dlen);
memcpy(data, newdata, new_dlen);
new_dlen += port_dlen;
free(newdata);
SetAckModified(lnk);
tc = (struct tcphdr *)ip_next(pip);
delta = GetDeltaSeqOut(tc->th_seq, lnk);
AddSeq(lnk, delta + (int)(new_dlen - dlen), pip->ip_hl, pip->ip_len,
tc->th_seq, tc->th_off);
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;
#ifdef _KERNEL
tcp_set_flags(tc, tcp_get_flags(tc) | TH_RES1);
#else
tc->th_sum = TcpChecksum(pip);
#endif
return (0);
fail:
free(newdata);
return (-1);
}
static int
alias_pna_out(struct libalias *la, struct ip *pip,
struct alias_link *lnk,
char *data,
size_t dlen)
{
struct alias_link *pna_links;
char *work;
struct tcphdr *tc;
u_short msg_id, msg_len;
u_short alias_port, port;
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);
(void)FindUdpTcpOut(la, pip->ip_src, GetDestAddress(lnk),
port, 0, IPPROTO_UDP, 1, &pna_links);
if (pna_links != NULL) {
#ifndef NO_FW_PUNCH
PunchFWHole(pna_links);
#endif
tc = (struct tcphdr *)ip_next(pip);
alias_port = GetAliasPort(pna_links);
memcpy(work, &alias_port, 2);
tc->th_sum = 0;
#ifdef _KERNEL
tcp_set_flags(tc, tcp_get_flags(tc) | TH_RES1);
#else
tc->th_sum = TcpChecksum(pip);
#endif
}
}
work += ntohs(msg_len);
}
return (0);
}
static void
AliasHandleRtspOut(struct libalias *la, struct ip *pip, struct alias_link *lnk,
size_t maxlen)
{
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";
size_t hlen, tlen, dlen;
size_t i;
tc = (struct tcphdr *)ip_next(pip);
hlen = (pip->ip_hl + tc->th_off) << 2;
tlen = ntohs(pip->ip_len);
dlen = tlen - hlen;
if (hlen > tlen || tlen > maxlen)
return;
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) &&
memcmp(data, setup, strlen(setup)) == 0) {
alias_rtsp_out(la, pip, lnk, data, maxlen,
client_port_str);
return;
}
if (dlen >= strlen(pna) &&
memcmp(data, pna, strlen(pna)) == 0)
alias_pna_out(la, pip, lnk, data, dlen);
} else {
if (dlen < strlen(str200) + 1 + strlen(okstr))
return;
for (i = 0; i <= dlen - strlen(str200) - 1; i++) {
if (memcmp(&data[i], str200, strlen(str200)) == 0 &&
data[i + strlen(str200)] == ' ') {
i += strlen(str200);
while (i < dlen && data[i] == ' ')
i++;
if (dlen - i >= strlen(okstr)) {
if (memcmp(&data[i], okstr, strlen(okstr)) == 0) {
alias_rtsp_out(la, pip, lnk, data,
maxlen, server_port_str);
break;
}
}
}
}
}
}