<|start_filename|>src/apps/relay/turn_ports.c<|end_filename|>
/*
 * Copyright (C) 2011, 2012, 2013 Citrix Systems
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the project nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include "ns_turn_maps.h"
#include "ns_turn_msg_defs.h"

#include "ns_turn_ioalib.h"
#include "ns_ioalib_impl.h"

#include "turn_ports.h"

////////// DATA ////////////////////////////////////////////

#define PORTS_SIZE (0xFFFF+1)
#define TPS_OUT_OF_RANGE ((u32bits)(-1))
#define TPS_TAKEN_SINGLE ((u32bits)(-2))
#define TPS_TAKEN_EVEN ((u32bits)(-3))
#define TPS_TAKEN_ODD ((u32bits)(-4))

struct _turnports {
  u32bits status[PORTS_SIZE];
  u32bits low;
  u32bits high;
  u16bits range_start;
  u16bits range_stop;
  u16bits ports[PORTS_SIZE];
  TURN_MUTEX_DECLARE(mutex)
};
typedef struct _turnports turnports;

/////////////// TURNPORTS statics //////////////////////////

static turnports* turnports_create(super_memory_t *sm, u16bits start, u16bits end);
static u16bits turnports_size(turnports* tp);

static int turnports_allocate(turnports* tp);
static int turnports_allocate_even(turnports* tp, int allocate_rtcp, u64bits *reservation_token);

static void turnports_release(turnports* tp, u16bits port);

static int turnports_is_allocated(turnports* tp, u16bits port);
static int turnports_is_available(turnports* tp, u16bits port);

/////////////// UTILS //////////////////////////////////////

static int is_taken(u32bits status) {
	int ret = -1;
	switch (status) {
	case TPS_TAKEN_SINGLE :
	case TPS_TAKEN_EVEN :
	case TPS_TAKEN_ODD :
		ret = 1;
		break;
	default:
		ret = 0;
	};
	return ret;
}

static void turnports_randomize(turnports* tp) {
  if(tp) {
    unsigned int size=(unsigned int)(tp->high-tp->low);
    unsigned int i=0;
    unsigned int cycles=size*10;
    for(i=0;i<cycles;i++) {
      u16bits port1 = (u16bits)(tp->low + (u16bits)(((unsigned long)random())%((unsigned long)size)));
      u16bits port2 = (u16bits)(tp->low + (u16bits)(((unsigned long)random())%((unsigned long)size)));
      if(port1!=port2) {
    	  int pos1=tp->status[port1];
    	  int pos2=tp->status[port2];
    	  int tmp=(int)tp->status[port1];
    	  tp->status[port1]=tp->status[port2];
    	  tp->status[port2]=(u32bits)tmp;
    	  tmp=(int)tp->ports[pos1];
    	  tp->ports[pos1]=tp->ports[pos2];
    	  tp->ports[pos2]=(u16bits)tmp;
      }
    }
  }
}   

static void turnports_init(turnports* tp, u16bits start, u16bits end) {

  tp->low=start;
  tp->high=((u32bits)end)+1;

  tp->range_start=start;
  tp->range_stop=end;
  
  int i=0;
  for(i=0;i<start;i++) {
    tp->status[i]=TPS_OUT_OF_RANGE;
    tp->ports[i]=(u16bits)i;
  }
  for(i=start;i<=end;i++) {
    tp->status[i]=(u32bits)i;
    tp->ports[i]=(u16bits)i;
  }
  for(i=((int)end)+1;i<PORTS_SIZE;i++) {
    tp->status[i]=TPS_OUT_OF_RANGE;
    tp->ports[i]=(u16bits)i;
  }

  turnports_randomize(tp);

  TURN_MUTEX_INIT_RECURSIVE(&(tp->mutex));
}

/////////////// FUNC ///////////////////////////////////////

turnports* turnports_create(super_memory_t *sm, u16bits start, u16bits end) {

  if(start>end) return NULL;

  turnports* ret=(turnports*)allocate_super_memory_region(sm, sizeof(turnports));
  turnports_init(ret,start,end);

  return ret;
}

u16bits turnports_size(turnports* tp) {
  if(!tp) return 0;
  else {
    TURN_MUTEX_LOCK(&tp->mutex);
    u16bits ret = (u16bits)((tp->high-tp->low));
    TURN_MUTEX_UNLOCK(&tp->mutex);
    return ret;
  }
}

int turnports_allocate(turnports* tp) {

  int port=-1;

  TURN_MUTEX_LOCK(&tp->mutex);

  if(tp) {

    while(1) {
      
      if(tp->high <= tp->low) {
    	  TURN_MUTEX_UNLOCK(&tp->mutex);
    	  return -1;
      }
      
      int position=(u16bits)(tp->low & 0x0000FFFF);
      
      port=(int)tp->ports[position];
      if(port<(int)(tp->range_start) || port>((int)(tp->range_stop))) {
    	  TURN_MUTEX_UNLOCK(&tp->mutex);
    	  return -1;
      }
      if(is_taken(tp->status[port])) {
    	  ++(tp->low);
    	  continue;
      } 
      if(tp->status[port]!=tp->low) {
    	  ++(tp->low);
    	  continue;
      }
      tp->status[port]=TPS_TAKEN_SINGLE;
      ++(tp->low);
      break;
    }
  }

  TURN_MUTEX_UNLOCK(&tp->mutex);

  return port;
}

void turnports_release(turnports* tp, u16bits port) {
  TURN_MUTEX_LOCK(&tp->mutex);
  if(tp && port>=tp->range_start && port<=tp->range_stop) {
    u16bits position=(u16bits)(tp->high & 0x0000FFFF);
    if(is_taken(tp->status[port])) {
      tp->status[port]=tp->high;
      tp->ports[position]=port;
      ++(tp->high);
    }
  }
  TURN_MUTEX_UNLOCK(&tp->mutex);
}

int turnports_allocate_even(turnports* tp, int allocate_rtcp, u64bits *reservation_token) {
  if(tp) {
    TURN_MUTEX_LOCK(&tp->mutex);
    u16bits size = turnports_size(tp);
    if(size>1) {
      u16bits i=0;
      for(i=0;i<size;i++) {
    	  int port=turnports_allocate(tp);
    	  if(port & 0x00000001) {
    		  turnports_release(tp,port);
    	  } else {
    		  if(!allocate_rtcp) {
    			  TURN_MUTEX_UNLOCK(&tp->mutex);
    			  return port;
    		  } else {
    			  int rtcp_port=port+1;
    			  if(rtcp_port>tp->range_stop) {
    				  turnports_release(tp,port);
    			  } else if(!turnports_is_available(tp,rtcp_port)) {
    				  turnports_release(tp,port);
    			  } else {
    				  tp->status[port]=TPS_TAKEN_EVEN;
    				  tp->status[rtcp_port]=TPS_TAKEN_ODD;
    				  if(reservation_token) {
    					  u16bits *v16=(u16bits*)reservation_token;
    					  u32bits *v32=(u32bits*)reservation_token;
    					  v16[0]=(u16bits)(tp->ports[(u16bits)(tp->low & 0x0000FFFF)]);
    					  v16[1]=(u16bits)(tp->ports[(u16bits)(tp->high & 0x0000FFFF)]);
    					  v32[1]=(u32bits)turn_random();
    				  }
    				  TURN_MUTEX_UNLOCK(&tp->mutex);
    				  return port;
    			  }
    		  }
    	  }
      }
    }
    TURN_MUTEX_UNLOCK(&tp->mutex);
  }
  return -1;
}

int turnports_is_allocated(turnports* tp, u16bits port) {
  if(!tp) return 0;
  else {
    TURN_MUTEX_LOCK(&tp->mutex);
    int ret = is_taken(tp->status[port]);
    TURN_MUTEX_UNLOCK(&tp->mutex);
    return ret;
  }
}

int turnports_is_available(turnports* tp, u16bits port) {
  if(tp) {
    TURN_MUTEX_LOCK(&tp->mutex);
    u32bits status = tp->status[port];
    if((status!=TPS_OUT_OF_RANGE) && !is_taken(status)) {
      u16bits position=(u16bits)(status & 0x0000FFFF);
      if(tp->ports[position]==port) {
    	  TURN_MUTEX_UNLOCK(&tp->mutex);
    	  return 1;
      }
    }
    TURN_MUTEX_UNLOCK(&tp->mutex);
  }
  return 0;
}

/////////////////// IP-mapped PORTS /////////////////////////////////////

struct _turnipports
{
	super_memory_t *sm;
	u16bits start;
	u16bits end;
	ur_addr_map ip_to_turnports_udp;
	ur_addr_map ip_to_turnports_tcp;
	TURN_MUTEX_DECLARE(mutex)
};

//////////////////////////////////////////////////

static ur_addr_map *get_map(turnipports *tp, u08bits transport)
{
	if(transport == STUN_ATTRIBUTE_TRANSPORT_TCP_VALUE)
		return &(tp->ip_to_turnports_tcp);
	return &(tp->ip_to_turnports_udp);
}
//////////////////////////////////////////////////

static turnipports* turnipports_singleton = NULL;

turnipports* turnipports_create(super_memory_t *sm, u16bits start, u16bits end)
{
	turnipports *ret = (turnipports*) allocate_super_memory_region(sm, sizeof(turnipports));
	ret->sm = sm;
	ur_addr_map_init(&(ret->ip_to_turnports_udp));
	ur_addr_map_init(&(ret->ip_to_turnports_tcp));
	ret->start = start;
	ret->end = end;
	TURN_MUTEX_INIT_RECURSIVE(&(ret->mutex));
	turnipports_singleton = ret;
	return ret;
}

static turnports* turnipports_add(turnipports* tp, u08bits transport, const ioa_addr *backend_addr)
{
	ur_addr_map_value_type t = 0;
	if (tp && backend_addr) {
		ioa_addr ba;
		addr_cpy(&ba, backend_addr);
		addr_set_port(&ba, 0);
		TURN_MUTEX_LOCK((const turn_mutex*)&(tp->mutex));
		if (!ur_addr_map_get(get_map(tp, transport), &ba, &t)) {
			t = (ur_addr_map_value_type) turnports_create(tp->sm, tp->start, tp->end);
			ur_addr_map_put(get_map(tp, transport), &ba, t);
		}
		TURN_MUTEX_UNLOCK((const turn_mutex*)&(tp->mutex));
	}
	return (turnports*) t;
}

void turnipports_add_ip(u08bits transport, const ioa_addr *backend_addr)
{
	turnipports_add(turnipports_singleton, transport, backend_addr);
}

int turnipports_allocate(turnipports* tp, u08bits transport, const ioa_addr *backend_addr)
{
	int ret = -1;
	if (tp && backend_addr) {
		TURN_MUTEX_LOCK((const turn_mutex*)&(tp->mutex));
		turnports *t = turnipports_add(tp, transport, backend_addr);
		ret = turnports_allocate(t);
		TURN_MUTEX_UNLOCK((const turn_mutex*)&(tp->mutex));
	}
	return ret;
}

int turnipports_allocate_even(turnipports* tp, const ioa_addr *backend_addr, int allocate_rtcp,
				u64bits *reservation_token)
{
	int ret = -1;
	if (tp && backend_addr) {
		TURN_MUTEX_LOCK((const turn_mutex*)&(tp->mutex));
		turnports *t = turnipports_add(tp, STUN_ATTRIBUTE_TRANSPORT_UDP_VALUE, backend_addr);
		ret = turnports_allocate_even(t, allocate_rtcp, reservation_token);
		TURN_MUTEX_UNLOCK((const turn_mutex*)&(tp->mutex));
	}
	return ret;
}

void turnipports_release(turnipports* tp, u08bits transport, const ioa_addr *socket_addr)
{
	if (tp && socket_addr) {
		ioa_addr ba;
		ur_addr_map_value_type t;
		addr_cpy(&ba, socket_addr);
		addr_set_port(&ba, 0);
		TURN_MUTEX_LOCK((const turn_mutex*)&(tp->mutex));
		if (ur_addr_map_get(get_map(tp, transport), &ba, &t)) {
			turnports_release((turnports*) t, addr_get_port(socket_addr));
		}
		TURN_MUTEX_UNLOCK((const turn_mutex*)&(tp->mutex));
	}
}

int turnipports_is_allocated(turnipports* tp, u08bits transport, const ioa_addr *backend_addr, u16bits port)
{
	int ret = 0;
	if (tp && backend_addr) {
		ioa_addr ba;
		ur_addr_map_value_type t;
		addr_cpy(&ba, backend_addr);
		addr_set_port(&ba, 0);
		TURN_MUTEX_LOCK((const turn_mutex*)&(tp->mutex));
		if (ur_addr_map_get(get_map(tp,transport), &ba, &t)) {
			ret = turnports_is_allocated((turnports*) t, port);
		}
		TURN_MUTEX_UNLOCK((const turn_mutex*)&(tp->mutex));
	}
	return ret;
}

int turnipports_is_available(turnipports* tp, u08bits transport, const ioa_addr *backend_addr, u16bits port)
{
	int ret = 0;
	if (tp && backend_addr) {
		ioa_addr ba;
		ur_addr_map_value_type t;
		addr_cpy(&ba, backend_addr);
		addr_set_port(&ba, 0);
		TURN_MUTEX_LOCK((const turn_mutex*)&(tp->mutex));
		if (!ur_addr_map_get(get_map(tp,transport), &ba, &t)) {
			ret = 1;
		} else {
			ret = turnports_is_available((turnports*) t, port);
		}
		TURN_MUTEX_UNLOCK((const turn_mutex*)&(tp->mutex));
	}
	return ret;
}

//////////////////////////////////////////////////////////////////




<|start_filename|>src/client/ns_turn_msg_addr.c<|end_filename|>
/*
 * Copyright (C) 2011, 2012, 2013 Citrix Systems
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the project nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include "ns_turn_msg_addr.h"

//////////////////////////////////////////////////////////////////////////////

int stun_addr_encode(const ioa_addr* ca, u08bits *cfield, int *clen, int xor_ed, u32bits mc, const u08bits *tsx_id) {

  if(!cfield || !clen || !ca || !tsx_id) return -1;

  if (ca->ss.sa_family == AF_INET || ca->ss.sa_family==0) {

    /* IPv4 address */

    *clen=8;
    
    cfield[0]=0;
    cfield[1]=1; //IPv4 family
    
    if (xor_ed) {

      /* Port */
      ((u16bits*)cfield)[1] = (ca->s4.sin_port) ^ nswap16(mc >> 16);

      /* Address */
      ((u32bits*)cfield)[1] = (ca->s4.sin_addr.s_addr) ^ nswap32(mc);

    } else {

      /* Port */
      ((u16bits*)cfield)[1]=ca->s4.sin_port;

      /* Address */
      ((u32bits*)cfield)[1]=ca->s4.sin_addr.s_addr;
    }

  } else if (ca->ss.sa_family == AF_INET6) {

    /* IPv6 address */

    *clen=20;

    cfield[0]=0;
    cfield[1]=2; //IPv6 family
    
    if (xor_ed) {

      unsigned int i;
      u08bits *dst = ((u08bits*)cfield)+4;
      const u08bits *src = (const u08bits*)&(ca->s6.sin6_addr);
      u32bits magic = nswap32(mc);

      /* Port */
      ((u16bits*)cfield)[1] = ca->s6.sin6_port ^ nswap16(mc >> 16);

      /* Address */

      for (i=0; i<4; ++i) {
	dst[i] = (u08bits)(src[i] ^ ((const u08bits*)&magic)[i]);
      }
      for (i=0; i<12; ++i) {
	dst[i+4] = (u08bits)(src[i+4] ^ tsx_id[i]);
      }

    } else {

      /* Port */
      ((u16bits*)cfield)[1]=ca->s6.sin6_port;
      
      /* Address */
      ns_bcopy(&ca->s6.sin6_addr, ((u08bits*)cfield)+4, 16);
    }

  } else {
    return -1;
  }

  return 0;
}

