summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2007-03-01 00:08:27 +0000
committerBenny Prijono <bennylp@teluu.com>2007-03-01 00:08:27 +0000
commit320ebe471866fd5c1ca2406e28aaa1fdce7695ef (patch)
tree49c2dc6ec738bc3fd6c656cfbe3deb0deebdfa9a
parent6212d1147cd26f46030ddf89f8d163d704e342f9 (diff)
More work on STUN session framework
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@1021 74dad513-b988-da41-8d7b-12977e46ad98
-rw-r--r--pjlib-util/include/pjlib-util/stun_msg.h16
-rw-r--r--pjlib-util/include/pjlib-util/stun_session.h159
-rw-r--r--pjlib-util/src/pjlib-util/stun_msg.c76
-rw-r--r--pjlib-util/src/pjlib-util/stun_session.c444
-rw-r--r--pjlib-util/src/pjstun-srv-test/server.h61
-rw-r--r--pjlib-util/src/pjstun-srv-test/server_main.c324
6 files changed, 641 insertions, 439 deletions
diff --git a/pjlib-util/include/pjlib-util/stun_msg.h b/pjlib-util/include/pjlib-util/stun_msg.h
index 05aa7d67..6d4d5a6b 100644
--- a/pjlib-util/include/pjlib-util/stun_msg.h
+++ b/pjlib-util/include/pjlib-util/stun_msg.h
@@ -1174,7 +1174,9 @@ PJ_DECL(pj_status_t) pj_stun_msg_add_attr(pj_stun_msg *msg,
* If FINGERPRINT attribute is present, this function will calculate
* the FINGERPRINT CRC attribute for the message.
*
- * @param msg The STUN message to be printed.
+ * @param msg The STUN message to be printed. Upon return,
+ * some fields in the header (such as message
+ * length) will be updated.
* @param pkt_buf The buffer to be filled with the packet.
* @param buf_size Size of the buffer.
* @param options Options, which currently must be zero.
@@ -1186,7 +1188,7 @@ PJ_DECL(pj_status_t) pj_stun_msg_add_attr(pj_stun_msg *msg,
*
* @return PJ_SUCCESS on success or the appropriate error code.
*/
-PJ_DECL(pj_status_t) pj_stun_msg_encode(const pj_stun_msg *msg,
+PJ_DECL(pj_status_t) pj_stun_msg_encode(pj_stun_msg *msg,
pj_uint8_t *pkt_buf,
unsigned buf_size,
unsigned options,
@@ -1422,8 +1424,8 @@ PJ_DECL(pj_stun_attr_hdr*) pj_stun_msg_find_attr(const pj_stun_msg *msg,
* @param attr_type Attribute type, from #pj_stun_attr_type.
* @param xor_ed If non-zero, the port and address will be XOR-ed
* with magic, to make the XOR-MAPPED-ADDRESS attribute.
- * @param addr_len Length of \a addr parameter.
* @param addr A pj_sockaddr_in or pj_sockaddr_in6 structure.
+ * @param addr_len Length of \a addr parameter.
* @param p_attr Pointer to receive the attribute.
*
* @return PJ_SUCCESS on success or the appropriate error code.
@@ -1432,8 +1434,8 @@ PJ_DECL(pj_status_t)
pj_stun_generic_ip_addr_attr_create(pj_pool_t *pool,
int attr_type,
pj_bool_t xor_ed,
- unsigned addr_len,
const pj_sockaddr_t *addr,
+ unsigned addr_len,
pj_stun_generic_ip_addr_attr **p_attr);
@@ -1447,8 +1449,8 @@ pj_stun_generic_ip_addr_attr_create(pj_pool_t *pool,
* @param attr_type Attribute type, from #pj_stun_attr_type.
* @param xor_ed If non-zero, the port and address will be XOR-ed
* with magic, to make the XOR-MAPPED-ADDRESS attribute.
- * @param addr_len Length of \a addr parameter.
* @param addr A pj_sockaddr_in or pj_sockaddr_in6 structure.
+ * @param addr_len Length of \a addr parameter.
*
* @return PJ_SUCCESS on success or the appropriate error code.
*/
@@ -1457,8 +1459,8 @@ pj_stun_msg_add_generic_ip_addr_attr(pj_pool_t *pool,
pj_stun_msg *msg,
int attr_type,
pj_bool_t xor_ed,
- unsigned addr_len,
- const pj_sockaddr_t *addr);
+ const pj_sockaddr_t *addr,
+ unsigned addr_len);
/**
* Create a STUN generic string attribute.
diff --git a/pjlib-util/include/pjlib-util/stun_session.h b/pjlib-util/include/pjlib-util/stun_session.h
index 057b35aa..94ea9ff6 100644
--- a/pjlib-util/include/pjlib-util/stun_session.h
+++ b/pjlib-util/include/pjlib-util/stun_session.h
@@ -24,6 +24,17 @@
#include <pjlib-util/stun_transaction.h>
#include <pj/list.h>
+PJ_BEGIN_DECL
+
+
+/* **************************************************************************/
+/**
+ * @defgroup PJLIB_UTIL_STUN_SESSION STUN Client/Server Session
+ * @brief STUN client and server session
+ * @ingroup PJLIB_UTIL_STUN
+ * @{
+ */
+
/** Forward declaration for pj_stun_tx_data */
typedef struct pj_stun_tx_data pj_stun_tx_data;
@@ -31,6 +42,7 @@ typedef struct pj_stun_tx_data pj_stun_tx_data;
/** Forward declaration for pj_stun_session */
typedef struct pj_stun_session pj_stun_session;
+
/**
* This is the callback to be registered to pj_stun_session, to send
* outgoing message and to receive various notifications from the STUN
@@ -51,49 +63,40 @@ typedef struct pj_stun_session_cb
* @return The callback should return the status of the
* packet sending.
*/
- pj_status_t (*on_send_msg)(pj_stun_tx_data *tdata,
+ pj_status_t (*on_send_msg)(pj_stun_session *sess,
const void *pkt,
pj_size_t pkt_size,
- unsigned addr_len,
- const pj_sockaddr_t *dst_addr);
-
- /**
- * Callback to be called when Binding response is received or the
- * transaction has timed out.
+ const pj_sockaddr_t *dst_addr,
+ unsigned addr_len);
+
+ /**
+ * Callback to be called on incoming STUN request message. In the
+ * callback processing, application MUST create a response by calling
+ * pj_stun_session_create_response() function and send the response
+ * with pj_stun_session_send_msg() function, before returning from
+ * the callback.
*
* @param sess The STUN session.
- * @param status Status of the request. If the value if not
- * PJ_SUCCESS, the transaction has timed-out
- * or other error has occurred, and the response
- * argument may be NULL.
- * @param request The original STUN request.
- * @param response The response message, on successful transaction.
- */
- void (*on_bind_response)(pj_stun_session *sess,
- pj_status_t status,
- pj_stun_tx_data *request,
- const pj_stun_msg *response);
-
- /**
- * Callback to be called when Allocate response is received or the
- * transaction has timed out.
+ * @param pkt Pointer to the original STUN packet.
+ * @param pkt_len Length of the STUN packet.
+ * @param msg The parsed STUN request.
+ * @param src_addr Source address of the packet.
+ * @param src_addr_len Length of the source address.
*
- * @param sess The STUN session.
- * @param status Status of the request. If the value if not
- * PJ_SUCCESS, the transaction has timed-out
- * or other error has occurred, and the response
- * argument may be NULL.
- * @param request The original STUN request.
- * @param response The response message, on successful transaction.
+ * @return The return value of this callback will be
+ * returned back to pj_stun_session_on_rx_pkt()
+ * function.
*/
- void (*on_allocate_response)(pj_stun_session *sess,
- pj_status_t status,
- pj_stun_tx_data *request,
- const pj_stun_msg *response);
+ pj_status_t (*on_rx_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);
/**
- * Callback to be called when Set Active Destination response is received
- * or the transaction has timed out.
+ * Callback to be called when response is received or the transaction
+ * has timed out.
*
* @param sess The STUN session.
* @param status Status of the request. If the value if not
@@ -103,27 +106,22 @@ typedef struct pj_stun_session_cb
* @param request The original STUN request.
* @param response The response message, on successful transaction.
*/
- void (*on_set_active_destination_response)(pj_stun_session *sess,
- pj_status_t status,
- pj_stun_tx_data *request,
- const pj_stun_msg *response);
+ void (*on_request_complete)(pj_stun_session *sess,
+ pj_status_t status,
+ pj_stun_tx_data *tdata,
+ const pj_stun_msg *response);
+
/**
- * Callback to be called when Connect response is received or the
- * transaction has timed out.
- *
- * @param sess The STUN session.
- * @param status Status of the request. If the value if not
- * PJ_SUCCESS, the transaction has timed-out
- * or other error has occurred, and the response
- * argument may be NULL.
- * @param request The original STUN request.
- * @param response The response message, on successful transaction.
+ * Type of callback to be called on incoming STUN indication.
*/
- void (*on_connect_response)( pj_stun_session *sess,
- pj_status_t status,
- pj_stun_tx_data *request,
- const pj_stun_msg *response);
+ pj_status_t (*on_rx_indication)(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);
+
} pj_stun_session_cb;
@@ -361,6 +359,32 @@ PJ_DECL(pj_status_t) pj_stun_session_create_data_ind(pj_stun_session *sess,
pj_stun_tx_data **p_tdata);
/**
+ * Create a STUN response message. After the message has been
+ * successfully created, application can send the message by calling
+ * pj_stun_session_send_msg().
+ *
+ * @param sess The STUN session instance.
+ * @param req The STUN request where the response is to be created.
+ * @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 p_tdata Pointer to receive the response message created.
+ *
+ * @return PJ_SUCCESS on success, or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pj_stun_session_create_response(pj_stun_session *sess,
+ const pj_stun_msg *req,
+ unsigned err_code,
+ 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
* fingerprint if necessary. If the message is a request, the session will
@@ -372,8 +396,8 @@ PJ_DECL(pj_status_t) pj_stun_session_create_data_ind(pj_stun_session *sess,
*
* @param sess The STUN session instance.
* @param options Optional flags, from pj_stun_session_option.
- * @param addr_len Length of destination address.
* @param dst_addr The destination socket address.
+ * @param addr_len Length of destination address.
* @param tdata The STUN transmit data containing the STUN message to
* be sent.
*
@@ -381,8 +405,8 @@ PJ_DECL(pj_status_t) pj_stun_session_create_data_ind(pj_stun_session *sess,
*/
PJ_DECL(pj_status_t) pj_stun_session_send_msg(pj_stun_session *sess,
unsigned options,
- unsigned addr_len,
const pj_sockaddr_t *dst_addr,
+ unsigned addr_len,
pj_stun_tx_data *tdata);
/**
@@ -402,6 +426,7 @@ PJ_DECL(pj_status_t) pj_stun_session_send_msg(pj_stun_session *sess,
* @param sess The STUN session instance.
* @param packet The packet containing STUN message.
* @param pkt_size Size of the packet.
+ * @param options Options, from pj_stun_options.
* @param parsed_len Optional pointer to receive the size of the parsed
* STUN message (useful if packet is received via a
* stream oriented protocol).
@@ -411,9 +436,31 @@ PJ_DECL(pj_status_t) pj_stun_session_send_msg(pj_stun_session *sess,
PJ_DECL(pj_status_t) pj_stun_session_on_rx_pkt(pj_stun_session *sess,
const void *packet,
pj_size_t pkt_size,
- unsigned *parsed_len);
+ unsigned options,
+ unsigned *parsed_len,
+ const pj_sockaddr_t *src_addr,
+ unsigned src_addr_len);
+
+/**
+ * Destroy the transmit data. Call this function only when tdata has been
+ * created but application doesn't want to send the message (perhaps
+ * because of other error).
+ *
+ * @param sess The STUN session.
+ * @param tdata The transmit data.
+ *
+ * @return PJ_SUCCESS on success, or the appropriate error code.
+ */
+PJ_DECL(void) pj_stun_msg_destroy_tdata(pj_stun_session *sess,
+ pj_stun_tx_data *tdata);
+
+
+/**
+ * @}
+ */
+PJ_END_DECL
#endif /* __PJLIB_UTIL_STUN_SESSION_H__ */
diff --git a/pjlib-util/src/pjlib-util/stun_msg.c b/pjlib-util/src/pjlib-util/stun_msg.c
index 47d70dc0..e8153e10 100644
--- a/pjlib-util/src/pjlib-util/stun_msg.c
+++ b/pjlib-util/src/pjlib-util/stun_msg.c
@@ -517,8 +517,8 @@ PJ_DEF(pj_status_t)
pj_stun_generic_ip_addr_attr_create(pj_pool_t *pool,
int attr_type,
pj_bool_t xor_ed,
- unsigned addr_len,
const pj_sockaddr_t *addr,
+ unsigned addr_len,
pj_stun_generic_ip_addr_attr **p_attr)
{
pj_stun_generic_ip_addr_attr *attr;
@@ -559,14 +559,14 @@ pj_stun_msg_add_generic_ip_addr_attr(pj_pool_t *pool,
pj_stun_msg *msg,
int attr_type,
pj_bool_t xor_ed,
- unsigned addr_len,
- const pj_sockaddr_t *addr)
+ const pj_sockaddr_t *addr,
+ unsigned addr_len)
{
pj_stun_generic_ip_addr_attr *attr;
pj_status_t status;
status = pj_stun_generic_ip_addr_attr_create(pool, attr_type, xor_ed,
- addr_len, addr, &attr);
+ addr, addr_len, &attr);
if (status != PJ_SUCCESS)
return status;
@@ -1434,17 +1434,15 @@ PJ_DEF(pj_status_t) pj_stun_msg_add_attr(pj_stun_msg *msg,
PJ_INLINE(pj_uint16_t) GET_VAL16(const pj_uint8_t *pdu, unsigned pos)
{
- pj_uint16_t val = (pj_uint16_t) ((pdu[pos] << 8) + pdu[pos+1]);
- return pj_ntohs(val);
+ return (pj_uint16_t) ((pdu[pos] << 8) + pdu[pos+1]);
}
PJ_INLINE(pj_uint32_t) GET_VAL32(const pj_uint8_t *pdu, unsigned pos)
{
- pj_uint32_t val = (pdu[pos+0] << 24) +
- (pdu[pos+1] << 16) +
- (pdu[pos+2] << 8) +
- (pdu[pos+3]);
- return pj_ntohl(val);
+ return (pdu[pos+0] << 24) +
+ (pdu[pos+1] << 16) +
+ (pdu[pos+2] << 8) +
+ (pdu[pos+3]);
}
@@ -1465,34 +1463,34 @@ PJ_DEF(pj_status_t) pj_stun_msg_check(const pj_uint8_t *pdu, unsigned pdu_len,
if (*pdu != 0x00 && *pdu != 0x01)
return PJLIB_UTIL_ESTUNINMSGTYPE;
- /* If magic is set, then there is great possibility that this is
- * a STUN message.
- */
- if (GET_VAL32(pdu, 4) != PJ_STUN_MAGIC)
- return PJLIB_UTIL_ESTUNNOTMAGIC;
-
/* Check the PDU length */
msg_len = GET_VAL16(pdu, 2);
- if ((msg_len > pdu_len) ||
- ((options & PJ_STUN_IS_DATAGRAM) && msg_len != pdu_len))
+ if ((msg_len + 20 > pdu_len) ||
+ ((options & PJ_STUN_IS_DATAGRAM) && msg_len + 20 != pdu_len))
{
return PJLIB_UTIL_ESTUNINMSGLEN;
}
- /* Check if FINGERPRINT attribute is present */
- if (GET_VAL16(pdu, msg_len + 20) == PJ_STUN_ATTR_FINGERPRINT) {
- pj_uint16_t attr_len = GET_VAL16(pdu, msg_len + 22);
- pj_uint32_t fingerprint = GET_VAL32(pdu, msg_len + 24);
- pj_uint32_t crc;
+ /* If magic is set, then there is great possibility that this is
+ * a STUN message.
+ */
+ if (GET_VAL32(pdu, 4) == PJ_STUN_MAGIC) {
+
+ /* Check if FINGERPRINT attribute is present */
+ if (GET_VAL16(pdu, msg_len + 20) == PJ_STUN_ATTR_FINGERPRINT) {
+ pj_uint16_t attr_len = GET_VAL16(pdu, msg_len + 22);
+ pj_uint32_t fingerprint = GET_VAL32(pdu, msg_len + 24);
+ pj_uint32_t crc;
- if (attr_len != 4)
- return PJLIB_UTIL_ESTUNINATTRLEN;
+ if (attr_len != 4)
+ return PJLIB_UTIL_ESTUNINATTRLEN;
- crc = pj_crc32_calc(pdu, msg_len + 20);
- crc ^= STUN_XOR_FINGERPRINT;
+ crc = pj_crc32_calc(pdu, msg_len + 20);
+ crc ^= STUN_XOR_FINGERPRINT;
- if (crc != fingerprint)
- return PJLIB_UTIL_ESTUNFINGERPRINT;
+ if (crc != fingerprint)
+ return PJLIB_UTIL_ESTUNFINGERPRINT;
+ }
}
/* Could be a STUN message */
@@ -1819,7 +1817,7 @@ static void calc_md5_key(pj_uint8_t digest[16],
/*
* Print the message structure to a buffer.
*/
-PJ_DEF(pj_status_t) pj_stun_msg_encode(const pj_stun_msg *msg,
+PJ_DEF(pj_status_t) pj_stun_msg_encode(pj_stun_msg *msg,
pj_uint8_t *buf, unsigned buf_size,
unsigned options,
const pj_str_t *password,
@@ -1833,7 +1831,7 @@ PJ_DEF(pj_status_t) pj_stun_msg_encode(const pj_stun_msg *msg,
pj_stun_fingerprint_attr *afingerprint = NULL;
unsigned printed;
pj_status_t status;
- unsigned i, length;
+ unsigned i;
PJ_ASSERT_RETURN(msg && buf && buf_size, PJ_EINVAL);
@@ -1898,18 +1896,18 @@ PJ_DEF(pj_status_t) pj_stun_msg_encode(const pj_stun_msg *msg,
* Note that length is not including the 20 bytes header.
*/
if (amsg_integrity && afingerprint) {
- length = (pj_uint16_t)((buf - start) - 20 + 24 + 8);
+ msg->hdr.length = (pj_uint16_t)((buf - start) - 20 + 24 + 8);
} else if (amsg_integrity) {
- length = (pj_uint16_t)((buf - start) - 20 + 24);
+ msg->hdr.length = (pj_uint16_t)((buf - start) - 20 + 24);
} else if (afingerprint) {
- length = (pj_uint16_t)((buf - start) - 20 + 8);
+ msg->hdr.length = (pj_uint16_t)((buf - start) - 20 + 8);
} else {
- length = (pj_uint16_t)((buf - start) - 20);
+ msg->hdr.length = (pj_uint16_t)((buf - start) - 20);
}
/* hdr->length = pj_htons(length); */
- *(buf+2) = (pj_uint8_t)((length >> 8) & 0x00FF);
- *(buf+3) = (pj_uint8_t)(length & 0x00FF);
+ start[2] = (pj_uint8_t)((msg->hdr.length >> 8) & 0x00FF);
+ start[3] = (pj_uint8_t)(msg->hdr.length & 0x00FF);
/* Calculate message integrity, if present */
if (amsg_integrity != NULL) {
@@ -2088,7 +2086,7 @@ PJ_DEF(pj_status_t) pj_stun_verify_credential( const pj_uint8_t *pkt,
if (p_response)
*p_response = NULL;
- if (PJ_STUN_IS_REQUEST(msg->hdr.type))
+ if (!PJ_STUN_IS_REQUEST(msg->hdr.type))
p_response = NULL;
/* Get realm and nonce */
diff --git a/pjlib-util/src/pjlib-util/stun_session.c b/pjlib-util/src/pjlib-util/stun_session.c
index ce15a610..579f0aca 100644
--- a/pjlib-util/src/pjlib-util/stun_session.c
+++ b/pjlib-util/src/pjlib-util/stun_session.c
@@ -23,6 +23,7 @@ struct pj_stun_session
{
pj_stun_endpoint *endpt;
pj_pool_t *pool;
+ pj_mutex_t *mutex;
pj_stun_session_cb cb;
void *user_data;
@@ -47,7 +48,7 @@ struct pj_stun_session
#endif
#if PJ_LOG_MAX_LEVEL >= 4
-# define LOG_ERR_(sess, title, rc)
+# define LOG_ERR_(sess, title, rc) stun_perror(sess, title, rc)
static void stun_perror(pj_stun_session *sess, const char *title,
pj_status_t status)
{
@@ -59,7 +60,7 @@ static void stun_perror(pj_stun_session *sess, const char *title,
}
#else
-# define ERR_(sess, title, rc)
+# define LOG_ERR_(sess, title, rc)
#endif
#define TDATA_POOL_SIZE 1024
@@ -115,12 +116,10 @@ static pj_stun_tx_data* tsx_lookup(pj_stun_session *sess,
}
static pj_status_t create_tdata(pj_stun_session *sess,
- unsigned msg_type,
void *user_data,
pj_stun_tx_data **p_tdata)
{
pj_pool_t *pool;
- pj_status_t status;
pj_stun_tx_data *tdata;
/* Create pool and initialize basic tdata attributes */
@@ -133,22 +132,35 @@ static pj_status_t create_tdata(pj_stun_session *sess,
tdata->sess = sess;
tdata->user_data = user_data;
+ *p_tdata = tdata;
+
+ return PJ_SUCCESS;
+}
+
+static pj_status_t create_request_tdata(pj_stun_session *sess,
+ unsigned msg_type,
+ void *user_data,
+ pj_stun_tx_data **p_tdata)
+{
+ pj_status_t status;
+ pj_stun_tx_data *tdata;
+
+ status = create_tdata(sess, user_data, &tdata);
+ if (status != PJ_SUCCESS)
+ return status;
+
/* Create STUN message */
- status = pj_stun_msg_create(pool, msg_type, PJ_STUN_MAGIC,
+ status = pj_stun_msg_create(tdata->pool, msg_type, PJ_STUN_MAGIC,
NULL, &tdata->msg);
if (status != PJ_SUCCESS) {
- pj_pool_release(pool);
+ pj_pool_release(tdata->pool);
return status;
}
- /* If this is a request, then copy the request's transaction ID
- * as the transaction key.
- */
- if (PJ_STUN_IS_REQUEST(msg_type)) {
- pj_assert(sizeof(tdata->client_key)==sizeof(tdata->msg->hdr.tsx_id));
- pj_memcpy(tdata->client_key, tdata->msg->hdr.tsx_id,
- sizeof(tdata->msg->hdr.tsx_id));
- }
+ /* copy the request's transaction ID as the transaction key. */
+ pj_assert(sizeof(tdata->client_key)==sizeof(tdata->msg->hdr.tsx_id));
+ pj_memcpy(tdata->client_key, tdata->msg->hdr.tsx_id,
+ sizeof(tdata->msg->hdr.tsx_id));
*p_tdata = tdata;
@@ -166,10 +178,21 @@ static void destroy_tdata(pj_stun_tx_data *tdata)
pj_pool_release(tdata->pool);
}
-static pj_status_t session_apply_req(pj_stun_session *sess,
+/*
+ * Destroy the transmit data.
+ */
+PJ_DEF(void) pj_stun_msg_destroy_tdata( pj_stun_session *sess,
+ pj_stun_tx_data *tdata)
+{
+ PJ_UNUSED_ARG(sess);
+ destroy_tdata(tdata);
+}
+
+static pj_status_t apply_msg_options(pj_stun_session *sess,
pj_pool_t *pool,
unsigned options,
- pj_stun_msg *msg)
+ pj_stun_msg *msg,
+ pj_str_t **p_passwd)
{
pj_status_t status;
@@ -181,8 +204,10 @@ static pj_status_t session_apply_req(pj_stun_session *sess,
pj_stun_msg_integrity_attr *amsgi;
pj_stun_generic_string_attr *arealm;
+ *p_passwd = &sess->l_password;
+
/* Create and add USERNAME attribute */
- status = pj_stun_generic_string_attr_create(sess->pool,
+ status = pj_stun_generic_string_attr_create(pool,
PJ_STUN_ATTR_USERNAME,
&sess->l_username,
&auname);
@@ -192,7 +217,7 @@ static pj_status_t session_apply_req(pj_stun_session *sess,
PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
/* Add REALM only when long term credential is used */
- status = pj_stun_generic_string_attr_create(sess->pool,
+ status = pj_stun_generic_string_attr_create(pool,
PJ_STUN_ATTR_REALM,
&sess->l_realm,
&arealm);
@@ -202,20 +227,20 @@ static pj_status_t session_apply_req(pj_stun_session *sess,
PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
/* Add MESSAGE-INTEGRITY attribute */
- status = pj_stun_msg_integrity_attr_create(sess->pool, &amsgi);
+ status = pj_stun_msg_integrity_attr_create(pool, &amsgi);
PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
status = pj_stun_msg_add_attr(msg, &amsgi->hdr);
PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
- PJ_TODO(COMPUTE_MESSAGE_INTEGRITY1);
-
} else if (options & PJ_STUN_USE_SHORT_TERM_CRED) {
pj_stun_generic_string_attr *auname;
pj_stun_msg_integrity_attr *amsgi;
+ *p_passwd = &sess->s_password;
+
/* Create and add USERNAME attribute */
- status = pj_stun_generic_string_attr_create(sess->pool,
+ status = pj_stun_generic_string_attr_create(pool,
PJ_STUN_ATTR_USERNAME,
&sess->s_username,
&auname);
@@ -225,20 +250,21 @@ static pj_status_t session_apply_req(pj_stun_session *sess,
PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
/* Add MESSAGE-INTEGRITY attribute */
- status = pj_stun_msg_integrity_attr_create(sess->pool, &amsgi);
+ status = pj_stun_msg_integrity_attr_create(pool, &amsgi);
PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
status = pj_stun_msg_add_attr(msg, &amsgi->hdr);
PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
- PJ_TODO(COMPUTE_MESSAGE_INTEGRITY2);
+ } else {
+ *p_passwd = NULL;
}
/* Add FINGERPRINT attribute if necessary */
if (options & PJ_STUN_USE_FINGERPRINT) {
pj_stun_fingerprint_attr *af;
- status = pj_stun_generic_uint_attr_create(sess->pool,
+ status = pj_stun_generic_uint_attr_create(pool,
PJ_STUN_ATTR_FINGERPRINT,
0, &af);
PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
@@ -259,25 +285,9 @@ static void tsx_on_complete(pj_stun_client_tsx *tsx,
tdata = (pj_stun_tx_data*) pj_stun_client_tsx_get_data(tsx);
- switch (PJ_STUN_GET_METHOD(tdata->msg->hdr.type)) {
- case PJ_STUN_BINDING_METHOD:
- tdata->sess->cb.on_bind_response(tdata->sess, status, tdata, response);
- break;
- case PJ_STUN_ALLOCATE_METHOD:
- tdata->sess->cb.on_allocate_response(tdata->sess, status,
- tdata, response);
- break;
- case PJ_STUN_SET_ACTIVE_DESTINATION_METHOD:
- tdata->sess->cb.on_set_active_destination_response(tdata->sess, status,
- tdata, response);
- break;
- case PJ_STUN_CONNECT_METHOD:
- tdata->sess->cb.on_connect_response(tdata->sess, status, tdata,
- response);
- break;
- default:
- pj_assert(!"Unknown method");
- break;
+ if (tdata->sess->cb.on_request_complete) {
+ (*tdata->sess->cb.on_request_complete)(tdata->sess, status, tdata,
+ response);
}
}
@@ -289,8 +299,8 @@ static pj_status_t tsx_on_send_msg(pj_stun_client_tsx *tsx,
tdata = (pj_stun_tx_data*) pj_stun_client_tsx_get_data(tsx);
- return tdata->sess->cb.on_send_msg(tdata, stun_pkt, pkt_size,
- tdata->addr_len, tdata->dst_addr);
+ return tdata->sess->cb.on_send_msg(tdata->sess, stun_pkt, pkt_size,
+ tdata->dst_addr, tdata->addr_len);
}
/* **************************************************************************/
@@ -302,9 +312,13 @@ PJ_DEF(pj_status_t) pj_stun_session_create( pj_stun_endpoint *endpt,
{
pj_pool_t *pool;
pj_stun_session *sess;
+ pj_status_t status;
PJ_ASSERT_RETURN(endpt && cb && p_sess, PJ_EINVAL);
+ if (name==NULL)
+ name = "sess%p";
+
pool = pj_pool_create(endpt->pf, name, 4000, 4000, NULL);
PJ_ASSERT_RETURN(pool, PJ_ENOMEM);
@@ -315,9 +329,13 @@ PJ_DEF(pj_status_t) pj_stun_session_create( pj_stun_endpoint *endpt,
pj_list_init(&sess->pending_request_list);
- *p_sess = sess;
+ status = pj_mutex_create_recursive(pool, name, &sess->mutex);
+ if (status != PJ_SUCCESS) {
+ pj_pool_release(pool);
+ return status;
+ }
- PJ_TODO(MUTEX_PROTECTION);
+ *p_sess = sess;
return PJ_SUCCESS;
}
@@ -326,6 +344,7 @@ PJ_DEF(pj_status_t) pj_stun_session_destroy(pj_stun_session *sess)
{
PJ_ASSERT_RETURN(sess, PJ_EINVAL);
+ pj_mutex_destroy(sess->mutex);
pj_pool_release(sess->pool);
return PJ_SUCCESS;
@@ -336,7 +355,9 @@ PJ_DEF(pj_status_t) pj_stun_session_set_user_data( pj_stun_session *sess,
void *user_data)
{
PJ_ASSERT_RETURN(sess, PJ_EINVAL);
+ pj_mutex_lock(sess->mutex);
sess->user_data = user_data;
+ pj_mutex_unlock(sess->mutex);
return PJ_SUCCESS;
}
@@ -355,9 +376,12 @@ pj_stun_session_set_long_term_credential(pj_stun_session *sess,
pj_str_t nil = { NULL, 0 };
PJ_ASSERT_RETURN(sess, PJ_EINVAL);
+
+ pj_mutex_lock(sess->mutex);
pj_strdup_with_null(sess->pool, &sess->l_realm, realm ? realm : &nil);
pj_strdup_with_null(sess->pool, &sess->l_username, user ? user : &nil);
pj_strdup_with_null(sess->pool, &sess->l_password, passwd ? passwd : &nil);
+ pj_mutex_unlock(sess->mutex);
return PJ_SUCCESS;
}
@@ -371,8 +395,11 @@ pj_stun_session_set_short_term_credential(pj_stun_session *sess,
pj_str_t nil = { NULL, 0 };
PJ_ASSERT_RETURN(sess, PJ_EINVAL);
+
+ pj_mutex_lock(sess->mutex);
pj_strdup_with_null(sess->pool, &sess->s_username, user ? user : &nil);
pj_strdup_with_null(sess->pool, &sess->s_password, passwd ? passwd : &nil);
+ pj_mutex_unlock(sess->mutex);
return PJ_SUCCESS;
}
@@ -386,7 +413,8 @@ PJ_DEF(pj_status_t) pj_stun_session_create_bind_req(pj_stun_session *sess,
PJ_ASSERT_RETURN(sess && p_tdata, PJ_EINVAL);
- status = create_tdata(sess, PJ_STUN_BINDING_REQUEST, NULL, &tdata);
+ status = create_request_tdata(sess, PJ_STUN_BINDING_REQUEST, NULL,
+ &tdata);
if (status != PJ_SUCCESS)
return status;
@@ -397,6 +425,8 @@ PJ_DEF(pj_status_t) pj_stun_session_create_bind_req(pj_stun_session *sess,
PJ_DEF(pj_status_t) pj_stun_session_create_allocate_req(pj_stun_session *sess,
pj_stun_tx_data **p_tdata)
{
+ PJ_UNUSED_ARG(sess);
+ PJ_UNUSED_ARG(p_tdata);
PJ_ASSERT_RETURN(PJ_FALSE, PJ_ENOTSUP);
}
@@ -405,12 +435,16 @@ PJ_DEF(pj_status_t)
pj_stun_session_create_set_active_destination_req(pj_stun_session *sess,
pj_stun_tx_data **p_tdata)
{
+ PJ_UNUSED_ARG(sess);
+ PJ_UNUSED_ARG(p_tdata);
PJ_ASSERT_RETURN(PJ_FALSE, PJ_ENOTSUP);
}
PJ_DEF(pj_status_t) pj_stun_session_create_connect_req( pj_stun_session *sess,
pj_stun_tx_data **p_tdata)
{
+ PJ_UNUSED_ARG(sess);
+ PJ_UNUSED_ARG(p_tdata);
PJ_ASSERT_RETURN(PJ_FALSE, PJ_ENOTSUP);
}
@@ -418,27 +452,102 @@ PJ_DEF(pj_status_t)
pj_stun_session_create_connection_status_ind(pj_stun_session *sess,
pj_stun_tx_data **p_tdata)
{
+ PJ_UNUSED_ARG(sess);
+ PJ_UNUSED_ARG(p_tdata);
PJ_ASSERT_RETURN(PJ_FALSE, PJ_ENOTSUP);
}
PJ_DEF(pj_status_t) pj_stun_session_create_send_ind( pj_stun_session *sess,
pj_stun_tx_data **p_tdata)
{
+ PJ_UNUSED_ARG(sess);
+ PJ_UNUSED_ARG(p_tdata);
PJ_ASSERT_RETURN(PJ_FALSE, PJ_ENOTSUP);
}
PJ_DEF(pj_status_t) pj_stun_session_create_data_ind( pj_stun_session *sess,
pj_stun_tx_data **p_tdata)
{
+ PJ_UNUSED_ARG(sess);
+ PJ_UNUSED_ARG(p_tdata);
PJ_ASSERT_RETURN(PJ_FALSE, PJ_ENOTSUP);
}
+
+/*
+ * Create a STUN response message.
+ */
+PJ_DEF(pj_status_t) pj_stun_session_create_response( pj_stun_session *sess,
+ const pj_stun_msg *req,
+ unsigned err_code,
+ const pj_str_t *err_msg,
+ pj_stun_tx_data **p_tdata)
+{
+ pj_status_t status;
+ pj_stun_tx_data *tdata;
+
+ status = create_tdata(sess, NULL, &tdata);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Create STUN response message */
+ status = pj_stun_msg_create_response(tdata->pool, req, err_code, err_msg,
+ &tdata->msg);
+ if (status != PJ_SUCCESS) {
+ pj_pool_release(tdata->pool);
+ return status;
+ }
+
+ /* copy the request's transaction ID as the transaction key. */
+ pj_assert(sizeof(tdata->client_key)==sizeof(req->hdr.tsx_id));
+ pj_memcpy(tdata->client_key, req->hdr.tsx_id, sizeof(req->hdr.tsx_id));
+
+ *p_tdata = tdata;
+
+ return PJ_SUCCESS;
+}
+
+
+/* Print outgoing message to log */
+static void dump_tx_msg(pj_stun_session *sess, const pj_stun_msg *msg,
+ unsigned pkt_size, const pj_sockaddr_t *addr)
+{
+ const char *dst_name;
+ int dst_port;
+ const pj_sockaddr *dst = (const pj_sockaddr*)addr;
+ char buf[512];
+
+ if (dst->sa_family == PJ_AF_INET) {
+ const pj_sockaddr_in *dst4 = (const pj_sockaddr_in*)dst;
+ dst_name = pj_inet_ntoa(dst4->sin_addr);
+ dst_port = pj_ntohs(dst4->sin_port);
+ } else if (dst->sa_family == PJ_AF_INET6) {
+ const pj_sockaddr_in6 *dst6 = (const pj_sockaddr_in6*)dst;
+ dst_name = "IPv6";
+ dst_port = pj_ntohs(dst6->sin6_port);
+ } else {
+ LOG_ERR_(sess, "Invalid address family", PJ_EINVAL);
+ return;
+ }
+
+ PJ_LOG(5,(SNAME(sess),
+ "TX %d bytes STUN message to %s:%d:\n"
+ "--- begin STUN message ---\n"
+ "%s"
+ "--- end of STUN message ---\n",
+ pkt_size, dst_name, dst_port,
+ pj_stun_msg_dump(msg, buf, sizeof(buf), NULL)));
+
+}
+
+
PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess,
unsigned options,
- unsigned addr_len,
const pj_sockaddr_t *server,
+ unsigned addr_len,
pj_stun_tx_data *tdata)
{
+ pj_str_t *password;
pj_status_t status;
PJ_ASSERT_RETURN(sess && addr_len && server && tdata, PJ_EINVAL);
@@ -447,39 +556,16 @@ PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess,
tdata->max_len = PJ_STUN_MAX_PKT_LEN;
tdata->pkt = pj_pool_alloc(tdata->pool, tdata->max_len);
- if (PJ_LOG_MAX_LEVEL >= 5) {
- char *buf = (char*) tdata->pkt;
- const char *dst_name;
- int dst_port;
- const pj_sockaddr *dst = (const pj_sockaddr*)server;
-
- if (dst->sa_family == PJ_AF_INET) {
- const pj_sockaddr_in *dst4 = (const pj_sockaddr_in*)dst;
- dst_name = pj_inet_ntoa(dst4->sin_addr);
- dst_port = pj_ntohs(dst4->sin_port);
- } else if (dst->sa_family == PJ_AF_INET6) {
- const pj_sockaddr_in6 *dst6 = (const pj_sockaddr_in6*)dst;
- dst_name = "IPv6";
- dst_port = pj_ntohs(dst6->sin6_port);
- } else {
- LOG_ERR_(sess, "Invalid address family", PJ_EINVAL);
- return PJ_EINVAL;
- }
-
- PJ_LOG(5,(SNAME(sess),
- "Sending STUN message to %s:%d:\n"
- "--- begin STUN message ---\n"
- "%s"
- "--- end of STUN message ---\n",
- dst_name, dst_port,
- pj_stun_msg_dump(tdata->msg, buf, tdata->max_len, NULL)));
- }
+ /* Start locking the session now */
+ pj_mutex_lock(sess->mutex);
/* Apply options */
- status = session_apply_req(sess, tdata->pool, options, tdata->msg);
+ status = apply_msg_options(sess, tdata->pool, options,
+ tdata->msg, &password);
if (status != PJ_SUCCESS) {
+ pj_stun_msg_destroy_tdata(sess, tdata);
+ pj_mutex_unlock(sess->mutex);
LOG_ERR_(sess, "Error applying options", status);
- destroy_tdata(tdata);
return status;
}
@@ -487,11 +573,15 @@ PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess,
status = pj_stun_msg_encode(tdata->msg, tdata->pkt, tdata->max_len,
0, NULL, &tdata->pkt_size);
if (status != PJ_SUCCESS) {
+ pj_stun_msg_destroy_tdata(sess, tdata);
+ pj_mutex_unlock(sess->mutex);
LOG_ERR_(sess, "STUN encode() error", status);
- destroy_tdata(tdata);
return status;
}
+ /* Dump packet */
+ dump_tx_msg(sess, tdata->msg, tdata->pkt_size, server);
+
/* If this is a STUN request message, then send the request with
* a new STUN client transaction.
*/
@@ -511,8 +601,9 @@ PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess,
status = pj_stun_client_tsx_send_msg(tdata->client_tsx, PJ_TRUE,
tdata->pkt, tdata->pkt_size);
if (status != PJ_SUCCESS && status != PJ_EPENDING) {
+ pj_stun_msg_destroy_tdata(sess, tdata);
+ pj_mutex_unlock(sess->mutex);
LOG_ERR_(sess, "Error sending STUN request", status);
- destroy_tdata(tdata);
return status;
}
@@ -521,25 +612,151 @@ PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess,
} else {
/* Otherwise for non-request message, send directly to transport. */
- status = sess->cb.on_send_msg(tdata, tdata->pkt, tdata->pkt_size,
- addr_len, server);
+ status = sess->cb.on_send_msg(sess, tdata->pkt, tdata->pkt_size,
+ server, addr_len);
if (status != PJ_SUCCESS && status != PJ_EPENDING) {
LOG_ERR_(sess, "Error sending STUN request", status);
- destroy_tdata(tdata);
- return status;
}
+
+ /* Destroy */
+ pj_stun_msg_destroy_tdata(sess, tdata);
}
+ pj_mutex_unlock(sess->mutex);
return status;
}
+/* Handle incoming response */
+static pj_status_t on_incoming_response(pj_stun_session *sess,
+ pj_stun_msg *msg)
+{
+ pj_stun_tx_data *tdata;
+ pj_status_t status;
+
+ /* Lookup pending client transaction */
+ tdata = tsx_lookup(sess, msg);
+ if (tdata == NULL) {
+ LOG_ERR_(sess, "STUN error finding transaction", PJ_ENOTFOUND);
+ return PJ_ENOTFOUND;
+ }
+
+ /* Pass the response to the transaction.
+ * If the message is accepted, transaction callback will be called,
+ * and this will call the session callback too.
+ */
+ status = pj_stun_client_tsx_on_rx_msg(tdata->client_tsx, msg);
+ if (status != PJ_SUCCESS) {
+ return status;
+ }
+
+ /* If transaction has completed, destroy the transmit data.
+ * This will remove the transaction from the pending list too.
+ */
+ if (pj_stun_client_tsx_is_complete(tdata->client_tsx)) {
+ pj_stun_msg_destroy_tdata(sess, tdata);
+ tdata = NULL;
+ }
+
+ return PJ_SUCCESS;
+}
+
+
+/* Send response */
+static pj_status_t send_response(pj_stun_session *sess, unsigned options,
+ pj_pool_t *pool, pj_stun_msg *response,
+ const pj_sockaddr_t *addr, unsigned addr_len)
+{
+ pj_uint8_t *out_pkt;
+ unsigned out_max_len, out_len;
+ pj_str_t *passwd;
+ pj_status_t status;
+
+ /* Alloc packet buffer */
+ out_max_len = PJ_STUN_MAX_PKT_LEN;
+ out_pkt = pj_pool_alloc(pool, out_max_len);
+
+ /* Apply options */
+ apply_msg_options(sess, pool, options, response, &passwd);
+
+ /* Encode */
+ status = pj_stun_msg_encode(response, out_pkt, out_max_len, 0,
+ passwd, &out_len);
+ if (status != PJ_SUCCESS) {
+ LOG_ERR_(sess, "Error encoding message", status);
+ return status;
+ }
+
+ /* Print log */
+ dump_tx_msg(sess, response, out_len, addr);
+
+ /* Send packet */
+ status = sess->cb.on_send_msg(sess, out_pkt, out_len, addr, addr_len);
+
+ return status;
+}
+
+/* Handle incoming request */
+static pj_status_t on_incoming_request(pj_stun_session *sess,
+ pj_pool_t *tmp_pool,
+ const pj_uint8_t *in_pkt,
+ unsigned in_pkt_len,
+ const pj_stun_msg *msg,
+ const pj_sockaddr_t *src_addr,
+ unsigned src_addr_len)
+{
+ pj_status_t status;
+
+ /* Distribute to handler, or respond with Bad Request */
+ if (sess->cb.on_rx_request) {
+ status = (*sess->cb.on_rx_request)(sess, in_pkt, in_pkt_len, msg,
+ src_addr, src_addr_len);
+ } else {
+ pj_stun_msg *response = NULL;
+
+ status = pj_stun_msg_create_response(tmp_pool, msg,
+ PJ_STUN_STATUS_BAD_REQUEST, NULL,
+ &response);
+ if (status == PJ_SUCCESS && response) {
+ status = send_response(sess, 0, tmp_pool, response,
+ src_addr, src_addr_len);
+ }
+ }
+
+ return status;
+}
+
+
+/* Handle incoming indication */
+static pj_status_t on_incoming_indication(pj_stun_session *sess,
+ pj_pool_t *tmp_pool,
+ const pj_uint8_t *in_pkt,
+ unsigned in_pkt_len,
+ const pj_stun_msg *msg,
+ const pj_sockaddr_t *src_addr,
+ unsigned src_addr_len)
+{
+ PJ_UNUSED_ARG(tmp_pool);
+
+ /* Distribute to handler */
+ if (sess->cb.on_rx_indication) {
+ return (*sess->cb.on_rx_indication)(sess, in_pkt, in_pkt_len, msg,
+ src_addr, src_addr_len);
+ } else {
+ return PJ_SUCCESS;
+ }
+}
+
+
PJ_DEF(pj_status_t) pj_stun_session_on_rx_pkt(pj_stun_session *sess,
const void *packet,
pj_size_t pkt_size,
- unsigned *parsed_len)
+ unsigned options,
+ unsigned *parsed_len,
+ const pj_sockaddr_t *src_addr,
+ unsigned src_addr_len)
{
pj_stun_msg *msg, *response;
pj_pool_t *tmp_pool;
@@ -554,12 +771,13 @@ PJ_DEF(pj_status_t) pj_stun_session_on_rx_pkt(pj_stun_session *sess,
/* Try to parse the message */
status = pj_stun_msg_decode(tmp_pool, (const pj_uint8_t*)packet,
- pkt_size, 0, &msg, parsed_len,
- &response);
+ pkt_size, options,
+ &msg, parsed_len, &response);
if (status != PJ_SUCCESS) {
LOG_ERR_(sess, "STUN msg_decode() error", status);
if (response) {
- PJ_TODO(SEND_RESPONSE);
+ send_response(sess, 0, tmp_pool, response,
+ src_addr, src_addr_len);
}
pj_pool_release(tmp_pool);
return status;
@@ -567,61 +785,39 @@ PJ_DEF(pj_status_t) pj_stun_session_on_rx_pkt(pj_stun_session *sess,
dump = pj_pool_alloc(tmp_pool, PJ_STUN_MAX_PKT_LEN);
- PJ_LOG(4,(SNAME(sess),
+ PJ_LOG(4,(SNAME(sess),
"RX STUN message:\n"
- "--- begin STUN message ---"
+ "--- begin STUN message ---\n"
"%s"
"--- end of STUN message ---\n",
pj_stun_msg_dump(msg, dump, PJ_STUN_MAX_PKT_LEN, NULL)));
+ pj_mutex_lock(sess->mutex);
if (PJ_STUN_IS_RESPONSE(msg->hdr.type) ||
PJ_STUN_IS_ERROR_RESPONSE(msg->hdr.type))
{
- pj_stun_tx_data *tdata;
-
- /* Lookup pending client transaction */
- tdata = tsx_lookup(sess, msg);
- if (tdata == NULL) {
- LOG_ERR_(sess, "STUN error finding transaction", PJ_ENOTFOUND);
- pj_pool_release(tmp_pool);
- return PJ_ENOTFOUND;
- }
-
- /* Pass the response to the transaction.
- * If the message is accepted, transaction callback will be called,
- * and this will call the session callback too.
- */
- status = pj_stun_client_tsx_on_rx_msg(tdata->client_tsx, msg);
- if (status != PJ_SUCCESS) {
- pj_pool_release(tmp_pool);
- return status;
- }
-
- /* If transaction has completed, destroy the transmit data.
- * This will remove the transaction from the pending list too.
- */
- if (pj_stun_client_tsx_is_complete(tdata->client_tsx)) {
- destroy_tdata(tdata);
- tdata = NULL;
- }
-
- pj_pool_release(tmp_pool);
- return PJ_SUCCESS;
+ status = on_incoming_response(sess, msg);
} else if (PJ_STUN_IS_REQUEST(msg->hdr.type)) {
- PJ_TODO(HANDLE_INCOMING_STUN_REQUEST);
+ status = on_incoming_request(sess, tmp_pool, packet, pkt_size, msg,
+ src_addr, src_addr_len);
} else if (PJ_STUN_IS_INDICATION(msg->hdr.type)) {
- PJ_TODO(HANDLE_INCOMING_STUN_INDICATION);
+ status = on_incoming_indication(sess, tmp_pool, packet, pkt_size,
+ msg, src_addr, src_addr_len);
} else {
pj_assert(!"Unexpected!");
+ status = PJ_EBUG;
}
+ pj_mutex_unlock(sess->mutex);
+
pj_pool_release(tmp_pool);
- return PJ_ENOTSUP;
+ return status;
}
+
diff --git a/pjlib-util/src/pjstun-srv-test/server.h b/pjlib-util/src/pjstun-srv-test/server.h
deleted file mode 100644
index 9de5adce..00000000
--- a/pjlib-util/src/pjstun-srv-test/server.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2005 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 __PJSTUN_SERVER_H__
-#define __PJSTUN_SERVER_H__
-
-
-#define MAX_SERVICE 16
-#define MAX_PKT_LEN 512
-
-struct service
-{
- unsigned index;
- pj_uint16_t port;
- pj_bool_t is_stream;
- pj_sock_t sock;
- pj_ioqueue_key_t *key;
- pj_ioqueue_op_key_t recv_opkey,
- send_opkey;
-
- int src_addr_len;
- pj_sockaddr_in src_addr;
- pj_ssize_t rx_pkt_len;
- pj_uint8_t rx_pkt[MAX_PKT_LEN];
- pj_uint8_t tx_pkt[MAX_PKT_LEN];
-};
-
-struct stun_server_tag
-{
- pj_caching_pool cp;
- pj_pool_t *pool;
- pj_ioqueue_t *ioqueue;
- unsigned service_cnt;
- struct service services[MAX_SERVICE];
-
- pj_bool_t thread_quit_flag;
- unsigned thread_cnt;
- pj_thread_t *threads[16];
-
-};
-
-extern struct stun_server_tag server;
-
-
-#endif /* __PJSTUN_SERVER_H__ */
-
diff --git a/pjlib-util/src/pjstun-srv-test/server_main.c b/pjlib-util/src/pjstun-srv-test/server_main.c
index 46dc2752..6c15b5ff 100644
--- a/pjlib-util/src/pjstun-srv-test/server_main.c
+++ b/pjlib-util/src/pjstun-srv-test/server_main.c
@@ -18,7 +18,6 @@
*/
#include <pjlib-util.h>
#include <pjlib.h>
-#include "server.h"
#include <stdio.h>
#include <conio.h>
@@ -26,12 +25,47 @@
#define THIS_FILE "server_main.c"
#define MAX_THREADS 8
+#define MAX_SERVICE 16
+#define MAX_PKT_LEN 512
-struct stun_server_tag server;
+struct service
+{
+ unsigned index;
+ pj_uint16_t port;
+ pj_bool_t is_stream;
+ pj_sock_t sock;
+ pj_ioqueue_key_t *key;
+ pj_ioqueue_op_key_t recv_opkey,
+ send_opkey;
+
+ pj_stun_session *sess;
+
+ int src_addr_len;
+ pj_sockaddr_in src_addr;
+ pj_ssize_t rx_pkt_len;
+ pj_uint8_t rx_pkt[MAX_PKT_LEN];
+ pj_uint8_t tx_pkt[MAX_PKT_LEN];
+};
+
+static struct stun_server
+{
+ pj_caching_pool cp;
+ pj_pool_t *pool;
+ pj_stun_endpoint *endpt;
+ pj_ioqueue_t *ioqueue;
+ pj_timer_heap_t *timer_heap;
+ unsigned service_cnt;
+ struct service services[MAX_SERVICE];
+
+ pj_bool_t thread_quit_flag;
+ unsigned thread_cnt;
+ pj_thread_t *threads[16];
+} server;
-pj_status_t server_perror(const char *sender, const char *title,
- pj_status_t status)
+
+static pj_status_t server_perror(const char *sender, const char *title,
+ pj_status_t status)
{
char errmsg[PJ_ERR_MSG_SIZE];
pj_strerror(status, errmsg, sizeof(errmsg));
@@ -42,209 +76,168 @@ pj_status_t server_perror(const char *sender, const char *title,
}
-static pj_status_t create_response(pj_pool_t *pool,
- const pj_stun_msg *req_msg,
- unsigned err_code,
- unsigned uattr_cnt,
- pj_uint16_t uattr_types[],
- pj_stun_msg **p_response)
-{
- pj_uint32_t msg_type = req_msg->hdr.type;
- pj_stun_msg *response;
- pj_status_t status;
-
- status = pj_stun_msg_create_response(pool, req_msg, err_code, NULL,
- &response);
- if (status != PJ_SUCCESS)
- return status;
-
- /* Add unknown_attribute attributes if err_code is 420 */
- if (err_code == PJ_STUN_STATUS_UNKNOWN_ATTRIBUTE) {
- pj_stun_unknown_attr *uattr;
-
- status = pj_stun_unknown_attr_create(pool, uattr_cnt, uattr_types,
- &uattr);
- if (status != PJ_SUCCESS)
- return status;
-
- pj_stun_msg_add_attr(response, &uattr->hdr);
- }
-
- *p_response = response;
- return PJ_SUCCESS;
-}
-
-
-static pj_status_t send_msg(struct service *svc, const pj_stun_msg *msg)
+/* Callback to be called to send outgoing message */
+static pj_status_t on_send_msg(pj_stun_session *sess,
+ const void *pkt,
+ pj_size_t pkt_size,
+ const pj_sockaddr_t *dst_addr,
+ unsigned addr_len)
{
- unsigned tx_pkt_len;
+ struct service *svc;
pj_ssize_t length;
pj_status_t status;
- /* Print to log */
- PJ_LOG(4,(THIS_FILE, "TX STUN message: \n"
- "--- begin STUN message ---\n"
- "%s"
- "--- end of STUN message ---\n",
- pj_stun_msg_dump(msg, svc->tx_pkt, sizeof(svc->tx_pkt), NULL)));
-
- /* Encode packet */
- tx_pkt_len = sizeof(svc->tx_pkt);
- status = pj_stun_msg_encode(msg, svc->tx_pkt, tx_pkt_len, 0,
- NULL, &tx_pkt_len);
- if (status != PJ_SUCCESS)
- return status;
-
- length = tx_pkt_len;
+ svc = (struct service*) pj_stun_session_get_user_data(sess);
/* Send packet */
+ length = pkt_size;
if (svc->is_stream) {
- status = pj_ioqueue_send(svc->key, &svc->send_opkey, svc->tx_pkt,
- &length, 0);
+ status = pj_ioqueue_send(svc->key, &svc->send_opkey, pkt, &length, 0);
} else {
- status = pj_ioqueue_sendto(svc->key, &svc->send_opkey, svc->tx_pkt,
- &length, 0, &svc->src_addr,
- svc->src_addr_len);
+#if 0
+ pj_pool_t *pool;
+ char *buf;
+ pj_stun_msg *msg;
+
+ pool = pj_pool_create(&server.cp.factory, "", 4000, 4000, NULL);
+ status = pj_stun_msg_decode(pool, pkt, pkt_size, PJ_STUN_CHECK_PACKET, &msg, NULL, NULL);
+ buf = pj_pool_alloc(pool, 512);
+ PJ_LOG(3,("", "%s", pj_stun_msg_dump(msg, buf, 512, NULL)));
+#endif
+ status = pj_ioqueue_sendto(svc->key, &svc->send_opkey, pkt, &length,
+ 0, dst_addr, addr_len);
}
- PJ_LOG(4,(THIS_FILE, "Sending STUN %s %s",
- pj_stun_get_method_name(msg->hdr.type),
- pj_stun_get_class_name(msg->hdr.type)));
-
return (status == PJ_SUCCESS || status == PJ_EPENDING) ?
PJ_SUCCESS : status;
}
-static pj_status_t err_respond(struct service *svc,
- pj_pool_t *pool,
- const pj_stun_msg *req_msg,
- unsigned err_code,
- unsigned uattr_cnt,
- pj_uint16_t uattr_types[])
+/* Handle STUN binding request */
+static pj_status_t on_rx_binding_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)
{
- pj_stun_msg *response;
+ struct service *svc = (struct service *) pj_stun_session_get_user_data(sess);
+ pj_stun_tx_data *tdata;
+ pj_stun_auth_policy pol;
pj_status_t status;
- /* Create the error response */
- status = create_response(pool, req_msg, err_code,
- uattr_cnt, uattr_types, &response);
- if (status != PJ_SUCCESS) {
- server_perror(THIS_FILE, "Error creating response", status);
+ /* Create response */
+ status = pj_stun_session_create_response(sess, msg, 0, NULL, &tdata);
+ if (status != PJ_SUCCESS)
return status;
- }
- /* Send response */
- status = send_msg(svc, response);
- if (status != PJ_SUCCESS) {
- server_perror(THIS_FILE, "Error sending response", status);
+#if 1
+ pj_memset(&pol, 0, sizeof(pol));
+ pol.type = PJ_STUN_POLICY_STATIC_LONG_TERM;
+ pol.user_data = NULL;
+ pol.data.static_long_term.realm = pj_str("realm");
+ pol.data.static_long_term.username = pj_str("user");
+ pol.data.static_long_term.password = pj_str("password");
+ pol.data.static_long_term.nonce = pj_str("nonce");
+ status = pj_stun_verify_credential(pkt, pkt_len, msg, &pol, tdata->pool,
+ &tdata->msg);
+ if (!tdata->msg)
return status;
- }
-
- return PJ_SUCCESS;
-}
-
-
-static void handle_binding_request(struct service *svc, pj_pool_t *pool,
- const pj_stun_msg *rx_msg)
-{
- pj_stun_msg *response;
- pj_stun_generic_ip_addr_attr *m_attr;
- pj_status_t status;
-
- status = create_response(pool, rx_msg, 0, 0, NULL, &response);
- if (status != PJ_SUCCESS) {
- server_perror(THIS_FILE, "Error creating response", status);
- return;
- }
+#endif
/* Create MAPPED-ADDRESS attribute */
- status = pj_stun_generic_ip_addr_attr_create(pool,
- PJ_STUN_ATTR_MAPPED_ADDR,
- PJ_FALSE,
- svc->src_addr_len,
- &svc->src_addr, &m_attr);
+ status = pj_stun_msg_add_generic_ip_addr_attr(tdata->pool, tdata->msg,
+ PJ_STUN_ATTR_MAPPED_ADDR,
+ PJ_FALSE,
+ src_addr, src_addr_len);
if (status != PJ_SUCCESS) {
server_perror(THIS_FILE, "Error creating response", status);
- return;
+ pj_stun_msg_destroy_tdata(sess, tdata);
+ return status;
}
- pj_stun_msg_add_attr(response, &m_attr->hdr);
/* On the presence of magic, create XOR-MAPPED-ADDRESS attribute */
- if (rx_msg->hdr.magic == PJ_STUN_MAGIC) {
+ if (msg->hdr.magic == PJ_STUN_MAGIC) {
status =
- pj_stun_generic_ip_addr_attr_create(pool,
- PJ_STUN_ATTR_XOR_MAPPED_ADDRESS,
- PJ_TRUE,
- svc->src_addr_len,
- &svc->src_addr, &m_attr);
+ pj_stun_msg_add_generic_ip_addr_attr(tdata->pool, tdata->msg,
+ PJ_STUN_ATTR_XOR_MAPPED_ADDRESS,
+ PJ_TRUE,
+ src_addr, src_addr_len);
if (status != PJ_SUCCESS) {
server_perror(THIS_FILE, "Error creating response", status);
- return;
+ pj_stun_msg_destroy_tdata(sess, tdata);
+ return status;
}
}
/* Send */
- status = send_msg(svc, response);
- if (status != PJ_SUCCESS)
- server_perror(THIS_FILE, "Error sending response", status);
+ status = pj_stun_session_send_msg(sess, 0, src_addr, src_addr_len, tdata);
+ return status;
}
-static void handle_unknown_request(struct service *svc, pj_pool_t *pool,
- pj_stun_msg *rx_msg)
+/* Handle unknown request */
+static pj_status_t on_rx_unknown_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)
{
- err_respond(svc, pool, rx_msg, PJ_STUN_STATUS_BAD_REQUEST, 0, NULL);
+ pj_stun_tx_data *tdata;
+ pj_status_t status;
+
+ /* Create response */
+ status = pj_stun_session_create_response(sess, msg,
+ PJ_STUN_STATUS_BAD_REQUEST,
+ NULL, &tdata);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Send */
+ status = pj_stun_session_send_msg(sess, 0, src_addr, src_addr_len, tdata);
+ return status;
+}
+
+/* Callback to be called by STUN session on incoming STUN requests */
+static pj_status_t on_rx_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)
+{
+ switch (PJ_STUN_GET_METHOD(msg->hdr.type)) {
+ case PJ_STUN_BINDING_METHOD:
+ return on_rx_binding_request(sess, pkt, pkt_len, msg,
+ src_addr, src_addr_len);
+ default:
+ return on_rx_unknown_request(sess, pkt, pkt_len, msg,
+ src_addr, src_addr_len);
+ }
}
+/* Callback on ioqueue read completion */
static void on_read_complete(pj_ioqueue_key_t *key,
pj_ioqueue_op_key_t *op_key,
pj_ssize_t bytes_read)
{
struct service *svc = (struct service *) pj_ioqueue_get_user_data(key);
- pj_pool_t *pool = NULL;
- pj_stun_msg *rx_msg, *response;
- char dump[512];
pj_status_t status;
if (bytes_read <= 0)
goto next_read;
- pool = pj_pool_create(&server.cp.factory, "service", 4000, 4000, NULL);
-
- rx_msg = NULL;
- status = pj_stun_msg_decode(pool, svc->rx_pkt, bytes_read, 0, &rx_msg,
- NULL, &response);
+ /* Handle packet to session */
+ status = pj_stun_session_on_rx_pkt(svc->sess, svc->rx_pkt, bytes_read,
+ PJ_STUN_IS_DATAGRAM | PJ_STUN_CHECK_PACKET,
+ NULL, &svc->src_addr, svc->src_addr_len);
if (status != PJ_SUCCESS) {
- server_perror(THIS_FILE, "STUN msg_decode() error", status);
- if (response) {
- send_msg(svc, response);
- }
- goto next_read;
- }
-
- PJ_LOG(4,(THIS_FILE, "RX STUN message: \n"
- "--- begin STUN message ---\n"
- "%s"
- "--- end of STUN message ---\n",
- pj_stun_msg_dump(rx_msg, dump, sizeof(dump), NULL)));
-
- if (PJ_STUN_IS_REQUEST(rx_msg->hdr.type)) {
- switch (rx_msg->hdr.type) {
- case PJ_STUN_BINDING_REQUEST:
- handle_binding_request(svc, pool, rx_msg);
- break;
- default:
- handle_unknown_request(svc, pool, rx_msg);
- }
-
+ server_perror(THIS_FILE, "Error processing incoming packet", status);
}
next_read:
- if (pool != NULL)
- pj_pool_release(pool);
-
if (bytes_read < 0) {
server_perror(THIS_FILE, "on_read_complete()", -bytes_read);
}
@@ -265,6 +258,7 @@ static pj_status_t init_service(struct service *svc)
{
pj_status_t status;
pj_ioqueue_callback service_callback;
+ pj_stun_session_cb sess_cb;
pj_sockaddr_in addr;
status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &svc->sock);
@@ -279,6 +273,16 @@ static pj_status_t init_service(struct service *svc)
if (status != PJ_SUCCESS)
goto on_error;
+ pj_bzero(&sess_cb, sizeof(sess_cb));
+ sess_cb.on_send_msg = &on_send_msg;
+ sess_cb.on_rx_request = &on_rx_request;
+ status = pj_stun_session_create(server.endpt, "session",
+ &sess_cb, &svc->sess);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ pj_stun_session_set_user_data(svc->sess, (void*)svc);
+
pj_bzero(&service_callback, sizeof(service_callback));
service_callback.on_read_complete = &on_read_complete;
@@ -316,6 +320,7 @@ static int worker_thread(void *p)
while (!server.thread_quit_flag) {
pj_time_val timeout = { 0, 50 };
+ pj_timer_heap_poll(server.timer_heap, NULL);
pj_ioqueue_poll(server.ioqueue, &timeout);
}
@@ -347,6 +352,16 @@ pj_status_t server_init(void)
if (status != PJ_SUCCESS)
return server_perror(THIS_FILE, "pj_ioqueue_create()", status);
+ status = pj_timer_heap_create(server.pool, 1024, &server.timer_heap);
+ if (status != PJ_SUCCESS)
+ return server_perror(THIS_FILE, "Error creating timer heap", status);
+
+ status = pj_stun_endpoint_create(&server.cp.factory, 0,
+ server.ioqueue, server.timer_heap,
+ &server.endpt);
+ if (status != PJ_SUCCESS)
+ return server_perror(THIS_FILE, "Error creating endpoint", status);
+
server.service_cnt = 1;
server.services[0].index = 0;
server.services[0].port = PJ_STUN_PORT;
@@ -361,9 +376,10 @@ pj_status_t server_init(void)
pj_status_t server_main(void)
{
-#if 1
+#if 0
for (;;) {
pj_time_val timeout = { 0, 50 };
+ pj_timer_heap_poll(server.timer_heap, NULL);
pj_ioqueue_poll(server.ioqueue, &timeout);
if (kbhit() && _getch()==27)
@@ -413,8 +429,12 @@ pj_status_t server_destroy(void)
}
}
+ pj_stun_session_destroy(server.services[0].sess);
+ pj_stun_endpoint_destroy(server.endpt);
pj_ioqueue_destroy(server.ioqueue);
pj_pool_release(server.pool);
+
+ pj_pool_factory_dump(&server.cp.factory, PJ_TRUE);
pj_caching_pool_destroy(&server.cp);
pj_shutdown();