summaryrefslogtreecommitdiff
path: root/pjnath
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2008-03-19 23:00:30 +0000
committerBenny Prijono <bennylp@teluu.com>2008-03-19 23:00:30 +0000
commit97dfbff2ebfae83b7cb10517247215d38e4a74fd (patch)
tree0edea19f19049829e444733eced5d892b5a95a84 /pjnath
parentb15dff0bed0f34a3371bce1b17e5a13ade0fb194 (diff)
Related to ticket #485: huge changeset to update STUN relating to managing authentication. See the ticket for the details
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@1877 74dad513-b988-da41-8d7b-12977e46ad98
Diffstat (limited to 'pjnath')
-rw-r--r--pjnath/build/Makefile2
-rw-r--r--pjnath/build/pjnath_test.dsp4
-rw-r--r--pjnath/include/pjnath/stun_auth.h137
-rw-r--r--pjnath/include/pjnath/stun_msg.h47
-rw-r--r--pjnath/include/pjnath/stun_session.h40
-rw-r--r--pjnath/src/pjnath-test/sess_auth.c1125
-rw-r--r--pjnath/src/pjnath-test/stun.c57
-rw-r--r--pjnath/src/pjnath-test/test.c1
-rw-r--r--pjnath/src/pjnath-test/test.h7
-rw-r--r--pjnath/src/pjnath/ice_session.c32
-rw-r--r--pjnath/src/pjnath/stun_auth.c291
-rw-r--r--pjnath/src/pjnath/stun_msg.c344
-rw-r--r--pjnath/src/pjnath/stun_session.c453
-rw-r--r--pjnath/src/pjnath/turn_session.c2
-rw-r--r--pjnath/src/pjturn-srv/allocation.c65
-rw-r--r--pjnath/src/pjturn-srv/auth.c23
-rw-r--r--pjnath/src/pjturn-srv/auth.h12
-rw-r--r--pjnath/src/pjturn-srv/server.c117
-rw-r--r--pjnath/src/pjturn-srv/turn.h23
19 files changed, 2093 insertions, 689 deletions
diff --git a/pjnath/build/Makefile b/pjnath/build/Makefile
index 319893e3..393562f5 100644
--- a/pjnath/build/Makefile
+++ b/pjnath/build/Makefile
@@ -38,7 +38,7 @@ export PJNATH_CFLAGS += $(_CFLAGS)
# Defines for building test application
#
export PJNATH_TEST_SRCDIR = ../src/pjnath-test
-export PJNATH_TEST_OBJS += ice_test.o stun.o test.o
+export PJNATH_TEST_OBJS += ice_test.o stun.o sess_auth.o test.o
export PJNATH_TEST_CFLAGS += $(_CFLAGS)
export PJNATH_TEST_LDFLAGS += $(_LDFLAGS)
export PJNATH_TEST_EXE:=../bin/pjnath-test-$(TARGET_NAME)$(HOST_EXE)
diff --git a/pjnath/build/pjnath_test.dsp b/pjnath/build/pjnath_test.dsp
index cabea6a7..0c6b862f 100644
--- a/pjnath/build/pjnath_test.dsp
+++ b/pjnath/build/pjnath_test.dsp
@@ -95,6 +95,10 @@ SOURCE="..\src\pjnath-test\main.c"
# End Source File
# Begin Source File
+SOURCE="..\src\pjnath-test\sess_auth.c"
+# End Source File
+# Begin Source File
+
SOURCE="..\src\pjnath-test\stun.c"
# End Source File
# Begin Source File
diff --git a/pjnath/include/pjnath/stun_auth.h b/pjnath/include/pjnath/stun_auth.h
index 840e473d..249b0622 100644
--- a/pjnath/include/pjnath/stun_auth.h
+++ b/pjnath/include/pjnath/stun_auth.h
@@ -39,6 +39,29 @@ PJ_BEGIN_DECL
*/
/**
+ * Type of authentication.
+ */
+typedef enum pj_stun_auth_type
+{
+ /**
+ * No authentication.
+ */
+ PJ_STUN_AUTH_NONE = 0,
+
+ /**
+ * Authentication using short term credential.
+ */
+ PJ_STUN_AUTH_SHORT_TERM = 1,
+
+ /**
+ * Authentication using long term credential.
+ */
+ PJ_STUN_AUTH_LONG_TERM = 2
+
+} pj_stun_auth_type;
+
+
+/**
* Type of authentication data in the credential.
*/
typedef enum pj_stun_auth_cred_type
@@ -62,6 +85,26 @@ typedef enum pj_stun_auth_cred_type
/**
+ * Type of encoding applied to the password stored in the credential.
+ */
+typedef enum pj_stun_passwd_type
+{
+ /**
+ * Plain text password.
+ */
+ PJ_STUN_PASSWD_PLAIN = 0,
+
+ /**
+ * Hashed password, valid for long term credential only. The hash value
+ * of the password is calculated as MD5(USERNAME ":" REALM ":" PASSWD)
+ * with all quotes removed from the username and realm values.
+ */
+ PJ_STUN_PASSWD_HASHED = 1
+
+} pj_stun_passwd_type;
+
+
+/**
* This structure contains the descriptions needed to perform server side
* authentication. Depending on the \a type set in the structure, application
* may specify a static username/password combination, or to have callbacks
@@ -89,31 +132,29 @@ typedef struct pj_stun_auth_cred
/**
* If not-empty, it indicates that this is a long term credential.
*/
- pj_str_t realm;
+ pj_str_t realm;
/**
* The username of the credential.
*/
- pj_str_t username;
+ pj_str_t username;
/**
* Data type to indicate the type of password in the \a data field.
- * Value zero indicates that the data contains a plaintext
- * password.
*/
- int data_type;
+ pj_stun_passwd_type data_type;
/**
* The data, which depends depends on the value of \a data_type
* field. When \a data_type is zero, this field will contain the
* plaintext password.
*/
- pj_str_t data;
+ pj_str_t data;
/**
* Optional NONCE.
*/
- pj_str_t nonce;
+ pj_str_t nonce;
} static_cred;
@@ -156,7 +197,7 @@ typedef struct pj_stun_auth_cred
pj_str_t *nonce);
/**
- * Get the credential to be put in outgoing message.
+ * Get the credential to be put in outgoing request.
*
* @param msg The outgoing message where the credential is
* to be applied.
@@ -186,7 +227,7 @@ typedef struct pj_stun_auth_cred
pj_str_t *realm,
pj_str_t *username,
pj_str_t *nonce,
- int *data_type,
+ pj_stun_passwd_type *data_type,
pj_str_t *data);
/**
@@ -217,7 +258,7 @@ typedef struct pj_stun_auth_cred
const pj_str_t *realm,
const pj_str_t *username,
pj_pool_t *pool,
- int *data_type,
+ pj_stun_passwd_type *data_type,
pj_str_t *data);
/**
@@ -250,6 +291,40 @@ typedef struct pj_stun_auth_cred
/**
+ * This structure contains the credential information that is found and
+ * used to authenticate incoming requests. Application may use this
+ * information when generating authentication for the outgoing response.
+ */
+typedef struct pj_stun_req_cred_info
+{
+ /**
+ * The REALM value found in the incoming request. If short term
+ * credential is used, the value will be empty.
+ */
+ pj_str_t realm;
+
+ /**
+ * The USERNAME value found in the incoming request.
+ */
+ pj_str_t username;
+
+ /**
+ * Optional NONCE.
+ */
+ pj_str_t nonce;
+
+ /**
+ * Authentication key that was used to authenticate the incoming
+ * request. This key is created with #pj_stun_create_key(), and
+ * it can be used to encode the credential of the outgoing
+ * response.
+ */
+ pj_str_t auth_key;
+
+} pj_stun_req_cred_info;
+
+
+/**
* Duplicate authentication credential.
*
* @param pool Pool to be used to allocate memory.
@@ -260,6 +335,40 @@ PJ_DECL(void) pj_stun_auth_cred_dup(pj_pool_t *pool,
pj_stun_auth_cred *dst,
const pj_stun_auth_cred *src);
+/**
+ * Duplicate request credential.
+ *
+ * @param pool Pool to be used to allocate memory.
+ * @param dst Destination credential.
+ * @param src Source credential.
+ */
+PJ_DECL(void) pj_stun_req_cred_info_dup(pj_pool_t *pool,
+ pj_stun_req_cred_info *dst,
+ const pj_stun_req_cred_info *src);
+
+
+/**
+ * Create authentication key to be used for encoding the message with
+ * MESSAGE-INTEGRITY. If short term credential is used (i.e. the realm
+ * argument is NULL or empty), the key will be copied from the password.
+ * If long term credential is used, the key will be calculated from the
+ * MD5 hash of the realm, username, and password.
+ *
+ * @param pool Pool to allocate memory for the key.
+ * @param key String to receive the key.
+ * @param realm The realm of the credential, if long term credential
+ * is to be used. If short term credential is wanted,
+ * application can put NULL or empty string here.
+ * @param username The username.
+ * @param data_type Password encoding.
+ * @param data The password.
+ */
+PJ_DECL(void) pj_stun_create_key(pj_pool_t *pool,
+ pj_str_t *key,
+ const pj_str_t *realm,
+ const pj_str_t *username,
+ pj_stun_passwd_type data_type,
+ const pj_str_t *data);
/**
* Verify credential in the STUN request. Note that before calling this
@@ -277,9 +386,9 @@ PJ_DECL(void) pj_stun_auth_cred_dup(pj_pool_t *pool,
* the message.
* @param pool If response is to be created, then memory will
* be allocated from this pool.
- * @param auth_key Optional pointer to receive authentication key to
- * calculate MESSAGE-INTEGRITY of the response, if
- * the response needs to be authenticated.
+ * @param info Optional pointer to receive authentication information
+ * found in the request and the credential that is used
+ * to authenticate the request.
* @param p_response Optional pointer to receive the response message
* then the credential in the request fails to
* authenticate.
@@ -294,7 +403,7 @@ PJ_DECL(pj_status_t) pj_stun_authenticate_request(const pj_uint8_t *pkt,
const pj_stun_msg *msg,
pj_stun_auth_cred *cred,
pj_pool_t *pool,
- pj_str_t *auth_key,
+ pj_stun_req_cred_info *info,
pj_stun_msg **p_response);
diff --git a/pjnath/include/pjnath/stun_msg.h b/pjnath/include/pjnath/stun_msg.h
index cde21fa9..31e40333 100644
--- a/pjnath/include/pjnath/stun_msg.h
+++ b/pjnath/include/pjnath/stun_msg.h
@@ -1225,6 +1225,17 @@ PJ_DECL(pj_status_t) pj_stun_msg_create(pj_pool_t *pool,
pj_stun_msg **p_msg);
/**
+ * Clone a STUN message with all of its attributes.
+ *
+ * @param pool Pool to allocate memory for the new message.
+ * @param msg The message to be cloned.
+ *
+ * @return The duplicate message.
+ */
+PJ_DECL(pj_stun_msg*) pj_stun_msg_clone(pj_pool_t *pool,
+ const pj_stun_msg *msg);
+
+/**
* Create STUN response message.
*
* @param pool Pool to create the mesage.
@@ -1300,30 +1311,6 @@ PJ_DECL(pj_status_t) pj_stun_msg_encode(pj_stun_msg *msg,
const pj_str_t *key,
unsigned *p_msg_len);
-
-/**
- * Create authentication key to be used for encoding the message with
- * MESSAGE-INTEGRITY. If short term credential is used (i.e. the realm
- * argument is NULL or empty), the key will be copied from the password.
- * If long term credential is used, the key will be calculated from the
- * MD5 hash of the realm, username, and password.
- *
- * @param pool Pool to allocate memory for the key.
- * @param key String to receive the key.
- * @param realm The realm of the credential, if long term credential
- * is to be used. If short term credential is wanted,
- * application can put NULL or empty string here.
- * @param username The username.
- * @param passwd The clear text password.
- */
-PJ_DECL(void) pj_stun_create_key(pj_pool_t *pool,
- pj_str_t *key,
- const pj_str_t *realm,
- const pj_str_t *username,
- const pj_str_t *passwd);
-
-
-
/**
* Check that the PDU is potentially a valid STUN message. This function
* is useful when application needs to multiplex STUN packets with other
@@ -1417,6 +1404,18 @@ PJ_DECL(pj_stun_attr_hdr*) pj_stun_msg_find_attr(const pj_stun_msg *msg,
/**
+ * Clone a STUN attribute.
+ *
+ * @param pool Pool to allocate memory.
+ * @param attr Attribute to clone.
+ *
+ * @return Duplicate attribute.
+ */
+PJ_DECL(pj_stun_attr_hdr*) pj_stun_attr_clone(pj_pool_t *pool,
+ const pj_stun_attr_hdr *attr);
+
+
+/**
* Create a generic STUN IP address attribute. The \a addr_len and
* \a addr parameters specify whether the address is IPv4 or IPv4
* address.
diff --git a/pjnath/include/pjnath/stun_session.h b/pjnath/include/pjnath/stun_session.h
index b28763ac..187064ed 100644
--- a/pjnath/include/pjnath/stun_session.h
+++ b/pjnath/include/pjnath/stun_session.h
@@ -46,6 +46,9 @@ PJ_BEGIN_DECL
/** Forward declaration for pj_stun_tx_data */
typedef struct pj_stun_tx_data pj_stun_tx_data;
+/** Forward declaration for pj_stun_rx_data */
+typedef struct pj_stun_rx_data pj_stun_rx_data;
+
/** Forward declaration for pj_stun_session */
typedef struct pj_stun_session pj_stun_session;
@@ -85,7 +88,7 @@ typedef struct pj_stun_session_cb
* @param sess The STUN session.
* @param pkt Pointer to the original STUN packet.
* @param pkt_len Length of the STUN packet.
- * @param msg The parsed STUN request.
+ * @param rdata Data containing incoming request message.
* @param src_addr Source address of the packet.
* @param src_addr_len Length of the source address.
*
@@ -96,7 +99,7 @@ typedef struct pj_stun_session_cb
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_stun_rx_data *rdata,
const pj_sockaddr_t *src_addr,
unsigned src_addr_len);
@@ -144,6 +147,25 @@ typedef struct pj_stun_session_cb
/**
+ * This structure describes incoming request message.
+ */
+struct pj_stun_rx_data
+{
+ /**
+ * The parsed request message.
+ */
+ pj_stun_msg *msg;
+
+ /**
+ * Credential information that is found and used to authenticate
+ * incoming request. Application may use this information when
+ * generating authentication for the outgoing response.
+ */
+ pj_stun_req_cred_info info;
+};
+
+
+/**
* This structure describe the outgoing STUN transmit data to carry the
* message to be sent.
*/
@@ -161,7 +183,7 @@ struct pj_stun_tx_data
pj_uint32_t msg_magic; /**< Message magic. */
pj_uint8_t msg_key[12]; /**< Message/transaction key. */
- pj_str_t auth_key; /**< Auth key. */
+ pj_stun_req_cred_info auth_info; /**< Credential info */
void *pkt; /**< The STUN packet. */
unsigned max_len; /**< Length of packet buffer. */
@@ -259,13 +281,15 @@ PJ_DECL(pj_status_t) pj_stun_session_set_server_name(pj_stun_session *sess,
* again with NULL as the argument.
*
* @param sess The STUN session instance.
+ * @param auth_type Type of authentication.
* @param cred The credential to be used by this session. If NULL
* is specified, authentication will be disabled.
*
* @return PJ_SUCCESS on success, or the appropriate error code.
*/
-PJ_DECL(void) pj_stun_session_set_credential(pj_stun_session *sess,
- const pj_stun_auth_cred *cred);
+PJ_DECL(pj_status_t) pj_stun_session_set_credential(pj_stun_session *sess,
+ pj_stun_auth_type auth_type,
+ const pj_stun_auth_cred *cred);
/**
* Create a STUN request message. After the message has been successfully
@@ -328,7 +352,7 @@ PJ_DECL(pj_status_t) pj_stun_session_create_ind(pj_stun_session *sess,
* @return PJ_SUCCESS on success, or the appropriate error code.
*/
PJ_DECL(pj_status_t) pj_stun_session_create_res(pj_stun_session *sess,
- const pj_stun_msg *req,
+ const pj_stun_rx_data *rdata,
unsigned err_code,
const pj_str_t *err_msg,
pj_stun_tx_data **p_tdata);
@@ -362,7 +386,7 @@ PJ_DECL(pj_status_t) pj_stun_session_send_msg(pj_stun_session *sess,
* Create and send STUN response message.
*
* @param sess The STUN session instance.
- * @param req The STUN request message to be responded.
+ * @param rdata 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
@@ -382,7 +406,7 @@ PJ_DECL(pj_status_t) pj_stun_session_send_msg(pj_stun_session *sess,
* @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,
+ const pj_stun_rx_data *rdata,
unsigned code,
const char *err_msg,
pj_bool_t cache,
diff --git a/pjnath/src/pjnath-test/sess_auth.c b/pjnath/src/pjnath-test/sess_auth.c
new file mode 100644
index 00000000..86ac5d8a
--- /dev/null
+++ b/pjnath/src/pjnath-test/sess_auth.c
@@ -0,0 +1,1125 @@
+/* $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 "test.h"
+
+#define THIS_FILE "sess_auth.c"
+
+#define REALM "STUN session test"
+#define USERNAME "theusername"
+#define PASSWORD "thepassword"
+#define NONCE "thenonce"
+
+
+/* STUN config */
+static pj_stun_config stun_cfg;
+
+
+//////////////////////////////////////////////////////////////////////////////////////////
+//
+// SERVER PART
+//
+
+
+/* Server instance */
+static struct server
+{
+ pj_pool_t *pool;
+ pj_sockaddr addr;
+ pj_stun_session *sess;
+
+ pj_bool_t responding;
+ unsigned recv_count;
+ pj_stun_auth_type auth_type;
+
+ pj_sock_t sock;
+
+ pj_bool_t quit;
+ pj_thread_t *thread;
+} *server;
+
+
+static pj_status_t server_send_msg(pj_stun_session *sess,
+ const void *pkt,
+ pj_size_t pkt_size,
+ const pj_sockaddr_t *dst_addr,
+ unsigned addr_len)
+{
+ pj_ssize_t len = pkt_size;
+
+ PJ_UNUSED_ARG(sess);
+
+ return pj_sock_sendto(server->sock, pkt, &len, 0, dst_addr, addr_len);
+}
+
+static pj_status_t server_on_rx_request(pj_stun_session *sess,
+ const pj_uint8_t *pkt,
+ unsigned pkt_len,
+ const pj_stun_rx_data *rdata,
+ const pj_sockaddr_t *src_addr,
+ unsigned src_addr_len)
+{
+ PJ_UNUSED_ARG(pkt);
+ PJ_UNUSED_ARG(pkt_len);
+
+ return pj_stun_session_respond(sess, rdata, 0, NULL, PJ_TRUE,
+ src_addr, src_addr_len);
+}
+
+
+static pj_status_t server_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);
+
+ if (server->auth_type == PJ_STUN_AUTH_SHORT_TERM) {
+ realm->slen = nonce->slen = 0;
+ } else {
+ *realm = pj_str(REALM);
+ *nonce = pj_str(NONCE);
+ }
+
+ return PJ_SUCCESS;
+}
+
+
+static pj_status_t server_get_password( const pj_stun_msg *msg,
+ void *user_data,
+ const pj_str_t *realm,
+ const pj_str_t *username,
+ pj_pool_t *pool,
+ pj_stun_passwd_type *data_type,
+ pj_str_t *data)
+{
+ PJ_UNUSED_ARG(msg);
+ PJ_UNUSED_ARG(user_data);
+ PJ_UNUSED_ARG(pool);
+
+ if (server->auth_type == PJ_STUN_AUTH_SHORT_TERM) {
+ if (realm && realm->slen) {
+ PJ_LOG(4,(THIS_FILE, " server expecting short term"));
+ return -1;
+ }
+ } else {
+ if (realm==NULL || realm->slen==0) {
+ PJ_LOG(4,(THIS_FILE, " realm not present"));
+ return -1;
+ }
+ }
+
+ if (pj_strcmp2(username, USERNAME) != 0) {
+ PJ_LOG(4,(THIS_FILE, " wrong username"));
+ return -1;
+ }
+
+ *data_type = PJ_STUN_PASSWD_PLAIN;
+ *data = pj_str(PASSWORD);
+
+ return PJ_SUCCESS;
+}
+
+
+static pj_bool_t server_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_strcmp2(nonce, NONCE) != 0)
+ return PJ_FALSE;
+
+ return PJ_TRUE;
+}
+
+
+static int server_thread(void *unused)
+{
+ PJ_UNUSED_ARG(unused);
+
+ while (!server->quit) {
+ pj_fd_set_t readset;
+ pj_time_val delay = {0, 10};
+
+ PJ_FD_ZERO(&readset);
+ PJ_FD_SET(server->sock, &readset);
+
+ if (pj_sock_select(server->sock, &readset, NULL, NULL, &delay)==1 &&
+ PJ_FD_ISSET(server->sock, &readset))
+ {
+ char pkt[1000];
+ pj_ssize_t len;
+ pj_status_t status;
+ pj_sockaddr src_addr;
+ int src_addr_len;
+
+ len = sizeof(pkt);
+ src_addr_len = sizeof(src_addr);
+
+ status = pj_sock_recvfrom(server->sock, pkt, &len, 0, &src_addr, &src_addr_len);
+ if (status != PJ_SUCCESS)
+ continue;
+
+ /* Increment server's receive count */
+ server->recv_count++;
+
+ /* Only pass to server if we allow to respond */
+ if (!server->responding)
+ continue;
+
+ pj_stun_session_on_rx_pkt(server->sess, pkt, len,
+ PJ_STUN_CHECK_PACKET | PJ_STUN_IS_DATAGRAM,
+ NULL, &src_addr, src_addr_len);
+ }
+ }
+
+ return 0;
+}
+
+
+/* Destroy server */
+static void destroy_server(void)
+{
+ if (server->thread) {
+ server->quit = PJ_TRUE;
+ pj_thread_join(server->thread);
+ pj_thread_destroy(server->thread);
+ }
+
+ if (server->sock) {
+ pj_sock_close(server->sock);
+ }
+
+ if (server->sess) {
+ pj_stun_session_destroy(server->sess);
+ }
+
+ pj_pool_release(server->pool);
+ server = NULL;
+}
+
+/* Instantiate standard server */
+static int create_std_server(pj_stun_auth_type auth_type,
+ pj_bool_t responding)
+{
+ pj_pool_t *pool;
+ pj_stun_session_cb sess_cb;
+ pj_stun_auth_cred cred;
+ pj_status_t status;
+
+ /* Create server */
+ pool = pj_pool_create(mem, "server", 1000, 1000, NULL);
+ server = PJ_POOL_ZALLOC_T(pool, struct server);
+ server->pool = pool;
+ server->auth_type = auth_type;
+ server->responding = responding;
+
+ /* Create STUN session */
+ pj_bzero(&sess_cb, sizeof(sess_cb));
+ sess_cb.on_rx_request = &server_on_rx_request;
+ sess_cb.on_send_msg = &server_send_msg;
+ status = pj_stun_session_create(&stun_cfg, "server", &sess_cb, PJ_FALSE, &server->sess);
+ if (status != PJ_SUCCESS) {
+ destroy_server();
+ return -10;
+ }
+
+ /* Configure credential */
+ pj_bzero(&cred, sizeof(cred));
+ cred.type = PJ_STUN_AUTH_CRED_DYNAMIC;
+ cred.data.dyn_cred.get_auth = &server_get_auth;
+ cred.data.dyn_cred.get_password = &server_get_password;
+ cred.data.dyn_cred.verify_nonce = &server_verify_nonce;
+ status = pj_stun_session_set_credential(server->sess, auth_type, &cred);
+ if (status != PJ_SUCCESS) {
+ destroy_server();
+ return -20;
+ }
+
+ /* Create socket */
+ status = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &server->sock);
+ if (status != PJ_SUCCESS) {
+ destroy_server();
+ return -30;
+ }
+
+ /* Bind */
+ pj_sockaddr_in_init(&server->addr.ipv4, NULL, 0);
+ status = pj_sock_bind(server->sock, &server->addr, pj_sockaddr_get_len(&server->addr));
+ if (status != PJ_SUCCESS) {
+ destroy_server();
+ return -40;
+ } else {
+ /* Get the bound IP address */
+ int namelen = sizeof(server->addr);
+ pj_sockaddr addr;
+
+ status = pj_sock_getsockname(server->sock, &server->addr, &namelen);
+ if (status != PJ_SUCCESS) {
+ destroy_server();
+ return -43;
+ }
+
+ status = pj_gethostip(pj_AF_INET(), &addr);
+ if (status != PJ_SUCCESS) {
+ destroy_server();
+ return -45;
+ }
+
+ pj_sockaddr_copy_addr(&server->addr, &addr);
+ }
+
+
+ /* Create worker thread */
+ status = pj_thread_create(pool, "server", &server_thread, 0, 0, 0, &server->thread);
+ if (status != PJ_SUCCESS) {
+ destroy_server();
+ return -30;
+ }
+
+ return 0;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////
+//
+// CLIENT PART
+//
+
+static struct client
+{
+ pj_pool_t *pool;
+ pj_stun_session *sess;
+ pj_sem_t *test_complete;
+ pj_sock_t sock;
+
+ pj_bool_t responding;
+ unsigned recv_count;
+
+ pj_status_t response_status;
+ pj_stun_msg *response;
+
+ pj_bool_t quit;
+ pj_thread_t *thread;
+} *client;
+
+
+static pj_status_t client_send_msg(pj_stun_session *sess,
+ const void *pkt,
+ pj_size_t pkt_size,
+ const pj_sockaddr_t *dst_addr,
+ unsigned addr_len)
+{
+ pj_ssize_t len = pkt_size;
+
+ PJ_UNUSED_ARG(sess);
+
+ return pj_sock_sendto(client->sock, pkt, &len, 0, dst_addr, addr_len);
+}
+
+
+static void client_on_request_complete( pj_stun_session *sess,
+ pj_status_t status,
+ pj_stun_tx_data *tdata,
+ const pj_stun_msg *response,
+ const pj_sockaddr_t *src_addr,
+ unsigned src_addr_len)
+{
+ PJ_UNUSED_ARG(sess);
+ PJ_UNUSED_ARG(tdata);
+ PJ_UNUSED_ARG(src_addr);
+ PJ_UNUSED_ARG(src_addr_len);
+
+ client->response_status = status;
+ if (response)
+ client->response = pj_stun_msg_clone(client->pool, response);
+
+ pj_sem_post(client->test_complete);
+}
+
+
+static int client_thread(void *unused)
+{
+ PJ_UNUSED_ARG(unused);
+
+ while (!client->quit) {
+ pj_fd_set_t readset;
+ pj_time_val delay = {0, 10};
+
+ /* Also poll the timer heap */
+ pj_timer_heap_poll(stun_cfg.timer_heap, NULL);
+
+ /* Poll client socket */
+ PJ_FD_ZERO(&readset);
+ PJ_FD_SET(client->sock, &readset);
+
+ if (pj_sock_select(client->sock, &readset, NULL, NULL, &delay)==1 &&
+ PJ_FD_ISSET(client->sock, &readset))
+ {
+ char pkt[1000];
+ pj_ssize_t len;
+ pj_status_t status;
+ pj_sockaddr src_addr;
+ int src_addr_len;
+
+ len = sizeof(pkt);
+ src_addr_len = sizeof(src_addr);
+
+ status = pj_sock_recvfrom(client->sock, pkt, &len, 0, &src_addr, &src_addr_len);
+ if (status != PJ_SUCCESS)
+ continue;
+
+ /* Increment client's receive count */
+ client->recv_count++;
+
+ /* Only pass to client if we allow to respond */
+ if (!client->responding)
+ continue;
+
+ pj_stun_session_on_rx_pkt(client->sess, pkt, len,
+ PJ_STUN_CHECK_PACKET | PJ_STUN_IS_DATAGRAM,
+ NULL, &src_addr, src_addr_len);
+ }
+
+ }
+
+ return 0;
+}
+
+
+static void destroy_client_server(void)
+{
+ if (client->thread) {
+ client->quit = 1;
+ pj_thread_join(client->thread);
+ pj_thread_destroy(client->thread);
+ }
+
+ if (client->sess)
+ pj_stun_session_destroy(client->sess);
+
+ if (client->sock)
+ pj_sock_close(client->sock);
+
+ if (client->test_complete)
+ pj_sem_destroy(client->test_complete);
+
+ if (server)
+ destroy_server();
+}
+
+static int run_client_test(const char *title,
+
+ pj_bool_t server_responding,
+ pj_stun_auth_type server_auth_type,
+
+ pj_stun_auth_type client_auth_type,
+ const char *realm,
+ const char *username,
+ const char *nonce,
+ const char *password,
+ pj_bool_t dummy_mi,
+
+ pj_stun_status expected_error,
+ pj_stun_status expected_code,
+ const char *expected_realm,
+ const char *expected_nonce,
+
+ int (*more_check)(void))
+{
+ pj_pool_t *pool;
+ pj_stun_session_cb sess_cb;
+ pj_stun_auth_cred cred;
+ pj_stun_tx_data *tdata;
+ pj_status_t status;
+ int rc = 0;
+
+ PJ_LOG(3,(THIS_FILE, " %s test", title));
+
+ /* Create client */
+ pool = pj_pool_create(mem, "client", 1000, 1000, NULL);
+ client = PJ_POOL_ZALLOC_T(pool, struct client);
+ client->pool = pool;
+ client->responding = PJ_TRUE;
+
+ /* Create STUN session */
+ pj_bzero(&sess_cb, sizeof(sess_cb));
+ sess_cb.on_request_complete = &client_on_request_complete;
+ sess_cb.on_send_msg = &client_send_msg;
+ status = pj_stun_session_create(&stun_cfg, "client", &sess_cb, PJ_FALSE, &client->sess);
+ if (status != PJ_SUCCESS) {
+ destroy_client_server();
+ return -200;
+ }
+
+ /* Create semaphore */
+ status = pj_sem_create(pool, "client", 0, 1, &client->test_complete);
+ if (status != PJ_SUCCESS) {
+ destroy_client_server();
+ return -205;
+ }
+
+ /* Create client socket */
+ status = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &client->sock);
+ if (status != PJ_SUCCESS) {
+ destroy_client_server();
+ return -210;
+ }
+
+ /* Bind client socket */
+ status = pj_sock_bind_in(client->sock, 0, 0);
+ if (status != PJ_SUCCESS) {
+ destroy_client_server();
+ return -220;
+ }
+
+ /* Create client thread */
+ status = pj_thread_create(pool, "client", &client_thread, NULL, 0, 0, &client->thread);
+ if (status != PJ_SUCCESS) {
+ destroy_client_server();
+ return -230;
+ }
+
+ /* Initialize credential */
+ pj_bzero(&cred, sizeof(cred));
+ cred.type = PJ_STUN_AUTH_CRED_STATIC;
+ if (realm) cred.data.static_cred.realm = pj_str((char*)realm);
+ if (username) cred.data.static_cred.username = pj_str((char*)username);
+ if (nonce) cred.data.static_cred.nonce = pj_str((char*)nonce);
+ if (password) cred.data.static_cred.data = pj_str((char*)password);
+ cred.data.static_cred.data_type = PJ_STUN_PASSWD_PLAIN;
+ status = pj_stun_session_set_credential(client->sess, client_auth_type, &cred);
+ if (status != PJ_SUCCESS) {
+ destroy_client_server();
+ return -240;
+ }
+
+ /* Create the server */
+ status = create_std_server(server_auth_type, server_responding);
+ if (status != 0) {
+ destroy_client_server();
+ return status;
+ }
+
+ /* Create request */
+ status = pj_stun_session_create_req(client->sess, PJ_STUN_BINDING_REQUEST,
+ PJ_STUN_MAGIC, NULL, &tdata);
+ if (status != PJ_SUCCESS) {
+ destroy_client_server();
+ return -250;
+ }
+
+ /* Add our own attributes if client authentication is set to none */
+ if (client_auth_type == PJ_STUN_AUTH_NONE) {
+ pj_str_t tmp;
+ if (realm)
+ pj_stun_msg_add_string_attr(tdata->pool, tdata->msg, PJ_STUN_ATTR_REALM, pj_cstr(&tmp, realm));
+ if (username)
+ pj_stun_msg_add_string_attr(tdata->pool, tdata->msg, PJ_STUN_ATTR_USERNAME, pj_cstr(&tmp, username));
+ if (nonce)
+ pj_stun_msg_add_string_attr(tdata->pool, tdata->msg, PJ_STUN_ATTR_NONCE, pj_cstr(&tmp, nonce));
+ if (password) {
+ // ignored
+ }
+ if (dummy_mi) {
+ pj_stun_msgint_attr *mi;
+
+ pj_stun_msgint_attr_create(tdata->pool, &mi);
+ pj_stun_msg_add_attr(tdata->msg, &mi->hdr);
+ }
+
+ }
+
+ /* Send the request */
+ status = pj_stun_session_send_msg(client->sess, PJ_FALSE, &server->addr,
+ pj_sockaddr_get_len(&server->addr), tdata);
+ if (status != PJ_SUCCESS) {
+ destroy_client_server();
+ return -270;
+ }
+
+ /* Wait until test complete */
+ pj_sem_wait(client->test_complete);
+
+
+ /* Verify response */
+ if (expected_error) {
+ if (expected_code != client->response_status) {
+ char e1[PJ_ERR_MSG_SIZE], e2[PJ_ERR_MSG_SIZE];
+
+ pj_strerror(expected_code, e1, sizeof(e1));
+ pj_strerror(client->response_status, e2, sizeof(e2));
+
+ PJ_LOG(3,(THIS_FILE, " err: expecting %d (%s) but got %d (%s) response",
+ expected_code, e1, client->response_status, e2));
+ rc = -500;
+ }
+
+ } else {
+ int res_code = 0;
+ pj_stun_realm_attr *arealm;
+ pj_stun_nonce_attr *anonce;
+
+ if (client->response_status != 0) {
+ PJ_LOG(3,(THIS_FILE, " err: expecting successful operation but got error %d",
+ client->response_status));
+ rc = -600;
+ goto done;
+ }
+
+ if (PJ_STUN_IS_ERROR_RESPONSE(client->response->hdr.type)) {
+ pj_stun_errcode_attr *aerr = NULL;
+
+ aerr = (pj_stun_errcode_attr*)
+ pj_stun_msg_find_attr(client->response,
+ PJ_STUN_ATTR_ERROR_CODE, 0);
+ if (aerr == NULL) {
+ PJ_LOG(3,(THIS_FILE, " err: received error response without ERROR-CODE"));
+ rc = -610;
+ goto done;
+ }
+
+ res_code = aerr->err_code;
+ } else {
+ res_code = 0;
+ }
+
+ /* Check that code matches */
+ if (expected_code != res_code) {
+ PJ_LOG(3,(THIS_FILE, " err: expecting response code %d but got %d",
+ expected_code, res_code));
+ rc = -620;
+ goto done;
+ }
+
+ /* Find REALM and NONCE attributes */
+ arealm = (pj_stun_realm_attr*)
+ pj_stun_msg_find_attr(client->response, PJ_STUN_ATTR_REALM, 0);
+ anonce = (pj_stun_nonce_attr*)
+ pj_stun_msg_find_attr(client->response, PJ_STUN_ATTR_NONCE, 0);
+
+ if (expected_realm) {
+ if (arealm == NULL) {
+ PJ_LOG(3,(THIS_FILE, " err: expecting REALM in esponse"));
+ rc = -630;
+ goto done;
+ }
+ if (pj_strcmp2(&arealm->value, expected_realm)!=0) {
+ PJ_LOG(3,(THIS_FILE, " err: REALM mismatch in response"));
+ rc = -640;
+ goto done;
+ }
+ } else {
+ if (arealm != NULL) {
+ PJ_LOG(3,(THIS_FILE, " err: non expecting REALM in response"));
+ rc = -650;
+ goto done;
+ }
+ }
+
+ if (expected_nonce) {
+ if (anonce == NULL) {
+ PJ_LOG(3,(THIS_FILE, " err: expecting NONCE in esponse"));
+ rc = -660;
+ goto done;
+ }
+ if (pj_strcmp2(&anonce->value, expected_nonce)!=0) {
+ PJ_LOG(3,(THIS_FILE, " err: NONCE mismatch in response"));
+ rc = -670;
+ goto done;
+ }
+ } else {
+ if (anonce != NULL) {
+ PJ_LOG(3,(THIS_FILE, " err: non expecting NONCE in response"));
+ rc = -680;
+ goto done;
+ }
+ }
+ }
+
+ /* Our tests are okay so far. Let caller do some more tests if
+ * it wants to.
+ */
+ if (rc==0 && more_check) {
+ rc = (*more_check)();
+ }
+
+
+done:
+ destroy_client_server();
+ return rc;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////
+//
+// More verification
+//
+
+/* Retransmission test */
+static int retransmit_check(void)
+{
+ if (server->recv_count != PJ_STUN_MAX_TRANSMIT_COUNT)
+ return -700;
+ if (client->recv_count != 0)
+ return -710;
+
+ return 0;
+}
+
+static int long_term_check1(void)
+{
+ /* SHOULD NOT contain USERNAME or MESSAGE-INTEGRITY */
+ if (pj_stun_msg_find_attr(client->response, PJ_STUN_ATTR_USERNAME, 0))
+ return -800;
+ if (pj_stun_msg_find_attr(client->response, PJ_STUN_ATTR_MESSAGE_INTEGRITY, 0))
+ return -800;
+
+ return 0;
+}
+
+static int long_term_check2(void)
+{
+ /* response SHOULD NOT include a USERNAME, NONCE, REALM or
+ * MESSAGE-INTEGRITY attribute.
+ */
+ if (pj_stun_msg_find_attr(client->response, PJ_STUN_ATTR_USERNAME, 0))
+ return -900;
+ if (pj_stun_msg_find_attr(client->response, PJ_STUN_ATTR_NONCE, 0))
+ return -910;
+ if (pj_stun_msg_find_attr(client->response, PJ_STUN_ATTR_REALM, 0))
+ return -920;
+ if (pj_stun_msg_find_attr(client->response, PJ_STUN_ATTR_MESSAGE_INTEGRITY, 0))
+ return -930;
+
+ return 0;
+}
+
+static int long_term_check3(void)
+{
+ /* response SHOULD NOT include a USERNAME, NONCE, and REALM */
+ if (pj_stun_msg_find_attr(client->response, PJ_STUN_ATTR_USERNAME, 0))
+ return -1000;
+ if (pj_stun_msg_find_attr(client->response, PJ_STUN_ATTR_NONCE, 0))
+ return -1010;
+ if (pj_stun_msg_find_attr(client->response, PJ_STUN_ATTR_REALM, 0))
+ return -1020;
+
+ return 0;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+//
+// TEST MAIN
+//
+
+
+int sess_auth_test(void)
+{
+ pj_pool_t *pool;
+ int rc;
+
+ PJ_LOG(3,(THIS_FILE, " STUN session authentication test"));
+
+ /* Init STUN config */
+ pj_stun_config_init(&stun_cfg, mem, 0, NULL, NULL);
+
+ /* Create pool and timer heap */
+ pool = pj_pool_create(mem, "authtest", 200, 200, NULL);
+ if (pj_timer_heap_create(pool, 20, &stun_cfg.timer_heap)) {
+ pj_pool_release(pool);
+ return -5;
+ }
+
+ /* Basic retransmission test */
+ rc = run_client_test("Retransmission", // title
+ PJ_FALSE, // server responding
+ PJ_STUN_AUTH_NONE, // server auth
+ PJ_STUN_AUTH_NONE, // client auth
+ NULL, // realm
+ NULL, // username
+ NULL, // nonce
+ NULL, // password
+ PJ_FALSE, // dummy MI
+ PJ_TRUE, // expected error
+ PJNATH_ESTUNTIMEDOUT,// expected code
+ NULL, // expected realm
+ NULL, // expected nonce
+ &retransmit_check // more check
+ );
+ if (rc != 0) {
+ goto done;
+ }
+
+ /*
+ * Short term credential.
+ * draft-ietf-behave-rfc3489bis-15#section-10.1.2
+ */
+
+ /*
+ * If the message does not contain both a MESSAGE-INTEGRITY and a
+ * USERNAME attribute, If the message is a request, the server MUST
+ * reject the request with an error response. This response MUST
+ * use an error code of 400 (Bad Request).
+ */
+ rc = run_client_test("Missing MESSAGE-INTEGRITY (short term)", // title
+ PJ_TRUE, // server responding
+ PJ_STUN_AUTH_SHORT_TERM, // server auth
+ PJ_STUN_AUTH_NONE, // client auth
+ NULL, // realm
+ NULL, // username
+ NULL, // nonce
+ NULL, // password
+ PJ_FALSE, // dummy MI
+ PJ_TRUE, // expected error
+ PJ_STATUS_FROM_STUN_CODE(400),// expected code
+ NULL, // expected realm
+ NULL, // expected nonce
+ NULL // more check
+ );
+ if (rc != 0) {
+ goto done;
+ }
+
+ /* If the USERNAME does not contain a username value currently valid
+ * within the server: If the message is a request, the server MUST
+ * reject the request with an error response. This response MUST use
+ * an error code of 401 (Unauthorized).
+ */
+ rc = run_client_test("USERNAME mismatch (short term)", // title
+ PJ_TRUE, // server responding
+ PJ_STUN_AUTH_SHORT_TERM, // server auth
+ PJ_STUN_AUTH_SHORT_TERM, // client auth
+ NULL, // realm
+ "anotheruser", // username
+ NULL, // nonce
+ "anotherpass", // password
+ PJ_FALSE, // dummy MI
+ PJ_TRUE, // expected error
+ PJ_STATUS_FROM_STUN_CODE(401),// expected code
+ NULL, // expected realm
+ NULL, // expected nonce
+ NULL // more check
+ );
+ if (rc != 0) {
+ goto done;
+ }
+
+ /* Using the password associated with the username, compute the value
+ * for the message-integrity as described in Section 15.4. If the
+ * resulting value does not match the contents of the MESSAGE-
+ * INTEGRITY attribute:
+ *
+ * - If the message is a request, the server MUST reject the request
+ * with an error response. This response MUST use an error code
+ * of 401 (Unauthorized).
+ */
+ rc = run_client_test("MESSAGE-INTEGRITY mismatch (short term)", // title
+ PJ_TRUE, // server responding
+ PJ_STUN_AUTH_SHORT_TERM, // server auth
+ PJ_STUN_AUTH_SHORT_TERM, // client auth
+ NULL, // realm
+ USERNAME, // username
+ NULL, // nonce
+ "anotherpass", // password
+ PJ_FALSE, // dummy MI
+ PJ_TRUE, // expected error
+ PJ_STATUS_FROM_STUN_CODE(401),// expected code
+ NULL, // expected realm
+ NULL, // expected nonce
+ NULL // more check
+ );
+ if (rc != 0) {
+ goto done;
+ }
+
+ /* USERNAME is not present, server must respond with 400 (Bad
+ * Request).
+ */
+ rc = run_client_test("Missing USERNAME (short term)",// title
+ PJ_TRUE, // server responding
+ PJ_STUN_AUTH_SHORT_TERM, // server auth
+ PJ_STUN_AUTH_NONE, // client auth
+ NULL, // realm
+ NULL, // username
+ NULL, // nonce
+ NULL, // password
+ PJ_TRUE, // dummy MI
+ PJ_TRUE, // expected error
+ PJ_STATUS_FROM_STUN_CODE(400), // expected code
+ NULL, // expected realm
+ NULL, // expected nonce
+ NULL // more check
+ );
+ if (rc != 0) {
+ goto done;
+ }
+
+ /* Successful short term authentication */
+ rc = run_client_test("Successful scenario (short term)", // title
+ PJ_TRUE, // server responding
+ PJ_STUN_AUTH_SHORT_TERM, // server auth
+ PJ_STUN_AUTH_SHORT_TERM, // client auth
+ NULL, // realm
+ USERNAME, // username
+ NULL, // nonce
+ PASSWORD, // password
+ PJ_FALSE, // dummy MI
+ PJ_FALSE, // expected error
+ PJ_SUCCESS, // expected code
+ NULL, // expected realm
+ NULL, // expected nonce
+ NULL // more check
+ );
+ if (rc != 0) {
+ goto done;
+ }
+
+ /*
+ * (our own) Extended tests for long term credential
+ */
+
+ /* When server wants to use short term credential, but request has
+ * REALM, reject with .... 401 ???
+ */
+ rc = run_client_test("Unwanted REALM (short term)", // title
+ PJ_TRUE, // server responding
+ PJ_STUN_AUTH_SHORT_TERM, // server auth
+ PJ_STUN_AUTH_NONE, // client auth
+ REALM, // realm
+ USERNAME, // username
+ NULL, // nonce
+ PASSWORD, // password
+ PJ_TRUE, // dummy MI
+ PJ_TRUE, // expected error
+ PJ_STATUS_FROM_STUN_CODE(401), // expected code
+ NULL, // expected realm
+ NULL, // expected nonce
+ &long_term_check2 // more check
+ );
+ if (rc != 0) {
+ goto done;
+ }
+
+
+ /*
+ * Long term credential.
+ * draft-ietf-behave-rfc3489bis-15#section-10.2.2
+ */
+
+ /* If the message does not contain a MESSAGE-INTEGRITY attribute, the
+ * server MUST generate an error response with an error code of 401
+ * (Unauthorized). This response MUST include a REALM value. It is
+ * RECOMMENDED that the REALM value be the domain name of the
+ * provider of the STUN server. The response MUST include a NONCE,
+ * selected by the server. The response SHOULD NOT contain a
+ * USERNAME or MESSAGE-INTEGRITY attribute.
+ */
+ rc = run_client_test("Missing M-I (long term)", // title
+ PJ_TRUE, // server responding
+ PJ_STUN_AUTH_LONG_TERM, // server auth
+ PJ_STUN_AUTH_NONE, // client auth
+ NULL, // client realm
+ NULL, // client username
+ NULL, // client nonce
+ NULL, // client password
+ PJ_FALSE, // client dummy MI
+ PJ_TRUE, // expected error
+ PJ_STATUS_FROM_STUN_CODE(401), // expected code
+ REALM, // expected realm
+ NONCE, // expected nonce
+ &long_term_check1 // more check
+ );
+ if (rc != 0) {
+ goto done;
+ }
+
+ /* If the message contains a MESSAGE-INTEGRITY attribute, but is
+ * missing the USERNAME, REALM or NONCE attributes, the server MUST
+ * generate an error response with an error code of 400 (Bad
+ * Request). This response SHOULD NOT include a USERNAME, NONCE,
+ * REALM or MESSAGE-INTEGRITY attribute.
+ */
+ /* Missing USERNAME */
+ rc = run_client_test("Missing USERNAME (long term)", // title
+ PJ_TRUE, // server responding
+ PJ_STUN_AUTH_LONG_TERM, // server auth
+ PJ_STUN_AUTH_NONE, // client auth
+ REALM, // client realm
+ NULL, // client username
+ NONCE, // client nonce
+ PASSWORD, // client password
+ PJ_TRUE, // client dummy MI
+ PJ_TRUE, // expected error
+ PJ_STATUS_FROM_STUN_CODE(400), // expected code
+ NULL, // expected realm
+ NULL, // expected nonce
+ &long_term_check2 // more check
+ );
+ if (rc != 0) {
+ goto done;
+ }
+
+ /* Missing REALM */
+ rc = run_client_test("Missing REALM (long term)", // title
+ PJ_TRUE, // server responding
+ PJ_STUN_AUTH_LONG_TERM, // server auth
+ PJ_STUN_AUTH_NONE, // client auth
+ NULL, // client realm
+ USERNAME, // client username
+ NONCE, // client nonce
+ PASSWORD, // client password
+ PJ_TRUE, // client dummy MI
+ PJ_TRUE, // expected error
+ PJ_STATUS_FROM_STUN_CODE(400), // expected code
+ NULL, // expected realm
+ NULL, // expected nonce
+ &long_term_check2 // more check
+ );
+ if (rc != 0) {
+ goto done;
+ }
+
+ /* Missing NONCE */
+ rc = run_client_test("Missing NONCE (long term)", // title
+ PJ_TRUE, // server responding
+ PJ_STUN_AUTH_LONG_TERM, // server auth
+ PJ_STUN_AUTH_NONE, // client auth
+ REALM, // client realm
+ USERNAME, // client username
+ NULL, // client nonce
+ PASSWORD, // client password
+ PJ_TRUE, // client dummy MI
+ PJ_TRUE, // expected error
+ PJ_STATUS_FROM_STUN_CODE(400), // expected code
+ NULL, // expected realm
+ NULL, // expected nonce
+ &long_term_check2 // more check
+ );
+ if (rc != 0) {
+ goto done;
+ }
+
+ /* If the NONCE is no longer valid, the server MUST generate an error
+ * response with an error code of 438 (Stale Nonce). This response
+ * MUST include a NONCE and REALM attribute and SHOULD NOT incude the
+ * USERNAME or MESSAGE-INTEGRITY attribute. Servers can invalidate
+ * nonces in order to provide additional security. See Section 4.3
+ * of [RFC2617] for guidelines.
+ */
+ // how??
+
+ /* If the username in the USERNAME attribute is not valid, the server
+ * MUST generate an error response with an error code of 401
+ * (Unauthorized). This response MUST include a REALM value. It is
+ * RECOMMENDED that the REALM value be the domain name of the
+ * provider of the STUN server. The response MUST include a NONCE,
+ * selected by the server. The response SHOULD NOT contain a
+ * USERNAME or MESSAGE-INTEGRITY attribute.
+ */
+ rc = run_client_test("Invalid username (long term)", // title
+ PJ_TRUE, // server responding
+ PJ_STUN_AUTH_LONG_TERM, // server auth
+ PJ_STUN_AUTH_LONG_TERM, // client auth
+ REALM, // client realm
+ "anotheruser", // client username
+ "a nonce", // client nonce
+ "somepassword", // client password
+ PJ_FALSE, // client dummy MI
+ PJ_TRUE, // expected error
+ PJ_STATUS_FROM_STUN_CODE(401), // expected code
+ REALM, // expected realm
+ NONCE, // expected nonce
+ &long_term_check1 // more check
+ );
+ if (rc != 0) {
+ goto done;
+ }
+
+ /* Successful long term authentication */
+ rc = run_client_test("Successful scenario (long term)", // title
+ PJ_TRUE, // server responding
+ PJ_STUN_AUTH_LONG_TERM, // server auth
+ PJ_STUN_AUTH_LONG_TERM, // client auth
+ REALM, // client realm
+ USERNAME, // client username
+ "anothernonce", // client nonce
+ PASSWORD, // client password
+ PJ_FALSE, // client dummy MI
+ PJ_FALSE, // expected error
+ 0, // expected code
+ NULL, // expected realm
+ NULL, // expected nonce
+ &long_term_check3 // more check
+ );
+ if (rc != 0) {
+ goto done;
+ }
+
+ /*
+ * (our own) Extended tests for long term credential
+ */
+
+ /* If REALM doesn't match, server must respond with 401
+ */
+ rc = run_client_test("Invalid REALM (long term)", // title
+ PJ_TRUE, // server responding
+ PJ_STUN_AUTH_LONG_TERM, // server auth
+ PJ_STUN_AUTH_LONG_TERM, // client auth
+ "anotherrealm", // client realm
+ USERNAME, // client username
+ NONCE, // client nonce
+ PASSWORD, // client password
+ PJ_FALSE, // client dummy MI
+ PJ_TRUE, // expected error
+ PJ_STATUS_FROM_STUN_CODE(401), // expected code
+ REALM, // expected realm
+ NONCE, // expected nonce
+ &long_term_check1 // more check
+ );
+ if (rc != 0) {
+ goto done;
+ }
+
+ /* Invalid HMAC */
+
+ /* Valid static short term, without NONCE */
+
+ /* Valid static short term, WITH NONCE */
+
+ /* Valid static long term (with NONCE */
+
+ /* Valid dynamic short term (without NONCE) */
+
+ /* Valid dynamic short term (with NONCE) */
+
+ /* Valid dynamic long term (with NONCE) */
+
+
+done:
+ pj_timer_heap_destroy(stun_cfg.timer_heap);
+ pj_pool_release(pool);
+ return rc;
+}
diff --git a/pjnath/src/pjnath-test/stun.c b/pjnath/src/pjnath-test/stun.c
index c0d74380..1b11f89b 100644
--- a/pjnath/src/pjnath-test/stun.c
+++ b/pjnath/src/pjnath-test/stun.c
@@ -269,7 +269,7 @@ static int decode_test(void)
continue;
/* Try to encode message */
- pj_stun_create_key(pool, &key, NULL, &USERNAME, &PASSWORD);
+ pj_stun_create_key(pool, &key, NULL, &USERNAME, PJ_STUN_PASSWD_PLAIN, &PASSWORD);
status = pj_stun_msg_encode(msg, buf, sizeof(buf), 0, &key, &len);
if (status != PJ_SUCCESS) {
PJ_LOG(1,(THIS_FILE, " encode error: %s", err(status)));
@@ -408,55 +408,6 @@ static int decode_verify(void)
return 0;
}
-static int auth_test(void)
-{
- /* REALM and USERNAME is present, but MESSAGE-INTEGRITY is not present.
- * For short term, must with reply 401 without REALM.
- * For long term, must reply with 401 with REALM.
- */
-
- /* USERNAME is not present, server must respond with 432 (Missing
- * Username).
- */
-
- /* If long term credential is wanted and REALM is not present, server
- * must respond with 434 (Missing Realm)
- */
-
- /* If REALM doesn't match, server must respond with 434 (Missing Realm)
- * too, containing REALM and NONCE attribute.
- */
-
- /* When long term authentication is wanted and NONCE is NOT present,
- * server must respond with 435 (Missing Nonce), containing REALM and
- * NONCE attribute.
- */
-
- /* Simulate 438 (Stale Nonce) */
-
- /* Simulate 436 (Unknown Username) */
-
- /* When server wants to use short term credential, but request has
- * REALM, reject with .... ???
- */
-
- /* Invalid HMAC */
-
- /* Valid static short term, without NONCE */
-
- /* Valid static short term, WITH NONCE */
-
- /* Valid static long term (with NONCE */
-
- /* Valid dynamic short term (without NONCE) */
-
- /* Valid dynamic short term (with NONCE) */
-
- /* Valid dynamic long term (with NONCE) */
-
- return 0;
-}
-
typedef struct test_vector test_vector;
static pj_stun_msg* create_msgint1(pj_pool_t *pool, test_vector *v);
@@ -614,7 +565,7 @@ static int fingerprint_test_vector()
pj_str_t s1, s2;
pj_stun_create_key(pool, &key, NULL, pj_cstr(&s1, v->username),
- pj_cstr(&s2, v->password));
+ PJ_STUN_PASSWD_PLAIN, pj_cstr(&s2, v->password));
pj_stun_msg_encode(msg, buf, sizeof(buf), 0, &key, &len);
} else {
@@ -751,10 +702,6 @@ int stun_test(void)
if (rc != 0)
goto on_return;
- rc = auth_test();
- if (rc != 0)
- goto on_return;
-
rc = fingerprint_test_vector();
if (rc != 0)
goto on_return;
diff --git a/pjnath/src/pjnath-test/test.c b/pjnath/src/pjnath-test/test.c
index fc1c124c..312c1ea2 100644
--- a/pjnath/src/pjnath-test/test.c
+++ b/pjnath/src/pjnath-test/test.c
@@ -68,6 +68,7 @@ static int test_inner(void)
#if INCLUDE_STUN_TEST
DO_TEST(stun_test());
+ DO_TEST(sess_auth_test());
#endif
#if INCLUDE_ICE_TEST
diff --git a/pjnath/src/pjnath-test/test.h b/pjnath/src/pjnath-test/test.h
index 39ad4112..713f3da8 100644
--- a/pjnath/src/pjnath-test/test.h
+++ b/pjnath/src/pjnath-test/test.h
@@ -23,9 +23,10 @@
#define INCLUDE_STUN_TEST 1
#define INCLUDE_ICE_TEST 1
-extern int stun_test(void);
-extern int ice_test(void);
-extern int test_main(void);
+int stun_test(void);
+int sess_auth_test(void);
+int ice_test(void);
+int test_main(void);
extern void app_perror(const char *title, pj_status_t rc);
extern pj_pool_factory *mem;
diff --git a/pjnath/src/pjnath/ice_session.c b/pjnath/src/pjnath/ice_session.c
index 71ff41c4..5ecac387 100644
--- a/pjnath/src/pjnath/ice_session.c
+++ b/pjnath/src/pjnath/ice_session.c
@@ -120,7 +120,7 @@ static pj_status_t on_stun_send_msg(pj_stun_session *sess,
static pj_status_t on_stun_rx_request(pj_stun_session *sess,
const pj_uint8_t *pkt,
unsigned pkt_len,
- const pj_stun_msg *msg,
+ const pj_stun_rx_data *rdata,
const pj_sockaddr_t *src_addr,
unsigned src_addr_len);
static void on_stun_request_complete(pj_stun_session *stun_sess,
@@ -147,14 +147,14 @@ static pj_status_t stun_auth_get_cred(const pj_stun_msg *msg,
pj_str_t *realm,
pj_str_t *username,
pj_str_t *nonce,
- int *data_type,
+ pj_stun_passwd_type *data_type,
pj_str_t *data);
static pj_status_t stun_auth_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_stun_passwd_type *data_type,
pj_str_t *data);
@@ -232,7 +232,8 @@ static pj_status_t init_comp(pj_ice_sess *ice,
auth_cred.data.dyn_cred.get_cred = &stun_auth_get_cred;
auth_cred.data.dyn_cred.get_password = &stun_auth_get_password;
auth_cred.data.dyn_cred.user_data = comp->stun_sess;
- pj_stun_session_set_credential(comp->stun_sess, &auth_cred);
+ pj_stun_session_set_credential(comp->stun_sess, PJ_STUN_AUTH_SHORT_TERM,
+ &auth_cred);
return PJ_SUCCESS;
}
@@ -446,7 +447,7 @@ static pj_status_t stun_auth_get_cred(const pj_stun_msg *msg,
pj_str_t *realm,
pj_str_t *username,
pj_str_t *nonce,
- int *data_type,
+ pj_stun_passwd_type *data_type,
pj_str_t *data)
{
pj_stun_session *sess = (pj_stun_session *)user_data;
@@ -461,12 +462,12 @@ static pj_status_t stun_auth_get_cred(const pj_stun_msg *msg,
* incoming requests.
*/
*username = ice->rx_uname;
- *data_type = 0;
+ *data_type = PJ_STUN_PASSWD_PLAIN;
*data = ice->rx_pass;
}
else {
*username = ice->tx_uname;
- *data_type = 0;
+ *data_type = PJ_STUN_PASSWD_PLAIN;
*data = ice->tx_pass;
}
@@ -479,7 +480,7 @@ static pj_status_t stun_auth_get_password(const pj_stun_msg *msg,
const pj_str_t *realm,
const pj_str_t *username,
pj_pool_t *pool,
- int *data_type,
+ pj_stun_passwd_type *data_type,
pj_str_t *data)
{
pj_stun_session *sess = (pj_stun_session *)user_data;
@@ -496,7 +497,7 @@ static pj_status_t stun_auth_get_password(const pj_stun_msg *msg,
/* Verify username */
if (pj_strcmp(username, &ice->tx_uname) != 0)
return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNAUTHORIZED);
- *data_type = 0;
+ *data_type = PJ_STUN_PASSWD_PLAIN;
*data = ice->tx_pass;
} else {
@@ -521,7 +522,7 @@ static pj_status_t stun_auth_get_password(const pj_stun_msg *msg,
if (pj_strcmp(&ufrag, &ice->rx_ufrag) != 0)
return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNAUTHORIZED);
- *data_type = 0;
+ *data_type = PJ_STUN_PASSWD_PLAIN;
*data = ice->rx_pass;
}
@@ -1903,11 +1904,12 @@ static void on_stun_request_complete(pj_stun_session *stun_sess,
static pj_status_t on_stun_rx_request(pj_stun_session *sess,
const pj_uint8_t *pkt,
unsigned pkt_len,
- const pj_stun_msg *msg,
+ const pj_stun_rx_data *rdata,
const pj_sockaddr_t *src_addr,
unsigned src_addr_len)
{
stun_data *sd;
+ const pj_stun_msg *msg = rdata->msg;
pj_ice_sess *ice;
pj_stun_priority_attr *prio_attr;
pj_stun_use_candidate_attr *uc_attr;
@@ -1921,7 +1923,7 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess,
/* Reject any requests except Binding request */
if (msg->hdr.type != PJ_STUN_BINDING_REQUEST) {
- status = pj_stun_session_create_res(sess, msg,
+ status = pj_stun_session_create_res(sess, rdata,
PJ_STUN_SC_BAD_REQUEST,
NULL, &tdata);
if (status != PJ_SUCCESS)
@@ -1992,7 +1994,7 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess,
pj_ice_sess_change_role(ice, PJ_ICE_SESS_ROLE_CONTROLLED);
} else {
/* Generate 487 response */
- status = pj_stun_session_create_res(sess, msg,
+ status = pj_stun_session_create_res(sess, rdata,
PJ_STUN_SC_ROLE_CONFLICT,
NULL, &tdata);
if (status == PJ_SUCCESS) {
@@ -2008,7 +2010,7 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess,
{
if (pj_cmp_timestamp(&ice->tie_breaker, &role_attr->value) < 0) {
/* Generate 487 response */
- status = pj_stun_session_create_res(sess, msg,
+ status = pj_stun_session_create_res(sess, rdata,
PJ_STUN_SC_ROLE_CONFLICT,
NULL, &tdata);
if (status == PJ_SUCCESS) {
@@ -2028,7 +2030,7 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess,
/*
* First send response to this request
*/
- status = pj_stun_session_create_res(sess, msg, 0, NULL, &tdata);
+ status = pj_stun_session_create_res(sess, rdata, 0, NULL, &tdata);
if (status != PJ_SUCCESS) {
pj_mutex_unlock(ice->mutex);
return status;
diff --git a/pjnath/src/pjnath/stun_auth.c b/pjnath/src/pjnath/stun_auth.c
index 4a6024f0..4ef29599 100644
--- a/pjnath/src/pjnath/stun_auth.c
+++ b/pjnath/src/pjnath/stun_auth.c
@@ -19,9 +19,11 @@
#include <pjnath/stun_auth.h>
#include <pjnath/errno.h>
#include <pjlib-util/hmac_sha1.h>
+#include <pjlib-util/md5.h>
#include <pjlib-util/sha1.h>
#include <pj/assert.h>
#include <pj/log.h>
+#include <pj/pool.h>
#include <pj/string.h>
#define THIS_FILE "stun_auth.c"
@@ -53,6 +55,99 @@ PJ_DEF(void) pj_stun_auth_cred_dup( pj_pool_t *pool,
}
+/*
+ * Duplicate request credential.
+ */
+PJ_DEF(void) pj_stun_req_cred_info_dup( pj_pool_t *pool,
+ pj_stun_req_cred_info *dst,
+ const pj_stun_req_cred_info *src)
+{
+ pj_strdup(pool, &dst->realm, &src->realm);
+ pj_strdup(pool, &dst->username, &src->username);
+ pj_strdup(pool, &dst->nonce, &src->nonce);
+ pj_strdup(pool, &dst->auth_key, &src->auth_key);
+}
+
+
+/* Calculate HMAC-SHA1 key for long term credential, by getting
+ * MD5 digest of username, realm, and password.
+ */
+static void calc_md5_key(pj_uint8_t digest[16],
+ const pj_str_t *realm,
+ const pj_str_t *username,
+ const pj_str_t *passwd)
+{
+ /* The 16-byte key for MESSAGE-INTEGRITY HMAC is formed by taking
+ * the MD5 hash of the result of concatenating the following five
+ * fields: (1) The username, with any quotes and trailing nulls
+ * removed, (2) A single colon, (3) The realm, with any quotes and
+ * trailing nulls removed, (4) A single colon, and (5) The
+ * password, with any trailing nulls removed.
+ */
+ pj_md5_context ctx;
+ pj_str_t s;
+
+ pj_md5_init(&ctx);
+
+#define REMOVE_QUOTE(s) if (s.slen && *s.ptr=='"') \
+ s.ptr++, s.slen--; \
+ if (s.slen && s.ptr[s.slen-1]=='"') \
+ s.slen--;
+
+ /* Add username */
+ s = *username;
+ REMOVE_QUOTE(s);
+ pj_md5_update(&ctx, (pj_uint8_t*)s.ptr, s.slen);
+
+ /* Add single colon */
+ pj_md5_update(&ctx, (pj_uint8_t*)":", 1);
+
+ /* Add realm */
+ s = *realm;
+ REMOVE_QUOTE(s);
+ pj_md5_update(&ctx, (pj_uint8_t*)s.ptr, s.slen);
+
+#undef REMOVE_QUOTE
+
+ /* Another colon */
+ pj_md5_update(&ctx, (pj_uint8_t*)":", 1);
+
+ /* Add password */
+ pj_md5_update(&ctx, (pj_uint8_t*)passwd->ptr, passwd->slen);
+
+ /* Done */
+ pj_md5_final(&ctx, digest);
+}
+
+
+/*
+ * Create authentication key to be used for encoding the message with
+ * MESSAGE-INTEGRITY.
+ */
+PJ_DEF(void) pj_stun_create_key(pj_pool_t *pool,
+ pj_str_t *key,
+ const pj_str_t *realm,
+ const pj_str_t *username,
+ pj_stun_passwd_type data_type,
+ const pj_str_t *data)
+{
+ PJ_ASSERT_ON_FAIL(pool && key && username && data, return);
+
+ if (realm && realm->slen) {
+ if (data_type == PJ_STUN_PASSWD_PLAIN) {
+ key->ptr = (char*) pj_pool_alloc(pool, 16);
+ calc_md5_key((pj_uint8_t*)key->ptr, realm, username, data);
+ key->slen = 16;
+ } else {
+ pj_strdup(pool, key, data);
+ }
+ } else {
+ pj_assert(data_type == PJ_STUN_PASSWD_PLAIN);
+ pj_strdup(pool, key, data);
+ }
+}
+
+
PJ_INLINE(pj_uint16_t) GET_VAL16(const pj_uint8_t *pdu, unsigned pos)
{
return (pj_uint16_t) ((pdu[pos] << 8) + pdu[pos+1]);
@@ -86,8 +181,8 @@ static pj_status_t create_challenge(pj_pool_t *pool,
if (rc != PJ_SUCCESS)
return rc;
-
- if (realm && realm->slen) {
+ /* SHOULD NOT add REALM, NONCE, USERNAME, and M-I on 400 response */
+ if (err_code!=400 && realm && realm->slen) {
rc = pj_stun_msg_add_string_attr(pool, response,
PJ_STUN_ATTR_REALM,
realm);
@@ -101,7 +196,7 @@ static pj_status_t create_challenge(pj_pool_t *pool,
}
}
- if (nonce && nonce->slen) {
+ if (err_code!=400 && nonce && nonce->slen) {
rc = pj_stun_msg_add_string_attr(pool, response,
PJ_STUN_ATTR_NONCE,
nonce);
@@ -121,20 +216,20 @@ PJ_DEF(pj_status_t) pj_stun_authenticate_request(const pj_uint8_t *pkt,
const pj_stun_msg *msg,
pj_stun_auth_cred *cred,
pj_pool_t *pool,
- pj_str_t *auth_key,
+ pj_stun_req_cred_info *p_info,
pj_stun_msg **p_response)
{
- pj_str_t realm, nonce, password;
+ pj_stun_req_cred_info tmp_info;
const pj_stun_msgint_attr *amsgi;
unsigned i, amsgi_pos;
pj_bool_t has_attr_beyond_mi;
const pj_stun_username_attr *auser;
- pj_bool_t username_ok;
const pj_stun_realm_attr *arealm;
const pj_stun_realm_attr *anonce;
pj_hmac_sha1_context ctx;
pj_uint8_t digest[PJ_SHA1_DIGEST_SIZE];
- pj_str_t key;
+ pj_stun_status err_code;
+ const char *err_text = NULL;
pj_status_t status;
/* msg and credential MUST be specified */
@@ -149,18 +244,24 @@ PJ_DEF(pj_status_t) pj_stun_authenticate_request(const pj_uint8_t *pkt,
if (!PJ_STUN_IS_REQUEST(msg->hdr.type))
p_response = NULL;
+ if (p_info == NULL)
+ p_info = &tmp_info;
+
+ pj_bzero(p_info, sizeof(pj_stun_req_cred_info));
+
/* Get realm and nonce from credential */
- realm.slen = nonce.slen = 0;
+ p_info->realm.slen = p_info->nonce.slen = 0;
if (cred->type == PJ_STUN_AUTH_CRED_STATIC) {
- realm = cred->data.static_cred.realm;
- nonce = cred->data.static_cred.nonce;
+ p_info->realm = cred->data.static_cred.realm;
+ p_info->nonce = cred->data.static_cred.nonce;
} else if (cred->type == PJ_STUN_AUTH_CRED_DYNAMIC) {
status = cred->data.dyn_cred.get_auth(cred->data.dyn_cred.user_data,
- pool, &realm, &nonce);
+ pool, &p_info->realm,
+ &p_info->nonce);
if (status != PJ_SUCCESS)
return status;
} else {
- pj_assert(!"Unexpected");
+ pj_assert(!"Invalid credential type");
return PJ_EBUG;
}
@@ -184,14 +285,9 @@ PJ_DEF(pj_status_t) pj_stun_authenticate_request(const pj_uint8_t *pkt,
for short term, and 401 for long term.
The rule has been changed from rfc3489bis-06
*/
- int code;
-
- code = realm.slen ? PJ_STUN_SC_UNAUTHORIZED : PJ_STUN_SC_BAD_REQUEST;
- if (p_response) {
- create_challenge(pool, msg, code, NULL,
- &realm, &nonce, p_response);
- }
- return PJ_STATUS_FROM_STUN_CODE(code);
+ err_code = p_info->realm.slen ? PJ_STUN_SC_UNAUTHORIZED :
+ PJ_STUN_SC_BAD_REQUEST;
+ goto on_auth_failed;
}
/* Next check that USERNAME is present */
@@ -202,48 +298,67 @@ PJ_DEF(pj_status_t) pj_stun_authenticate_request(const pj_uint8_t *pkt,
for both short and long term, since M-I is present.
The rule has been changed from rfc3489bis-06
*/
- int code = PJ_STUN_SC_BAD_REQUEST;
- if (p_response) {
- create_challenge(pool, msg, code, "Missing USERNAME",
- &realm, &nonce, p_response);
- }
- return PJ_STATUS_FROM_STUN_CODE(code);
+ err_code = PJ_STUN_SC_BAD_REQUEST;
+ err_text = "Missing USERNAME";
+ goto on_auth_failed;
}
/* Get REALM, if any */
arealm = (const pj_stun_realm_attr*)
pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_REALM, 0);
+ /* Reject with 400 if we have long term credential and the request
+ * is missing REALM attribute.
+ */
+ if (p_info->realm.slen && arealm==NULL) {
+ err_code = PJ_STUN_SC_BAD_REQUEST;
+ err_text = "Missing REALM";
+ goto on_auth_failed;
+ }
+
/* Check if username match */
if (cred->type == PJ_STUN_AUTH_CRED_STATIC) {
+ pj_bool_t username_ok;
username_ok = !pj_strcmp(&auser->value,
&cred->data.static_cred.username);
- password = cred->data.static_cred.data;
+ if (username_ok) {
+ pj_strdup(pool, &p_info->username,
+ &cred->data.static_cred.username);
+ pj_stun_create_key(pool, &p_info->auth_key, &p_info->realm,
+ &auser->value, cred->data.static_cred.data_type,
+ &cred->data.static_cred.data);
+ } else {
+ /* Username mismatch */
+ /* According to rfc3489bis-10 Sec 10.1.2/10.2.2, we should
+ * return 401
+ */
+ err_code = PJ_STUN_SC_UNAUTHORIZED;
+ goto on_auth_failed;
+ }
} else if (cred->type == PJ_STUN_AUTH_CRED_DYNAMIC) {
- int data_type = 0;
+ pj_stun_passwd_type data_type = PJ_STUN_PASSWD_PLAIN;
+ pj_str_t password;
pj_status_t rc;
+
rc = cred->data.dyn_cred.get_password(msg,
cred->data.dyn_cred.user_data,
(arealm?&arealm->value:NULL),
&auser->value, pool,
&data_type, &password);
- username_ok = (rc == PJ_SUCCESS);
+ if (rc == PJ_SUCCESS) {
+ pj_strdup(pool, &p_info->username, &auser->value);
+ pj_stun_create_key(pool, &p_info->auth_key,
+ (arealm?&arealm->value:NULL), &auser->value,
+ data_type, &password);
+ } else {
+ err_code = PJ_STUN_SC_UNAUTHORIZED;
+ goto on_auth_failed;
+ }
} else {
- username_ok = PJ_TRUE;
- password.slen = 0;
+ pj_assert(!"Invalid credential type");
+ return PJ_EBUG;
}
- if (!username_ok) {
- /* Username mismatch */
- /* According to rfc3489bis-10 Sec 10.1.2/10.2.2, we should
- * return 401
- */
- if (p_response) {
- create_challenge(pool, msg, PJ_STUN_SC_UNAUTHORIZED, NULL,
- &realm, &nonce, p_response);
- }
- return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNAUTHORIZED);
- }
/* Get NONCE attribute */
@@ -251,40 +366,33 @@ PJ_DEF(pj_status_t) pj_stun_authenticate_request(const pj_uint8_t *pkt,
pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_NONCE, 0);
/* Check for long term/short term requirements. */
- if (realm.slen != 0 && arealm == NULL) {
+ if (p_info->realm.slen != 0 && arealm == NULL) {
/* Long term credential is required and REALM is not present */
- if (p_response) {
- create_challenge(pool, msg, PJ_STUN_SC_BAD_REQUEST,
- "Missing REALM",
- &realm, &nonce, p_response);
- }
- return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_BAD_REQUEST);
+ err_code = PJ_STUN_SC_BAD_REQUEST;
+ err_text = "Missing REALM";
+ goto on_auth_failed;
- } else if (realm.slen != 0 && arealm != NULL) {
+ } else if (p_info->realm.slen != 0 && arealm != NULL) {
/* We want long term, and REALM is present */
/* NONCE must be present. */
- if (anonce == NULL && nonce.slen) {
- if (p_response) {
- create_challenge(pool, msg, PJ_STUN_SC_BAD_REQUEST,
- "Missing NONCE", &realm, &nonce, p_response);
- }
- return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_BAD_REQUEST);
+ if (anonce == NULL && p_info->nonce.slen) {
+ err_code = PJ_STUN_SC_BAD_REQUEST;
+ err_text = "Missing NONCE";
+ goto on_auth_failed;
}
/* Verify REALM matches */
- if (pj_stricmp(&arealm->value, &realm)) {
+ if (pj_stricmp(&arealm->value, &p_info->realm)) {
/* REALM doesn't match */
- if (p_response) {
- create_challenge(pool, msg, PJ_STUN_SC_UNAUTHORIZED,
- "Invalid REALM", &realm, &nonce, p_response);
- }
- return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNAUTHORIZED);
+ err_code = PJ_STUN_SC_UNAUTHORIZED;
+ err_text = "Invalid REALM";
+ goto on_auth_failed;
}
/* Valid case, will validate the message integrity later */
- } else if (realm.slen == 0 && arealm != NULL) {
+ } else if (p_info->realm.slen == 0 && arealm != NULL) {
/* We want to use short term credential, but client uses long
* term credential. The draft doesn't mention anything about
* switching between long term and short term.
@@ -293,16 +401,14 @@ PJ_DEF(pj_status_t) pj_stun_authenticate_request(const pj_uint8_t *pkt,
/* For now just accept the credential, anyway it will probably
* cause wrong message integrity value later.
*/
- } else if (realm.slen==0 && arealm == NULL) {
+ } else if (p_info->realm.slen==0 && arealm == NULL) {
/* Short term authentication is wanted, and one is supplied */
/* Application MAY request NONCE to be supplied */
- if (nonce.slen != 0) {
- if (p_response) {
- create_challenge(pool, msg, PJ_STUN_SC_UNAUTHORIZED,
- "NONCE required", &realm, &nonce, p_response);
- }
- return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNAUTHORIZED);
+ if (p_info->nonce.slen != 0) {
+ err_code = PJ_STUN_SC_UNAUTHORIZED;
+ err_text = "NONCE required";
+ goto on_auth_failed;
}
}
@@ -321,31 +427,22 @@ PJ_DEF(pj_status_t) pj_stun_authenticate_request(const pj_uint8_t *pkt,
} else if (cred->type == PJ_STUN_AUTH_CRED_DYNAMIC) {
ok = PJ_TRUE;
} else {
- if (nonce.slen) {
- ok = !pj_strcmp(&anonce->value, &nonce);
+ if (p_info->nonce.slen) {
+ ok = !pj_strcmp(&anonce->value, &p_info->nonce);
} else {
ok = PJ_TRUE;
}
}
if (!ok) {
- if (p_response) {
- create_challenge(pool, msg, PJ_STUN_SC_STALE_NONCE,
- NULL, &realm, &nonce, p_response);
- }
- if (auth_key) {
- pj_stun_create_key(pool, auth_key, &realm,
- &auser->value, &password);
- }
- return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_STALE_NONCE);
+ err_code = PJ_STUN_SC_STALE_NONCE;
+ goto on_auth_failed;
}
}
- /* Calculate key */
- pj_stun_create_key(pool, &key, &realm, &auser->value, &password);
-
/* Now calculate HMAC of the message. */
- pj_hmac_sha1_init(&ctx, (pj_uint8_t*)key.ptr, key.slen);
+ pj_hmac_sha1_init(&ctx, (pj_uint8_t*)p_info->auth_key.ptr,
+ p_info->auth_key.slen);
#if PJ_STUN_OLD_STYLE_MI_FINGERPRINT
/* Pre rfc3489bis-06 style of calculation */
@@ -382,15 +479,20 @@ PJ_DEF(pj_status_t) pj_stun_authenticate_request(const pj_uint8_t *pkt,
if (pj_memcmp(amsgi->hmac, digest, 20)) {
/* HMAC value mismatch */
/* According to rfc3489bis-10 Sec 10.1.2 we should return 401 */
- if (p_response) {
- create_challenge(pool, msg, PJ_STUN_SC_UNAUTHORIZED,
- NULL, &realm, &nonce, p_response);
- }
- return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNAUTHORIZED);
+ err_code = PJ_STUN_SC_UNAUTHORIZED;
+ err_text = "MESSAGE-INTEGRITY mismatch";
+ goto on_auth_failed;
}
/* Everything looks okay! */
return PJ_SUCCESS;
+
+on_auth_failed:
+ if (p_response) {
+ create_challenge(pool, msg, err_code, err_text,
+ &p_info->realm, &p_info->nonce, p_response);
+ }
+ return PJ_STATUS_FROM_STUN_CODE(err_code);
}
@@ -425,11 +527,10 @@ PJ_DEF(pj_bool_t) pj_stun_auth_valid_for_msg(const pj_stun_msg *msg)
switch (err_attr->err_code) {
case PJ_STUN_SC_BAD_REQUEST: /* 400 (Bad Request) */
case PJ_STUN_SC_UNAUTHORIZED: /* 401 (Unauthorized) */
- //case PJ_STUN_SC_STALE_CREDENTIALS: /* 430 (Stale Credential) */
- //case PJ_STUN_SC_MISSING_USERNAME: /* 432 (Missing Username) */
- //case PJ_STUN_SC_MISSING_REALM: /* 434 (Missing Realm) */
- //case PJ_STUN_SC_UNKNOWN_USERNAME: /* 436 (Unknown Username) */
- //case PJ_STUN_SC_INTEGRITY_CHECK_FAILURE:/* 431 (Integrity Check Fail) */
+
+ /* Due to the way this response is generated here, we can't really
+ * authenticate 420 (Unknown Attribute) response */
+ case PJ_STUN_SC_UNKNOWN_ATTRIBUTE:
return PJ_FALSE;
default:
return PJ_TRUE;
diff --git a/pjnath/src/pjnath/stun_msg.c b/pjnath/src/pjnath/stun_msg.c
index 3e01d4aa..716683fb 100644
--- a/pjnath/src/pjnath/stun_msg.c
+++ b/pjnath/src/pjnath/stun_msg.c
@@ -20,7 +20,6 @@
#include <pjnath/errno.h>
#include <pjlib-util/crc32.h>
#include <pjlib-util/hmac_sha1.h>
-#include <pjlib-util/md5.h>
#include <pj/assert.h>
#include <pj/log.h>
#include <pj/os.h>
@@ -90,6 +89,7 @@ struct attr_desc
void **p_attr);
pj_status_t (*encode_attr)(const void *a, pj_uint8_t *buf,
unsigned len, unsigned *printed);
+ void* (*clone_attr)(pj_pool_t *pool, const void *src);
};
static pj_status_t decode_sockaddr_attr(pj_pool_t *pool,
@@ -101,47 +101,55 @@ static pj_status_t decode_xored_sockaddr_attr(pj_pool_t *pool,
static pj_status_t encode_sockaddr_attr(const void *a, pj_uint8_t *buf,
unsigned len,
unsigned *printed);
+static void* clone_sockaddr_attr(pj_pool_t *pool, const void *src);
static pj_status_t decode_string_attr(pj_pool_t *pool,
const pj_uint8_t *buf,
void **p_attr);
static pj_status_t encode_string_attr(const void *a, pj_uint8_t *buf,
unsigned len, unsigned *printed);
+static void* clone_string_attr(pj_pool_t *pool, const void *src);
static pj_status_t decode_msgint_attr(pj_pool_t *pool,
const pj_uint8_t *buf,
void **p_attr);
static pj_status_t encode_msgint_attr(const void *a, pj_uint8_t *buf,
unsigned len, unsigned *printed);
+static void* clone_msgint_attr(pj_pool_t *pool, const void *src);
static pj_status_t decode_errcode_attr(pj_pool_t *pool,
const pj_uint8_t *buf,
void **p_attr);
static pj_status_t encode_errcode_attr(const void *a, pj_uint8_t *buf,
unsigned len, unsigned *printed);
+static void* clone_errcode_attr(pj_pool_t *pool, const void *src);
static pj_status_t decode_unknown_attr(pj_pool_t *pool,
const pj_uint8_t *buf,
void **p_attr);
static pj_status_t encode_unknown_attr(const void *a, pj_uint8_t *buf,
unsigned len, unsigned *printed);
+static void* clone_unknown_attr(pj_pool_t *pool, const void *src);
static pj_status_t decode_uint_attr(pj_pool_t *pool,
const pj_uint8_t *buf,
void **p_attr);
static pj_status_t encode_uint_attr(const void *a, pj_uint8_t *buf,
unsigned len, unsigned *printed);
+static void* clone_uint_attr(pj_pool_t *pool, const void *src);
static pj_status_t decode_uint64_attr(pj_pool_t *pool,
const pj_uint8_t *buf,
void **p_attr);
static pj_status_t encode_uint64_attr(const void *a, pj_uint8_t *buf,
unsigned len, unsigned *printed);
+static void* clone_uint64_attr(pj_pool_t *pool, const void *src);
static pj_status_t decode_binary_attr(pj_pool_t *pool,
const pj_uint8_t *buf,
void **p_attr);
static pj_status_t encode_binary_attr(const void *a, pj_uint8_t *buf,
unsigned len, unsigned *printed);
+static void* clone_binary_attr(pj_pool_t *pool, const void *src);
static pj_status_t decode_empty_attr(pj_pool_t *pool,
const pj_uint8_t *buf,
void **p_attr);
static pj_status_t encode_empty_attr(const void *a, pj_uint8_t *buf,
unsigned len, unsigned *printed);
-
+static void* clone_empty_attr(pj_pool_t *pool, const void *src);
static struct attr_desc mandatory_attr_desc[] =
{
@@ -149,235 +157,274 @@ static struct attr_desc mandatory_attr_desc[] =
/* type zero */
NULL,
NULL,
+ NULL,
NULL
},
{
/* PJ_STUN_ATTR_MAPPED_ADDR, */
"MAPPED-ADDRESS",
&decode_sockaddr_attr,
- &encode_sockaddr_attr
+ &encode_sockaddr_attr,
+ &clone_sockaddr_attr
},
{
/* PJ_STUN_ATTR_RESPONSE_ADDR, */
"RESPONSE-ADDRESS",
&decode_sockaddr_attr,
- &encode_sockaddr_attr
+ &encode_sockaddr_attr,
+ &clone_sockaddr_attr
},
{
/* PJ_STUN_ATTR_CHANGE_REQUEST, */
"CHANGE-REQUEST",
&decode_uint_attr,
- &encode_uint_attr
+ &encode_uint_attr,
+ &clone_uint_attr
},
{
/* PJ_STUN_ATTR_SOURCE_ADDR, */
"SOURCE-ADDRESS",
&decode_sockaddr_attr,
- &encode_sockaddr_attr
+ &encode_sockaddr_attr,
+ &clone_sockaddr_attr
},
{
/* PJ_STUN_ATTR_CHANGED_ADDR, */
"CHANGED-ADDRESS",
&decode_sockaddr_attr,
- &encode_sockaddr_attr
+ &encode_sockaddr_attr,
+ &clone_sockaddr_attr
},
{
/* PJ_STUN_ATTR_USERNAME, */
"USERNAME",
&decode_string_attr,
- &encode_string_attr
+ &encode_string_attr,
+ &clone_string_attr
},
{
/* PJ_STUN_ATTR_PASSWORD, */
"PASSWORD",
&decode_string_attr,
- &encode_string_attr
+ &encode_string_attr,
+ &clone_string_attr
},
{
/* PJ_STUN_ATTR_MESSAGE_INTEGRITY, */
"MESSAGE-INTEGRITY",
&decode_msgint_attr,
- &encode_msgint_attr
+ &encode_msgint_attr,
+ &clone_msgint_attr
},
{
/* PJ_STUN_ATTR_ERROR_CODE, */
"ERROR-CODE",
&decode_errcode_attr,
- &encode_errcode_attr
+ &encode_errcode_attr,
+ &clone_errcode_attr
},
{
/* PJ_STUN_ATTR_UNKNOWN_ATTRIBUTES, */
"UNKNOWN-ATTRIBUTES",
&decode_unknown_attr,
- &encode_unknown_attr
+ &encode_unknown_attr,
+ &clone_unknown_attr
},
{
/* PJ_STUN_ATTR_REFLECTED_FROM, */
"REFLECTED-FROM",
&decode_sockaddr_attr,
- &encode_sockaddr_attr
+ &encode_sockaddr_attr,
+ &clone_sockaddr_attr
},
{
/* PJ_STUN_ATTR_CHANNEL_NUMBER (0x000C) */
"CHANNEL-NUMBER",
&decode_uint_attr,
- &encode_uint_attr
+ &encode_uint_attr,
+ &clone_uint_attr
},
{
/* PJ_STUN_ATTR_LIFETIME, */
"LIFETIME",
&decode_uint_attr,
- &encode_uint_attr
+ &encode_uint_attr,
+ &clone_uint_attr
},
{
/* ID 0x000E is not assigned */
NULL,
NULL,
+ NULL,
NULL
},
{
/* PJ_STUN_ATTR_MAGIC_COOKIE */
"MAGIC-COOKIE",
&decode_uint_attr,
- &encode_uint_attr
+ &encode_uint_attr,
+ &clone_uint_attr
},
{
/* PJ_STUN_ATTR_BANDWIDTH, */
"BANDWIDTH",
&decode_uint_attr,
- &encode_uint_attr
+ &encode_uint_attr,
+ &clone_uint_attr
},
{
/* ID 0x0011 is not assigned */
NULL,
NULL,
+ NULL,
NULL
},
{
/* PJ_STUN_ATTR_PEER_ADDRESS, */
"PEER-ADDRESS",
&decode_xored_sockaddr_attr,
- &encode_sockaddr_attr
+ &encode_sockaddr_attr,
+ &clone_sockaddr_attr
},
{
/* PJ_STUN_ATTR_DATA, */
"DATA",
&decode_binary_attr,
- &encode_binary_attr
+ &encode_binary_attr,
+ &clone_binary_attr
},
{
/* PJ_STUN_ATTR_REALM, */
"REALM",
&decode_string_attr,
- &encode_string_attr
+ &encode_string_attr,
+ &clone_string_attr
},
{
/* PJ_STUN_ATTR_NONCE, */
"NONCE",
&decode_string_attr,
- &encode_string_attr
+ &encode_string_attr,
+ &clone_string_attr
},
{
/* PJ_STUN_ATTR_RELAY_ADDRESS, */
"RELAY-ADDRESS",
&decode_xored_sockaddr_attr,
- &encode_sockaddr_attr
+ &encode_sockaddr_attr,
+ &clone_sockaddr_attr
},
{
/* PJ_STUN_ATTR_REQUESTED_ADDR_TYPE, */
"REQUESTED-ADDRESS-TYPE",
&decode_uint_attr,
- &encode_uint_attr
+ &encode_uint_attr,
+ &clone_uint_attr
},
{
/* PJ_STUN_ATTR_REQUESTED_PROPS, */
"REQUESTED-PROPS",
&decode_uint_attr,
- &encode_uint_attr
+ &encode_uint_attr,
+ &clone_uint_attr
},
{
/* PJ_STUN_ATTR_REQUESTED_TRANSPORT, */
"REQUESTED-TRANSPORT",
&decode_uint_attr,
- &encode_uint_attr
+ &encode_uint_attr,
+ &clone_uint_attr
},
{
/* ID 0x001A is not assigned */
NULL,
NULL,
+ NULL,
NULL
},
{
/* ID 0x001B is not assigned */
NULL,
NULL,
+ NULL,
NULL
},
{
/* ID 0x001C is not assigned */
NULL,
NULL,
+ NULL,
NULL
},
{
/* ID 0x001D is not assigned */
NULL,
NULL,
+ NULL,
NULL
},
{
/* ID 0x001E is not assigned */
NULL,
NULL,
+ NULL,
NULL
},
{
/* ID 0x001F is not assigned */
NULL,
NULL,
+ NULL,
NULL
},
{
/* PJ_STUN_ATTR_XOR_MAPPED_ADDRESS, */
"XOR-MAPPED-ADDRESS",
&decode_xored_sockaddr_attr,
- &encode_sockaddr_attr
+ &encode_sockaddr_attr,
+ &clone_sockaddr_attr
},
{
/* PJ_STUN_ATTR_TIMER_VAL, */
"TIMER-VAL",
&decode_uint_attr,
- &encode_uint_attr
+ &encode_uint_attr,
+ &clone_uint_attr
},
{
/* PJ_STUN_ATTR_RESERVATION_TOKEN, */
"RESERVATION-TOKEN",
&decode_uint64_attr,
- &encode_uint64_attr
+ &encode_uint64_attr,
+ &clone_uint64_attr
},
{
/* PJ_STUN_ATTR_XOR_REFLECTED_FROM, */
"XOR-REFLECTED-FROM",
&decode_xored_sockaddr_attr,
- &encode_sockaddr_attr
+ &encode_sockaddr_attr,
+ &clone_sockaddr_attr
},
{
/* PJ_STUN_ATTR_PRIORITY, */
"PRIORITY",
&decode_uint_attr,
- &encode_uint_attr
+ &encode_uint_attr,
+ &clone_uint_attr
},
{
/* PJ_STUN_ATTR_USE_CANDIDATE, */
"USE-CANDIDATE",
&decode_empty_attr,
- &encode_empty_attr
+ &encode_empty_attr,
+ &clone_empty_attr
},
{
/* PJ_STUN_ATTR_XOR_INTERNAL_ADDR, */
"XOR-INTERNAL-ADDRESS",
&decode_xored_sockaddr_attr,
- &encode_sockaddr_attr
+ &encode_sockaddr_attr,
+ &clone_sockaddr_attr
},
/* Sentinel */
@@ -385,6 +432,7 @@ static struct attr_desc mandatory_attr_desc[] =
/* PJ_STUN_ATTR_END_MANDATORY_ATTR */
NULL,
NULL,
+ NULL,
NULL
}
};
@@ -395,61 +443,71 @@ static struct attr_desc extended_attr_desc[] =
/* ID 0x8021 is not assigned */
NULL,
NULL,
+ NULL,
NULL
},
{
/* PJ_STUN_ATTR_SERVER, */
"SERVER",
&decode_string_attr,
- &encode_string_attr
+ &encode_string_attr,
+ &clone_string_attr
},
{
/* PJ_STUN_ATTR_ALTERNATE_SERVER, */
"ALTERNATE-SERVER",
&decode_sockaddr_attr,
- &encode_sockaddr_attr
+ &encode_sockaddr_attr,
+ &clone_sockaddr_attr
},
{
/* PJ_STUN_ATTR_REFRESH_INTERVAL, */
"REFRESH-INTERVAL",
&decode_uint_attr,
- &encode_uint_attr
+ &encode_uint_attr,
+ &clone_uint_attr
},
{
/* ID 0x8025 is not assigned*/
NULL,
NULL,
+ NULL,
NULL
},
{
/* PADDING, 0x8026 */
NULL,
NULL,
+ NULL,
NULL
},
{
/* CACHE-TIMEOUT, 0x8027 */
NULL,
NULL,
+ NULL,
NULL
},
{
/* PJ_STUN_ATTR_FINGERPRINT, */
"FINGERPRINT",
&decode_uint_attr,
- &encode_uint_attr
+ &encode_uint_attr,
+ &clone_uint_attr
},
{
/* PJ_STUN_ATTR_ICE_CONTROLLED, */
"ICE-CONTROLLED",
&decode_uint64_attr,
- &encode_uint64_attr
+ &encode_uint64_attr,
+ &clone_uint64_attr
},
{
/* PJ_STUN_ATTR_ICE_CONTROLLING, */
"ICE-CONTROLLING",
&decode_uint64_attr,
- &encode_uint64_attr
+ &encode_uint64_attr,
+ &clone_uint64_attr
}
};
@@ -826,6 +884,13 @@ static pj_status_t encode_sockaddr_attr(const void *a, pj_uint8_t *buf,
}
+static void* clone_sockaddr_attr(pj_pool_t *pool, const void *src)
+{
+ pj_stun_sockaddr_attr *dst = PJ_POOL_ALLOC_T(pool, pj_stun_sockaddr_attr);
+ pj_memcpy(dst, src, sizeof(pj_stun_sockaddr_attr));
+ return (void*)dst;
+}
+
//////////////////////////////////////////////////////////////////////////////
/*
* STUN generic string attribute
@@ -934,6 +999,17 @@ static pj_status_t encode_string_attr(const void *a, pj_uint8_t *buf,
}
+static void* clone_string_attr(pj_pool_t *pool, const void *src)
+{
+ const pj_stun_string_attr *asrc = (const pj_stun_string_attr*)src;
+ pj_stun_string_attr *dst = PJ_POOL_ALLOC_T(pool, pj_stun_string_attr);
+
+ pj_memcpy(dst, src, sizeof(pj_stun_attr_hdr));
+ pj_strdup(pool, &dst->value, &asrc->value);
+
+ return (void*)dst;
+}
+
//////////////////////////////////////////////////////////////////////////////
/*
* STUN empty attribute (used by USE-CANDIDATE).
@@ -1018,6 +1094,15 @@ static pj_status_t encode_empty_attr(const void *a, pj_uint8_t *buf,
}
+static void* clone_empty_attr(pj_pool_t *pool, const void *src)
+{
+ pj_stun_empty_attr *dst = PJ_POOL_ALLOC_T(pool, pj_stun_empty_attr);
+
+ pj_memcpy(dst, src, sizeof(pj_stun_empty_attr));
+
+ return (void*) dst;
+}
+
//////////////////////////////////////////////////////////////////////////////
/*
* STUN generic 32bit integer attribute.
@@ -1103,6 +1188,16 @@ static pj_status_t encode_uint_attr(const void *a, pj_uint8_t *buf,
return PJ_SUCCESS;
}
+
+static void* clone_uint_attr(pj_pool_t *pool, const void *src)
+{
+ pj_stun_uint_attr *dst = PJ_POOL_ALLOC_T(pool, pj_stun_uint_attr);
+
+ pj_memcpy(dst, src, sizeof(pj_stun_uint_attr));
+
+ return (void*)dst;
+}
+
//////////////////////////////////////////////////////////////////////////////
/*
@@ -1189,6 +1284,16 @@ static pj_status_t encode_uint64_attr(const void *a, pj_uint8_t *buf,
}
+static void* clone_uint64_attr(pj_pool_t *pool, const void *src)
+{
+ pj_stun_uint64_attr *dst = PJ_POOL_ALLOC_T(pool, pj_stun_uint64_attr);
+
+ pj_memcpy(dst, src, sizeof(pj_stun_uint64_attr));
+
+ return (void*)dst;
+}
+
+
//////////////////////////////////////////////////////////////////////////////
/*
* STUN MESSAGE-INTEGRITY attribute.
@@ -1271,6 +1376,16 @@ static pj_status_t encode_msgint_attr(const void *a, pj_uint8_t *buf,
return PJ_SUCCESS;
}
+
+static void* clone_msgint_attr(pj_pool_t *pool, const void *src)
+{
+ pj_stun_msgint_attr *dst = PJ_POOL_ALLOC_T(pool, pj_stun_msgint_attr);
+
+ pj_memcpy(dst, src, sizeof(pj_stun_msgint_attr));
+
+ return (void*) dst;
+}
+
//////////////////////////////////////////////////////////////////////////////
/*
* STUN ERROR-CODE
@@ -1381,6 +1496,18 @@ static pj_status_t encode_errcode_attr(const void *a, pj_uint8_t *buf,
return PJ_SUCCESS;
}
+
+static void* clone_errcode_attr(pj_pool_t *pool, const void *src)
+{
+ const pj_stun_errcode_attr *asrc = (const pj_stun_errcode_attr*)src;
+ pj_stun_errcode_attr *dst = PJ_POOL_ALLOC_T(pool, pj_stun_errcode_attr);
+
+ pj_memcpy(dst, src, sizeof(pj_stun_errcode_attr));
+ pj_strdup(pool, &dst->reason, &asrc->reason);
+
+ return (void*)dst;
+}
+
//////////////////////////////////////////////////////////////////////////////
/*
* STUN UNKNOWN-ATTRIBUTES attribute
@@ -1499,6 +1626,15 @@ static pj_status_t encode_unknown_attr(const void *a, pj_uint8_t *buf,
}
+static void* clone_unknown_attr(pj_pool_t *pool, const void *src)
+{
+ pj_stun_unknown_attr *dst = PJ_POOL_ALLOC_T(pool, pj_stun_unknown_attr);
+
+ pj_memcpy(dst, src, sizeof(pj_stun_unknown_attr));
+
+ return (void*)dst;
+}
+
//////////////////////////////////////////////////////////////////////////////
/*
* STUN generic binary attribute
@@ -1597,6 +1733,21 @@ static pj_status_t encode_binary_attr(const void *a, pj_uint8_t *buf,
}
+static void* clone_binary_attr(pj_pool_t *pool, const void *src)
+{
+ const pj_stun_binary_attr *asrc = (const pj_stun_binary_attr*)src;
+ pj_stun_binary_attr *dst = PJ_POOL_ALLOC_T(pool, pj_stun_binary_attr);
+
+ pj_memcpy(dst, src, sizeof(pj_stun_binary_attr));
+
+ if (asrc->length) {
+ dst->data = (pj_uint8_t*) pj_pool_alloc(pool, asrc->length);
+ pj_memcpy(dst->data, asrc->data, asrc->length);
+ }
+
+ return (void*)dst;
+}
+
//////////////////////////////////////////////////////////////////////////////
/*
@@ -1641,6 +1792,31 @@ PJ_DEF(pj_status_t) pj_stun_msg_create( pj_pool_t *pool,
/*
+ * Clone a STUN message with all of its attributes.
+ */
+PJ_DEF(pj_stun_msg*) pj_stun_msg_clone( pj_pool_t *pool,
+ const pj_stun_msg *src)
+{
+ pj_stun_msg *dst;
+ unsigned i;
+
+ PJ_ASSERT_RETURN(pool && src, NULL);
+
+ dst = PJ_POOL_ZALLOC_T(pool, pj_stun_msg);
+ pj_memcpy(dst, src, sizeof(pj_stun_msg));
+
+ /* Duplicate the attributes */
+ for (i=0, dst->attr_count=0; i<src->attr_count; ++i) {
+ dst->attr[dst->attr_count] = pj_stun_attr_clone(pool, src->attr[i]);
+ if (dst->attr[dst->attr_count])
+ ++dst->attr_count;
+ }
+
+ return dst;
+}
+
+
+/*
* Add STUN attribute to STUN message.
*/
PJ_DEF(pj_status_t) pj_stun_msg_add_attr(pj_stun_msg *msg,
@@ -1981,79 +2157,6 @@ PJ_DEF(pj_status_t) pj_stun_msg_decode(pj_pool_t *pool,
return PJ_SUCCESS;
}
-/* Calculate HMAC-SHA1 key for long term credential, by getting
- * MD5 digest of username, realm, and password.
- */
-static void calc_md5_key(pj_uint8_t digest[16],
- const pj_str_t *realm,
- const pj_str_t *username,
- const pj_str_t *passwd)
-{
- /* The 16-byte key for MESSAGE-INTEGRITY HMAC is formed by taking
- * the MD5 hash of the result of concatenating the following five
- * fields: (1) The username, with any quotes and trailing nulls
- * removed, (2) A single colon, (3) The realm, with any quotes and
- * trailing nulls removed, (4) A single colon, and (5) The
- * password, with any trailing nulls removed.
- */
- pj_md5_context ctx;
- pj_str_t s;
-
- pj_md5_init(&ctx);
-
-#define REMOVE_QUOTE(s) if (s.slen && *s.ptr=='"') \
- s.ptr++, s.slen--; \
- if (s.slen && s.ptr[s.slen-1]=='"') \
- s.slen--;
-
- /* Add username */
- s = *username;
- REMOVE_QUOTE(s);
- pj_md5_update(&ctx, (pj_uint8_t*)s.ptr, s.slen);
-
- /* Add single colon */
- pj_md5_update(&ctx, (pj_uint8_t*)":", 1);
-
- /* Add realm */
- s = *realm;
- REMOVE_QUOTE(s);
- pj_md5_update(&ctx, (pj_uint8_t*)s.ptr, s.slen);
-
-#undef REMOVE_QUOTE
-
- /* Another colon */
- pj_md5_update(&ctx, (pj_uint8_t*)":", 1);
-
- /* Add password */
- pj_md5_update(&ctx, (pj_uint8_t*)passwd->ptr, passwd->slen);
-
- /* Done */
- pj_md5_final(&ctx, digest);
-}
-
-
-/*
- * Create authentication key to be used for encoding the message with
- * MESSAGE-INTEGRITY.
- */
-PJ_DEF(void) pj_stun_create_key(pj_pool_t *pool,
- pj_str_t *key,
- const pj_str_t *realm,
- const pj_str_t *username,
- const pj_str_t *passwd)
-{
- PJ_ASSERT_ON_FAIL(pool && key && username && passwd, return);
-
- if (realm && realm->slen) {
- key->ptr = (char*) pj_pool_alloc(pool, 16);
- calc_md5_key((pj_uint8_t*)key->ptr, realm, username, passwd);
- key->slen = 16;
- } else {
- pj_strdup(pool, key, passwd);
- }
-}
-
-
/*
static char *print_binary(const pj_uint8_t *data, unsigned data_len)
{
@@ -2207,7 +2310,7 @@ PJ_DEF(pj_status_t) pj_stun_msg_encode(pj_stun_msg *msg,
/* MESSAGE-INTEGRITY must be the last attribute in the message, or
* the last attribute before FINGERPRINT.
*/
- if (i < msg->attr_count-2) {
+ if (msg->attr_count>1 && i < msg->attr_count-2) {
/* Should not happen for message generated by us */
pj_assert(PJ_FALSE);
return PJNATH_ESTUNMSGINTPOS;
@@ -2298,3 +2401,20 @@ PJ_DEF(pj_stun_attr_hdr*) pj_stun_msg_find_attr( const pj_stun_msg *msg,
return NULL;
}
+
+/*
+ * Clone a STUN attribute.
+ */
+PJ_DEF(pj_stun_attr_hdr*) pj_stun_attr_clone( pj_pool_t *pool,
+ const pj_stun_attr_hdr *attr)
+{
+ const struct attr_desc *adesc;
+
+ /* Get the attribute descriptor */
+ adesc = find_attr_desc(attr->type);
+ PJ_ASSERT_RETURN(adesc != NULL, NULL);
+
+ return (pj_stun_attr_hdr*) (*adesc->clone_attr)(pool, attr);
+}
+
+
diff --git a/pjnath/src/pjnath/stun_session.c b/pjnath/src/pjnath/stun_session.c
index 29dc3e57..02b017cf 100644
--- a/pjnath/src/pjnath/stun_session.c
+++ b/pjnath/src/pjnath/stun_session.c
@@ -17,6 +17,7 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <pjnath/stun_session.h>
+#include <pjnath/errno.h>
#include <pjlib.h>
struct pj_stun_session
@@ -29,7 +30,14 @@ struct pj_stun_session
void *user_data;
pj_bool_t use_fingerprint;
- pj_stun_auth_cred *cred;
+
+ char dump_buf[1000];
+
+ pj_stun_auth_type auth_type;
+ pj_stun_auth_cred cred;
+ int auth_retry;
+ pj_str_t next_nonce;
+
pj_str_t srv_name;
pj_stun_tx_data pending_request_list;
@@ -125,39 +133,6 @@ static pj_status_t create_tdata(pj_stun_session *sess,
return PJ_SUCCESS;
}
-static pj_status_t create_request_tdata(pj_stun_session *sess,
- unsigned msg_type,
- pj_uint32_t magic,
- const pj_uint8_t tsx_id[12],
- pj_stun_tx_data **p_tdata)
-{
- pj_status_t status;
- pj_stun_tx_data *tdata;
-
- status = create_tdata(sess, &tdata);
- if (status != PJ_SUCCESS)
- return status;
-
- /* Create STUN message */
- status = pj_stun_msg_create(tdata->pool, msg_type, magic,
- tsx_id, &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->msg_key)==sizeof(tdata->msg->hdr.tsx_id));
- tdata->msg_magic = tdata->msg->hdr.magic;
- pj_memcpy(tdata->msg_key, tdata->msg->hdr.tsx_id,
- sizeof(tdata->msg->hdr.tsx_id));
-
- *p_tdata = tdata;
-
- return PJ_SUCCESS;
-}
-
-
static void stun_tsx_on_destroy(pj_stun_client_tsx *tsx)
{
pj_stun_tx_data *tdata;
@@ -215,55 +190,13 @@ static void on_cache_timeout(pj_timer_heap_t *timer_heap,
pj_stun_msg_destroy_tdata(tdata->sess, tdata);
}
-static pj_status_t get_key(pj_stun_session *sess, pj_pool_t *pool,
- const pj_stun_msg *msg, pj_str_t *auth_key)
-{
- if (sess->cred == NULL) {
- auth_key->slen = 0;
- return PJ_SUCCESS;
- } else if (sess->cred->type == PJ_STUN_AUTH_CRED_STATIC) {
- pj_stun_create_key(pool, auth_key,
- &sess->cred->data.static_cred.realm,
- &sess->cred->data.static_cred.username,
- &sess->cred->data.static_cred.data);
- return PJ_SUCCESS;
- } else if (sess->cred->type == PJ_STUN_AUTH_CRED_DYNAMIC) {
- pj_str_t realm, username, nonce;
- pj_str_t *password;
- void *user_data = sess->cred->data.dyn_cred.user_data;
- int data_type = 0;
- pj_status_t status;
-
- realm.slen = username.slen = nonce.slen = 0;
- password = PJ_POOL_ZALLOC_T(pool, pj_str_t);
- status = (*sess->cred->data.dyn_cred.get_cred)(msg, user_data, pool,
- &realm, &username,
- &nonce, &data_type,
- password);
- if (status != PJ_SUCCESS)
- return status;
-
- pj_stun_create_key(pool, auth_key,
- &realm, &username, password);
-
- return PJ_SUCCESS;
-
- } else {
- pj_assert(!"Unknown credential type");
- return PJ_EBUG;
- }
-}
-
static pj_status_t apply_msg_options(pj_stun_session *sess,
pj_pool_t *pool,
+ const pj_stun_req_cred_info *auth_info,
pj_stun_msg *msg)
{
pj_status_t status = 0;
- pj_bool_t need_auth;
- pj_str_t realm, username, nonce, password;
- int data_type = 0;
-
- realm.slen = username.slen = nonce.slen = password.slen = 0;
+ pj_str_t realm, username, nonce, auth_key;
/* The server SHOULD include a SERVER attribute in all responses */
if (sess->srv_name.slen && PJ_STUN_IS_RESPONSE(msg->hdr.type)) {
@@ -271,32 +204,16 @@ static pj_status_t apply_msg_options(pj_stun_session *sess,
&sess->srv_name);
}
- need_auth = pj_stun_auth_valid_for_msg(msg);
-
- if (sess->cred && sess->cred->type == PJ_STUN_AUTH_CRED_STATIC &&
- need_auth)
- {
- realm = sess->cred->data.static_cred.realm;
- username = sess->cred->data.static_cred.username;
- data_type = sess->cred->data.static_cred.data_type;
- password = sess->cred->data.static_cred.data;
- nonce = sess->cred->data.static_cred.nonce;
-
- } else if (sess->cred && sess->cred->type == PJ_STUN_AUTH_CRED_DYNAMIC &&
- need_auth)
- {
- void *user_data = sess->cred->data.dyn_cred.user_data;
-
- status = (*sess->cred->data.dyn_cred.get_cred)(msg, user_data, pool,
- &realm, &username,
- &nonce, &data_type,
- &password);
- if (status != PJ_SUCCESS)
- return status;
+ if (pj_stun_auth_valid_for_msg(msg) && auth_info) {
+ realm = auth_info->realm;
+ username = auth_info->username;
+ nonce = auth_info->nonce;
+ auth_key = auth_info->auth_key;
+ } else {
+ realm.slen = username.slen = nonce.slen = auth_key.slen = 0;
}
-
- /* Create and add USERNAME attribute for */
+ /* Create and add USERNAME attribute if needed */
if (username.slen && PJ_STUN_IS_REQUEST(msg->hdr.type)) {
status = pj_stun_msg_add_string_attr(pool, msg,
PJ_STUN_ATTR_USERNAME,
@@ -323,7 +240,7 @@ static pj_status_t apply_msg_options(pj_stun_session *sess,
}
/* Add MESSAGE-INTEGRITY attribute */
- if (username.slen && need_auth) {
+ if (username.slen && auth_key.slen) {
status = pj_stun_msg_add_msgint_attr(pool, msg);
PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
}
@@ -339,6 +256,105 @@ static pj_status_t apply_msg_options(pj_stun_session *sess,
return PJ_SUCCESS;
}
+static pj_status_t handle_auth_challenge(pj_stun_session *sess,
+ const pj_stun_tx_data *request,
+ const pj_stun_msg *response,
+ const pj_sockaddr_t *src_addr,
+ unsigned src_addr_len,
+ pj_bool_t *notify_user)
+{
+ const pj_stun_errcode_attr *ea;
+
+ *notify_user = PJ_TRUE;
+
+ if (sess->auth_type != PJ_STUN_AUTH_LONG_TERM)
+ return PJ_SUCCESS;
+
+ if (!PJ_STUN_IS_ERROR_RESPONSE(response->hdr.type))
+ return PJ_SUCCESS;
+
+ ea = (const pj_stun_errcode_attr*)
+ pj_stun_msg_find_attr(response, PJ_STUN_ATTR_ERROR_CODE, 0);
+ if (!ea) {
+ PJ_LOG(4,(SNAME(sess), "Invalid error response: no ERROR-CODE"
+ " attribute"));
+ *notify_user = PJ_FALSE;
+ return PJNATH_EINSTUNMSG;
+ }
+
+ if (ea->err_code == PJ_STUN_SC_UNAUTHORIZED ||
+ ea->err_code == PJ_STUN_SC_STALE_NONCE)
+ {
+ const pj_stun_nonce_attr *anonce;
+ pj_stun_tx_data *tdata;
+ unsigned i;
+ pj_status_t status;
+
+ anonce = (const pj_stun_nonce_attr*)
+ pj_stun_msg_find_attr(response, PJ_STUN_ATTR_NONCE, 0);
+ if (!anonce) {
+ PJ_LOG(4,(SNAME(sess), "Invalid response: missing NONCE"));
+ *notify_user = PJ_FALSE;
+ return PJNATH_EINSTUNMSG;
+ }
+
+ /* Bail out if we've supplied the correct nonce */
+ if (pj_strcmp(&anonce->value, &sess->next_nonce)==0) {
+ return PJ_SUCCESS;
+ }
+
+ /* Bail out if we've tried too many */
+ if (++sess->auth_retry > 3) {
+ PJ_LOG(4,(SNAME(sess), "Error: authentication failed (too "
+ "many retries)"));
+ return PJ_STATUS_FROM_STUN_CODE(401);
+ }
+
+ /* Save next_nonce */
+ pj_strdup(sess->pool, &sess->next_nonce, &anonce->value);
+
+ /* Create new request */
+ status = pj_stun_session_create_req(sess, request->msg->hdr.type,
+ request->msg->hdr.magic,
+ NULL, &tdata);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Duplicate all the attributes in the old request, except
+ * USERNAME, REALM, M-I, and NONCE
+ */
+ for (i=0; i<request->msg->attr_count; ++i) {
+ const pj_stun_attr_hdr *asrc = request->msg->attr[i];
+
+ if (asrc->type == PJ_STUN_ATTR_USERNAME ||
+ asrc->type == PJ_STUN_ATTR_REALM ||
+ asrc->type == PJ_STUN_ATTR_MESSAGE_INTEGRITY ||
+ asrc->type == PJ_STUN_ATTR_NONCE)
+ {
+ continue;
+ }
+
+ tdata->msg->attr[tdata->msg->attr_count++] =
+ pj_stun_attr_clone(tdata->pool, asrc);
+ }
+
+ /* Will retry the request with authentication, no need to
+ * notify user.
+ */
+ *notify_user = PJ_FALSE;
+
+ PJ_LOG(4,(SNAME(sess), "Retrying request with new authentication"));
+
+ /* Retry the request */
+ status = pj_stun_session_send_msg(sess, PJ_TRUE, src_addr,
+ src_addr_len, tdata);
+
+ } else {
+ sess->auth_retry = 0;
+ }
+
+ return PJ_SUCCESS;
+}
static void stun_tsx_on_complete(pj_stun_client_tsx *tsx,
pj_status_t status,
@@ -346,14 +362,21 @@ static void stun_tsx_on_complete(pj_stun_client_tsx *tsx,
const pj_sockaddr_t *src_addr,
unsigned src_addr_len)
{
+ pj_stun_session *sess;
+ pj_bool_t notify_user = PJ_TRUE;
pj_stun_tx_data *tdata;
tdata = (pj_stun_tx_data*) pj_stun_client_tsx_get_data(tsx);
+ sess = tdata->sess;
+
+ /* Handle authentication challenge */
+ handle_auth_challenge(sess, tdata, response, src_addr,
+ src_addr_len, &notify_user);
- if (tdata->sess->cb.on_request_complete) {
- (*tdata->sess->cb.on_request_complete)(tdata->sess, status, tdata,
- response,
- src_addr, src_addr_len);
+ if (notify_user && sess->cb.on_request_complete) {
+ (*sess->cb.on_request_complete)(sess, status, tdata,
+ response,
+ src_addr, src_addr_len);
}
}
@@ -488,20 +511,65 @@ PJ_DEF(pj_status_t) pj_stun_session_set_server_name(pj_stun_session *sess,
return PJ_SUCCESS;
}
-PJ_DEF(void) pj_stun_session_set_credential(pj_stun_session *sess,
- const pj_stun_auth_cred *cred)
+PJ_DEF(pj_status_t) pj_stun_session_set_credential(pj_stun_session *sess,
+ pj_stun_auth_type auth_type,
+ const pj_stun_auth_cred *cred)
{
- PJ_ASSERT_ON_FAIL(sess, return);
+ PJ_ASSERT_RETURN(sess, PJ_EINVAL);
+
+ sess->auth_type = auth_type;
if (cred) {
- if (!sess->cred)
- sess->cred = PJ_POOL_ALLOC_T(sess->pool, pj_stun_auth_cred);
- pj_stun_auth_cred_dup(sess->pool, sess->cred, cred);
+ pj_stun_auth_cred_dup(sess->pool, &sess->cred, cred);
} else {
- sess->cred = NULL;
+ sess->auth_type = PJ_STUN_AUTH_NONE;
+ pj_bzero(&sess->cred, sizeof(sess->cred));
}
+
+ return PJ_SUCCESS;
}
+static pj_status_t get_auth(pj_stun_session *sess,
+ pj_stun_tx_data *tdata)
+{
+ if (sess->cred.type == PJ_STUN_AUTH_CRED_STATIC) {
+ tdata->auth_info.realm = sess->cred.data.static_cred.realm;
+ tdata->auth_info.username = sess->cred.data.static_cred.username;
+ tdata->auth_info.nonce = sess->cred.data.static_cred.nonce;
+
+ pj_stun_create_key(tdata->pool, &tdata->auth_info.auth_key,
+ &tdata->auth_info.realm,
+ &tdata->auth_info.username,
+ sess->cred.data.static_cred.data_type,
+ &sess->cred.data.static_cred.data);
+
+ } else if (sess->cred.type == PJ_STUN_AUTH_CRED_DYNAMIC) {
+ pj_str_t password;
+ void *user_data = sess->cred.data.dyn_cred.user_data;
+ pj_stun_passwd_type data_type = PJ_STUN_PASSWD_PLAIN;
+ pj_status_t rc;
+
+ rc = (*sess->cred.data.dyn_cred.get_cred)(tdata->msg, user_data,
+ tdata->pool,
+ &tdata->auth_info.realm,
+ &tdata->auth_info.username,
+ &tdata->auth_info.nonce,
+ &data_type, &password);
+ if (rc != PJ_SUCCESS)
+ return rc;
+
+ pj_stun_create_key(tdata->pool, &tdata->auth_info.auth_key,
+ &tdata->auth_info.realm, &tdata->auth_info.username,
+ data_type, &password);
+
+ } else {
+ pj_assert(!"Unknown credential type");
+ return PJ_EBUG;
+ }
+
+ return PJ_SUCCESS;
+}
+
PJ_DEF(pj_status_t) pj_stun_session_create_req(pj_stun_session *sess,
int method,
pj_uint32_t magic,
@@ -513,10 +581,56 @@ PJ_DEF(pj_status_t) pj_stun_session_create_req(pj_stun_session *sess,
PJ_ASSERT_RETURN(sess && p_tdata, PJ_EINVAL);
- status = create_request_tdata(sess, method, magic, tsx_id, &tdata);
+ status = create_tdata(sess, &tdata);
if (status != PJ_SUCCESS)
return status;
+ /* Create STUN message */
+ status = pj_stun_msg_create(tdata->pool, method, magic,
+ tsx_id, &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->msg_key)==sizeof(tdata->msg->hdr.tsx_id));
+ tdata->msg_magic = tdata->msg->hdr.magic;
+ pj_memcpy(tdata->msg_key, tdata->msg->hdr.tsx_id,
+ sizeof(tdata->msg->hdr.tsx_id));
+
+
+ /* Get authentication information for the request */
+ if (sess->auth_type == PJ_STUN_AUTH_NONE) {
+ /* No authentication */
+
+ } else if (sess->auth_type == PJ_STUN_AUTH_SHORT_TERM) {
+ /* MUST put authentication in request */
+ status = get_auth(sess, tdata);
+ if (status != PJ_SUCCESS) {
+ pj_pool_release(tdata->pool);
+ return status;
+ }
+
+ } else if (sess->auth_type == PJ_STUN_AUTH_LONG_TERM) {
+ /* Only put authentication information if we've received
+ * response from server.
+ */
+ if (sess->next_nonce.slen != 0) {
+ status = get_auth(sess, tdata);
+ if (status != PJ_SUCCESS) {
+ pj_pool_release(tdata->pool);
+ return status;
+ }
+ tdata->auth_info.nonce = sess->next_nonce;
+ }
+
+ } else {
+ pj_assert(!"Invalid authentication type");
+ pj_pool_release(tdata->pool);
+ return PJ_EBUG;
+ }
+
*p_tdata = tdata;
return PJ_SUCCESS;
}
@@ -551,7 +665,7 @@ PJ_DEF(pj_status_t) pj_stun_session_create_ind(pj_stun_session *sess,
* Create a STUN response message.
*/
PJ_DEF(pj_status_t) pj_stun_session_create_res( pj_stun_session *sess,
- const pj_stun_msg *req,
+ const pj_stun_rx_data *rdata,
unsigned err_code,
const pj_str_t *err_msg,
pj_stun_tx_data **p_tdata)
@@ -564,17 +678,21 @@ PJ_DEF(pj_status_t) pj_stun_session_create_res( pj_stun_session *sess,
return status;
/* Create STUN response message */
- status = pj_stun_msg_create_response(tdata->pool, req, err_code, err_msg,
- &tdata->msg);
+ status = pj_stun_msg_create_response(tdata->pool, rdata->msg,
+ 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->msg_key)==sizeof(req->hdr.tsx_id));
- tdata->msg_magic = req->hdr.magic;
- pj_memcpy(tdata->msg_key, req->hdr.tsx_id, sizeof(req->hdr.tsx_id));
+ pj_assert(sizeof(tdata->msg_key)==sizeof(rdata->msg->hdr.tsx_id));
+ tdata->msg_magic = rdata->msg->hdr.magic;
+ pj_memcpy(tdata->msg_key, rdata->msg->hdr.tsx_id,
+ sizeof(rdata->msg->hdr.tsx_id));
+
+ /* copy the credential found in the request */
+ pj_stun_req_cred_info_dup(tdata->pool, &tdata->auth_info, &rdata->info);
*p_tdata = tdata;
@@ -586,29 +704,18 @@ PJ_DEF(pj_status_t) pj_stun_session_create_res( pj_stun_session *sess,
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[800];
+ char dst_name[80];
- if (dst->addr.sa_family == pj_AF_INET()) {
- dst_name = pj_inet_ntoa(dst->ipv4.sin_addr);
- dst_port = pj_ntohs(dst->ipv4.sin_port);
- } else if (dst->addr.sa_family == pj_AF_INET6()) {
- dst_name = "IPv6";
- dst_port = pj_ntohs(dst->ipv6.sin6_port);
- } else {
- LOG_ERR_(sess, "Invalid address family", PJ_EINVAL);
- return;
- }
+ pj_sockaddr_print(addr, dst_name, sizeof(dst_name), 3);
PJ_LOG(5,(SNAME(sess),
- "TX %d bytes STUN message to %s:%d:\n"
+ "TX %d bytes STUN message to %s:\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)));
+ pkt_size, dst_name,
+ pj_stun_msg_dump(msg, sess->dump_buf, sizeof(sess->dump_buf),
+ NULL)));
}
@@ -631,7 +738,8 @@ PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess,
pj_lock_acquire(sess->lock);
/* Apply options */
- status = apply_msg_options(sess, tdata->pool, tdata->msg);
+ status = apply_msg_options(sess, tdata->pool, &tdata->auth_info,
+ tdata->msg);
if (status != PJ_SUCCESS) {
pj_stun_msg_destroy_tdata(sess, tdata);
pj_lock_release(sess->lock);
@@ -639,18 +747,10 @@ PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess,
return status;
}
- status = get_key(sess, tdata->pool, tdata->msg, &tdata->auth_key);
- if (status != PJ_SUCCESS) {
- pj_stun_msg_destroy_tdata(sess, tdata);
- pj_lock_release(sess->lock);
- LOG_ERR_(sess, "Error getting creadential's key", status);
- return status;
- }
-
/* Encode message */
status = pj_stun_msg_encode(tdata->msg, (pj_uint8_t*)tdata->pkt,
tdata->max_len, 0,
- &tdata->auth_key,
+ &tdata->auth_info.auth_key,
&tdata->pkt_size);
if (status != PJ_SUCCESS) {
pj_stun_msg_destroy_tdata(sess, tdata);
@@ -742,7 +842,7 @@ PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess,
* Create and send STUN response message.
*/
PJ_DEF(pj_status_t) pj_stun_session_respond( pj_stun_session *sess,
- const pj_stun_msg *req,
+ const pj_stun_rx_data *rdata,
unsigned code,
const char *errmsg,
pj_bool_t cache,
@@ -753,7 +853,7 @@ PJ_DEF(pj_status_t) pj_stun_session_respond( pj_stun_session *sess,
pj_str_t reason;
pj_stun_tx_data *tdata;
- status = pj_stun_session_create_res(sess, req, code,
+ status = pj_stun_session_create_res(sess, rdata, code,
(errmsg?pj_cstr(&reason,errmsg):NULL),
&tdata);
if (status != PJ_SUCCESS)
@@ -813,7 +913,7 @@ PJ_DEF(pj_status_t) pj_stun_session_retransmit_req(pj_stun_session *sess,
/* Send response */
static pj_status_t send_response(pj_stun_session *sess,
pj_pool_t *pool, pj_stun_msg *response,
- const pj_str_t *auth_key,
+ const pj_stun_req_cred_info *auth_info,
pj_bool_t retransmission,
const pj_sockaddr_t *addr, unsigned addr_len)
{
@@ -823,7 +923,7 @@ static pj_status_t send_response(pj_stun_session *sess,
/* Apply options */
if (!retransmission) {
- status = apply_msg_options(sess, pool, response);
+ status = apply_msg_options(sess, pool, auth_info, response);
if (status != PJ_SUCCESS)
return status;
}
@@ -834,7 +934,7 @@ static pj_status_t send_response(pj_stun_session *sess,
/* Encode */
status = pj_stun_msg_encode(response, out_pkt, out_max_len, 0,
- auth_key, &out_len);
+ &auth_info->auth_key, &out_len);
if (status != PJ_SUCCESS) {
LOG_ERR_(sess, "Error encoding message", status);
return status;
@@ -853,23 +953,26 @@ static pj_status_t send_response(pj_stun_session *sess,
static pj_status_t authenticate_req(pj_stun_session *sess,
const pj_uint8_t *pkt,
unsigned pkt_len,
- const pj_stun_msg *msg,
+ pj_stun_rx_data *rdata,
pj_pool_t *tmp_pool,
const pj_sockaddr_t *src_addr,
unsigned src_addr_len)
{
pj_stun_msg *response;
- pj_str_t auth_key;
pj_status_t status;
- if (PJ_STUN_IS_ERROR_RESPONSE(msg->hdr.type) || sess->cred == NULL)
+ if (PJ_STUN_IS_ERROR_RESPONSE(rdata->msg->hdr.type) ||
+ sess->auth_type == PJ_STUN_AUTH_NONE)
+ {
return PJ_SUCCESS;
+ }
- status = pj_stun_authenticate_request(pkt, pkt_len, msg, sess->cred,
- tmp_pool, &auth_key, &response);
+ status = pj_stun_authenticate_request(pkt, pkt_len, rdata->msg,
+ &sess->cred, tmp_pool, &rdata->info,
+ &response);
if (status != PJ_SUCCESS && response != NULL) {
PJ_LOG(5,(SNAME(sess), "Message authentication failed"));
- send_response(sess, tmp_pool, response, &auth_key, PJ_FALSE,
+ send_response(sess, tmp_pool, response, &rdata->info, PJ_FALSE,
src_addr, src_addr_len);
}
@@ -897,14 +1000,18 @@ static pj_status_t on_incoming_response(pj_stun_session *sess,
return PJ_SUCCESS;
}
+ if (sess->auth_type == PJ_STUN_AUTH_NONE)
+ options |= PJ_STUN_NO_AUTHENTICATE;
+
/* Authenticate the message, unless PJ_STUN_NO_AUTHENTICATE
* is specified in the option.
*/
- if ((options & PJ_STUN_NO_AUTHENTICATE) == 0 && tdata->auth_key.slen != 0
- && pj_stun_auth_valid_for_msg(msg))
+ if ((options & PJ_STUN_NO_AUTHENTICATE) == 0 &&
+ tdata->auth_info.auth_key.slen != 0 &&
+ pj_stun_auth_valid_for_msg(msg))
{
status = pj_stun_authenticate_response(pkt, pkt_len, msg,
- &tdata->auth_key);
+ &tdata->auth_info.auth_key);
if (status != PJ_SUCCESS) {
PJ_LOG(5,(SNAME(sess),
"Response authentication failed"));
@@ -962,7 +1069,7 @@ static pj_status_t check_cached_response(pj_stun_session *sess,
PJ_LOG(5,(SNAME(sess),
"Request retransmission, sending cached response"));
- send_response(sess, tmp_pool, t->msg, &t->auth_key, PJ_TRUE,
+ send_response(sess, tmp_pool, t->msg, &t->auth_info, PJ_TRUE,
src_addr, src_addr_len);
return PJ_SUCCESS;
}
@@ -976,18 +1083,26 @@ 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,
+ pj_stun_msg *msg,
const pj_sockaddr_t *src_addr,
unsigned src_addr_len)
{
+ pj_stun_rx_data rdata;
pj_status_t status;
+ /* Init rdata */
+ rdata.msg = msg;
+ pj_bzero(&rdata.info, sizeof(rdata.info));
+
+ if (sess->auth_type == PJ_STUN_AUTH_NONE)
+ options |= PJ_STUN_NO_AUTHENTICATE;
+
/* Authenticate the message, unless PJ_STUN_NO_AUTHENTICATE
* is specified in the option.
*/
if ((options & PJ_STUN_NO_AUTHENTICATE) == 0) {
status = authenticate_req(sess, (const pj_uint8_t*) in_pkt, in_pkt_len,
- msg, tmp_pool, src_addr, src_addr_len);
+ &rdata, tmp_pool, src_addr, src_addr_len);
if (status != PJ_SUCCESS) {
return status;
}
@@ -995,14 +1110,16 @@ static pj_status_t on_incoming_request(pj_stun_session *sess,
/* 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,
+ status = (*sess->cb.on_rx_request)(sess, in_pkt, in_pkt_len, &rdata,
src_addr, src_addr_len);
} else {
+ pj_str_t err_text;
pj_stun_msg *response;
+ err_text = pj_str("Callback is not set to handle request");
status = pj_stun_msg_create_response(tmp_pool, msg,
- PJ_STUN_SC_BAD_REQUEST, NULL,
- &response);
+ PJ_STUN_SC_BAD_REQUEST,
+ &err_text, &response);
if (status == PJ_SUCCESS && response) {
status = send_response(sess, tmp_pool, response,
NULL, PJ_FALSE, src_addr, src_addr_len);
diff --git a/pjnath/src/pjnath/turn_session.c b/pjnath/src/pjnath/turn_session.c
index 7b772dcf..e5540b00 100644
--- a/pjnath/src/pjnath/turn_session.c
+++ b/pjnath/src/pjnath/turn_session.c
@@ -521,7 +521,7 @@ PJ_DEF(pj_status_t) pj_turn_session_set_cred(pj_turn_session *sess,
pj_lock_acquire(sess->lock);
- pj_stun_session_set_credential(sess->stun, cred);
+ pj_stun_session_set_credential(sess->stun, PJ_STUN_AUTH_LONG_TERM, cred);
pj_lock_release(sess->lock);
diff --git a/pjnath/src/pjturn-srv/allocation.c b/pjnath/src/pjturn-srv/allocation.c
index da35266c..14ed228b 100644
--- a/pjnath/src/pjturn-srv/allocation.c
+++ b/pjnath/src/pjturn-srv/allocation.c
@@ -72,7 +72,7 @@ static pj_status_t stun_on_send_msg(pj_stun_session *sess,
static pj_status_t stun_on_rx_request(pj_stun_session *sess,
const pj_uint8_t *pkt,
unsigned pkt_len,
- const pj_stun_msg *msg,
+ const pj_stun_rx_data *rdata,
const pj_sockaddr_t *src_addr,
unsigned src_addr_len);
static pj_status_t stun_on_rx_indication(pj_stun_session *sess,
@@ -97,10 +97,11 @@ static void alloc_err(pj_turn_allocation *alloc, const char *title,
/* Parse ALLOCATE request */
static pj_status_t parse_allocate_req(alloc_request *cfg,
pj_stun_session *sess,
- const pj_stun_msg *req,
+ const pj_stun_rx_data *rdata,
const pj_sockaddr_t *src_addr,
unsigned src_addr_len)
{
+ const pj_stun_msg *req = rdata->msg;
pj_stun_bandwidth_attr *attr_bw;
pj_stun_req_transport_attr *attr_req_tp;
pj_stun_res_token_attr *attr_res_token;
@@ -120,7 +121,8 @@ static pj_status_t parse_allocate_req(alloc_request *cfg,
/* 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,
+ pj_stun_session_respond(sess, rdata,
+ 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);
@@ -130,7 +132,7 @@ static pj_status_t parse_allocate_req(alloc_request *cfg,
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,
+ pj_stun_session_respond(sess, rdata, 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);
@@ -140,7 +142,7 @@ static pj_status_t parse_allocate_req(alloc_request *cfg,
/* 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,
+ pj_stun_session_respond(sess, rdata, 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);
}
@@ -151,7 +153,7 @@ static pj_status_t parse_allocate_req(alloc_request *cfg,
0);
if (attr_res_token) {
/* We don't support RESERVATION-TOKEN for now */
- pj_stun_session_respond(sess, req,
+ pj_stun_session_respond(sess, rdata,
PJ_STUN_SC_BAD_REQUEST,
"RESERVATION-TOKEN is not supported", PJ_TRUE,
src_addr, src_addr_len);
@@ -163,7 +165,7 @@ static pj_status_t parse_allocate_req(alloc_request *cfg,
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_session_respond(sess, rdata,
PJ_STUN_SC_BAD_REQUEST,
"REQUESTED-PROPS is not supported", PJ_TRUE,
src_addr, src_addr_len);
@@ -176,7 +178,7 @@ static pj_status_t parse_allocate_req(alloc_request *cfg,
if (attr_lifetime) {
cfg->lifetime = attr_lifetime->value;
if (cfg->lifetime < MIN_LIFETIME) {
- pj_stun_session_respond(sess, req, PJ_STUN_SC_BAD_REQUEST,
+ pj_stun_session_respond(sess, rdata, 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);
@@ -194,13 +196,13 @@ static pj_status_t parse_allocate_req(alloc_request *cfg,
/* 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)
+ const pj_stun_rx_data *rdata)
{
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);
+ status = pj_stun_session_create_res(srv_sess, rdata, 0, NULL, &tdata);
if (status != PJ_SUCCESS)
return status;
@@ -284,11 +286,12 @@ static pj_status_t init_cred(pj_turn_allocation *alloc, const pj_stun_msg *req)
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,
+ const pj_stun_rx_data *rdata,
pj_stun_session *srv_sess,
pj_turn_allocation **p_alloc)
{
pj_turn_srv *srv = listener->server;
+ const pj_stun_msg *msg = rdata->msg;
pj_pool_t *pool;
alloc_request req;
pj_turn_allocation *alloc;
@@ -297,7 +300,7 @@ PJ_DEF(pj_status_t) pj_turn_allocation_create(pj_turn_listener *listener,
pj_status_t status;
/* Parse ALLOCATE request */
- status = parse_allocate_req(&req, srv_sess, msg, src_addr, src_addr_len);
+ status = parse_allocate_req(&req, srv_sess, rdata, src_addr, src_addr_len);
if (status != PJ_SUCCESS)
return status;
@@ -354,7 +357,8 @@ PJ_DEF(pj_status_t) pj_turn_allocation_create(pj_turn_listener *listener,
}
/* Attach authentication credential to STUN session */
- pj_stun_session_set_credential(alloc->sess, &alloc->cred);
+ pj_stun_session_set_credential(alloc->sess, PJ_STUN_AUTH_LONG_TERM,
+ &alloc->cred);
/* Create the relay resource */
status = create_relay(srv, alloc, msg, &req, &alloc->relay);
@@ -366,7 +370,7 @@ PJ_DEF(pj_status_t) pj_turn_allocation_create(pj_turn_listener *listener,
pj_turn_srv_register_allocation(srv, alloc);
/* Respond to ALLOCATE request */
- status = send_allocate_response(alloc, srv_sess, msg);
+ status = send_allocate_response(alloc, srv_sess, rdata);
if (status != PJ_SUCCESS)
goto on_error;
@@ -383,7 +387,7 @@ PJ_DEF(pj_status_t) pj_turn_allocation_create(pj_turn_listener *listener,
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_stun_session_respond(srv_sess, rdata, PJ_STUN_SC_BAD_REQUEST, str_tmp,
PJ_TRUE, src_addr, src_addr_len);
/* Cleanup */
@@ -709,13 +713,13 @@ static pj_status_t create_relay(pj_turn_srv *srv,
/* Create and send error response */
static void send_reply_err(pj_turn_allocation *alloc,
- const pj_stun_msg *req,
+ const pj_stun_rx_data *rdata,
pj_bool_t cache,
int code, const char *errmsg)
{
pj_status_t status;
- status = pj_stun_session_respond(alloc->sess, req, code, errmsg, cache,
+ status = pj_stun_session_respond(alloc->sess, rdata, code, errmsg, cache,
&alloc->hkey.clt_addr,
pj_sockaddr_get_len(&alloc->hkey.clt_addr.addr));
if (status != PJ_SUCCESS) {
@@ -726,13 +730,13 @@ static void send_reply_err(pj_turn_allocation *alloc,
/* Create and send successful response */
static void send_reply_ok(pj_turn_allocation *alloc,
- const pj_stun_msg *req)
+ const pj_stun_rx_data *rdata)
{
pj_status_t status;
unsigned interval;
pj_stun_tx_data *tdata;
- status = pj_stun_session_create_res(alloc->sess, req, 0, NULL, &tdata);
+ status = pj_stun_session_create_res(alloc->sess, rdata, 0, NULL, &tdata);
if (status != PJ_SUCCESS) {
alloc_err(alloc, "Error creating STUN success response", status);
return;
@@ -1072,10 +1076,11 @@ static pj_status_t stun_on_send_msg(pj_stun_session *sess,
static pj_status_t stun_on_rx_request(pj_stun_session *sess,
const pj_uint8_t *pkt,
unsigned pkt_len,
- const pj_stun_msg *msg,
+ const pj_stun_rx_data *rdata,
const pj_sockaddr_t *src_addr,
unsigned src_addr_len)
{
+ const pj_stun_msg *msg = rdata->msg;
pj_turn_allocation *alloc;
PJ_UNUSED_ARG(pkt);
@@ -1088,7 +1093,7 @@ static pj_status_t stun_on_rx_request(pj_stun_session *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,
+ send_reply_err(alloc, rdata, PJ_TRUE,
PJ_STUN_SC_ALLOCATION_MISMATCH, NULL);
return PJ_SUCCESS;
}
@@ -1115,7 +1120,7 @@ static pj_status_t stun_on_rx_request(pj_stun_session *sess,
alloc->relay.lifetime = 0;
/* Respond first */
- send_reply_ok(alloc, msg);
+ send_reply_ok(alloc, rdata);
/* Shutdown allocation */
PJ_LOG(4,(alloc->obj_name,
@@ -1141,7 +1146,7 @@ static pj_status_t stun_on_rx_request(pj_stun_session *sess,
resched_timeout(alloc);
/* Send reply */
- send_reply_ok(alloc, msg);
+ send_reply_ok(alloc, rdata);
}
} else if (msg->hdr.type == PJ_STUN_CHANNEL_BIND_REQUEST) {
@@ -1158,7 +1163,8 @@ static pj_status_t stun_on_rx_request(pj_stun_session *sess,
pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_PEER_ADDR, 0);
if (!ch_attr || !peer_attr) {
- send_reply_err(alloc, msg, PJ_TRUE, PJ_STUN_SC_BAD_REQUEST, NULL);
+ send_reply_err(alloc, rdata, PJ_TRUE,
+ PJ_STUN_SC_BAD_REQUEST, NULL);
return PJ_SUCCESS;
}
@@ -1171,7 +1177,7 @@ static pj_status_t stun_on_rx_request(pj_stun_session *sess,
if (p1) {
if (pj_sockaddr_cmp(&p1->hkey.peer_addr, &peer_attr->sockaddr)) {
/* Address mismatch. Send 400 */
- send_reply_err(alloc, msg, PJ_TRUE,
+ send_reply_err(alloc, rdata, PJ_TRUE,
PJ_STUN_SC_BAD_REQUEST,
"Peer address mismatch");
return PJ_SUCCESS;
@@ -1190,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 != PJ_TURN_INVALID_CHANNEL) {
- send_reply_err(alloc, msg, PJ_TRUE, PJ_STUN_SC_BAD_REQUEST,
+ send_reply_err(alloc, rdata, PJ_TRUE, PJ_STUN_SC_BAD_REQUEST,
"Peer address already assigned a channel number");
return PJ_SUCCESS;
}
@@ -1210,19 +1216,20 @@ static pj_status_t stun_on_rx_request(pj_stun_session *sess,
refresh_permission(p2);
/* Reply */
- send_reply_ok(alloc, msg);
+ send_reply_ok(alloc, rdata);
return PJ_SUCCESS;
} else if (msg->hdr.type == PJ_STUN_ALLOCATE_REQUEST) {
/* Respond with 437 (section 6.3 turn-07) */
- send_reply_err(alloc, msg, PJ_TRUE, PJ_STUN_SC_ALLOCATION_MISMATCH, NULL);
+ send_reply_err(alloc, rdata, PJ_TRUE, PJ_STUN_SC_ALLOCATION_MISMATCH,
+ NULL);
} else {
/* Respond with Bad Request? */
- send_reply_err(alloc, msg, PJ_TRUE, PJ_STUN_SC_BAD_REQUEST, NULL);
+ send_reply_err(alloc, rdata, PJ_TRUE, PJ_STUN_SC_BAD_REQUEST, NULL);
}
diff --git a/pjnath/src/pjturn-srv/auth.c b/pjnath/src/pjturn-srv/auth.c
index ad930251..3071221c 100644
--- a/pjnath/src/pjturn-srv/auth.c
+++ b/pjnath/src/pjturn-srv/auth.c
@@ -58,29 +58,6 @@ PJ_DEF(void) pj_turn_auth_dinit(void)
}
-PJ_DEF(pj_status_t) pj_turn_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)
-{
- PJ_UNUSED_ARG(msg);
- PJ_UNUSED_ARG(pool);
- PJ_UNUSED_ARG(user_data);
-
- *realm = pj_str(g_realm);
- *username = pj_str(g_cred[0].username);
- *nonce = pj_str(THE_NONCE);
- *data_type = 0;
- *data = pj_str(g_cred[0].passwd);
-
- return PJ_SUCCESS;
-}
-
-
/*
* This function is called by pj_stun_verify_credential() when
* server needs to challenge the request with 401 response.
diff --git a/pjnath/src/pjturn-srv/auth.h b/pjnath/src/pjturn-srv/auth.h
index d0b5a0ec..db928051 100644
--- a/pjnath/src/pjturn-srv/auth.h
+++ b/pjnath/src/pjturn-srv/auth.h
@@ -61,18 +61,6 @@ PJ_DECL(pj_status_t) pj_turn_get_auth(void *user_data,
pj_str_t *nonce);
/**
- * Get credential.
- */
-PJ_DECL(pj_status_t) pj_turn_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);
-
-/**
* This function is called to get the password for the specified username.
* This function is also used to check whether the username is valid.
*
diff --git a/pjnath/src/pjturn-srv/server.c b/pjnath/src/pjturn-srv/server.c
index 11180dbf..b22cc53b 100644
--- a/pjnath/src/pjturn-srv/server.c
+++ b/pjnath/src/pjturn-srv/server.c
@@ -40,7 +40,7 @@ static pj_status_t on_tx_stun_msg( pj_stun_session *sess,
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_stun_rx_data *rdata,
const pj_sockaddr_t *src_addr,
unsigned src_addr_len);
@@ -147,7 +147,6 @@ PJ_DEF(pj_status_t) pj_turn_srv_create(pj_pool_factory *pf,
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_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;
@@ -368,7 +367,8 @@ PJ_DEF(pj_status_t) pj_turn_srv_add_listener(pj_turn_srv *srv,
}
pj_stun_session_set_user_data(sess, lis);
- pj_stun_session_set_credential(sess, &srv->core.cred);
+ pj_stun_session_set_credential(sess, PJ_STUN_AUTH_LONG_TERM,
+ &srv->core.cred);
srv->core.stun_sess[index] = sess;
lis->id = index;
@@ -483,9 +483,8 @@ static pj_status_t on_tx_stun_msg( pj_stun_session *sess,
/* Respond to STUN request */
-static pj_status_t stun_respond(pj_turn_srv *srv,
- pj_stun_session *sess,
- const pj_stun_msg *req,
+static pj_status_t stun_respond(pj_stun_session *sess,
+ const pj_stun_rx_data *rdata,
unsigned code,
const char *errmsg,
pj_bool_t cache,
@@ -497,114 +496,17 @@ static pj_status_t stun_respond(pj_turn_srv *srv,
pj_stun_tx_data *tdata;
/* Create response */
- status = pj_stun_session_create_res(sess, req, code,
+ status = pj_stun_session_create_res(sess, rdata, code,
(errmsg?pj_cstr(&reason,errmsg):NULL),
&tdata);
if (status != PJ_SUCCESS)
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);
}
-/*
- * 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_username_attr *user;
- pj_stun_realm_attr *realm;
- pj_stun_nonce_attr *nonce;
- struct saved_cred *saved_cred;
- pj_status_t status;
-
- realm = (pj_stun_realm_attr*)
- pj_stun_msg_find_attr(req, PJ_STUN_ATTR_REALM, 0);
- PJ_ASSERT_RETURN(realm != NULL, PJ_EBUG);
-
- user = (pj_stun_username_attr*)
- pj_stun_msg_find_attr(req, PJ_STUN_ATTR_USERNAME, 0);
- PJ_ASSERT_RETURN(user != NULL, PJ_EBUG);
-
- nonce = (pj_stun_nonce_attr*)
- pj_stun_msg_find_attr(req, PJ_STUN_ATTR_NONCE, 0);
- PJ_ASSERT_RETURN(nonce != NULL, PJ_EBUG);
-
- saved_cred = PJ_POOL_ALLOC_T(response->pool, struct saved_cred);
-
- /* 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;
-
- /* 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;
-}
-
-
-/**
- * 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)
-{
- pj_turn_srv *srv;
- const pj_stun_msg *saved_msg;
- struct saved_cred *saved_cred;
-
- PJ_UNUSED_ARG(pool);
-
- srv = (pj_turn_srv*)user_data;
-
- /* 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);
-
- /* Lookup saved credential */
- saved_cred = (struct saved_cred*)
- pj_thread_local_get(srv->core.tls_data);
- PJ_ASSERT_RETURN(saved_cred != NULL, PJ_ENOTFOUND);
-
-
- *realm = saved_cred->realm;
- *username = saved_cred->username;
- *nonce = saved_cred->nonce;
- *data_type = saved_cred->data_type;
- *data = saved_cred->data;
-
-
- /* Don't clear saved_cred as this may be called more than once */
-
- return PJ_SUCCESS;
-}
-
-
/* 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.
@@ -612,11 +514,12 @@ PJ_DEF(pj_status_t) pj_turn_srv_get_cred(const pj_stun_msg *msg,
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_stun_rx_data *rdata,
const pj_sockaddr_t *src_addr,
unsigned src_addr_len)
{
pj_turn_listener *listener;
+ const pj_stun_msg *msg = rdata->msg;
pj_turn_srv *srv;
pj_turn_allocation *alloc;
pj_status_t status;
@@ -629,7 +532,7 @@ static pj_status_t on_rx_stun_request(pj_stun_session *sess,
/* 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,
+ stun_respond(sess, rdata, PJ_STUN_SC_ALLOCATION_MISMATCH,
NULL, PJ_FALSE, src_addr, src_addr_len);
return PJ_SUCCESS;
}
@@ -638,7 +541,7 @@ static pj_status_t on_rx_stun_request(pj_stun_session *sess,
* in this function.
*/
status = pj_turn_allocation_create(listener, src_addr, src_addr_len,
- msg, sess, &alloc);
+ rdata, sess, &alloc);
if (status != PJ_SUCCESS) {
/* STUN response has been sent, no need to reply here */
return PJ_SUCCESS;
diff --git a/pjnath/src/pjturn-srv/turn.h b/pjnath/src/pjturn-srv/turn.h
index 871db425..2eb99254 100644
--- a/pjnath/src/pjturn-srv/turn.h
+++ b/pjnath/src/pjturn-srv/turn.h
@@ -204,7 +204,7 @@ struct pj_turn_permission
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 pj_stun_rx_data *rdata,
pj_stun_session *srv_sess,
pj_turn_allocation **p_alloc);
/**
@@ -459,26 +459,5 @@ 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 /* __PJ_TURN_SRV_TURN_H__ */