re/src/natbd/genalg.c
2012-08-09 15:30:41 +00:00

155 lines
3.4 KiB
C

/**
* @file genalg.c Detecting Generic ALGs
*
* Copyright (C) 2010 Creytiv.com
*/
#include <re_types.h>
#include <re_mbuf.h>
#include <re_fmt.h>
#include <re_mem.h>
#include <re_sa.h>
#include <re_list.h>
#include <re_stun.h>
#include <re_natbd.h>
#define DEBUG_MODULE "natbd_genalg"
#define DEBUG_LEVEL 7
#include <re_dbg.h>
/*
Detecting Generic ALGs
A number of NAT boxes are now being deployed into the market which
try to provide "generic" ALG functionality. These generic ALGs hunt
for IP addresses, either in text or binary form within a packet, and
rewrite them if they match a binding. This behavior can be detected
because the STUN server returns both the MAPPED-ADDRESS and XOR-
MAPPED-ADDRESS in the same response. If the result in the two does
not match, there a NAT with a generic ALG in the path.
*/
/** Defines a NAT Generic ALG detection session */
struct nat_genalg {
struct stun *stun; /**< STUN Client */
struct sa srv; /**< Server address and port */
int proto; /**< IP protocol */
nat_genalg_h *h; /**< Result handler */
void *arg; /**< Handler argument */
};
static void stun_response_handler(int err, uint16_t scode, const char *reason,
const struct stun_msg *msg, void *arg)
{
struct stun_attr *xmap, *map;
struct nat_genalg *ng = arg;
int status = 0;
(void)reason;
if (err) {
ng->h(err, 0, NULL, -1, NULL, ng->arg);
return;
}
switch (scode) {
case 0:
map = stun_msg_attr(msg, STUN_ATTR_MAPPED_ADDR);
xmap = stun_msg_attr(msg, STUN_ATTR_XOR_MAPPED_ADDR);
if (!map || !xmap) {
ng->h(EINVAL, scode, reason, -1, NULL, ng->arg);
break;
}
status = sa_cmp(&map->v.sa, &xmap->v.sa, SA_ALL) ? -1 : 1;
ng->h(0, scode, reason, status, &xmap->v.sa, ng->arg);
break;
default:
ng->h(0, scode, reason, -1, NULL, ng->arg);
break;
}
}
static void genalg_destructor(void *data)
{
struct nat_genalg *ng = data;
mem_deref(ng->stun);
}
/**
* Allocate a new NAT Generic ALG detection session
*
* @param ngp Pointer to allocated NAT Generic ALG object
* @param srv STUN Server IP address and port
* @param proto Transport protocol
* @param conf STUN configuration (Optional)
* @param gh Generic ALG handler
* @param arg Handler argument
*
* @return 0 if success, errorcode if failure
*/
int nat_genalg_alloc(struct nat_genalg **ngp, const struct sa *srv, int proto,
const struct stun_conf *conf,
nat_genalg_h *gh, void *arg)
{
struct nat_genalg *ng;
int err;
if (!ngp || !srv || !proto || !gh)
return EINVAL;
ng = mem_zalloc(sizeof(*ng), genalg_destructor);
if (!ng)
return ENOMEM;
err = stun_alloc(&ng->stun, conf, NULL, NULL);
if (err)
goto out;
sa_cpy(&ng->srv, srv);
ng->proto = proto;
ng->h = gh;
ng->arg = arg;
out:
if (err)
mem_deref(ng);
else
*ngp = ng;
return err;
}
/**
* Start the NAT Generic ALG detection
*
* @param ng NAT Generic ALG object
*
* @return 0 if success, errorcode if failure
*/
int nat_genalg_start(struct nat_genalg *ng)
{
int err;
if (!ng)
return EINVAL;
err = stun_request(NULL, ng->stun, ng->proto, NULL, &ng->srv, 0,
STUN_METHOD_BINDING, NULL, 0, false,
stun_response_handler, ng, 1,
STUN_ATTR_SOFTWARE, stun_software);
if (err) {
DEBUG_WARNING("start: stunc_request_send(): (%m)\n", err);
}
return err;
}