summaryrefslogtreecommitdiff
path: root/pjnath
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2008-03-09 12:55:00 +0000
committerBenny Prijono <bennylp@teluu.com>2008-03-09 12:55:00 +0000
commit9e0ebffd26e56586f6f2ab0469144c3685fc388c (patch)
tree7c06e7a3db2b951290462c2ca9580ab5daf57b79 /pjnath
parentfee9975ecfd058c88d5dd8648ec6ff6bc6ff40c3 (diff)
More work for ticket #485: updated pjnath with TURN-07 and added authentication in the server
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@1852 74dad513-b988-da41-8d7b-12977e46ad98
Diffstat (limited to 'pjnath')
-rw-r--r--pjnath/build/pjturn_srv.dsp12
-rw-r--r--pjnath/include/pjnath/stun_msg.h84
-rw-r--r--pjnath/include/pjnath/stun_session.h36
-rw-r--r--pjnath/src/pjnath/stun_msg.c13
-rw-r--r--pjnath/src/pjnath/stun_msg_dump.c4
-rw-r--r--pjnath/src/pjnath/stun_session.c26
-rw-r--r--pjnath/src/pjturn-srv/allocation.c550
-rw-r--r--pjnath/src/pjturn-srv/auth.c132
-rw-r--r--pjnath/src/pjturn-srv/auth.h115
-rw-r--r--pjnath/src/pjturn-srv/listener_udp.c43
-rw-r--r--pjnath/src/pjturn-srv/main.c50
-rw-r--r--pjnath/src/pjturn-srv/server.c743
-rw-r--r--pjnath/src/pjturn-srv/turn.h215
13 files changed, 1436 insertions, 587 deletions
diff --git a/pjnath/build/pjturn_srv.dsp b/pjnath/build/pjturn_srv.dsp
index 34f90001..01316e48 100644
--- a/pjnath/build/pjturn_srv.dsp
+++ b/pjnath/build/pjturn_srv.dsp
@@ -42,7 +42,7 @@ RSC=rc.exe
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
-# ADD CPP /nologo /MD /W3 /GX /O2 /I "../include" /I "../../pjlib/include" /I "../../pjlib-util/include" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /MD /W4 /GX /O2 /I "../include" /I "../../pjlib/include" /I "../../pjlib-util/include" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
# ADD BASE RSC /l 0x409 /d "NDEBUG"
# ADD RSC /l 0x409 /d "NDEBUG"
BSC32=bscmake.exe
@@ -66,7 +66,7 @@ LINK32=link.exe
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
-# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../include" /I "../../pjlib/include" /I "../../pjlib-util/include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "../include" /I "../../pjlib/include" /I "../../pjlib-util/include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR /YX /FD /GZ /c
# ADD BASE RSC /l 0x409 /d "_DEBUG"
# ADD RSC /l 0x409 /d "_DEBUG"
BSC32=bscmake.exe
@@ -91,6 +91,10 @@ SOURCE="..\src\pjturn-srv\allocation.c"
# End Source File
# Begin Source File
+SOURCE="..\src\pjturn-srv\auth.c"
+# End Source File
+# Begin Source File
+
SOURCE="..\src\pjturn-srv\listener_udp.c"
# End Source File
# Begin Source File
@@ -107,6 +111,10 @@ SOURCE="..\src\pjturn-srv\server.c"
# PROP Default_Filter "h;hpp;hxx;hm;inl"
# Begin Source File
+SOURCE="..\src\pjturn-srv\auth.h"
+# End Source File
+# Begin Source File
+
SOURCE="..\src\pjturn-srv\turn.h"
# End Source File
# End Group
diff --git a/pjnath/include/pjnath/stun_msg.h b/pjnath/include/pjnath/stun_msg.h
index 8946a71b..cde21fa9 100644
--- a/pjnath/include/pjnath/stun_msg.h
+++ b/pjnath/include/pjnath/stun_msg.h
@@ -297,11 +297,11 @@ typedef enum pj_stun_attr_type
PJ_STUN_ATTR_NONCE = 0x0015,/**< NONCE attribute. */
PJ_STUN_ATTR_RELAY_ADDR = 0x0016,/**< RELAY-ADDRESS attribute. */
PJ_STUN_ATTR_REQ_ADDR_TYPE = 0x0017,/**< REQUESTED-ADDRESS-TYPE */
- PJ_STUN_ATTR_REQ_PORT_PROPS = 0x0018,/**< REQUESTED-PORT-PROPS */
+ PJ_STUN_ATTR_REQ_PROPS = 0x0018,/**< REQUESTED-PROPS */
PJ_STUN_ATTR_REQ_TRANSPORT = 0x0019,/**< REQUESTED-TRANSPORT */
PJ_STUN_ATTR_XOR_MAPPED_ADDR = 0x0020,/**< XOR-MAPPED-ADDRESS */
PJ_STUN_ATTR_TIMER_VAL = 0x0021,/**< TIMER-VAL attribute. */
- PJ_STUN_ATTR_REQ_IP = 0x0022,/**< REQUESTED-IP attribute */
+ PJ_STUN_ATTR_RESERVATION_TOKEN = 0x0022,/**< TURN RESERVATION-TOKEN */
PJ_STUN_ATTR_XOR_REFLECTED_FROM = 0x0023,/**< XOR-REFLECTED-FROM */
PJ_STUN_ATTR_PRIORITY = 0x0024,/**< PRIORITY */
PJ_STUN_ATTR_USE_CANDIDATE = 0x0025,/**< USE-CANDIDATE */
@@ -358,6 +358,8 @@ typedef enum pj_stun_status
PJ_STUN_SC_SERVER_ERROR = 500, /**< Server Error */
PJ_STUN_SC_INSUFFICIENT_CAPACITY = 507, /**< Insufficient Capacity
(TURN) */
+ PJ_STUN_SC_INSUFFICIENT_PORT_CAPACITY=508, /**< Insufficient Port Capacity
+ (TURN) */
PJ_STUN_SC_GLOBAL_FAILURE = 600 /**< Global Failure */
} pj_stun_status;
@@ -945,61 +947,48 @@ typedef struct pj_stun_sockaddr_attr pj_stun_relay_addr_attr;
typedef struct pj_stun_uint_attr pj_stun_req_addr_type;
/**
- * This describes the TURN REQUESTED-PORT-PROPS attribute, encoded as
+ * This describes the TURN REQUESTED-PROPS attribute, encoded as
* STUN 32bit integer attribute. Few macros are provided to manipulate
* the values in this attribute: #PJ_STUN_GET_RPP_BITS(),
* #PJ_STUN_SET_RPP_BITS(), #PJ_STUN_GET_RPP_PORT(), and
* #PJ_STUN_SET_RPP_PORT().
- *
+ *
* This attribute allows the client to request certain properties for
- * the port that is allocated by the server. The attribute can be used
- * with any transport protocol that has the notion of a 16 bit port
- * space (including TCP and UDP). The attribute is 32 bits long. Its
- * format is:
+ * the relayed transport address that is allocated by the server. The
+ * attribute is 32 bits long. Its format is:
\verbatim
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Reserved = 0 | A | Specific Port Number |
+ | Prop-type | Reserved = 0 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
\endverbatim
- * The two bits labeled A in the diagram above are for requested port
- * alignment and have the following meaning:
- *
- * - 00 no specific port alignment
- * - 01 odd port number
- * - 10 even port number
- * - 11 even port number; reserve next higher port
- */
-typedef struct pj_stun_uint_attr pj_stun_req_port_props_attr;
-
-/**
- * Get the 2 bits requested port alignment value from a 32bit integral
- * value of TURN REQUESTED-PORT-PROPS attribute.
- */
-#define PJ_STUN_GET_RPP_BITS(u32) ((u32 >> 16) & 0x03)
-
-/**
- * Convert 2 bits requested port alignment value to a 32bit integral
- * value of TURN REQUESTED-PORT-PROPS attribute.
+ * The field labeled "Prop-type" is an 8-bit field specifying the
+ * desired property. The rest of the attribute is RFFU (Reserved For
+ * Future Use) and MUST be set to 0 on transmission and ignored on
+ * reception. The values of the "Prop-type" field are:
+ *
+ * 0x00 (Reserved)
+ * 0x01 Even port number
+ * 0x02 Pair of ports
*/
-#define PJ_STUN_SET_RPP_BITS(A) (A << 16)
+typedef struct pj_stun_uint_attr pj_stun_req_props_attr;
/**
- * Get the port number in TURN REQUESTED-PORT-PROPS attribute. The port
- * number is returned in host byte order.
+ * Get the 8bit Prop-type value from a 32bit integral value of TURN
+ * TURN REQUESTED-PROPS attribute.
*/
-#define PJ_STUN_GET_RPP_PORT(u32) pj_ntohs((pj_uint16_t)(u32 & 0x0000FFFFL))
+#define PJ_STUN_GET_PROP_TYPE(u32) (u32 >> 24)
/**
- * Convert port number in host byte order to 32bit value to be encoded in
- * TURN REQUESTED-PORT-PROPS attribute.
+ * Convert 8bit Prop-type value to a 32bit integral value of TURN
+ * REQUESTED-PROPS attribute.
*/
-#define PJ_STUN_SET_RPP_PORT(port) ((pj_uint32_t)pj_htons((pj_uint16_t)(port)))
+#define PJ_STUN_SET_PROP_TYPE(PropType) (PropType << 24)
/**
@@ -1046,23 +1035,18 @@ typedef struct pj_stun_uint_attr pj_stun_req_transport_attr;
/**
- * This describes the TURN REQUESTED-IP attribute.
- * The REQUESTED-IP attribute is used by the client to request that a
- * specific IP address be allocated by the TURN server. This attribute
- * is needed since it is anticipated that TURN servers will be multi-
- * homed so as to be able to allocate more than 64k transport addresses.
- * As a consequence, a client needing a second transport address on the
- * same interface as a previous one can use this attribute to request a
- * remote address from the same TURN server interface as the TURN
- * client's previous remote address.
+ * This describes the TURN RESERVATION-TOKEN attribute.
+ * The RESERVATION-TOKEN attribute contains a token that uniquely
+ * identifies a relayed transport address being held in reserve by the
+ * server. The server includes this attribute in a success response to
+ * tell the client about the token, and the client includes this
+ * attribute in a subsequent Allocate request to request the server use
+ * that relayed transport address for the allocation.
*
- * The format of this attribute is identical to XOR-MAPPED-ADDRESS.
- * However, the port component of the attribute MUST be ignored by the
- * server. If a client wishes to request a specific IP address and
- * port, it uses both the REQUESTED-IP and REQUESTED-PORT-PROPS
- * attributes.
+ * The attribute value is a 64-bit-long field containing the token
+ * value.
*/
-typedef struct pj_stun_sockaddr_attr pj_stun_req_ip_attr;
+typedef struct pj_stun_uint64_attr pj_stun_res_token_attr;
/**
* This describes the XOR-REFLECTED-FROM attribute, as described by
diff --git a/pjnath/include/pjnath/stun_session.h b/pjnath/include/pjnath/stun_session.h
index e0d93d31..2f096d57 100644
--- a/pjnath/include/pjnath/stun_session.h
+++ b/pjnath/include/pjnath/stun_session.h
@@ -293,7 +293,9 @@ PJ_DECL(pj_status_t) pj_stun_session_create_ind(pj_stun_session *sess,
/**
* Create a STUN response message. After the message has been
* successfully created, application can send the message by calling
- * pj_stun_session_send_msg().
+ * pj_stun_session_send_msg(). Alternatively application may use
+ * pj_stun_session_respond() to create and send response in one function
+ * call.
*
* @param sess The STUN session instance.
* @param req The STUN request where the response is to be created.
@@ -315,7 +317,6 @@ PJ_DECL(pj_status_t) pj_stun_session_create_res(pj_stun_session *sess,
const pj_str_t *err_msg,
pj_stun_tx_data **p_tdata);
-
/**
* Send STUN message to the specified destination. This function will encode
* the pj_stun_msg instance to a packet buffer, and add credential or
@@ -342,6 +343,37 @@ PJ_DECL(pj_status_t) pj_stun_session_send_msg(pj_stun_session *sess,
pj_stun_tx_data *tdata);
/**
+ * Create and send STUN response message.
+ *
+ * @param sess The STUN session instance.
+ * @param req The STUN request message to be responded.
+ * @param err_code Error code to be set in the response, if error response
+ * is to be created, according to pj_stun_status enumeration.
+ * This argument MUST be zero if successful response is
+ * to be created.
+ * @param err_msg Optional pointer for the error message string, when
+ * creating error response. If the value is NULL and the
+ * \a err_code is non-zero, then default error message will
+ * be used.
+ * @param cache Specify whether session should cache this response for
+ * future request retransmission. If TRUE, subsequent request
+ * retransmission will be handled by the session and it
+ * will not call request callback.
+ * @param dst_addr Destination address of the response (or equal to the
+ * source address of the original request).
+ * @param addr_len Address length.
+ *
+ * @return PJ_SUCCESS on success, or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pj_stun_session_respond(pj_stun_session *sess,
+ const pj_stun_msg *req,
+ unsigned code,
+ const char *err_msg,
+ pj_bool_t cache,
+ const pj_sockaddr_t *dst_addr,
+ unsigned addr_len);
+
+/**
* Cancel outgoing STUN transaction. This operation is only valid for outgoing
* STUN request, to cease retransmission of the request and destroy the
* STUN client transaction that is used to send the request.
diff --git a/pjnath/src/pjnath/stun_msg.c b/pjnath/src/pjnath/stun_msg.c
index f0b48c90..3e01d4aa 100644
--- a/pjnath/src/pjnath/stun_msg.c
+++ b/pjnath/src/pjnath/stun_msg.c
@@ -77,6 +77,7 @@ static struct
{ PJ_STUN_SC_ROLE_CONFLICT, "Role Conflict"},
{ PJ_STUN_SC_SERVER_ERROR, "Server Error"},
{ PJ_STUN_SC_INSUFFICIENT_CAPACITY, "Insufficient Capacity"},
+ { PJ_STUN_SC_INSUFFICIENT_PORT_CAPACITY,"Insufficient Port Capacity"},
{ PJ_STUN_SC_GLOBAL_FAILURE, "Global Failure"}
};
@@ -289,8 +290,8 @@ static struct attr_desc mandatory_attr_desc[] =
&encode_uint_attr
},
{
- /* PJ_STUN_ATTR_REQUESTED_PORT_PROPS, */
- "REQUESTED-PORT-PROPS",
+ /* PJ_STUN_ATTR_REQUESTED_PROPS, */
+ "REQUESTED-PROPS",
&decode_uint_attr,
&encode_uint_attr
},
@@ -349,10 +350,10 @@ static struct attr_desc mandatory_attr_desc[] =
&encode_uint_attr
},
{
- /* PJ_STUN_ATTR_REQUESTED_IP, */
- "REQUESTED-IP",
- &decode_xored_sockaddr_attr,
- &encode_sockaddr_attr
+ /* PJ_STUN_ATTR_RESERVATION_TOKEN, */
+ "RESERVATION-TOKEN",
+ &decode_uint64_attr,
+ &encode_uint64_attr
},
{
/* PJ_STUN_ATTR_XOR_REFLECTED_FROM, */
diff --git a/pjnath/src/pjnath/stun_msg_dump.c b/pjnath/src/pjnath/stun_msg_dump.c
index ecf65504..a5556acc 100644
--- a/pjnath/src/pjnath/stun_msg_dump.c
+++ b/pjnath/src/pjnath/stun_msg_dump.c
@@ -74,7 +74,6 @@ static int print_attr(char *buffer, unsigned length,
case PJ_STUN_ATTR_PEER_ADDR:
case PJ_STUN_ATTR_RELAY_ADDR:
case PJ_STUN_ATTR_XOR_MAPPED_ADDR:
- case PJ_STUN_ATTR_REQ_IP:
case PJ_STUN_ATTR_XOR_REFLECTED_FROM:
case PJ_STUN_ATTR_XOR_INTERNAL_ADDR:
case PJ_STUN_ATTR_ALTERNATE_SERVER:
@@ -117,7 +116,7 @@ static int print_attr(char *buffer, unsigned length,
case PJ_STUN_ATTR_LIFETIME:
case PJ_STUN_ATTR_BANDWIDTH:
case PJ_STUN_ATTR_REQ_ADDR_TYPE:
- case PJ_STUN_ATTR_REQ_PORT_PROPS:
+ case PJ_STUN_ATTR_REQ_PROPS:
case PJ_STUN_ATTR_REQ_TRANSPORT:
case PJ_STUN_ATTR_TIMER_VAL:
case PJ_STUN_ATTR_PRIORITY:
@@ -207,6 +206,7 @@ static int print_attr(char *buffer, unsigned length,
break;
case PJ_STUN_ATTR_ICE_CONTROLLED:
case PJ_STUN_ATTR_ICE_CONTROLLING:
+ case PJ_STUN_ATTR_RESERVATION_TOKEN:
{
const pj_stun_uint64_attr *attr;
pj_uint8_t data[8];
diff --git a/pjnath/src/pjnath/stun_session.c b/pjnath/src/pjnath/stun_session.c
index 0f9fc733..4225fe77 100644
--- a/pjnath/src/pjnath/stun_session.c
+++ b/pjnath/src/pjnath/stun_session.c
@@ -711,6 +711,32 @@ PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess,
return status;
}
+
+/*
+ * Create and send STUN response message.
+ */
+PJ_DEF(pj_status_t) pj_stun_session_respond( pj_stun_session *sess,
+ const pj_stun_msg *req,
+ unsigned code,
+ const char *errmsg,
+ pj_bool_t cache,
+ const pj_sockaddr_t *dst_addr,
+ unsigned addr_len)
+{
+ pj_status_t status;
+ pj_str_t reason;
+ pj_stun_tx_data *tdata;
+
+ status = pj_stun_session_create_res(sess, req, code,
+ (errmsg?pj_cstr(&reason,errmsg):NULL),
+ &tdata);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ return pj_stun_session_send_msg(sess, cache, dst_addr, addr_len, tdata);
+}
+
+
/*
* Cancel outgoing STUN transaction.
*/
diff --git a/pjnath/src/pjturn-srv/allocation.c b/pjnath/src/pjturn-srv/allocation.c
index 725863ce..5698ea41 100644
--- a/pjnath/src/pjturn-srv/allocation.c
+++ b/pjnath/src/pjturn-srv/allocation.c
@@ -17,6 +17,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "turn.h"
+#include "auth.h"
+
#define THIS_FILE "allocation.c"
@@ -30,6 +32,15 @@ enum {
#define DESTROY_DELAY {0, 500}
#define PEER_TABLE_SIZE 32
+#define MAX_CLIENT_BANDWIDTH 128 /* In Kbps */
+#define DEFA_CLIENT_BANDWIDTH 64
+
+#define MIN_LIFETIME 30
+#define MAX_LIFETIME 600
+#define DEF_LIFETIME 300
+
+
+
/* ChannelData header */
typedef struct channel_data_hdr
{
@@ -38,13 +49,30 @@ typedef struct channel_data_hdr
} channel_data_hdr;
+/* Parsed Allocation request. */
+typedef struct alloc_request
+{
+ unsigned tp_type; /* Requested transport */
+ char addr[PJ_INET6_ADDRSTRLEN]; /* Requested IP */
+ unsigned bandwidth; /* Requested bandwidth */
+ unsigned lifetime; /* Lifetime. */
+ unsigned rpp_bits; /* A bits */
+ unsigned rpp_port; /* Requested port */
+} alloc_request;
+
+
+
/* Prototypes */
-static pj_status_t create_relay(pjturn_allocation *alloc,
- const pjturn_allocation_req *req);
+static void destroy_allocation(pj_turn_allocation *alloc);
+static pj_status_t create_relay(pj_turn_srv *srv,
+ pj_turn_allocation *alloc,
+ const pj_stun_msg *msg,
+ const alloc_request *req,
+ pj_turn_relay_res *relay);
+static void destroy_relay(pj_turn_relay_res *relay);
static void on_rx_from_peer(pj_ioqueue_key_t *key,
pj_ioqueue_op_key_t *op_key,
pj_ssize_t bytes_read);
-static void destroy_relay(pjturn_relay_res *relay);
static pj_status_t stun_on_send_msg(pj_stun_session *sess,
const void *pkt,
pj_size_t pkt_size,
@@ -64,7 +92,7 @@ static pj_status_t stun_on_rx_indication(pj_stun_session *sess,
unsigned src_addr_len);
/* Log allocation error */
-static void alloc_err(pjturn_allocation *alloc, const char *title,
+static void alloc_err(pj_turn_allocation *alloc, const char *title,
pj_status_t status)
{
char errmsg[PJ_ERR_MSG_SIZE];
@@ -74,34 +102,225 @@ static void alloc_err(pjturn_allocation *alloc, const char *title,
title, alloc->info, errmsg));
}
+
+/* Parse ALLOCATE request */
+static pj_status_t parse_allocate_req(alloc_request *cfg,
+ pj_stun_session *sess,
+ const pj_stun_msg *req,
+ const pj_sockaddr_t *src_addr,
+ unsigned src_addr_len)
+{
+ pj_stun_bandwidth_attr *attr_bw;
+ pj_stun_req_transport_attr *attr_req_tp;
+ pj_stun_res_token_attr *attr_res_token;
+ pj_stun_req_props_attr *attr_rpp;
+ pj_stun_lifetime_attr *attr_lifetime;
+
+ pj_bzero(cfg, sizeof(*cfg));
+
+ /* Get BANDWIDTH attribute, if any. */
+ attr_bw = (pj_stun_uint_attr*)
+ pj_stun_msg_find_attr(req, PJ_STUN_ATTR_BANDWIDTH, 0);
+ if (attr_bw) {
+ cfg->bandwidth = attr_bw->value;
+ } else {
+ cfg->bandwidth = DEFA_CLIENT_BANDWIDTH;
+ }
+
+ /* Check if we can satisfy the bandwidth */
+ if (cfg->bandwidth > MAX_CLIENT_BANDWIDTH) {
+ pj_stun_session_respond(sess, req, PJ_STUN_SC_ALLOCATION_QUOTA_REACHED,
+ "Invalid bandwidth", PJ_TRUE,
+ src_addr, src_addr_len);
+ return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_ALLOCATION_QUOTA_REACHED);
+ }
+
+ /* MUST have REQUESTED-TRANSPORT attribute */
+ attr_req_tp = (pj_stun_uint_attr*)
+ pj_stun_msg_find_attr(req, PJ_STUN_ATTR_REQ_TRANSPORT, 0);
+ if (attr_req_tp == NULL) {
+ pj_stun_session_respond(sess, req, PJ_STUN_SC_BAD_REQUEST,
+ "Missing REQUESTED-TRANSPORT attribute",
+ PJ_TRUE, src_addr, src_addr_len);
+ return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_BAD_REQUEST);
+ }
+
+ cfg->tp_type = PJ_STUN_GET_RT_PROTO(attr_req_tp->value);
+
+ /* Can only support UDP for now */
+ if (cfg->tp_type != PJ_TURN_TP_UDP) {
+ pj_stun_session_respond(sess, req, PJ_STUN_SC_UNSUPP_TRANSPORT_PROTO,
+ NULL, PJ_TRUE, src_addr, src_addr_len);
+ return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNSUPP_TRANSPORT_PROTO);
+ }
+
+ /* Get RESERVATION-TOKEN attribute, if any */
+ attr_res_token = (pj_stun_res_token_attr*)
+ pj_stun_msg_find_attr(req, PJ_STUN_ATTR_RESERVATION_TOKEN,
+ 0);
+ if (attr_res_token) {
+ /* We don't support RESERVATION-TOKEN for now */
+ pj_stun_session_respond(sess, req,
+ PJ_STUN_SC_BAD_REQUEST,
+ "RESERVATION-TOKEN is not supported", PJ_TRUE,
+ src_addr, src_addr_len);
+ return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_BAD_REQUEST);
+ }
+
+ /* Get REQUESTED-PROPS attribute, if any */
+ attr_rpp = (pj_stun_req_props_attr*)
+ pj_stun_msg_find_attr(req, PJ_STUN_ATTR_REQ_PROPS, 0);
+ if (attr_rpp) {
+ /* We don't support REQUESTED-PROPS for now */
+ pj_stun_session_respond(sess, req,
+ PJ_STUN_SC_BAD_REQUEST,
+ "REQUESTED-PROPS is not supported", PJ_TRUE,
+ src_addr, src_addr_len);
+ return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_BAD_REQUEST);
+ }
+
+ /* Get LIFETIME attribute */
+ attr_lifetime = (pj_stun_uint_attr*)
+ pj_stun_msg_find_attr(req, PJ_STUN_ATTR_LIFETIME, 0);
+ if (attr_lifetime) {
+ cfg->lifetime = attr_lifetime->value;
+ if (cfg->lifetime < MIN_LIFETIME) {
+ pj_stun_session_respond(sess, req, PJ_STUN_SC_BAD_REQUEST,
+ "LIFETIME too short", PJ_TRUE,
+ src_addr, src_addr_len);
+ return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_BAD_REQUEST);
+ }
+ if (cfg->lifetime > MAX_LIFETIME)
+ cfg->lifetime = MAX_LIFETIME;
+ } else {
+ cfg->lifetime = DEF_LIFETIME;
+ }
+
+ return PJ_SUCCESS;
+}
+
+
+/* Respond to ALLOCATE request */
+static pj_status_t send_allocate_response(pj_turn_allocation *alloc,
+ pj_stun_session *srv_sess,
+ const pj_stun_msg *msg)
+{
+ pj_stun_tx_data *tdata;
+ pj_status_t status;
+
+ /* Respond the original ALLOCATE request */
+ status = pj_stun_session_create_res(srv_sess, msg, 0, NULL, &tdata);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Add RELAYED-ADDRESS attribute */
+ pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg,
+ PJ_STUN_ATTR_RELAY_ADDR, PJ_TRUE,
+ &alloc->relay.hkey.addr,
+ pj_sockaddr_get_len(&alloc->relay.hkey.addr));
+
+ /* Add LIFETIME. */
+ pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg,
+ PJ_STUN_ATTR_LIFETIME,
+ (unsigned)alloc->relay.lifetime);
+
+ /* Add BANDWIDTH */
+ pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg,
+ PJ_STUN_ATTR_BANDWIDTH,
+ alloc->bandwidth);
+
+ /* Add RESERVATION-TOKEN */
+ PJ_TODO(ADD_RESERVATION_TOKEN);
+
+ /* Add XOR-MAPPED-ADDRESS */
+ pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg,
+ PJ_STUN_ATTR_XOR_MAPPED_ADDR, PJ_TRUE,
+ &alloc->hkey.clt_addr,
+ pj_sockaddr_get_len(&alloc->hkey.clt_addr));
+
+ /* Send the response */
+ return pj_stun_session_send_msg(srv_sess, PJ_TRUE,
+ &alloc->hkey.clt_addr,
+ pj_sockaddr_get_len(&alloc->hkey.clt_addr),
+ tdata);
+}
+
+
+/*
+ * Init credential for the allocation. We use static credential, meaning that
+ * the user's password must not change during allocation.
+ */
+static pj_status_t init_cred(pj_turn_allocation *alloc, const pj_stun_msg *req)
+{
+ const pj_stun_username_attr *user;
+ const pj_stun_realm_attr *realm;
+ const pj_stun_nonce_attr *nonce;
+ pj_status_t status;
+
+ realm = (const pj_stun_realm_attr*)
+ pj_stun_msg_find_attr(req, PJ_STUN_ATTR_REALM, 0);
+ PJ_ASSERT_RETURN(realm != NULL, PJ_EBUG);
+
+ user = (const pj_stun_username_attr*)
+ pj_stun_msg_find_attr(req, PJ_STUN_ATTR_USERNAME, 0);
+ PJ_ASSERT_RETURN(user != NULL, PJ_EBUG);
+
+ nonce = (const pj_stun_nonce_attr*)
+ pj_stun_msg_find_attr(req, PJ_STUN_ATTR_NONCE, 0);
+ PJ_ASSERT_RETURN(nonce != NULL, PJ_EBUG);
+
+ /* Lookup the password */
+ status = pj_turn_get_password(NULL, NULL, &realm->value,
+ &user->value, alloc->pool,
+ &alloc->cred.data.static_cred.data_type,
+ &alloc->cred.data.static_cred.data);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Save credential */
+ alloc->cred.type = PJ_STUN_AUTH_CRED_STATIC;
+ pj_strdup(alloc->pool, &alloc->cred.data.static_cred.realm, &realm->value);
+ pj_strdup(alloc->pool, &alloc->cred.data.static_cred.username, &user->value);
+ pj_strdup(alloc->pool, &alloc->cred.data.static_cred.nonce, &nonce->value);
+
+ return PJ_SUCCESS;
+}
+
+
/*
* Create new allocation.
*/
-PJ_DEF(pj_status_t) pjturn_allocation_create(pjturn_listener *listener,
- const pj_sockaddr_t *src_addr,
- unsigned src_addr_len,
- const pj_stun_msg *msg,
- const pjturn_allocation_req *req,
- pjturn_allocation **p_alloc)
+PJ_DEF(pj_status_t) pj_turn_allocation_create(pj_turn_listener *listener,
+ const pj_sockaddr_t *src_addr,
+ unsigned src_addr_len,
+ const pj_stun_msg *msg,
+ pj_stun_session *srv_sess,
+ pj_turn_allocation **p_alloc)
{
- pjturn_srv *srv = listener->server;
+ pj_turn_srv *srv = listener->server;
pj_pool_t *pool;
- pjturn_allocation *alloc;
+ alloc_request req;
+ pj_turn_allocation *alloc;
pj_stun_session_cb sess_cb;
- char relay_info[80];
+ char str_tmp[80];
pj_status_t status;
+ /* Parse ALLOCATE request */
+ status = parse_allocate_req(&req, srv_sess, msg, src_addr, src_addr_len);
+ if (status != PJ_SUCCESS)
+ return status;
+
pool = pj_pool_create(srv->core.pf, "alloc%p", 1000, 1000, NULL);
/* Init allocation structure */
- alloc = PJ_POOL_ZALLOC_T(pool, pjturn_allocation);
+ alloc = PJ_POOL_ZALLOC_T(pool, pj_turn_allocation);
alloc->pool = pool;
alloc->obj_name = pool->obj_name;
alloc->listener = listener;
alloc->clt_sock = PJ_INVALID_SOCKET;
alloc->relay.tp.sock = PJ_INVALID_SOCKET;
- alloc->bandwidth = req->bandwidth;
+ alloc->bandwidth = req.bandwidth;
alloc->hkey.tp_type = listener->tp_type;
pj_memcpy(&alloc->hkey.clt_addr, src_addr, src_addr_len);
@@ -109,8 +328,7 @@ PJ_DEF(pj_status_t) pjturn_allocation_create(pjturn_listener *listener,
status = pj_lock_create_recursive_mutex(pool, alloc->obj_name,
&alloc->lock);
if (status != PJ_SUCCESS) {
- pjturn_allocation_destroy(alloc);
- return status;
+ goto on_error;
}
/* Create peer hash table */
@@ -120,7 +338,7 @@ PJ_DEF(pj_status_t) pjturn_allocation_create(pjturn_listener *listener,
alloc->ch_table = pj_hash_create(pool, PEER_TABLE_SIZE);
/* Print info */
- pj_ansi_strcpy(alloc->info, pjturn_tp_type_name(listener->tp_type));
+ pj_ansi_strcpy(alloc->info, pj_turn_tp_type_name(listener->tp_type));
alloc->info[3] = ':';
pj_sockaddr_print(src_addr, alloc->info+4, sizeof(alloc->info)-4, 3);
@@ -132,44 +350,89 @@ PJ_DEF(pj_status_t) pjturn_allocation_create(pjturn_listener *listener,
status = pj_stun_session_create(&srv->core.stun_cfg, alloc->obj_name,
&sess_cb, PJ_FALSE, &alloc->sess);
if (status != PJ_SUCCESS) {
- pjturn_allocation_destroy(alloc);
- return status;
+ goto on_error;
}
/* Attach to STUN session */
pj_stun_session_set_user_data(alloc->sess, alloc);
+ /* Init authentication credential */
+ status = init_cred(alloc, msg);
+ if (status != PJ_SUCCESS) {
+ goto on_error;
+ }
+
+ /* Attach authentication credential to STUN session */
+ pj_stun_session_set_credential(alloc->sess, &alloc->cred);
+
/* Create the relay resource */
- status = pjturn_allocation_create_relay(srv, alloc, msg, req,
- &alloc->relay);
+ status = create_relay(srv, alloc, msg, &req, &alloc->relay);
if (status != PJ_SUCCESS) {
- pjturn_allocation_destroy(alloc);
- return status;
+ goto on_error;
}
/* Register this allocation */
- pjturn_srv_register_allocation(srv, alloc);
+ pj_turn_srv_register_allocation(srv, alloc);
- pj_sockaddr_print(&alloc->relay.hkey.addr, relay_info,
- sizeof(relay_info), 3);
+ /* Respond to ALLOCATE request */
+ status = send_allocate_response(alloc, srv_sess, msg);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ /* Done */
+ pj_sockaddr_print(&alloc->relay.hkey.addr, str_tmp,
+ sizeof(str_tmp), 3);
PJ_LOG(4,(alloc->obj_name, "Client %s created, relay addr=%s:%s",
- alloc->info, pjturn_tp_type_name(req->tp_type), relay_info));
+ alloc->info, pj_turn_tp_type_name(req.tp_type), str_tmp));
/* Success */
*p_alloc = alloc;
return PJ_SUCCESS;
+
+on_error:
+ /* Send reply to the ALLOCATE request */
+ pj_strerror(status, str_tmp, sizeof(str_tmp));
+ pj_stun_session_respond(srv_sess, msg, PJ_STUN_SC_BAD_REQUEST, str_tmp,
+ PJ_TRUE, src_addr, src_addr_len);
+
+ /* Cleanup */
+ destroy_allocation(alloc);
+ return status;
+}
+
+
+/* Destroy relay resource */
+static void destroy_relay(pj_turn_relay_res *relay)
+{
+ if (relay->timer.id) {
+ pj_timer_heap_cancel(relay->allocation->listener->server->core.timer_heap,
+ &relay->timer);
+ relay->timer.id = PJ_FALSE;
+ }
+
+ if (relay->tp.key) {
+ pj_ioqueue_unregister(relay->tp.key);
+ relay->tp.key = NULL;
+ relay->tp.sock = PJ_INVALID_SOCKET;
+ } else if (relay->tp.sock != PJ_INVALID_SOCKET) {
+ pj_sock_close(relay->tp.sock);
+ relay->tp.sock = PJ_INVALID_SOCKET;
+ }
+
+ /* Mark as shutdown */
+ relay->lifetime = 0;
}
/*
- * Destroy allocation.
+ * Really destroy allocation.
*/
-PJ_DECL(void) pjturn_allocation_destroy(pjturn_allocation *alloc)
+static void destroy_allocation(pj_turn_allocation *alloc)
{
pj_pool_t *pool;
/* Unregister this allocation */
- pjturn_srv_unregister_allocation(alloc->listener->server, alloc);
+ pj_turn_srv_unregister_allocation(alloc->listener->server, alloc);
/* Destroy relay */
destroy_relay(&alloc->relay);
@@ -201,36 +464,23 @@ PJ_DECL(void) pjturn_allocation_destroy(pjturn_allocation *alloc)
}
-/* Destroy relay resource */
-static void destroy_relay(pjturn_relay_res *relay)
+PJ_DECL(void) pj_turn_allocation_destroy(pj_turn_allocation *alloc)
{
- if (relay->timer.id) {
- pj_timer_heap_cancel(relay->allocation->listener->server->core.timer_heap,
- &relay->timer);
- relay->timer.id = PJ_FALSE;
- }
-
- if (relay->tp.key) {
- pj_ioqueue_unregister(relay->tp.key);
- relay->tp.key = NULL;
- relay->tp.sock = PJ_INVALID_SOCKET;
- } else if (relay->tp.sock != PJ_INVALID_SOCKET) {
- pj_sock_close(relay->tp.sock);
- relay->tp.sock = PJ_INVALID_SOCKET;
- }
-
- /* Mark as shutdown */
- relay->lifetime = 0;
+ destroy_allocation(alloc);
}
-/* Initiate shutdown sequence for this allocation */
-static void alloc_shutdown(pjturn_allocation *alloc)
+
+/* Initiate shutdown sequence for this allocation and start destroy timer.
+ * Once allocation is marked as shutting down, any packets will be
+ * rejected/discarded
+ */
+static void alloc_shutdown(pj_turn_allocation *alloc)
{
pj_time_val destroy_delay = DESTROY_DELAY;
/* Work with existing schedule */
if (alloc->relay.timer.id == TIMER_ID_TIMEOUT) {
- /* Cancel existing timer */
+ /* Cancel existing shutdown timer */
pj_timer_heap_cancel(alloc->listener->server->core.timer_heap,
&alloc->relay.timer);
alloc->relay.timer.id = TIMER_ID_NONE;
@@ -257,8 +507,9 @@ static void alloc_shutdown(pjturn_allocation *alloc)
&alloc->relay.timer, &destroy_delay);
}
+
/* Reschedule timeout using current lifetime setting */
-static pj_status_t resched_timeout(pjturn_allocation *alloc)
+static pj_status_t resched_timeout(pj_turn_allocation *alloc)
{
pj_time_val delay;
pj_status_t status;
@@ -291,10 +542,12 @@ static pj_status_t resched_timeout(pjturn_allocation *alloc)
/* Timer timeout callback */
static void relay_timeout_cb(pj_timer_heap_t *heap, pj_timer_entry *e)
{
- pjturn_relay_res *rel;
- pjturn_allocation *alloc;
+ pj_turn_relay_res *rel;
+ pj_turn_allocation *alloc;
- rel = (pjturn_relay_res*) e->user_data;
+ PJ_UNUSED_ARG(heap);
+
+ rel = (pj_turn_relay_res*) e->user_data;
alloc = rel->allocation;
if (e->id == TIMER_ID_TIMEOUT) {
@@ -313,7 +566,7 @@ static void relay_timeout_cb(pj_timer_heap_t *heap, pj_timer_entry *e)
PJ_LOG(4,(alloc->obj_name, "Client %s destroying..",
alloc->info));
- pjturn_allocation_destroy(alloc);
+ destroy_allocation(alloc);
}
}
@@ -321,11 +574,11 @@ static void relay_timeout_cb(pj_timer_heap_t *heap, pj_timer_entry *e)
/*
* Create relay.
*/
-PJ_DEF(pj_status_t) pjturn_allocation_create_relay(pjturn_srv *srv,
- pjturn_allocation *alloc,
- const pj_stun_msg *msg,
- const pjturn_allocation_req *req,
- pjturn_relay_res *relay)
+static pj_status_t create_relay(pj_turn_srv *srv,
+ pj_turn_allocation *alloc,
+ const pj_stun_msg *msg,
+ const alloc_request *req,
+ pj_turn_relay_res *relay)
{
enum { RETRY = 40 };
pj_pool_t *pool = alloc->pool;
@@ -365,9 +618,9 @@ PJ_DEF(pj_status_t) pjturn_allocation_create_relay(pjturn_srv *srv,
relay->hkey.tp_type = req->tp_type;
/* Create the socket */
- if (req->tp_type == PJTURN_TP_UDP) {
+ if (req->tp_type == PJ_TURN_TP_UDP) {
sock_type = pj_SOCK_DGRAM();
- } else if (req->tp_type == PJTURN_TP_TCP) {
+ } else if (req->tp_type == PJ_TURN_TP_TCP) {
sock_type = pj_SOCK_STREAM();
} else {
pj_assert(!"Unknown transport");
@@ -395,16 +648,17 @@ PJ_DEF(pj_status_t) pjturn_allocation_create_relay(pjturn_srv *srv,
if (req->rpp_port) {
port = (pj_uint16_t) req->rpp_port;
- } else if (req->tp_type == PJTURN_TP_UDP) {
+ } else if (req->tp_type == PJ_TURN_TP_UDP) {
port = (pj_uint16_t) srv->ports.next_udp++;
if (srv->ports.next_udp > srv->ports.max_udp)
srv->ports.next_udp = srv->ports.min_udp;
- } else if (req->tp_type == PJTURN_TP_TCP) {
+ } else if (req->tp_type == PJ_TURN_TP_TCP) {
port = (pj_uint16_t) srv->ports.next_tcp++;
if (srv->ports.next_tcp > srv->ports.max_tcp)
srv->ports.next_tcp = srv->ports.min_tcp;
} else {
pj_assert(!"Invalid transport");
+ port = 0;
}
pj_lock_release(srv->core.lock);
@@ -463,27 +717,16 @@ PJ_DEF(pj_status_t) pjturn_allocation_create_relay(pjturn_srv *srv,
}
/* Create and send error response */
-static void send_reply_err(pjturn_allocation *alloc,
+static void send_reply_err(pj_turn_allocation *alloc,
const pj_stun_msg *req,
pj_bool_t cache,
int code, const char *errmsg)
{
pj_status_t status;
- pj_str_t reason;
- pj_stun_tx_data *tdata;
- status = pj_stun_session_create_res(alloc->sess, req,
- code, (errmsg?pj_cstr(&reason,errmsg):NULL),
- &tdata);
- if (status != PJ_SUCCESS) {
- alloc_err(alloc, "Error creating STUN error response", status);
- return;
- }
-
- status = pj_stun_session_send_msg(alloc->sess, cache,
- &alloc->hkey.clt_addr,
- pj_sockaddr_get_len(&alloc->hkey.clt_addr),
- tdata);
+ status = pj_stun_session_respond(alloc->sess, req, code, errmsg, cache,
+ &alloc->hkey.clt_addr,
+ pj_sockaddr_get_len(&alloc->hkey.clt_addr.addr));
if (status != PJ_SUCCESS) {
alloc_err(alloc, "Error sending STUN error response", status);
return;
@@ -491,7 +734,7 @@ static void send_reply_err(pjturn_allocation *alloc,
}
/* Create and send successful response */
-static void send_reply_ok(pjturn_allocation *alloc,
+static void send_reply_ok(pj_turn_allocation *alloc,
const pj_stun_msg *req)
{
pj_status_t status;
@@ -534,16 +777,16 @@ static void send_reply_ok(pjturn_allocation *alloc,
/* Create new permission */
-static pjturn_permission *create_permission(pjturn_allocation *alloc,
+static pj_turn_permission *create_permission(pj_turn_allocation *alloc,
const pj_sockaddr_t *peer_addr,
unsigned addr_len)
{
- pjturn_permission *perm;
+ pj_turn_permission *perm;
- perm = PJ_POOL_ZALLOC_T(alloc->pool, pjturn_permission);
+ perm = PJ_POOL_ZALLOC_T(alloc->pool, pj_turn_permission);
pj_memcpy(&perm->hkey.peer_addr, peer_addr, addr_len);
- if (alloc->listener->tp_type == PJTURN_TP_UDP) {
+ if (alloc->listener->tp_type == PJ_TURN_TP_UDP) {
perm->sock = alloc->listener->sock;
} else {
pj_assert(!"TCP is not supported yet");
@@ -551,18 +794,18 @@ static pjturn_permission *create_permission(pjturn_allocation *alloc,
}
perm->allocation = alloc;
- perm->channel = PJTURN_INVALID_CHANNEL;
+ perm->channel = PJ_TURN_INVALID_CHANNEL;
pj_gettimeofday(&perm->expiry);
- perm->expiry.sec += PJTURN_PERM_TIMEOUT;
+ perm->expiry.sec += PJ_TURN_PERM_TIMEOUT;
return perm;
}
/* Check if a permission isn't expired. Return NULL if expired. */
-static pjturn_permission *check_permission_expiry(pjturn_permission *perm)
+static pj_turn_permission *check_permission_expiry(pj_turn_permission *perm)
{
- pjturn_allocation *alloc = perm->allocation;
+ pj_turn_allocation *alloc = perm->allocation;
pj_time_val now;
pj_gettimeofday(&now);
@@ -576,7 +819,7 @@ static pjturn_permission *check_permission_expiry(pjturn_permission *perm)
0, NULL);
/* Remove from channel hash table, if assigned a channel number */
- if (perm->channel != PJTURN_INVALID_CHANNEL) {
+ if (perm->channel != PJ_TURN_INVALID_CHANNEL) {
pj_hash_set(NULL, alloc->ch_table, &perm->channel,
sizeof(perm->channel), 0, NULL);
}
@@ -585,33 +828,33 @@ static pjturn_permission *check_permission_expiry(pjturn_permission *perm)
}
/* Lookup permission in hash table by the peer address */
-static pjturn_permission*
-lookup_permission_by_addr(pjturn_allocation *alloc,
+static pj_turn_permission*
+lookup_permission_by_addr(pj_turn_allocation *alloc,
const pj_sockaddr_t *peer_addr,
unsigned addr_len)
{
- pjturn_permission_key key;
- pjturn_permission *perm;
+ pj_turn_permission_key key;
+ pj_turn_permission *perm;
pj_bzero(&key, sizeof(key));
pj_memcpy(&key, peer_addr, addr_len);
/* Lookup in peer hash table */
- perm = (pjturn_permission*) pj_hash_get(alloc->peer_table, &key,
+ perm = (pj_turn_permission*) pj_hash_get(alloc->peer_table, &key,
sizeof(key), NULL);
return check_permission_expiry(perm);
}
/* Lookup permission in hash table by the channel number */
-static pjturn_permission*
-lookup_permission_by_chnum(pjturn_allocation *alloc,
+static pj_turn_permission*
+lookup_permission_by_chnum(pj_turn_allocation *alloc,
unsigned chnum)
{
pj_uint16_t chnum16 = (pj_uint16_t)chnum;
- pjturn_permission *perm;
+ pj_turn_permission *perm;
/* Lookup in peer hash table */
- perm = (pjturn_permission*) pj_hash_get(alloc->peer_table, &chnum16,
+ perm = (pj_turn_permission*) pj_hash_get(alloc->peer_table, &chnum16,
sizeof(chnum16), NULL);
return check_permission_expiry(perm);
}
@@ -619,25 +862,29 @@ lookup_permission_by_chnum(pjturn_allocation *alloc,
/* Update permission because of data from client to peer.
* Return PJ_TRUE is permission is found.
*/
-static pj_bool_t refresh_permission(pjturn_permission *perm)
+static pj_bool_t refresh_permission(pj_turn_permission *perm)
{
pj_gettimeofday(&perm->expiry);
- if (perm->channel == PJTURN_INVALID_CHANNEL)
- perm->expiry.sec += PJTURN_PERM_TIMEOUT;
+ if (perm->channel == PJ_TURN_INVALID_CHANNEL)
+ perm->expiry.sec += PJ_TURN_PERM_TIMEOUT;
else
- perm->expiry.sec += PJTURN_CHANNEL_TIMEOUT;
+ perm->expiry.sec += PJ_TURN_CHANNEL_TIMEOUT;
return PJ_TRUE;
}
/*
- * Handle incoming packet from client.
+ * Handle incoming packet from client. This would have been called by
+ * server upon receiving packet from a listener.
*/
-PJ_DEF(void) pjturn_allocation_on_rx_client_pkt( pjturn_allocation *alloc,
- pjturn_pkt *pkt)
+PJ_DEF(void) pj_turn_allocation_on_rx_client_pkt(pj_turn_allocation *alloc,
+ pj_turn_pkt *pkt)
{
pj_bool_t is_stun;
pj_status_t status;
+ /* Lock this allocation */
+ pj_lock_acquire(alloc->lock);
+
/* Quickly check if this is STUN message */
is_stun = ((*((pj_uint8_t*)pkt->pkt) & 0xC0) == 0);
@@ -649,7 +896,7 @@ PJ_DEF(void) pjturn_allocation_on_rx_client_pkt( pjturn_allocation *alloc,
* callbacks.
*/
unsigned options = PJ_STUN_CHECK_PACKET;
- if (pkt->listener->tp_type == PJTURN_TP_UDP)
+ if (pkt->listener->tp_type == PJ_TURN_TP_UDP)
options |= PJ_STUN_IS_DATAGRAM;
status = pj_stun_session_on_rx_pkt(alloc->sess, pkt->pkt, pkt->len,
@@ -658,7 +905,7 @@ PJ_DEF(void) pjturn_allocation_on_rx_client_pkt( pjturn_allocation *alloc,
pkt->src_addr_len);
if (status != PJ_SUCCESS) {
alloc_err(alloc, "Error handling STUN packet", status);
- return;
+ goto on_return;
}
} else {
@@ -666,20 +913,20 @@ PJ_DEF(void) pjturn_allocation_on_rx_client_pkt( pjturn_allocation *alloc,
* This is not a STUN packet, must be ChannelData packet.
*/
channel_data_hdr *cd = (channel_data_hdr*)pkt->pkt;
- pjturn_permission *perm;
+ pj_turn_permission *perm;
pj_ssize_t len;
/* For UDP check the packet length */
- if (alloc->listener->tp_type == PJTURN_TP_UDP) {
+ if (alloc->listener->tp_type == PJ_TURN_TP_UDP) {
if (pkt->len < pj_ntohs(cd->length)+sizeof(*cd)) {
PJ_LOG(4,(alloc->obj_name,
"ChannelData from %s discarded: UDP size error",
alloc->info));
- return;
+ goto on_return;
}
} else {
pj_assert(!"Unsupported transport");
- return;
+ goto on_return;
}
perm = lookup_permission_by_chnum(alloc, pj_ntohs(cd->ch_number));
@@ -688,7 +935,7 @@ PJ_DEF(void) pjturn_allocation_on_rx_client_pkt( pjturn_allocation *alloc,
PJ_LOG(4,(alloc->obj_name,
"ChannelData from %s discarded: not found",
alloc->info));
- return;
+ goto on_return;
}
/* Relay the data */
@@ -700,18 +947,23 @@ PJ_DEF(void) pjturn_allocation_on_rx_client_pkt( pjturn_allocation *alloc,
/* Refresh permission */
refresh_permission(perm);
}
+
+on_return:
+ /* Release lock */
+ pj_lock_release(alloc->lock);
}
+
/*
* Handle incoming packet from peer. This function is called by
* on_rx_from_peer().
*/
-static void on_rx_peer_pkt(pjturn_allocation *alloc,
- pjturn_relay_res *rel,
- char *pkt, pj_size_t len,
- const pj_sockaddr *src_addr)
+static void handle_peer_pkt(pj_turn_allocation *alloc,
+ pj_turn_relay_res *rel,
+ char *pkt, pj_size_t len,
+ const pj_sockaddr *src_addr)
{
- pjturn_permission *perm;
+ pj_turn_permission *perm;
/* Lookup permission */
perm = lookup_permission_by_addr(alloc, src_addr,
@@ -724,14 +976,14 @@ static void on_rx_peer_pkt(pjturn_allocation *alloc,
/* Send Data Indication or ChannelData, depends on whether
* this permission is attached to a channel number.
*/
- if (perm->channel != PJTURN_INVALID_CHANNEL) {
+ if (perm->channel != PJ_TURN_INVALID_CHANNEL) {
/* Send ChannelData */
channel_data_hdr *cd = (channel_data_hdr*)rel->tp.tx_pkt;
- if (len > PJTURN_MAX_PKT_LEN) {
+ if (len > PJ_TURN_MAX_PKT_LEN) {
char peer_addr[80];
pj_sockaddr_print(src_addr, peer_addr, sizeof(peer_addr), 3);
- PJ_LOG(1,(alloc->obj_name, "Client %s: discarded data from %s "
+ PJ_LOG(4,(alloc->obj_name, "Client %s: discarded data from %s "
"because it's too long (%d bytes)",
alloc->info, peer_addr, len));
return;
@@ -745,7 +997,7 @@ static void on_rx_peer_pkt(pjturn_allocation *alloc,
pj_memcpy(rel->tp.rx_pkt+sizeof(channel_data_hdr), pkt, len);
/* Send to client */
- pjturn_listener_sendto(alloc->listener, rel->tp.tx_pkt,
+ pj_turn_listener_sendto(alloc->listener, rel->tp.tx_pkt,
len+sizeof(channel_data_hdr), 0,
&alloc->hkey.clt_addr,
pj_sockaddr_get_len(&alloc->hkey.clt_addr));
@@ -770,15 +1022,18 @@ static void on_rx_from_peer(pj_ioqueue_key_t *key,
pj_ioqueue_op_key_t *op_key,
pj_ssize_t bytes_read)
{
- pjturn_relay_res *rel;
+ pj_turn_relay_res *rel;
pj_status_t status;
- rel = (pjturn_relay_res*) pj_ioqueue_get_user_data(key);
+ rel = (pj_turn_relay_res*) pj_ioqueue_get_user_data(key);
+
+ /* Lock the allocation */
+ pj_lock_acquire(rel->allocation->lock);
do {
if (bytes_read > 0) {
- on_rx_peer_pkt(rel->allocation, rel, rel->tp.rx_pkt,
- bytes_read, &rel->tp.src_addr);
+ handle_peer_pkt(rel->allocation, rel, rel->tp.rx_pkt,
+ bytes_read, &rel->tp.src_addr);
}
/* Read next packet */
@@ -794,6 +1049,8 @@ static void on_rx_from_peer(pj_ioqueue_key_t *key,
} while (status != PJ_EPENDING && status != PJ_ECANCELLED);
+ /* Release allocation lock */
+ pj_lock_release(rel->allocation->lock);
}
/*
@@ -806,18 +1063,18 @@ static pj_status_t stun_on_send_msg(pj_stun_session *sess,
const pj_sockaddr_t *dst_addr,
unsigned addr_len)
{
- pjturn_allocation *alloc;
+ pj_turn_allocation *alloc;
- alloc = (pjturn_allocation*) pj_stun_session_get_user_data(sess);
+ alloc = (pj_turn_allocation*) pj_stun_session_get_user_data(sess);
- return pjturn_listener_sendto(alloc->listener, pkt, pkt_size, 0,
+ return pj_turn_listener_sendto(alloc->listener, pkt, pkt_size, 0,
dst_addr, addr_len);
}
/*
* Callback notification from STUN session when it receives STUN
* requests. This callback was trigger by STUN incoming message
- * processing in pjturn_allocation_on_rx_client_pkt().
+ * processing in pj_turn_allocation_on_rx_client_pkt().
*/
static pj_status_t stun_on_rx_request(pj_stun_session *sess,
const pj_uint8_t *pkt,
@@ -826,12 +1083,18 @@ static pj_status_t stun_on_rx_request(pj_stun_session *sess,
const pj_sockaddr_t *src_addr,
unsigned src_addr_len)
{
- pjturn_allocation *alloc;
+ pj_turn_allocation *alloc;
- alloc = (pjturn_allocation*) pj_stun_session_get_user_data(sess);
+ PJ_UNUSED_ARG(pkt);
+ PJ_UNUSED_ARG(pkt_len);
+ PJ_UNUSED_ARG(src_addr);
+ PJ_UNUSED_ARG(src_addr_len);
+
+ alloc = (pj_turn_allocation*) pj_stun_session_get_user_data(sess);
/* Refuse to serve any request if we've been shutdown */
if (alloc->relay.lifetime == 0) {
+ /* Reject with 437 if we're shutting down */
send_reply_err(alloc, msg, PJ_TRUE,
PJ_STUN_SC_ALLOCATION_MISMATCH, NULL);
return PJ_SUCCESS;
@@ -894,7 +1157,7 @@ static pj_status_t stun_on_rx_request(pj_stun_session *sess,
*/
pj_stun_channel_number_attr *ch_attr;
pj_stun_peer_addr_attr *peer_attr;
- pjturn_permission *p1, *p2;
+ pj_turn_permission *p1, *p2;
ch_attr = (pj_stun_channel_number_attr*)
pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_CHANNEL_NUMBER, 0);
@@ -933,7 +1196,7 @@ static pj_status_t stun_on_rx_request(pj_stun_session *sess,
*/
p2 = lookup_permission_by_addr(alloc, &peer_attr->sockaddr,
pj_sockaddr_get_len(&peer_attr->sockaddr));
- if (p2 && p2->channel != PJTURN_INVALID_CHANNEL) {
+ if (p2 && p2->channel != PJ_TURN_INVALID_CHANNEL) {
send_reply_err(alloc, msg, PJ_TRUE, PJ_STUN_SC_BAD_REQUEST,
"Peer address already assigned a channel number");
return PJ_SUCCESS;
@@ -976,7 +1239,7 @@ static pj_status_t stun_on_rx_request(pj_stun_session *sess,
/*
* Callback notification from STUN session when it receives STUN
* indications. This callback was trigger by STUN incoming message
- * processing in pjturn_allocation_on_rx_client_pkt().
+ * processing in pj_turn_allocation_on_rx_client_pkt().
*/
static pj_status_t stun_on_rx_indication(pj_stun_session *sess,
const pj_uint8_t *pkt,
@@ -987,10 +1250,15 @@ static pj_status_t stun_on_rx_indication(pj_stun_session *sess,
{
pj_stun_peer_addr_attr *peer_attr;
pj_stun_data_attr *data_attr;
- pjturn_allocation *alloc;
- pjturn_permission *perm;
+ pj_turn_allocation *alloc;
+ pj_turn_permission *perm;
+
+ PJ_UNUSED_ARG(pkt);
+ PJ_UNUSED_ARG(pkt_len);
+ PJ_UNUSED_ARG(src_addr);
+ PJ_UNUSED_ARG(src_addr_len);
- alloc = (pjturn_allocation*) pj_stun_session_get_user_data(sess);
+ alloc = (pj_turn_allocation*) pj_stun_session_get_user_data(sess);
/* Only expect Send Indication */
if (msg->hdr.type != PJ_STUN_SEND_INDICATION) {
@@ -1024,7 +1292,7 @@ static pj_status_t stun_on_rx_indication(pj_stun_session *sess,
return PJ_SUCCESS;
/* Relay the data to client */
- if (alloc->hkey.tp_type == PJTURN_TP_UDP) {
+ if (alloc->hkey.tp_type == PJ_TURN_TP_UDP) {
pj_ssize_t len = data_attr->length;
pj_sock_sendto(alloc->listener->sock, data_attr->data,
&len, 0, &peer_attr->sockaddr,
diff --git a/pjnath/src/pjturn-srv/auth.c b/pjnath/src/pjturn-srv/auth.c
new file mode 100644
index 00000000..3071221c
--- /dev/null
+++ b/pjnath/src/pjturn-srv/auth.c
@@ -0,0 +1,132 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "auth.h"
+#include <pjlib.h>
+
+
+#define MAX_REALM 80
+#define MAX_USERNAME 32
+#define MAX_PASSWORD 32
+#define MAX_NONCE 32
+
+static char g_realm[MAX_REALM];
+
+static struct cred_t
+{
+ char username[MAX_USERNAME];
+ char passwd[MAX_PASSWORD];
+} g_cred[] =
+{
+ { "user", "passwd" },
+};
+
+#define THE_NONCE "pjnath"
+
+
+/*
+ * Initialize TURN authentication subsystem.
+ */
+PJ_DEF(pj_status_t) pj_turn_auth_init(const char *realm)
+{
+ PJ_ASSERT_RETURN(pj_ansi_strlen(realm) < MAX_REALM, PJ_ENAMETOOLONG);
+ pj_ansi_strcpy(g_realm, realm);
+ return PJ_SUCCESS;
+}
+
+/*
+ * Shutdown TURN authentication subsystem.
+ */
+PJ_DEF(void) pj_turn_auth_dinit(void)
+{
+ /* Nothing to do */
+}
+
+
+/*
+ * This function is called by pj_stun_verify_credential() when
+ * server needs to challenge the request with 401 response.
+ */
+PJ_DEF(pj_status_t) pj_turn_get_auth(void *user_data,
+ pj_pool_t *pool,
+ pj_str_t *realm,
+ pj_str_t *nonce)
+{
+ PJ_UNUSED_ARG(user_data);
+ PJ_UNUSED_ARG(pool);
+
+ *realm = pj_str(g_realm);
+ *nonce = pj_str(THE_NONCE);
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * This function is called to get the password for the specified username.
+ * This function is also used to check whether the username is valid.
+ */
+PJ_DEF(pj_status_t) pj_turn_get_password(const pj_stun_msg *msg,
+ void *user_data,
+ const pj_str_t *realm,
+ const pj_str_t *username,
+ pj_pool_t *pool,
+ int *data_type,
+ pj_str_t *data)
+{
+ unsigned i;
+
+ PJ_UNUSED_ARG(msg);
+ PJ_UNUSED_ARG(user_data);
+ PJ_UNUSED_ARG(pool);
+
+ if (pj_stricmp2(realm, g_realm))
+ PJ_EINVAL;
+
+ for (i=0; i<PJ_ARRAY_SIZE(g_cred); ++i) {
+ if (pj_stricmp2(username, g_cred[i].username) == 0) {
+ *data_type = 0;
+ *data = pj_str(g_cred[i].passwd);
+ return PJ_SUCCESS;
+ }
+ }
+
+ return PJ_ENOTFOUND;
+}
+
+/*
+ * This function will be called to verify that the NONCE given
+ * in the message can be accepted. If this callback returns
+ * PJ_FALSE, 438 (Stale Nonce) response will be created.
+ */
+PJ_DEF(pj_status_t) pj_turn_verify_nonce(const pj_stun_msg *msg,
+ void *user_data,
+ const pj_str_t *realm,
+ const pj_str_t *username,
+ const pj_str_t *nonce)
+{
+ PJ_UNUSED_ARG(msg);
+ PJ_UNUSED_ARG(user_data);
+ PJ_UNUSED_ARG(realm);
+ PJ_UNUSED_ARG(username);
+
+ if (pj_stricmp2(nonce, THE_NONCE))
+ return PJ_FALSE;
+
+ return PJ_SUCCESS;
+}
+
diff --git a/pjnath/src/pjturn-srv/auth.h b/pjnath/src/pjturn-srv/auth.h
new file mode 100644
index 00000000..db928051
--- /dev/null
+++ b/pjnath/src/pjturn-srv/auth.h
@@ -0,0 +1,115 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJ_TURN_SRV_AUTH_H__
+#define __PJ_TURN_SRV_AUTH_H__
+
+#include <pjnath.h>
+
+/**
+ * Initialize TURN authentication subsystem.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pj_turn_auth_init(const char *realm);
+
+/**
+ * Shutdown TURN authentication subsystem.
+ */
+PJ_DECL(void) pj_turn_auth_dinit(void);
+
+/**
+ * This function is called by pj_stun_verify_credential() when
+ * server needs to challenge the request with 401 response.
+ *
+ * @param user_data Should be ignored.
+ * @param pool Pool to allocate memory.
+ * @param realm On return, the function should fill in with
+ * realm if application wants to use long term
+ * credential. Otherwise application should set
+ * empty string for the realm.
+ * @param nonce On return, if application wants to use long
+ * term credential, it MUST fill in the nonce
+ * with some value. Otherwise if short term
+ * credential is wanted, it MAY set this value.
+ * If short term credential is wanted and the
+ * application doesn't want to include NONCE,
+ * then it must set this to empty string.
+ *
+ * @return The callback should return PJ_SUCCESS, or
+ * otherwise response message will not be
+ * created.
+ */
+PJ_DECL(pj_status_t) pj_turn_get_auth(void *user_data,
+ pj_pool_t *pool,
+ pj_str_t *realm,
+ pj_str_t *nonce);
+
+/**
+ * This function is called to get the password for the specified username.
+ * This function is also used to check whether the username is valid.
+ *
+ * @param msg The STUN message where the password will be
+ * applied to.
+ * @param user_data Should be ignored.
+ * @param realm The realm as specified in the message.
+ * @param username The username as specified in the message.
+ * @param pool Pool to allocate memory when necessary.
+ * @param data_type On return, application should fill up this
+ * argument with the type of data (which should
+ * be zero if data is a plaintext password).
+ * @param data On return, application should fill up this
+ * argument with the password according to
+ * data_type.
+ *
+ * @return The callback should return PJ_SUCCESS if
+ * username has been successfully verified
+ * and password was obtained. If non-PJ_SUCCESS
+ * is returned, it is assumed that the
+ * username is not valid.
+ */
+PJ_DECL(pj_status_t) pj_turn_get_password(const pj_stun_msg *msg,
+ void *user_data,
+ const pj_str_t *realm,
+ const pj_str_t *username,
+ pj_pool_t *pool,
+ int *data_type,
+ pj_str_t *data);
+
+/**
+ * This function will be called to verify that the NONCE given
+ * in the message can be accepted. If this callback returns
+ * PJ_FALSE, 438 (Stale Nonce) response will be created.
+ *
+ * @param msg The STUN message where the nonce was received.
+ * @param user_data Should be ignored.
+ * @param realm The realm as specified in the message.
+ * @param username The username as specified in the message.
+ * @param nonce The nonce to be verified.
+ *
+ * @return The callback MUST return non-zero if the
+ * NONCE can be accepted.
+ */
+PJ_DECL(pj_status_t) pj_turn_verify_nonce(const pj_stun_msg *msg,
+ void *user_data,
+ const pj_str_t *realm,
+ const pj_str_t *username,
+ const pj_str_t *nonce);
+
+#endif /* __PJ_TURN_SRV_AUTH_H__ */
+
diff --git a/pjnath/src/pjturn-srv/listener_udp.c b/pjnath/src/pjturn-srv/listener_udp.c
index d8f90ca4..b634d092 100644
--- a/pjnath/src/pjturn-srv/listener_udp.c
+++ b/pjnath/src/pjturn-srv/listener_udp.c
@@ -21,25 +21,25 @@
struct read_op
{
pj_ioqueue_op_key_t op_key;
- pjturn_pkt pkt;
+ pj_turn_pkt pkt;
};
struct udp_listener
{
- pjturn_listener base;
+ pj_turn_listener base;
pj_ioqueue_key_t *key;
unsigned read_cnt;
struct read_op **read_op; /* Array of read_op's */
};
-static pj_status_t udp_sendto(pjturn_listener *listener,
+static pj_status_t udp_sendto(pj_turn_listener *listener,
const void *packet,
pj_size_t size,
unsigned flag,
const pj_sockaddr_t *addr,
int addr_len);
-static pj_status_t udp_destroy(pjturn_listener *udp);
+static pj_status_t udp_destroy(pj_turn_listener *udp);
static void on_read_complete(pj_ioqueue_key_t *key,
pj_ioqueue_op_key_t *op_key,
pj_ssize_t bytes_read);
@@ -48,13 +48,13 @@ static void on_read_complete(pj_ioqueue_key_t *key,
/*
* Create a new listener on the specified port.
*/
-PJ_DEF(pj_status_t) pjturn_listener_create_udp( pjturn_srv *srv,
+PJ_DEF(pj_status_t) pj_turn_listener_create_udp( pj_turn_srv *srv,
int af,
const pj_str_t *bound_addr,
unsigned port,
unsigned concurrency_cnt,
unsigned flags,
- pjturn_listener **p_listener)
+ pj_turn_listener **p_listener)
{
pj_pool_t *pool;
struct udp_listener *udp;
@@ -63,11 +63,12 @@ PJ_DEF(pj_status_t) pjturn_listener_create_udp( pjturn_srv *srv,
pj_status_t status;
/* Create structure */
- pool = pj_pool_create(srv->core.pf, "udplis%p", 1000, 1000, NULL);
+ pool = pj_pool_create(srv->core.pf, "udp%p", 1000, 1000, NULL);
udp = PJ_POOL_ZALLOC_T(pool, struct udp_listener);
udp->base.pool = pool;
+ udp->base.obj_name = pool->obj_name;
udp->base.server = srv;
- udp->base.tp_type = PJTURN_TP_UDP;
+ udp->base.tp_type = PJ_TURN_TP_UDP;
udp->base.sock = PJ_INVALID_SOCKET;
udp->base.sendto = &udp_sendto;
udp->base.destroy = &udp_destroy;
@@ -85,6 +86,11 @@ PJ_DEF(pj_status_t) pjturn_listener_create_udp( pjturn_srv *srv,
if (status != PJ_SUCCESS)
goto on_error;
+ /* Create info */
+ pj_ansi_strcpy(udp->base.info, "UDP:");
+ pj_sockaddr_print(&udp->base.addr, udp->base.info+4,
+ sizeof(udp->base.info)-4, 3);
+
/* Bind socket */
status = pj_sock_bind(udp->base.sock, &udp->base.addr,
pj_sockaddr_get_len(&udp->base.addr));
@@ -104,7 +110,8 @@ PJ_DEF(pj_status_t) pjturn_listener_create_udp( pjturn_srv *srv,
/* Create each read_op and kick off read operation */
for (i=0; i<concurrency_cnt; ++i) {
pj_pool_t *rpool = pj_pool_create(srv->core.pf, "rop%p",
- 1000, 1000, NULL);
+ sizeof(struct read_op)+1000,
+ 1000, NULL);
udp->read_op[i] = PJ_POOL_ZALLOC_T(rpool, struct read_op);
udp->read_op[i]->pkt.pool = rpool;
@@ -113,6 +120,8 @@ PJ_DEF(pj_status_t) pjturn_listener_create_udp( pjturn_srv *srv,
}
/* Done */
+ PJ_LOG(4,(udp->base.obj_name, "Listener %s created", udp->base.info));
+
*p_listener = &udp->base;
return PJ_SUCCESS;
@@ -126,7 +135,7 @@ on_error:
/*
* Destroy listener.
*/
-static pj_status_t udp_destroy(pjturn_listener *listener)
+static pj_status_t udp_destroy(pj_turn_listener *listener)
{
struct udp_listener *udp = (struct udp_listener *)listener;
unsigned i;
@@ -149,8 +158,13 @@ static pj_status_t udp_destroy(pjturn_listener *listener)
}
if (udp->base.pool) {
- pj_pool_release(udp->base.pool);
+ pj_pool_t *pool = udp->base.pool;
+
+ PJ_LOG(4,(udp->base.obj_name, "Listener %s destroyed",
+ udp->base.info));
+
udp->base.pool = NULL;
+ pj_pool_release(pool);
}
return PJ_SUCCESS;
}
@@ -158,7 +172,7 @@ static pj_status_t udp_destroy(pjturn_listener *listener)
/*
* Callback to send packet.
*/
-static pj_status_t udp_sendto(pjturn_listener *listener,
+static pj_status_t udp_sendto(pj_turn_listener *listener,
const void *packet,
pj_size_t size,
unsigned flag,
@@ -166,8 +180,7 @@ static pj_status_t udp_sendto(pjturn_listener *listener,
int addr_len)
{
pj_ssize_t len = size;
- return pj_sock_sendto(listener->sock, packet, &len, flag, addr,
- pj_sockaddr_get_len(addr));
+ return pj_sock_sendto(listener->sock, packet, &len, flag, addr, addr_len);
}
/*
@@ -191,7 +204,7 @@ static void on_read_complete(pj_ioqueue_key_t *key,
read_op->pkt.len = bytes_read;
pj_gettimeofday(&read_op->pkt.rx_time);
- pjturn_srv_on_rx_pkt(udp->base.server, &read_op->pkt);
+ pj_turn_srv_on_rx_pkt(udp->base.server, &read_op->pkt);
}
/* Reset pool */
diff --git a/pjnath/src/pjturn-srv/main.c b/pjnath/src/pjturn-srv/main.c
index 823eb28a..55b450c9 100644
--- a/pjnath/src/pjturn-srv/main.c
+++ b/pjnath/src/pjturn-srv/main.c
@@ -1 +1,51 @@
#include "turn.h"
+
+int err(const char *title, pj_status_t status)
+{
+ char errmsg[PJ_ERR_MSG_SIZE];
+ pj_strerror(status, errmsg, sizeof(errmsg));
+
+ printf("%s: %s\n", title, errmsg);
+ return 1;
+}
+
+int main()
+{
+ pj_caching_pool cp;
+ pj_turn_srv *srv;
+ pj_turn_listener *listener;
+ pj_status_t status;
+
+ status = pj_init();
+ if (status != PJ_SUCCESS)
+ return err("pj_init() error", status);
+
+ pj_caching_pool_init(&cp, NULL, 0);
+
+ status = pj_turn_srv_create(&cp.factory, &srv);
+ if (status != PJ_SUCCESS)
+ return err("Error creating server", status);
+
+ status = pj_turn_listener_create_udp(srv, pj_AF_INET(), NULL, 3478, 1, 0, &listener);
+ if (status != PJ_SUCCESS)
+ return err("Error creating listener", status);
+
+ status = pj_turn_srv_add_listener(srv, listener);
+ if (status != PJ_SUCCESS)
+ return err("Error adding listener", status);
+
+ puts("Server is running");
+ puts("Press <ENTER> to quit");
+
+ {
+ char line[10];
+ fgets(line, sizeof(line), stdin);
+ }
+
+ pj_turn_srv_destroy(srv);
+ pj_caching_pool_destroy(&cp);
+ pj_shutdown();
+
+ return 0;
+}
+
diff --git a/pjnath/src/pjturn-srv/server.c b/pjnath/src/pjturn-srv/server.c
index c9fc40cf..6765b3ca 100644
--- a/pjnath/src/pjturn-srv/server.c
+++ b/pjnath/src/pjturn-srv/server.c
@@ -17,25 +17,21 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "turn.h"
+#include "auth.h"
#define MAX_CLIENTS 32
#define MAX_PEERS_PER_CLIENT 8
-#define MAX_HANDLES (MAX_CLIENTS*MAX_PEERS_PER_CLIENT+MAX_LISTENERS)
+//#define MAX_HANDLES (MAX_CLIENTS*MAX_PEERS_PER_CLIENT+MAX_LISTENERS)
+#define MAX_HANDLES PJ_IOQUEUE_MAX_HANDLES
#define MAX_TIMER (MAX_HANDLES * 2)
#define MIN_PORT 49152
#define MAX_PORT 65535
#define MAX_LISTENERS 16
#define MAX_THREADS 2
-
-#define MAX_CLIENT_BANDWIDTH 128 /* In Kbps */
-#define DEFA_CLIENT_BANDWIDTH 64
-
-#define MIN_LIFETIME 32
-#define MAX_LIFETIME 600
-#define DEF_LIFETIME 300
-
+#define MAX_NET_EVENTS 10
/* Prototypes */
+static int server_thread_proc(void *arg);
static pj_status_t on_tx_stun_msg( pj_stun_session *sess,
const void *pkt,
pj_size_t pkt_size,
@@ -48,60 +44,91 @@ static pj_status_t on_rx_stun_request(pj_stun_session *sess,
const pj_sockaddr_t *src_addr,
unsigned src_addr_len);
+struct saved_cred
+{
+ pj_str_t realm;
+ pj_str_t username;
+ pj_str_t nonce;
+ int data_type;
+ pj_str_t data;
+};
+
+
/*
- * Get transport type name.
+ * Get transport type name, normally for logging purpose only.
*/
-PJ_DEF(const char*) pjturn_tp_type_name(int tp_type)
+PJ_DEF(const char*) pj_turn_tp_type_name(int tp_type)
{
/* Must be 3 characters long! */
- if (tp_type == PJTURN_TP_UDP)
+ if (tp_type == PJ_TURN_TP_UDP) {
return "UDP";
- else if (tp_type == PJTURN_TP_TCP)
+ } else if (tp_type == PJ_TURN_TP_TCP) {
return "TCP";
- else
+ } else {
+ pj_assert(!"Unsupported transport");
return "???";
+ }
}
/*
* Create server.
*/
-PJ_DEF(pj_status_t) pjturn_srv_create( pj_pool_factory *pf,
- pjturn_srv **p_srv)
+PJ_DEF(pj_status_t) pj_turn_srv_create(pj_pool_factory *pf,
+ pj_turn_srv **p_srv)
{
pj_pool_t *pool;
- pjturn_srv *srv;
+ pj_turn_srv *srv;
+ unsigned i;
pj_status_t status;
PJ_ASSERT_RETURN(pf && p_srv, PJ_EINVAL);
/* Create server and init core settings */
pool = pj_pool_create(pf, "srv%p", 1000, 1000, NULL);
- srv = PJ_POOL_ZALLOC_T(pool, pjturn_srv);
- srv->core.obj_name = pool->obj_name;
+ srv = PJ_POOL_ZALLOC_T(pool, pj_turn_srv);
+ srv->obj_name = pool->obj_name;
srv->core.pf = pf;
srv->core.pool = pool;
+ srv->core.tls_key = srv->core.tls_data = -1;
+ /* Create ioqueue */
status = pj_ioqueue_create(pool, MAX_HANDLES, &srv->core.ioqueue);
if (status != PJ_SUCCESS)
goto on_error;
- status = pj_timer_heap_create(pool, MAX_TIMER, &srv->core.timer_heap);
+ /* Server mutex */
+ status = pj_lock_create_recursive_mutex(pool, srv->obj_name,
+ &srv->core.lock);
if (status != PJ_SUCCESS)
goto on_error;
- srv->core.listener = pj_pool_calloc(pool, MAX_LISTENERS,
- sizeof(srv->core.listener[0]));
- srv->core.stun_sess = pj_pool_calloc(pool, MAX_LISTENERS,
- (sizeof(srv->core.stun_sess[0])));
-
- srv->core.thread_cnt = MAX_THREADS;
- srv->core.thread = pj_pool_calloc(pool, srv->core.thread_cnt,
- sizeof(pj_thread_t*));
+ /* Allocate TLS */
+ status = pj_thread_local_alloc(&srv->core.tls_key);
+ if (status != PJ_SUCCESS)
+ goto on_error;
- status = pj_lock_create_recursive_mutex(pool, "srv%p", &srv->core.lock);
+ status = pj_thread_local_alloc(&srv->core.tls_data);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ /* Create timer heap */
+ status = pj_timer_heap_create(pool, MAX_TIMER, &srv->core.timer_heap);
if (status != PJ_SUCCESS)
goto on_error;
+ /* Configure lock for the timer heap */
+ pj_timer_heap_set_lock(srv->core.timer_heap, srv->core.lock, PJ_FALSE);
+
+ /* Array of listeners */
+ srv->core.listener = (pj_turn_listener**)
+ pj_pool_calloc(pool, MAX_LISTENERS,
+ sizeof(srv->core.listener[0]));
+
+ /* Array of STUN sessions, one for each listener */
+ srv->core.stun_sess = (pj_stun_session**)
+ pj_pool_calloc(pool, MAX_LISTENERS,
+ (sizeof(srv->core.stun_sess[0])));
+
/* Create hash tables */
srv->tables.alloc = pj_hash_create(pool, MAX_CLIENTS);
srv->tables.res = pj_hash_create(pool, MAX_CLIENTS);
@@ -116,27 +143,204 @@ PJ_DEF(pj_status_t) pjturn_srv_create( pj_pool_factory *pf,
pj_stun_config_init(&srv->core.stun_cfg, pf, 0, srv->core.ioqueue,
srv->core.timer_heap);
+ /* Init STUN credential */
+ srv->core.cred.type = PJ_STUN_AUTH_CRED_DYNAMIC;
+ srv->core.cred.data.dyn_cred.user_data = srv;
+ srv->core.cred.data.dyn_cred.get_auth = &pj_turn_get_auth;
+ srv->core.cred.data.dyn_cred.get_cred = &pj_turn_srv_get_cred;
+ srv->core.cred.data.dyn_cred.get_password = &pj_turn_get_password;
+ srv->core.cred.data.dyn_cred.verify_nonce = &pj_turn_verify_nonce;
+
+ /* Array of worker threads */
+ srv->core.thread_cnt = MAX_THREADS;
+ srv->core.thread = (pj_thread_t**)
+ pj_pool_calloc(pool, srv->core.thread_cnt,
+ sizeof(pj_thread_t*));
+
+ /* Start the worker threads */
+ for (i=0; i<srv->core.thread_cnt; ++i) {
+ status = pj_thread_create(pool, srv->obj_name, &server_thread_proc,
+ srv, 0, 0, &srv->core.thread[i]);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+ }
+
+ /* We're done. Application should add listeners now */
+ PJ_LOG(4,(srv->obj_name, "TURN server v%s is running",
+ pj_get_version()));
+
*p_srv = srv;
return PJ_SUCCESS;
on_error:
- pjturn_srv_destroy(srv);
+ pj_turn_srv_destroy(srv);
return status;
}
-/**
- * Create server.
+
+/*
+ * Handle timer and network events
+ */
+static void srv_handle_events(pj_turn_srv *srv, const pj_time_val *max_timeout)
+{
+ /* timeout is 'out' var. This just to make compiler happy. */
+ pj_time_val timeout = { 0, 0};
+ unsigned net_event_count = 0;
+ int c;
+
+ /* Poll the timer. The timer heap has its own mutex for better
+ * granularity, so we don't need to lock the server.
+ */
+ timeout.sec = timeout.msec = 0;
+ c = pj_timer_heap_poll( srv->core.timer_heap, &timeout );
+
+ /* timer_heap_poll should never ever returns negative value, or otherwise
+ * ioqueue_poll() will block forever!
+ */
+ pj_assert(timeout.sec >= 0 && timeout.msec >= 0);
+ if (timeout.msec >= 1000) timeout.msec = 999;
+
+ /* If caller specifies maximum time to wait, then compare the value with
+ * the timeout to wait from timer, and use the minimum value.
+ */
+ if (max_timeout && PJ_TIME_VAL_GT(timeout, *max_timeout)) {
+ timeout = *max_timeout;
+ }
+
+ /* Poll ioqueue.
+ * Repeat polling the ioqueue while we have immediate events, because
+ * timer heap may process more than one events, so if we only process
+ * one network events at a time (such as when IOCP backend is used),
+ * the ioqueue may have trouble keeping up with the request rate.
+ *
+ * For example, for each send() request, one network event will be
+ * reported by ioqueue for the send() completion. If we don't poll
+ * the ioqueue often enough, the send() completion will not be
+ * reported in timely manner.
+ */
+ do {
+ c = pj_ioqueue_poll( srv->core.ioqueue, &timeout);
+ if (c < 0) {
+ pj_thread_sleep(PJ_TIME_VAL_MSEC(timeout));
+ return;
+ } else if (c == 0) {
+ break;
+ } else {
+ net_event_count += c;
+ timeout.sec = timeout.msec = 0;
+ }
+ } while (c > 0 && net_event_count < MAX_NET_EVENTS);
+
+}
+
+/*
+ * Server worker thread proc.
+ */
+static int server_thread_proc(void *arg)
+{
+ pj_turn_srv *srv = (pj_turn_srv*)arg;
+
+ while (!srv->core.quit) {
+ pj_time_val timeout_max = {0, 500};
+ srv_handle_events(srv, &timeout_max);
+ }
+
+ return 0;
+}
+
+/*
+ * Destroy the server.
*/
-PJ_DEF(pj_status_t) pjturn_srv_destroy(pjturn_srv *srv)
+PJ_DEF(pj_status_t) pj_turn_srv_destroy(pj_turn_srv *srv)
{
+ pj_hash_iterator_t itbuf, *it;
+ unsigned i;
+
+ /* Stop all worker threads */
+ srv->core.quit = PJ_TRUE;
+ for (i=0; i<srv->core.thread_cnt; ++i) {
+ if (srv->core.thread[i]) {
+ pj_thread_join(srv->core.thread[i]);
+ pj_thread_destroy(srv->core.thread[i]);
+ srv->core.thread[i] = NULL;
+ }
+ }
+
+ /* Destroy all listeners and STUN sessions associated with them. */
+ for (i=0; i<srv->core.lis_cnt; ++i) {
+ if (srv->core.listener[i]) {
+ pj_turn_listener_destroy(srv->core.listener[i]);
+ srv->core.listener[i] = NULL;
+ }
+ if (srv->core.stun_sess[i]) {
+ pj_stun_session_destroy(srv->core.stun_sess[i]);
+ srv->core.stun_sess[i] = NULL;
+ }
+ }
+
+ /* Destroy all allocations */
+ if (srv->tables.alloc) {
+ it = pj_hash_first(srv->tables.alloc, &itbuf);
+ while (it != NULL) {
+ pj_turn_allocation *alloc = (pj_turn_allocation*)
+ pj_hash_this(srv->tables.alloc, it);
+ pj_turn_allocation_destroy(alloc);
+ it = pj_hash_next(srv->tables.alloc, it);
+ }
+ }
+
+
+ /* Destroy hash tables (well, sort of) */
+ if (srv->tables.alloc) {
+ srv->tables.alloc = NULL;
+ srv->tables.res = NULL;
+ }
+
+ /* Destroy timer heap */
+ if (srv->core.timer_heap) {
+ pj_timer_heap_destroy(srv->core.timer_heap);
+ srv->core.timer_heap = NULL;
+ }
+
+ /* Destroy ioqueue */
+ if (srv->core.ioqueue) {
+ pj_ioqueue_destroy(srv->core.ioqueue);
+ srv->core.ioqueue = NULL;
+ }
+
+ /* Destroy thread local IDs */
+ if (srv->core.tls_key != -1) {
+ pj_thread_local_free(srv->core.tls_key);
+ srv->core.tls_key = -1;
+ }
+ if (srv->core.tls_data != -1) {
+ pj_thread_local_free(srv->core.tls_data);
+ srv->core.tls_data = -1;
+ }
+
+ /* Destroy server lock */
+ if (srv->core.lock) {
+ pj_lock_destroy(srv->core.lock);
+ srv->core.lock = NULL;
+ }
+
+ /* Release pool */
+ if (srv->core.pool) {
+ pj_pool_t *pool = srv->core.pool;
+ srv->core.pool = NULL;
+ pj_pool_release(pool);
+ }
+
+ /* Done */
return PJ_SUCCESS;
}
-/**
+
+/*
* Add listener.
*/
-PJ_DEF(pj_status_t) pjturn_srv_add_listener(pjturn_srv *srv,
- pjturn_listener *lis)
+PJ_DEF(pj_status_t) pj_turn_srv_add_listener(pj_turn_srv *srv,
+ pj_turn_listener *lis)
{
pj_stun_session_cb sess_cb;
unsigned index;
@@ -156,27 +360,76 @@ PJ_DEF(pj_status_t) pjturn_srv_add_listener(pjturn_srv *srv,
sess_cb.on_rx_request = &on_rx_stun_request;
sess_cb.on_send_msg = &on_tx_stun_msg;
- status = pj_stun_session_create(&srv->core.stun_cfg, "lis%p", &sess_cb,
- PJ_FALSE, &sess);
+ status = pj_stun_session_create(&srv->core.stun_cfg, lis->obj_name,
+ &sess_cb, PJ_FALSE, &sess);
if (status != PJ_SUCCESS) {
srv->core.listener[index] = NULL;
return status;
}
pj_stun_session_set_user_data(sess, lis);
+ pj_stun_session_set_credential(sess, &srv->core.cred);
srv->core.stun_sess[index] = sess;
lis->id = index;
srv->core.lis_cnt++;
+ PJ_LOG(4,(srv->obj_name, "Listener %s/%s added at index %d",
+ lis->obj_name, lis->info, lis->id));
+
return PJ_SUCCESS;
}
-/**
- * Register an allocation.
+
+/*
+ * Send packet with this listener.
*/
-PJ_DEF(pj_status_t) pjturn_srv_register_allocation(pjturn_srv *srv,
- pjturn_allocation *alloc)
+PJ_DEF(pj_status_t) pj_turn_listener_sendto(pj_turn_listener *listener,
+ const void *packet,
+ pj_size_t size,
+ unsigned flag,
+ const pj_sockaddr_t *addr,
+ int addr_len)
+{
+ pj_assert(listener->id != PJ_TURN_INVALID_LIS_ID);
+ return listener->sendto(listener, packet, size, flag, addr, addr_len);
+}
+
+
+/*
+ * Destroy listener.
+ */
+PJ_DEF(pj_status_t) pj_turn_listener_destroy(pj_turn_listener *listener)
+{
+ pj_turn_srv *srv = listener->server;
+ unsigned i;
+
+ /* Remove from our listener list */
+ pj_lock_acquire(srv->core.lock);
+ for (i=0; i<srv->core.lis_cnt; ++i) {
+ if (srv->core.listener[i] == listener) {
+ srv->core.listener[i] = NULL;
+ srv->core.lis_cnt--;
+ listener->id = PJ_TURN_INVALID_LIS_ID;
+ if (srv->core.stun_sess[i]) {
+ pj_stun_session_destroy(srv->core.stun_sess[i]);
+ srv->core.stun_sess[i] = NULL;
+ }
+ break;
+ }
+ }
+ pj_lock_release(srv->core.lock);
+
+ /* Destroy */
+ return listener->destroy(listener);
+}
+
+
+/*
+ * Register an allocation to the hash tables.
+ */
+PJ_DEF(pj_status_t) pj_turn_srv_register_allocation(pj_turn_srv *srv,
+ pj_turn_allocation *alloc)
{
/* Add to hash tables */
pj_lock_acquire(srv->core.lock);
@@ -190,11 +443,12 @@ PJ_DEF(pj_status_t) pjturn_srv_register_allocation(pjturn_srv *srv,
return PJ_SUCCESS;
}
-/**
- * Unregister an allocation.
+
+/*
+ * Unregister an allocation from the hash tables.
*/
-PJ_DEF(pj_status_t) pjturn_srv_unregister_allocation(pjturn_srv *srv,
- pjturn_allocation *alloc)
+PJ_DEF(pj_status_t) pj_turn_srv_unregister_allocation(pj_turn_srv *srv,
+ pj_turn_allocation *alloc)
{
/* Unregister from hash tables */
pj_lock_acquire(srv->core.lock);
@@ -208,285 +462,204 @@ PJ_DEF(pj_status_t) pjturn_srv_unregister_allocation(pjturn_srv *srv,
}
-/* Callback from our own STUN session to send packet */
+/* Callback from our own STUN session whenever it needs to send
+ * outgoing STUN packet.
+ */
static pj_status_t on_tx_stun_msg( pj_stun_session *sess,
const void *pkt,
pj_size_t pkt_size,
const pj_sockaddr_t *dst_addr,
unsigned addr_len)
{
- pjturn_listener *listener;
+ pj_turn_listener *listener;
- listener = (pjturn_listener*) pj_stun_session_get_user_data(sess);
+ listener = (pj_turn_listener*) pj_stun_session_get_user_data(sess);
PJ_ASSERT_RETURN(listener!=NULL, PJ_EINVALIDOP);
- return pjturn_listener_sendto(listener, pkt, pkt_size, 0,
- dst_addr, addr_len);
+ return pj_turn_listener_sendto(listener, pkt, pkt_size, 0,
+ dst_addr, addr_len);
}
-/* Create and send error response */
-static pj_status_t respond_error(pj_stun_session *sess, const pj_stun_msg *req,
- pj_bool_t cache, int code, const char *errmsg,
- const pj_sockaddr_t *dst_addr,
- unsigned addr_len)
+
+/* Respond to STUN request */
+static pj_status_t stun_respond(pj_turn_srv *srv,
+ pj_stun_session *sess,
+ const pj_stun_msg *req,
+ unsigned code,
+ const char *errmsg,
+ pj_bool_t cache,
+ const pj_sockaddr_t *dst_addr,
+ unsigned addr_len)
{
pj_status_t status;
pj_str_t reason;
pj_stun_tx_data *tdata;
- status = pj_stun_session_create_res(sess, req,
- code, (errmsg?pj_cstr(&reason,errmsg):NULL),
+ /* Create response */
+ status = pj_stun_session_create_res(sess, req, code,
+ (errmsg?pj_cstr(&reason,errmsg):NULL),
&tdata);
if (status != PJ_SUCCESS)
return status;
- status = pj_stun_session_send_msg(sess, cache, dst_addr, addr_len, tdata);
- return status;
+ /* Store the credential for future lookup. */
+ if (pj_stun_auth_valid_for_msg(tdata->msg)) {
+ pj_turn_srv_put_cred(srv, req, tdata);
+ }
+ /* Send the response */
+ return pj_stun_session_send_msg(sess, cache, dst_addr, addr_len, tdata);
}
-/* Parse ALLOCATE request */
-static pj_status_t parse_allocate_req(pjturn_allocation_req *cfg,
- pjturn_listener *listener,
- pj_stun_session *sess,
- const pj_stun_msg *req,
- const pj_sockaddr_t *src_addr,
- unsigned src_addr_len)
+
+/*
+ * Store the credential to put placed for the specified message for
+ * future retrieval.
+ */
+PJ_DEF(pj_status_t) pj_turn_srv_put_cred(pj_turn_srv *srv,
+ const pj_stun_msg *req,
+ pj_stun_tx_data *response)
{
- pj_stun_bandwidth_attr *attr_bw;
- pj_stun_req_transport_attr *attr_req_tp;
- pj_stun_req_ip_attr *attr_req_ip;
- pj_stun_req_port_props_attr *attr_rpp;
- pj_stun_lifetime_attr *attr_lifetime;
-
- pj_bzero(cfg, sizeof(*cfg));
-
- /* Get BANDWIDTH attribute, if any. */
- attr_bw = (pj_stun_uint_attr*)
- pj_stun_msg_find_attr(req, PJ_STUN_ATTR_BANDWIDTH, 0);
- if (attr_bw) {
- cfg->bandwidth = attr_bw->value;
- } else {
- cfg->bandwidth = DEFA_CLIENT_BANDWIDTH;
- }
+ pj_stun_username_attr *user;
+ pj_stun_realm_attr *realm;
+ pj_stun_nonce_attr *nonce;
+ struct saved_cred *saved_cred;
+ pj_status_t status;
- /* Check if we can satisfy the bandwidth */
- if (cfg->bandwidth > MAX_CLIENT_BANDWIDTH) {
- respond_error(sess, req, PJ_FALSE,
- PJ_STUN_SC_ALLOCATION_QUOTA_REACHED,
- "Invalid bandwidth", src_addr, src_addr_len);
- return -1;
- }
+ realm = (pj_stun_realm_attr*)
+ pj_stun_msg_find_attr(req, PJ_STUN_ATTR_REALM, 0);
+ PJ_ASSERT_RETURN(realm != NULL, PJ_EBUG);
- /* Get REQUESTED-TRANSPORT attribute, is any */
- attr_req_tp = (pj_stun_uint_attr*)
- pj_stun_msg_find_attr(req, PJ_STUN_ATTR_REQ_TRANSPORT, 0);
- if (attr_req_tp) {
- cfg->tp_type = PJ_STUN_GET_RT_PROTO(attr_req_tp->value);
- } else {
- cfg->tp_type = listener->tp_type;
- }
+ user = (pj_stun_username_attr*)
+ pj_stun_msg_find_attr(req, PJ_STUN_ATTR_USERNAME, 0);
+ PJ_ASSERT_RETURN(user != NULL, PJ_EBUG);
- /* Can only support UDP for now */
- if (cfg->tp_type != PJTURN_TP_UDP) {
- respond_error(sess, req, PJ_FALSE,
- PJ_STUN_SC_UNSUPP_TRANSPORT_PROTO,
- NULL, src_addr, src_addr_len);
- return -1;
- }
+ nonce = (pj_stun_nonce_attr*)
+ pj_stun_msg_find_attr(req, PJ_STUN_ATTR_NONCE, 0);
+ PJ_ASSERT_RETURN(nonce != NULL, PJ_EBUG);
- /* Get REQUESTED-IP attribute, if any */
- attr_req_ip = (pj_stun_sockaddr_attr*)
- pj_stun_msg_find_attr(req, PJ_STUN_ATTR_REQ_IP, 0);
- if (attr_req_ip) {
- pj_sockaddr_print(&attr_req_ip->sockaddr, cfg->addr,
- sizeof(cfg->addr), 0);
- }
+ saved_cred = PJ_POOL_ALLOC_T(response->pool, struct saved_cred);
- /* Get REQUESTED-PORT-PROPS attribute, if any */
- attr_rpp = (pj_stun_uint_attr*)
- pj_stun_msg_find_attr(req, PJ_STUN_ATTR_REQ_PORT_PROPS, 0);
- if (attr_rpp) {
- cfg->rpp_bits = PJ_STUN_GET_RPP_BITS(attr_rpp->value);
- cfg->rpp_port = PJ_STUN_GET_RPP_PORT(attr_rpp->value);
- } else {
- cfg->rpp_bits = 0;
- cfg->rpp_port = 0;
- }
+ /* Lookup the password */
+ status = pj_turn_get_password(response->msg, NULL, &realm->value,
+ &user->value, response->pool,
+ &saved_cred->data_type,
+ &saved_cred->data);
+ if (status != PJ_SUCCESS)
+ return status;
- /* Get LIFETIME attribute */
- attr_lifetime = (pj_stun_uint_attr*)
- pj_stun_msg_find_attr(req, PJ_STUN_ATTR_LIFETIME, 0);
- if (attr_lifetime) {
- cfg->lifetime = attr_lifetime->value;
- if (cfg->lifetime < MIN_LIFETIME || cfg->lifetime > MAX_LIFETIME) {
- respond_error(sess, req, PJ_FALSE,
- PJ_STUN_SC_BAD_REQUEST,
- "Invalid LIFETIME value", src_addr,
- src_addr_len);
- return -1;
- }
- } else {
- cfg->lifetime = DEF_LIFETIME;
- }
+ /* Store credential */
+ pj_strdup(response->pool, &saved_cred->username, &user->value);
+ pj_strdup(response->pool, &saved_cred->realm, &realm->value);
+ pj_strdup(response->pool, &saved_cred->nonce, &nonce->value);
+
+ pj_thread_local_set(srv->core.tls_key, response->msg);
+ pj_thread_local_set(srv->core.tls_data, saved_cred);
return PJ_SUCCESS;
}
-/* Callback from our own STUN session when incoming request arrives */
-static pj_status_t on_rx_stun_request(pj_stun_session *sess,
- const pj_uint8_t *pkt,
- unsigned pkt_len,
- const pj_stun_msg *msg,
- const pj_sockaddr_t *src_addr,
- unsigned src_addr_len)
+
+/**
+ * Retrieve previously stored credential for the specified message.
+ */
+PJ_DEF(pj_status_t) pj_turn_srv_get_cred(const pj_stun_msg *msg,
+ void *user_data,
+ pj_pool_t *pool,
+ pj_str_t *realm,
+ pj_str_t *username,
+ pj_str_t *nonce,
+ int *data_type,
+ pj_str_t *data)
{
- pjturn_listener *listener;
- pjturn_srv *srv;
- pjturn_allocation_req req;
- pjturn_allocation *alloc;
- pj_stun_tx_data *tdata;
- pj_status_t status;
+ pj_turn_srv *srv;
+ const pj_stun_msg *saved_msg;
+ struct saved_cred *saved_cred;
- listener = (pjturn_listener*) pj_stun_session_get_user_data(sess);
- srv = listener->server;
+ PJ_UNUSED_ARG(pool);
- /* Handle strayed REFRESH request */
- if (msg->hdr.type == PJ_STUN_REFRESH_REQUEST) {
- return respond_error(sess, msg, PJ_FALSE,
- PJ_STUN_SC_ALLOCATION_MISMATCH,
- NULL, src_addr, src_addr_len);
- }
+ srv = (pj_turn_srv*)user_data;
- /* Respond any other requests with Bad Request response */
- if (msg->hdr.type != PJ_STUN_ALLOCATE_REQUEST) {
- return respond_error(sess, msg, PJ_FALSE, PJ_STUN_SC_BAD_REQUEST,
- NULL, src_addr, src_addr_len);
- }
+ /* Lookup stored message and make sure it's for the same message */
+ saved_msg = (const pj_stun_msg*)
+ pj_thread_local_get(srv->core.tls_key);
+ PJ_ASSERT_RETURN(saved_msg==msg, PJ_ENOTFOUND);
- /* We have ALLOCATE request here, and it's authenticated. Parse the
- * request.
- */
- status = parse_allocate_req(&req, listener, sess, msg, src_addr,
- src_addr_len);
- if (status != PJ_SUCCESS)
- return status;
+ /* Lookup saved credential */
+ saved_cred = (struct saved_cred*)
+ pj_thread_local_get(srv->core.tls_data);
+ PJ_ASSERT_RETURN(saved_cred != NULL, PJ_ENOTFOUND);
- /* Create new allocation. The relay resource will be allocated
- * in this function.
- */
- status = pjturn_allocation_create(listener, src_addr, src_addr_len,
- msg, &req, &alloc);
- if (status != PJ_SUCCESS) {
- char errmsg[PJ_ERR_MSG_SIZE];
- pj_strerror(status, errmsg, sizeof(errmsg));
- return respond_error(sess, msg, PJ_FALSE, PJ_STUN_SC_SERVER_ERROR,
- errmsg, src_addr, src_addr_len);
- }
+ *realm = saved_cred->realm;
+ *username = saved_cred->username;
+ *nonce = saved_cred->nonce;
+ *data_type = saved_cred->data_type;
+ *data = saved_cred->data;
- /* Respond the original ALLOCATE request */
- status = pj_stun_session_create_res(srv->core.stun_sess[listener->id],
- msg, 0, NULL, &tdata);
- if (status != PJ_SUCCESS) {
- char errmsg[PJ_ERR_MSG_SIZE];
-
- pjturn_allocation_destroy(alloc);
- pj_strerror(status, errmsg, sizeof(errmsg));
- return respond_error(sess, msg, PJ_FALSE, PJ_STUN_SC_SERVER_ERROR,
- errmsg, src_addr, src_addr_len);
- }
-
- /* Add RELAYED-ADDRESS attribute */
- pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg,
- PJ_STUN_ATTR_RELAY_ADDR, PJ_TRUE,
- &alloc->relay.hkey.addr,
- pj_sockaddr_get_len(&alloc->relay.hkey.addr));
-
- /* Add LIFETIME. */
- pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg,
- PJ_STUN_ATTR_LIFETIME,
- (unsigned)alloc->relay.lifetime);
-
- /* Add BANDWIDTH */
- pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg,
- PJ_STUN_ATTR_BANDWIDTH,
- alloc->bandwidth);
-
- /* Add RESERVATION-TOKEN */
- PJ_TODO(ADD_RESERVATION_TOKEN);
-
- /* Add XOR-MAPPED-ADDRESS */
- pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg,
- PJ_STUN_ATTR_XOR_MAPPED_ADDR, PJ_TRUE,
- &alloc->hkey.clt_addr,
- pj_sockaddr_get_len(&alloc->hkey.clt_addr));
-
- /* Send the response */
- pj_stun_session_send_msg(srv->core.stun_sess[listener->id], PJ_TRUE,
- src_addr, src_addr_len, tdata);
+ /* Don't clear saved_cred as this may be called more than once */
- /* Done. */
return PJ_SUCCESS;
}
-/* Handle packet from new client address. */
-static void handle_new_client( pjturn_srv *srv,
- pjturn_pkt *pkt)
+/* Callback from our own STUN session when incoming request arrives.
+ * This function is triggered by pj_stun_session_on_rx_pkt() call in
+ * pj_turn_srv_on_rx_pkt() function below.
+ */
+static pj_status_t on_rx_stun_request(pj_stun_session *sess,
+ const pj_uint8_t *pkt,
+ unsigned pkt_len,
+ const pj_stun_msg *msg,
+ const pj_sockaddr_t *src_addr,
+ unsigned src_addr_len)
{
- unsigned options, lis_id;
+ pj_turn_listener *listener;
+ pj_turn_srv *srv;
+ pj_turn_allocation *alloc;
pj_status_t status;
- /* Check that this is a STUN message */
- options = PJ_STUN_CHECK_PACKET;
- if (pkt->listener->tp_type == PJTURN_TP_UDP)
- options |= PJ_STUN_IS_DATAGRAM;
+ PJ_UNUSED_ARG(pkt);
+ PJ_UNUSED_ARG(pkt_len);
- status = pj_stun_msg_check(pkt->pkt, pkt->len, options);
- if (status != PJ_SUCCESS) {
- char errmsg[PJ_ERR_MSG_SIZE];
- char ip[PJ_INET6_ADDRSTRLEN+10];
-
- pj_strerror(status, errmsg, sizeof(errmsg));
- PJ_LOG(5,(srv->core.obj_name,
- "Non STUN packet from %s is dropped: %s",
- pj_sockaddr_print(&pkt->src.clt_addr, ip, sizeof(ip), 3),
- errmsg));
- return;
- }
+ listener = (pj_turn_listener*) pj_stun_session_get_user_data(sess);
+ srv = listener->server;
- lis_id = pkt->listener->id;
+ /* Respond any requests other than ALLOCATE with 437 response */
+ if (msg->hdr.type != PJ_STUN_ALLOCATE_REQUEST) {
+ stun_respond(srv, sess, msg, PJ_STUN_SC_ALLOCATION_MISMATCH,
+ NULL, PJ_FALSE, src_addr, src_addr_len);
+ return PJ_SUCCESS;
+ }
- /* Hand over processing to STUN session */
- options &= ~PJ_STUN_CHECK_PACKET;
- status = pj_stun_session_on_rx_pkt(srv->core.stun_sess[lis_id], pkt->pkt,
- pkt->len, options, NULL,
- &pkt->src.clt_addr,
- pkt->src_addr_len);
+ /* Create new allocation. The relay resource will be allocated
+ * in this function.
+ */
+ status = pj_turn_allocation_create(listener, src_addr, src_addr_len,
+ msg, sess, &alloc);
if (status != PJ_SUCCESS) {
- char errmsg[PJ_ERR_MSG_SIZE];
- char ip[PJ_INET6_ADDRSTRLEN+10];
-
- pj_strerror(status, errmsg, sizeof(errmsg));
- PJ_LOG(5,(srv->core.obj_name,
- "Error processing STUN packet from %s: %s",
- pj_sockaddr_print(&pkt->src.clt_addr, ip, sizeof(ip), 3),
- errmsg));
- return;
+ /* STUN response has been sent, no need to reply here */
+ return PJ_SUCCESS;
}
+
+ /* Done. */
+ return PJ_SUCCESS;
}
/*
- * This callback is called by UDP listener on incoming packet.
+ * This callback is called by UDP listener on incoming packet. This is
+ * the first entry for incoming packet (from client) to the server. From
+ * here, the packet may be handed over to an allocation if an allocation
+ * is found for the client address, or handed over to owned STUN session
+ * if an allocation is not found.
*/
-PJ_DEF(void) pjturn_srv_on_rx_pkt( pjturn_srv *srv,
- pjturn_pkt *pkt)
+PJ_DEF(void) pj_turn_srv_on_rx_pkt(pj_turn_srv *srv,
+ pj_turn_pkt *pkt)
{
- pjturn_allocation *alloc;
+ pj_turn_allocation *alloc;
/* Get TURN allocation from the source address */
pj_lock_acquire(srv->core.lock);
@@ -497,10 +670,52 @@ PJ_DEF(void) pjturn_srv_on_rx_pkt( pjturn_srv *srv,
* allocation.
*/
if (alloc) {
- pjturn_allocation_on_rx_client_pkt(alloc, pkt);
+ pj_turn_allocation_on_rx_client_pkt(alloc, pkt);
} else {
/* Otherwise this is a new client */
- handle_new_client(srv, pkt);
+ unsigned options, lis_id;
+ pj_status_t status;
+
+ /* Check that this is a STUN message */
+ options = PJ_STUN_CHECK_PACKET;
+ if (pkt->listener->tp_type == PJ_TURN_TP_UDP)
+ options |= PJ_STUN_IS_DATAGRAM;
+
+ status = pj_stun_msg_check(pkt->pkt, pkt->len, options);
+ if (status != PJ_SUCCESS) {
+ char errmsg[PJ_ERR_MSG_SIZE];
+ char ip[PJ_INET6_ADDRSTRLEN+10];
+
+ pj_strerror(status, errmsg, sizeof(errmsg));
+ PJ_LOG(5,(srv->obj_name,
+ "Non STUN packet from %s is dropped: %s",
+ pj_sockaddr_print(&pkt->src.clt_addr, ip, sizeof(ip), 3),
+ errmsg));
+ return;
+ }
+
+ lis_id = pkt->listener->id;
+
+ /* Hand over processing to STUN session. This will trigger
+ * on_rx_stun_request() callback to be called if the STUN
+ * message is a request.
+ */
+ options &= ~PJ_STUN_CHECK_PACKET;
+ status = pj_stun_session_on_rx_pkt(srv->core.stun_sess[lis_id],
+ pkt->pkt, pkt->len, options, NULL,
+ &pkt->src.clt_addr,
+ pkt->src_addr_len);
+ if (status != PJ_SUCCESS) {
+ char errmsg[PJ_ERR_MSG_SIZE];
+ char ip[PJ_INET6_ADDRSTRLEN+10];
+
+ pj_strerror(status, errmsg, sizeof(errmsg));
+ PJ_LOG(5,(srv->obj_name,
+ "Error processing STUN packet from %s: %s",
+ pj_sockaddr_print(&pkt->src.clt_addr, ip, sizeof(ip), 3),
+ errmsg));
+ return;
+ }
}
}
diff --git a/pjnath/src/pjturn-srv/turn.h b/pjnath/src/pjturn-srv/turn.h
index a53cabbf..a6dcd9ec 100644
--- a/pjnath/src/pjturn-srv/turn.h
+++ b/pjnath/src/pjturn-srv/turn.h
@@ -16,42 +16,43 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#ifndef __PJTURN_SRV_TURN_H__
-#define __PJTURN_SRV_TURN_H__
+#ifndef __PJ_TURN_SRV_TURN_H__
+#define __PJ_TURN_SRV_TURN_H__
#include <pjlib.h>
#include <pjnath.h>
-typedef struct pjturn_relay_res pjturn_relay_res;
-typedef struct pjturn_listener pjturn_listener;
-typedef struct pjturn_permission pjturn_permission;
-typedef struct pjturn_allocation pjturn_allocation;
-typedef struct pjturn_srv pjturn_srv;
-typedef struct pjturn_pkt pjturn_pkt;
+typedef struct pj_turn_relay_res pj_turn_relay_res;
+typedef struct pj_turn_listener pj_turn_listener;
+typedef struct pj_turn_permission pj_turn_permission;
+typedef struct pj_turn_allocation pj_turn_allocation;
+typedef struct pj_turn_srv pj_turn_srv;
+typedef struct pj_turn_pkt pj_turn_pkt;
-#define PJTURN_INVALID_CHANNEL 0xFFFF
-#define PJTURN_NO_TIMEOUT ((long)0x7FFFFFFF)
-#define PJTURN_MAX_PKT_LEN 3000
-#define PJTURN_PERM_TIMEOUT 300
-#define PJTURN_CHANNEL_TIMEOUT 600
+#define PJ_TURN_INVALID_CHANNEL 0xFFFF
+#define PJ_TURN_INVALID_LIS_ID ((unsigned)-1)
+#define PJ_TURN_NO_TIMEOUT ((long)0x7FFFFFFF)
+#define PJ_TURN_MAX_PKT_LEN 3000
+#define PJ_TURN_PERM_TIMEOUT 300
+#define PJ_TURN_CHANNEL_TIMEOUT 600
/** Transport types */
enum {
- PJTURN_TP_UDP = 16, /**< UDP. */
- PJTURN_TP_TCP = 6 /**< TCP. */
+ PJ_TURN_TP_UDP = 16, /**< UDP. */
+ PJ_TURN_TP_TCP = 6 /**< TCP. */
};
/**
* Get transport type name string.
*/
-PJ_DECL(const char*) pjturn_tp_type_name(int tp_type);
+PJ_DECL(const char*) pj_turn_tp_type_name(int tp_type);
/**
* This structure describes TURN relay resource. An allocation allocates
* one relay resource, and optionally it may reserve another resource.
*/
-struct pjturn_relay_res
+struct pj_turn_relay_res
{
/** Hash table key */
struct {
@@ -63,7 +64,7 @@ struct pjturn_relay_res
} hkey;
/** Allocation who requested or reserved this resource. */
- pjturn_allocation *allocation;
+ pj_turn_allocation *allocation;
/** Username used in credential */
pj_str_t user;
@@ -92,7 +93,7 @@ struct pjturn_relay_res
pj_ioqueue_op_key_t read_key;
/** The incoming packet buffer */
- char rx_pkt[PJTURN_MAX_PKT_LEN];
+ char rx_pkt[PJ_TURN_MAX_PKT_LEN];
/** Source address of the packet. */
pj_sockaddr src_addr;
@@ -101,7 +102,7 @@ struct pjturn_relay_res
int src_addr_len;
/** The outgoing packet buffer. This must be 3wbit aligned. */
- char tx_pkt[PJTURN_MAX_PKT_LEN+4];
+ char tx_pkt[PJ_TURN_MAX_PKT_LEN+4];
} tp;
};
@@ -115,46 +116,20 @@ struct pjturn_relay_res
* This structure describes key to lookup TURN allocations in the
* allocation hash table.
*/
-typedef struct pjturn_allocation_key
+typedef struct pj_turn_allocation_key
{
int tp_type; /**< Transport type. */
pj_sockaddr clt_addr; /**< Client's address. */
-} pjturn_allocation_key;
+} pj_turn_allocation_key;
/**
- * Allocation request.
+ * This structure describes TURN pj_turn_allocation session.
*/
-typedef struct pjturn_allocation_req
-{
- /** Requested transport */
- unsigned tp_type;
-
- /** Requested IP */
- char addr[PJ_INET6_ADDRSTRLEN];
-
- /** Requested bandwidth */
- unsigned bandwidth;
-
- /** Lifetime. */
- unsigned lifetime;
-
- /** A bits */
- unsigned rpp_bits;
-
- /** Requested port */
- unsigned rpp_port;
-
-} pjturn_allocation_req;
-
-
-/**
- * This structure describes TURN pjturn_allocation session.
- */
-struct pjturn_allocation
+struct pj_turn_allocation
{
/** Hash table key to identify client. */
- pjturn_allocation_key hkey;
+ pj_turn_allocation_key hkey;
/** Pool for this allocation. */
pj_pool_t *pool;
@@ -169,16 +144,16 @@ struct pjturn_allocation
pj_lock_t *lock;
/** TURN listener. */
- pjturn_listener *listener;
+ pj_turn_listener *listener;
/** Client socket, if connection to client is using TCP. */
pj_sock_t clt_sock;
/** The relay resource for this allocation. */
- pjturn_relay_res relay;
+ pj_turn_relay_res relay;
/** Relay resource reserved by this allocation, if any */
- pjturn_relay_res *resv;
+ pj_turn_relay_res *resv;
/** Requested bandwidth */
unsigned bandwidth;
@@ -186,6 +161,9 @@ struct pjturn_allocation
/** STUN session for this client */
pj_stun_session *sess;
+ /** Credential for this STUN session. */
+ pj_stun_auth_cred cred;
+
/** Peer hash table (keyed by peer address) */
pj_hash_table_t *peer_table;
@@ -198,21 +176,21 @@ struct pjturn_allocation
* This structure describes the hash table key to lookup TURN
* permission.
*/
-typedef struct pjturn_permission_key
+typedef struct pj_turn_permission_key
{
/** Peer address. */
pj_sockaddr peer_addr;
-} pjturn_permission_key;
+} pj_turn_permission_key;
/**
- * This structure describes TURN pjturn_permission or channel.
+ * This structure describes TURN pj_turn_permission or channel.
*/
-struct pjturn_permission
+struct pj_turn_permission
{
/** Hash table key */
- pjturn_permission_key hkey;
+ pj_turn_permission_key hkey;
/** Transport socket. If TCP is used, the value will be the actual
* TCP socket. If UDP is used, the value will be the relay address
@@ -220,9 +198,9 @@ struct pjturn_permission
pj_sock_t sock;
/** TURN allocation that owns this permission/channel */
- pjturn_allocation *allocation;
+ pj_turn_allocation *allocation;
- /** Optional channel number, or PJTURN_INVALID_CHANNEL if channel number
+ /** Optional channel number, or PJ_TURN_INVALID_CHANNEL if channel number
* is not requested for this permission.
*/
pj_uint16_t channel;
@@ -234,31 +212,23 @@ struct pjturn_permission
/**
* Create new allocation.
*/
-PJ_DECL(pj_status_t) pjturn_allocation_create(pjturn_listener *listener,
+PJ_DECL(pj_status_t) pj_turn_allocation_create(pj_turn_listener *listener,
const pj_sockaddr_t *src_addr,
unsigned src_addr_len,
const pj_stun_msg *msg,
- const pjturn_allocation_req *req,
- pjturn_allocation **p_alloc);
+ pj_stun_session *srv_sess,
+ pj_turn_allocation **p_alloc);
/**
* Destroy allocation.
*/
-PJ_DECL(void) pjturn_allocation_destroy(pjturn_allocation *alloc);
+PJ_DECL(void) pj_turn_allocation_destroy(pj_turn_allocation *alloc);
-/**
- * Create relay.
- */
-PJ_DECL(pj_status_t) pjturn_allocation_create_relay(pjturn_srv *srv,
- pjturn_allocation *alloc,
- const pj_stun_msg *msg,
- const pjturn_allocation_req *req,
- pjturn_relay_res *relay);
/**
* Handle incoming packet from client.
*/
-PJ_DECL(void) pjturn_allocation_on_rx_client_pkt(pjturn_allocation *alloc,
- pjturn_pkt *pkt);
+PJ_DECL(void) pj_turn_allocation_on_rx_client_pkt(pj_turn_allocation *alloc,
+ pj_turn_pkt *pkt);
/****************************************************************************/
/*
@@ -269,10 +239,16 @@ PJ_DECL(void) pjturn_allocation_on_rx_client_pkt(pjturn_allocation *alloc,
* This structure describes TURN listener socket. A TURN listener socket
* listens for incoming connections from clients.
*/
-struct pjturn_listener
+struct pj_turn_listener
{
+ /** Object name/identification */
+ char *obj_name;
+
+ /** Slightly longer info about this listener */
+ char info[80];
+
/** TURN server instance. */
- pjturn_srv *server;
+ pj_turn_srv *server;
/** Listener index in the server */
unsigned id;
@@ -293,7 +269,7 @@ struct pjturn_listener
unsigned flags;
/** Sendto handler */
- pj_status_t (*sendto)(pjturn_listener *listener,
+ pj_status_t (*sendto)(pj_turn_listener *listener,
const void *packet,
pj_size_t size,
unsigned flag,
@@ -301,23 +277,23 @@ struct pjturn_listener
int addr_len);
/** Destroy handler */
- pj_status_t (*destroy)(pjturn_listener*);
+ pj_status_t (*destroy)(pj_turn_listener*);
};
/**
* An incoming packet.
*/
-struct pjturn_pkt
+struct pj_turn_pkt
{
/** Pool for this packet */
pj_pool_t *pool;
/** Listener that owns this. */
- pjturn_listener *listener;
+ pj_turn_listener *listener;
/** Packet buffer (must be 32bit aligned). */
- pj_uint8_t pkt[PJTURN_MAX_PKT_LEN];
+ pj_uint8_t pkt[PJ_TURN_MAX_PKT_LEN];
/** Size of the packet */
pj_size_t len;
@@ -326,7 +302,7 @@ struct pjturn_pkt
pj_time_val rx_time;
/** Source transport type and source address. */
- pjturn_allocation_key src;
+ pj_turn_allocation_key src;
/** Source address length. */
int src_addr_len;
@@ -336,18 +312,18 @@ struct pjturn_pkt
/**
* Create a new listener on the specified port.
*/
-PJ_DECL(pj_status_t) pjturn_listener_create_udp(pjturn_srv *srv,
+PJ_DECL(pj_status_t) pj_turn_listener_create_udp(pj_turn_srv *srv,
int af,
const pj_str_t *bound_addr,
unsigned port,
unsigned concurrency_cnt,
unsigned flags,
- pjturn_listener **p_listener);
+ pj_turn_listener **p_listener);
/**
* Send packet with this listener.
*/
-PJ_DECL(pj_status_t) pjturn_listener_sendto(pjturn_listener *listener,
+PJ_DECL(pj_status_t) pj_turn_listener_sendto(pj_turn_listener *listener,
const void *packet,
pj_size_t size,
unsigned flag,
@@ -357,7 +333,7 @@ PJ_DECL(pj_status_t) pjturn_listener_sendto(pjturn_listener *listener,
/**
* Destroy listener.
*/
-PJ_DECL(pj_status_t) pjturn_listener_destroy(pjturn_listener *listener);
+PJ_DECL(pj_status_t) pj_turn_listener_destroy(pj_turn_listener *listener);
/****************************************************************************/
@@ -365,15 +341,15 @@ PJ_DECL(pj_status_t) pjturn_listener_destroy(pjturn_listener *listener);
* TURN Server API
*/
/**
- * This structure describes TURN pjturn_srv instance.
+ * This structure describes TURN pj_turn_srv instance.
*/
-struct pjturn_srv
+struct pj_turn_srv
{
+ /** Object name */
+ char *obj_name;
+
/** Core settings */
struct {
- /** Object name */
- char *obj_name;
-
/** Pool factory */
pj_pool_factory *pf;
@@ -393,7 +369,7 @@ struct pjturn_srv
unsigned lis_cnt;
/** Array of listeners. */
- pjturn_listener **listener;
+ pj_turn_listener **listener;
/** Array of STUN sessions, one for each listeners. */
pj_stun_session **stun_sess;
@@ -404,9 +380,17 @@ struct pjturn_srv
/** Array of worker threads. */
pj_thread_t **thread;
+ /** Thread quit signal */
+ pj_bool_t quit;
+
/** STUN config. */
pj_stun_config stun_cfg;
+ /** STUN auth credential. */
+ pj_stun_auth_cred cred;
+
+ /** Thread local ID for storing credential */
+ long tls_key, tls_data;
} core;
@@ -453,38 +437,59 @@ struct pjturn_srv
/**
* Create server.
*/
-PJ_DECL(pj_status_t) pjturn_srv_create(pj_pool_factory *pf,
- pjturn_srv **p_srv);
+PJ_DECL(pj_status_t) pj_turn_srv_create(pj_pool_factory *pf,
+ pj_turn_srv **p_srv);
/**
* Destroy server.
*/
-PJ_DECL(pj_status_t) pjturn_srv_destroy(pjturn_srv *srv);
+PJ_DECL(pj_status_t) pj_turn_srv_destroy(pj_turn_srv *srv);
/**
* Add listener.
*/
-PJ_DECL(pj_status_t) pjturn_srv_add_listener(pjturn_srv *srv,
- pjturn_listener *lis);
+PJ_DECL(pj_status_t) pj_turn_srv_add_listener(pj_turn_srv *srv,
+ pj_turn_listener *lis);
/**
* Register an allocation.
*/
-PJ_DECL(pj_status_t) pjturn_srv_register_allocation(pjturn_srv *srv,
- pjturn_allocation *alloc);
+PJ_DECL(pj_status_t) pj_turn_srv_register_allocation(pj_turn_srv *srv,
+ pj_turn_allocation *alloc);
/**
* Unregister an allocation.
*/
-PJ_DECL(pj_status_t) pjturn_srv_unregister_allocation(pjturn_srv *srv,
- pjturn_allocation *alloc);
+PJ_DECL(pj_status_t) pj_turn_srv_unregister_allocation(pj_turn_srv *srv,
+ pj_turn_allocation *alloc);
/**
* This callback is called by UDP listener on incoming packet.
*/
-PJ_DECL(void) pjturn_srv_on_rx_pkt(pjturn_srv *srv,
- pjturn_pkt *pkt);
+PJ_DECL(void) pj_turn_srv_on_rx_pkt(pj_turn_srv *srv,
+ pj_turn_pkt *pkt);
+
+
+/**
+ * Store the credential to put placed for the specified message for
+ * future retrieval.
+ */
+PJ_DECL(pj_status_t) pj_turn_srv_put_cred(pj_turn_srv *srv,
+ const pj_stun_msg *request,
+ pj_stun_tx_data *response);
+
+/**
+ * Retrieve previously stored credential for the specified message.
+ */
+PJ_DECL(pj_status_t) pj_turn_srv_get_cred(const pj_stun_msg *msg,
+ void *user_data,
+ pj_pool_t *pool,
+ pj_str_t *realm,
+ pj_str_t *username,
+ pj_str_t *nonce,
+ int *data_type,
+ pj_str_t *data);
-#endif /* __PJTURN_SRV_TURN_H__ */
+#endif /* __PJ_TURN_SRV_TURN_H__ */