int stun_addr_decode(ioa_addr* ca, const u08bits *cfield, int len, int xor_ed, u32bits mc, const u08bits *tsx_id) {

  if(!cfield || !len || !ca || !tsx_id || (len<8)) return -1;

  if(cfield[0]!=0) {
    return -1;
  }

  int sa_family;

  if(cfield[1]==1) sa_family=AF_INET;
  else if(cfield[1]==2) sa_family=AF_INET6;
  else return -1;
  
  ca->ss.sa_family=sa_family;

  if (sa_family == AF_INET) {

    if(len!=8) return -1;

    /* IPv4 address */

    /* Port */
    ca->s4.sin_port=((const u16bits*)cfield)[1];

    /* Address */
    ca->s4.sin_addr.s_addr=((const u32bits*)cfield)[1];
    
    if (xor_ed) {
      ca->s4.sin_port ^= nswap16(mc >> 16);
      ca->s4.sin_addr.s_addr ^= nswap32(mc);
    }

  } else if (sa_family == AF_INET6) {

    /* IPv6 address */

    if(len!=20) return -1;

    /* Port */
    ca->s6.sin6_port = ((const u16bits*)cfield)[1];

    /* Address */
    ns_bcopy(((const u08bits*)cfield)+4, &ca->s6.sin6_addr, 16);

    if (xor_ed) {

      unsigned int i;
      u08bits *dst;
      const u08bits *src;
      u32bits magic = nswap32(mc);

      /* Port */
      ca->s6.sin6_port ^= nswap16(mc >> 16);

      /* Address */
      src = ((const u08bits*)cfield)+4;
      dst = (u08bits*)&ca->s6.sin6_addr;
      for (i=0; i<4; ++i) {
	dst[i] = (u08bits)(src[i] ^ ((const u08bits*)&magic)[i]);
      }
      for (i=0; i<12; ++i) {
	dst[i+4] = (u08bits)(src[i+4] ^ tsx_id[i]);
      }
    }

  } else {
    return -1;
  }

  return 0;
}

//////////////////////////////////////////////////////////////////////////////



<|start_filename|>rpm/turnserver.init.el<|end_filename|>
#!/bin/bash
#
# Startup script for TURN Server
#
# chkconfig: 345 85 15
# description: RFC 5766 TURN Server
#
# processname: turnserver
# pidfile: /var/run/turnserver.pid
# config: /etc/turnserver/turnserver.conf
#
### BEGIN INIT INFO
# Provides: turnserver
# Required-Start: $local_fs $network
# Short-Description: RFC 5766 TURN Server
# Description: RFC 5766 TURN Server
### END INIT INFO

# Source function library.
. /etc/rc.d/init.d/functions

TURN=/usr/bin/turnserver
PROG=turnserver
TURNCFG=/etc/turnserver/$PROG.conf
PID_FILE=/var/run/$PROG.pid
LOCK_FILE=/var/lock/subsys/$PROG
DEFAULTS=/etc/sysconfig/$PROG
RETVAL=0
USER=turnserver

start() {
	echo -n $"Starting $PROG: "
	daemon --user=$USER $TURN $OPTIONS
	RETVAL=$?
	if [ $RETVAL = 0 ]; then
		pidofproc $TURN > $PID_FILE
		RETVAL=$?
		[ $RETVAL = 0 ] && touch $LOCK_FILE && success
	fi
	echo
	return $RETVAL
}

stop() {
	echo -n $"Stopping $PROG: "
	killproc $TURN
	RETVAL=$?
	echo
	[ $RETVAL = 0 ] && rm -f $LOCK_FILE $PID_FILE
}

[ -f $DEFAULTS ] && . $DEFAULTS
OPTIONS="-o -c $TURNCFG $EXTRA_OPTIONS"

# See how we were called.
case "$1" in
	start)
		start
		;;
	stop)
		stop
		;;
	status)
		status $TURN
		RETVAL=$?
		;;
	restart)
		stop
		start
		;;
	condrestart)
		if [ -f $PID_FILE ] ; then
			stop
			start
		fi
		;;
	*)
		echo $"Usage: $PROG {start|stop|restart|condrestart|status|help}"
		exit 1
esac

exit $RETVAL
