summaryrefslogtreecommitdiff
path: root/pjnath
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2009-04-13 08:54:10 +0000
committerBenny Prijono <bennylp@teluu.com>2009-04-13 08:54:10 +0000
commit0efc01a54a538cb2ecaf03dcc77e12d78b346c39 (patch)
treeee63745c12c13d0efea0a94040b4778ef3dcc138 /pjnath
parent9053655bfd453951291fc84fdc99ea254c028611 (diff)
Ticket #780: Update TURN from draft 09 to draft 13:
- new rules for permissions: - permission must be created and refreshed explicitly with CreatePermission - transmitting data does not refresh permission - attributes changed/added: XOR-PEER-ADDRESS, XOR-RELAYED-ADDRESS, EVEN-PORT, DONT-FRAGMENT - attribute(s) removed: REQUESTED-PROPS - new status codes: 401 (Forbidden), 507 (Insufficient Capacity) - removed status codes: 443 (Invalid IP address), 444 (Invalid Port), 508 (Insufficient Port Capacity) git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@2589 74dad513-b988-da41-8d7b-12977e46ad98
Diffstat (limited to 'pjnath')
-rw-r--r--pjnath/include/pjnath/stun_msg.h137
-rw-r--r--pjnath/include/pjnath/stun_session.h12
-rw-r--r--pjnath/include/pjnath/turn_session.h39
-rw-r--r--pjnath/include/pjnath/turn_sock.h23
-rw-r--r--pjnath/include/pjnath/types.h12
-rw-r--r--pjnath/src/pjnath-test/server.c4
-rw-r--r--pjnath/src/pjnath/stun_msg.c28
-rw-r--r--pjnath/src/pjnath/stun_msg_dump.c7
-rw-r--r--pjnath/src/pjnath/stun_session.c19
-rw-r--r--pjnath/src/pjnath/turn_session.c469
-rw-r--r--pjnath/src/pjnath/turn_sock.c14
-rw-r--r--pjnath/src/pjturn-client/client_main.c50
-rw-r--r--pjnath/src/pjturn-srv/allocation.c35
13 files changed, 609 insertions, 240 deletions
diff --git a/pjnath/include/pjnath/stun_msg.h b/pjnath/include/pjnath/stun_msg.h
index 5a7237f3..25082626 100644
--- a/pjnath/include/pjnath/stun_msg.h
+++ b/pjnath/include/pjnath/stun_msg.h
@@ -83,6 +83,11 @@ enum pj_stun_method_e
PJ_STUN_DATA_METHOD = 7,
/**
+ * STUN/TURN CreatePermission method as defined by draft-ietf-behave-turn
+ */
+ PJ_STUN_CREATE_PERM_METHOD = 8,
+
+ /**
* STUN/TURN ChannelBind as defined by draft-ietf-behave-turn
*/
PJ_STUN_CHANNEL_BIND_METHOD = 9,
@@ -254,6 +259,22 @@ typedef enum pj_stun_msg_type
/**
+ * TURN CreatePermission request
+ */
+ PJ_STUN_CREATE_PERM_REQUEST = 0x0008,
+
+ /**
+ * TURN CreatePermission successful response.
+ */
+ PJ_STUN_CREATE_PERM_RESPONSE = 0x0108,
+
+ /**
+ * TURN CreatePermission failure response
+ */
+ PJ_STUN_CREATE_PERM_ERROR_RESPONSE = 0x0118,
+
+
+ /**
* STUN/TURN ChannelBind Request
*/
PJ_STUN_CHANNEL_BIND_REQUEST = 0x0009,
@@ -292,14 +313,15 @@ typedef enum pj_stun_attr_type
PJ_STUN_ATTR_LIFETIME = 0x000D,/**< TURN LIFETIME attr. */
PJ_STUN_ATTR_MAGIC_COOKIE = 0x000F,/**< MAGIC-COOKIE attr (deprec)*/
PJ_STUN_ATTR_BANDWIDTH = 0x0010,/**< TURN BANDWIDTH (deprec) */
- PJ_STUN_ATTR_PEER_ADDR = 0x0012,/**< TURN PEER-ADDRESS attr. */
+ PJ_STUN_ATTR_XOR_PEER_ADDR = 0x0012,/**< TURN XOR-PEER-ADDRESS */
PJ_STUN_ATTR_DATA = 0x0013,/**< DATA attribute. */
PJ_STUN_ATTR_REALM = 0x0014,/**< REALM attribute. */
PJ_STUN_ATTR_NONCE = 0x0015,/**< NONCE attribute. */
- PJ_STUN_ATTR_RELAYED_ADDR = 0x0016,/**< RELAYED-ADDRESS attribute.*/
+ PJ_STUN_ATTR_XOR_RELAYED_ADDR = 0x0016,/**< TURN XOR-RELAYED-ADDRESS */
PJ_STUN_ATTR_REQ_ADDR_TYPE = 0x0017,/**< REQUESTED-ADDRESS-TYPE */
- PJ_STUN_ATTR_REQ_PROPS = 0x0018,/**< REQUESTED-PROPS */
- PJ_STUN_ATTR_REQ_TRANSPORT = 0x0019,/**< REQUESTED-TRANSPORT */
+ PJ_STUN_ATTR_EVEN_PORT = 0x0018,/**< TURN EVEN-PORT */
+ PJ_STUN_ATTR_REQ_TRANSPORT = 0x0019,/**< TURN REQUESTED-TRANSPORT */
+ PJ_STUN_ATTR_DONT_FRAGMENT = 0x001A,/**< TURN DONT-FRAGMENT */
PJ_STUN_ATTR_XOR_MAPPED_ADDR = 0x0020,/**< XOR-MAPPED-ADDRESS */
PJ_STUN_ATTR_TIMER_VAL = 0x0021,/**< TIMER-VAL attribute. */
PJ_STUN_ATTR_RESERVATION_TOKEN = 0x0022,/**< TURN RESERVATION-TOKEN */
@@ -332,6 +354,7 @@ typedef enum pj_stun_status
PJ_STUN_SC_TRY_ALTERNATE = 300, /**< Try Alternate */
PJ_STUN_SC_BAD_REQUEST = 400, /**< Bad Request */
PJ_STUN_SC_UNAUTHORIZED = 401, /**< Unauthorized */
+ PJ_STUN_SC_FORBIDDEN = 403, /**< Forbidden (TURN) */
PJ_STUN_SC_UNKNOWN_ATTRIBUTE = 420, /**< Unknown Attribute */
#if 0
/* These were obsolete in recent rfc3489bis */
@@ -349,8 +372,6 @@ typedef enum pj_stun_status
PJ_STUN_SC_WRONG_CREDENTIALS = 441, /**< TURN Wrong Credentials */
PJ_STUN_SC_UNSUPP_TRANSPORT_PROTO = 442, /**< Unsupported Transport or
Protocol (TURN) */
- PJ_STUN_SC_INVALID_IP_ADDR = 443, /**< Invalid IP Address(TURN)*/
- PJ_STUN_SC_INVALID_PORT = 444, /**< Invalid Port (TURN) */
PJ_STUN_SC_OPER_TCP_ONLY = 445, /**< Operation for TCP Only */
PJ_STUN_SC_CONNECTION_FAILURE = 446, /**< Connection Failure */
PJ_STUN_SC_CONNECTION_TIMEOUT = 447, /**< Connection Timeout */
@@ -358,9 +379,7 @@ typedef enum pj_stun_status
(TURN) */
PJ_STUN_SC_ROLE_CONFLICT = 487, /**< Role Conflict */
PJ_STUN_SC_SERVER_ERROR = 500, /**< Server Error */
- PJ_STUN_SC_INSUFFICIENT_CAPACITY = 507, /**< Insufficient Capacity
- (TURN) */
- PJ_STUN_SC_INSUFFICIENT_PORT_CAPACITY=508, /**< Insufficient Port Capacity
+ PJ_STUN_SC_INSUFFICIENT_CAPACITY = 508, /**< Insufficient Capacity
(TURN) */
PJ_STUN_SC_GLOBAL_FAILURE = 600 /**< Global Failure */
} pj_stun_status;
@@ -912,12 +931,12 @@ typedef struct pj_stun_uint_attr pj_stun_bandwidth_attr;
/**
- * This describes the STUN PEER-ADDRESS attribute.
- * The PEER-ADDRESS specifies the address and port of the peer as seen
+ * This describes the STUN XOR-PEER-ADDRESS attribute.
+ * The XOR-PEER-ADDRESS specifies the address and port of the peer as seen
* from the TURN server. It is encoded in the same way as XOR-MAPPED-
* ADDRESS.
*/
-typedef struct pj_stun_sockaddr_attr pj_stun_peer_addr_attr;
+typedef struct pj_stun_sockaddr_attr pj_stun_xor_peer_addr_attr;
/**
@@ -931,12 +950,12 @@ typedef struct pj_stun_binary_attr pj_stun_data_attr;
/**
- * This describes the STUN RELAYED-ADDRESS attribute.
- * The RELAYED-ADDRESS is present in Allocate responses. It specifies the
+ * This describes the STUN XOR-RELAYED-ADDRESS attribute. The
+ * XOR-RELAYED-ADDRESS is present in Allocate responses. It specifies the
* address and port that the server allocated to the client. It is
* encoded in the same way as XOR-MAPPED-ADDRESS.
*/
-typedef struct pj_stun_sockaddr_attr pj_stun_relayed_addr_attr;
+typedef struct pj_stun_sockaddr_attr pj_stun_xor_relayed_addr_attr;
/**
@@ -955,71 +974,37 @@ typedef struct pj_stun_sockaddr_attr pj_stun_relayed_addr_attr;
\endverbatim
*/
-typedef struct pj_stun_uint_attr pj_stun_req_addr_type;
-
-/**
- * This describes the TURN REQUESTED-PROPS attribute, encoded as
- * STUN 32bit integer attribute. Few macros are provided to manipulate
- * the values in this attribute: #PJ_STUN_GET_PROP_TYPE(), and
- * #PJ_STUN_SET_PROP_TYPE().
- *
- * This attribute allows the client to request that the allocation have
- * certain properties, and by the server to indicate which properties
- * are supported. The attribute is 32 bits long. Its format is:
-
- \verbatim
-
- 0 1 2 3
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Prop-type | Reserved = 0 |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+typedef struct pj_stun_uint_attr pj_stun_req_addr_type_attr;
- \endverbatim
- * The field labeled "Prop-type" is an 8-bit field specifying the
- * desired property. The rest of the attribute is RFFU (Reserved For
- * Future Use) and MUST be set to 0 on transmission and ignored on
- * reception.
+/**
+ * This describes the TURN REQUESTED-TRANSPORT attribute, encoded in
+ * STUN generic integer attribute.
*
- * The "Prop-type" field is formatted as follows:
-
- \verbatim
+ * This attribute allows the client to request that the port in the
+ * relayed-transport-address be even, and (optionally) that the server
+ * reserve the next-higher port number. The attribute is 8 bits long.
+ * Its format is:
- 0 1 2 3 4 5 6 7
+\verbatim
+ 0
+ 0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+
- |E|R|P| |
+ |R| RFFU |
+-+-+-+-+-+-+-+-+
- \endverbatim
-
- The bits in "Prop-type" are:
-
- E: If 1, the port number for the relayed-transport-address must be
- even. If 0, the port number can be even or odd.
-
- R: If 1, the server must reserve the next highest port for a
- subsequent allocation. If 0, no such reservation is requested.
- If the client sets the R bit to 1, it MUST also set the E bit to 1
- (however, the E bit may be 1 when the R bit is 0).
-
- P: If 1, the allocation must be a Preserving allocation. If 0, the
- allocation can be either Preserving or Non-Preserving.
-
- */
-typedef struct pj_stun_uint_attr pj_stun_req_props_attr;
-
-/**
- * Get the 8bit Prop-type value from a 32bit integral value of TURN
- * TURN REQUESTED-PROPS attribute.
- */
-#define PJ_STUN_GET_PROP_TYPE(u32) (u32 >> 24)
+\endverbatim
-/**
- * Convert 8bit Prop-type value to a 32bit integral value of TURN
- * REQUESTED-PROPS attribute.
+ * The attribute contains a single 1-bit flag:
+ *
+ * R: If 1, the server is requested to reserve the next higher port
+ * number (on the same IP address) for a subsequent allocation. If
+ * 0, no such reservation is requested.
+ *
+ * The other 7 bits of the attribute must be set to zero on transmission
+ * and ignored on reception.
*/
-#define PJ_STUN_SET_PROP_TYPE(PropType) (PropType << 24)
+typedef struct pj_stun_uint_attr pj_stun_even_port_attr;
/**
@@ -1064,6 +1049,16 @@ typedef struct pj_stun_uint_attr pj_stun_req_transport_attr;
#define PJ_STUN_SET_RT_PROTO(proto) (((pj_uint32_t)(proto)) << 24)
+/**
+ * This describes the TURN DONT-FRAGMENT attribute.
+ *
+ * This attribute is used by the client to request that the server set
+ * the DF (Don't Fragment) bit in the IP header when relaying the
+ * application data onward to the peer. This attribute has no value
+ * part and thus the attribute length field is 0.
+ */
+typedef struct pj_stun_empty_attr pj_stun_use_candidate_attr;
+
/**
* This describes the TURN RESERVATION-TOKEN attribute.
diff --git a/pjnath/include/pjnath/stun_session.h b/pjnath/include/pjnath/stun_session.h
index 518e4ad3..714cbf9e 100644
--- a/pjnath/include/pjnath/stun_session.h
+++ b/pjnath/include/pjnath/stun_session.h
@@ -327,15 +327,17 @@ PJ_DECL(pj_status_t) pj_stun_session_set_lock(pj_stun_session *sess,
pj_bool_t auto_del);
/**
- * Set server name to be included in all response.
+ * Set SOFTWARE name to be included in all requests and responses.
*
* @param sess The STUN session instance.
- * @param srv_name Server name string.
+ * @param sw Software name string. If this argument is NULL or
+ * empty, the session will not include SOFTWARE attribute
+ * in STUN requests and responses.
*
- * @return The user data associated with this STUN session.
+ * @return PJ_SUCCESS on success, or the appropriate error code.
*/
-PJ_DECL(pj_status_t) pj_stun_session_set_server_name(pj_stun_session *sess,
- const pj_str_t *srv_name);
+PJ_DECL(pj_status_t) pj_stun_session_set_software_name(pj_stun_session *sess,
+ const pj_str_t *sw);
/**
* Set credential to be used by this session. Once credential is set, all
diff --git a/pjnath/include/pjnath/turn_session.h b/pjnath/include/pjnath/turn_session.h
index f028fd8e..88cf1bfe 100644
--- a/pjnath/include/pjnath/turn_session.h
+++ b/pjnath/include/pjnath/turn_session.h
@@ -437,6 +437,21 @@ PJ_DECL(void) pj_turn_session_set_log(pj_turn_session *sess,
/**
+ * Configure the SOFTWARE name to be sent in all STUN requests by the
+ * TURN session.
+ *
+ * @param sess The TURN client session.
+ * @param sw Software name string. If this argument is NULL or
+ * empty, the session will not include SOFTWARE attribute
+ * in STUN requests and responses.
+ *
+ * @return PJ_SUCCESS on success, or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pj_turn_session_set_software_name(pj_turn_session *sess,
+ const pj_str_t *sw);
+
+
+/**
* Set the server or domain name of the server. Before the application
* can send Allocate request (with pj_turn_session_alloc()), it must first
* resolve the server address(es) using this function. This function will
@@ -519,6 +534,30 @@ PJ_DECL(pj_status_t) pj_turn_session_alloc(pj_turn_session *sess,
/**
+ * Create or renew permission in the TURN server for the specified peer IP
+ * addresses. Application must install permission for a particular (peer)
+ * IP address before it sends any data to that IP address, or otherwise
+ * the TURN server will drop the data.
+ *
+ * @param sess The TURN client session.
+ * @param addr_cnt Number of IP addresses.
+ * @param addr Array of peer IP addresses. Only the address family
+ * and IP address portion of the socket address matter.
+ * @param options Specify 1 to let the TURN client session automatically
+ * renew the permission later when they are about to
+ * expire.
+ *
+ * @return PJ_SUCCESS if the operation has been successfully
+ * issued, or the appropriate error code. Note that
+ * the operation itself will complete asynchronously.
+ */
+PJ_DECL(pj_status_t) pj_turn_session_set_perm(pj_turn_session *sess,
+ unsigned addr_cnt,
+ const pj_sockaddr addr[],
+ unsigned options);
+
+
+/**
* Send a data to the specified peer address via the TURN relay. This
* function will encapsulate the data as STUN Send Indication or TURN
* ChannelData packet and send the message to the TURN server. The TURN
diff --git a/pjnath/include/pjnath/turn_sock.h b/pjnath/include/pjnath/turn_sock.h
index e2e0c040..e0931ab9 100644
--- a/pjnath/include/pjnath/turn_sock.h
+++ b/pjnath/include/pjnath/turn_sock.h
@@ -248,6 +248,29 @@ PJ_DECL(pj_status_t) pj_turn_sock_alloc(pj_turn_sock *turn_sock,
const pj_turn_alloc_param *param);
/**
+ * Create or renew permission in the TURN server for the specified peer IP
+ * addresses. Application must install permission for a particular (peer)
+ * IP address before it sends any data to that IP address, or otherwise
+ * the TURN server will drop the data.
+ *
+ * @param turn_sock The TURN transport instance.
+ * @param addr_cnt Number of IP addresses.
+ * @param addr Array of peer IP addresses. Only the address family
+ * and IP address portion of the socket address matter.
+ * @param options Specify 1 to let the TURN client session automatically
+ * renew the permission later when they are about to
+ * expire.
+ *
+ * @return PJ_SUCCESS if the operation has been successfully
+ * issued, or the appropriate error code. Note that
+ * the operation itself will complete asynchronously.
+ */
+PJ_DECL(pj_status_t) pj_turn_sock_set_perm(pj_turn_sock *turn_sock,
+ unsigned addr_cnt,
+ const pj_sockaddr addr[],
+ unsigned options);
+
+/**
* Send a data to the specified peer address via the TURN relay. This
* function will encapsulate the data as STUN Send Indication or TURN
* ChannelData packet and send the message to the TURN server. The TURN
diff --git a/pjnath/include/pjnath/types.h b/pjnath/include/pjnath/types.h
index a7d66070..0f857d00 100644
--- a/pjnath/include/pjnath/types.h
+++ b/pjnath/include/pjnath/types.h
@@ -107,11 +107,15 @@ This version of PJNATH implements the following STUN RFC:
Traversal Using Relays around NAT (TURN) allows the host to control the
operation of the relay and to exchange packets with its peers using the relay.
-This version of PJNATH implements both TCP and UDP client transport and it
-complies with the following TURN draft:
- - <A HREF="http://www.ietf.org/internet-drafts/draft-ietf-behave-turn-09.txt">
- <B>draft-ietf-behave-turn-09</B></A>: Obtaining Relay Addresses
+Features:
+ - <A HREF="http://www.ietf.org/internet-drafts/draft-ietf-behave-turn-13.txt">
+ <B>draft-ietf-behave-turn-13</B></A>: Obtaining Relay Addresses
from Simple Traversal Underneath NAT (STUN)
+ - DNS SRV resolution
+ - Fallback to DNS A resolution if SRV record is not found
+ - UDP and TCP connection to TURN server
+ - automatic management of allocation refresh
+
\subsection comp_ice ICE
diff --git a/pjnath/src/pjnath-test/server.c b/pjnath/src/pjnath-test/server.c
index 75379724..aa044e9b 100644
--- a/pjnath/src/pjnath-test/server.c
+++ b/pjnath/src/pjnath-test/server.c
@@ -310,8 +310,8 @@ static pj_stun_msg* create_success_response(test_server *test_srv,
/* Add LIFETIME */
pj_stun_msg_add_uint_attr(pool, resp, PJ_STUN_ATTR_LIFETIME, lifetime);
if (lifetime != 0) {
- /* Add RELAYED-ADDRESS */
- pj_stun_msg_add_sockaddr_attr(pool, resp, PJ_STUN_ATTR_RELAYED_ADDR, PJ_TRUE, &alloc->alloc_addr,
+ /* Add XOR-RELAYED-ADDRESS */
+ pj_stun_msg_add_sockaddr_attr(pool, resp, PJ_STUN_ATTR_XOR_RELAYED_ADDR, PJ_TRUE, &alloc->alloc_addr,
pj_sockaddr_get_len(&alloc->alloc_addr));
/* Add XOR-MAPPED-ADDRESS */
pj_stun_msg_add_sockaddr_attr(pool, resp, PJ_STUN_ATTR_XOR_MAPPED_ADDR, PJ_TRUE, &alloc->client_addr,
diff --git a/pjnath/src/pjnath/stun_msg.c b/pjnath/src/pjnath/stun_msg.c
index e1cba80b..dedfb126 100644
--- a/pjnath/src/pjnath/stun_msg.c
+++ b/pjnath/src/pjnath/stun_msg.c
@@ -43,7 +43,7 @@ static const char *stun_method_names[PJ_STUN_METHOD_MAX] =
"???", /* 5 */
"Send", /* 6 */
"Data", /* 7 */
- "???", /* 8 */
+ "CreatePermission", /* 8 */
"ChannelBind", /* 9 */
};
@@ -56,6 +56,7 @@ static struct
{ PJ_STUN_SC_TRY_ALTERNATE, "Try Alternate"},
{ PJ_STUN_SC_BAD_REQUEST, "Bad Request"},
{ PJ_STUN_SC_UNAUTHORIZED, "Unauthorized"},
+ { PJ_STUN_SC_FORBIDDEN, "Forbidden"},
{ PJ_STUN_SC_UNKNOWN_ATTRIBUTE, "Unknown Attribute"},
//{ PJ_STUN_SC_STALE_CREDENTIALS, "Stale Credentials"},
//{ PJ_STUN_SC_INTEGRITY_CHECK_FAILURE, "Integrity Check Failure"},
@@ -69,8 +70,6 @@ static struct
{ PJ_STUN_SC_TRANSITIONING, "Active Destination Already Set"},
{ PJ_STUN_SC_WRONG_CREDENTIALS, "Wrong Credentials"},
{ PJ_STUN_SC_UNSUPP_TRANSPORT_PROTO, "Unsupported Transport Protocol"},
- { PJ_STUN_SC_INVALID_IP_ADDR, "Invalid IP Address"},
- { PJ_STUN_SC_INVALID_PORT, "Invalid Port"},
{ PJ_STUN_SC_OPER_TCP_ONLY, "Operation for TCP Only"},
{ PJ_STUN_SC_CONNECTION_FAILURE, "Connection Failure"},
{ PJ_STUN_SC_CONNECTION_TIMEOUT, "Connection Timeout"},
@@ -78,7 +77,6 @@ static struct
{ PJ_STUN_SC_ROLE_CONFLICT, "Role Conflict"},
{ PJ_STUN_SC_SERVER_ERROR, "Server Error"},
{ PJ_STUN_SC_INSUFFICIENT_CAPACITY, "Insufficient Capacity"},
- { PJ_STUN_SC_INSUFFICIENT_PORT_CAPACITY,"Insufficient Port Capacity"},
{ PJ_STUN_SC_GLOBAL_FAILURE, "Global Failure"}
};
@@ -310,8 +308,8 @@ static struct attr_desc mandatory_attr_desc[] =
NULL
},
{
- /* PJ_STUN_ATTR_PEER_ADDRESS, */
- "PEER-ADDRESS",
+ /* PJ_STUN_ATTR_XOR_PEER_ADDRESS, */
+ "XOR-PEER-ADDRESS",
&decode_xored_sockaddr_attr,
&encode_sockaddr_attr,
&clone_sockaddr_attr
@@ -338,8 +336,8 @@ static struct attr_desc mandatory_attr_desc[] =
&clone_string_attr
},
{
- /* PJ_STUN_ATTR_RELAY_ADDRESS, */
- "RELAYED-ADDRESS",
+ /* PJ_STUN_ATTR_XOR_RELAYED_ADDR, */
+ "XOR-RELAYED-ADDRESS",
&decode_xored_sockaddr_attr,
&encode_sockaddr_attr,
&clone_sockaddr_attr
@@ -352,8 +350,8 @@ static struct attr_desc mandatory_attr_desc[] =
&clone_uint_attr
},
{
- /* PJ_STUN_ATTR_REQUESTED_PROPS, */
- "REQUESTED-PROPS",
+ /* PJ_STUN_ATTR_EVEN_PORT, */
+ "EVEN-PORT",
&decode_uint_attr,
&encode_uint_attr,
&clone_uint_attr
@@ -366,11 +364,11 @@ static struct attr_desc mandatory_attr_desc[] =
&clone_uint_attr
},
{
- /* ID 0x001A is not assigned */
- NULL,
- NULL,
- NULL,
- NULL
+ /* PJ_STUN_ATTR_DONT_FRAGMENT */
+ "DONT-FRAGMENT",
+ &decode_empty_attr,
+ &encode_empty_attr,
+ &clone_empty_attr
},
{
/* ID 0x001B is not assigned */
diff --git a/pjnath/src/pjnath/stun_msg_dump.c b/pjnath/src/pjnath/stun_msg_dump.c
index 0416f574..2ff4a74b 100644
--- a/pjnath/src/pjnath/stun_msg_dump.c
+++ b/pjnath/src/pjnath/stun_msg_dump.c
@@ -72,8 +72,8 @@ static int print_attr(char *buffer, unsigned length,
case PJ_STUN_ATTR_SOURCE_ADDR:
case PJ_STUN_ATTR_CHANGED_ADDR:
case PJ_STUN_ATTR_REFLECTED_FROM:
- case PJ_STUN_ATTR_PEER_ADDR:
- case PJ_STUN_ATTR_RELAYED_ADDR:
+ case PJ_STUN_ATTR_XOR_PEER_ADDR:
+ case PJ_STUN_ATTR_XOR_RELAYED_ADDR:
case PJ_STUN_ATTR_XOR_MAPPED_ADDR:
case PJ_STUN_ATTR_XOR_REFLECTED_FROM:
case PJ_STUN_ATTR_ALTERNATE_SERVER:
@@ -116,7 +116,7 @@ static int print_attr(char *buffer, unsigned length,
case PJ_STUN_ATTR_LIFETIME:
case PJ_STUN_ATTR_BANDWIDTH:
case PJ_STUN_ATTR_REQ_ADDR_TYPE:
- case PJ_STUN_ATTR_REQ_PROPS:
+ case PJ_STUN_ATTR_EVEN_PORT:
case PJ_STUN_ATTR_REQ_TRANSPORT:
case PJ_STUN_ATTR_TIMER_VAL:
case PJ_STUN_ATTR_PRIORITY:
@@ -223,6 +223,7 @@ static int print_attr(char *buffer, unsigned length,
}
break;
case PJ_STUN_ATTR_USE_CANDIDATE:
+ case PJ_STUN_ATTR_DONT_FRAGMENT:
default:
len = pj_ansi_snprintf(p, end-p, "\n");
APPLY();
diff --git a/pjnath/src/pjnath/stun_session.c b/pjnath/src/pjnath/stun_session.c
index 45e4aa53..fd71beb8 100644
--- a/pjnath/src/pjnath/stun_session.c
+++ b/pjnath/src/pjnath/stun_session.c
@@ -217,8 +217,13 @@ static pj_status_t apply_msg_options(pj_stun_session *sess,
pj_status_t status = 0;
pj_str_t realm, username, nonce, auth_key;
- /* The server SHOULD include a SOFTWARE attribute in all responses */
- if (sess->srv_name.slen && PJ_STUN_IS_RESPONSE(msg->hdr.type)) {
+ /* If the agent is sending a request, it SHOULD add a SOFTWARE attribute
+ * to the request. The server SHOULD include a SOFTWARE attribute in all
+ * responses
+ */
+ if (sess->srv_name.slen && !PJ_STUN_IS_INDICATION(msg->hdr.type) &&
+ pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_SOFTWARE, 0)==NULL)
+ {
pj_stun_msg_add_string_attr(pool, msg, PJ_STUN_ATTR_SOFTWARE,
&sess->srv_name);
}
@@ -486,7 +491,7 @@ PJ_DEF(pj_status_t) pj_stun_session_create( pj_stun_config *cfg,
sess->srv_name.ptr = (char*) pj_pool_alloc(pool, 32);
sess->srv_name.slen = pj_ansi_snprintf(sess->srv_name.ptr, 32,
- "pj_stun-%s", pj_get_version());
+ "pjnath-%s", pj_get_version());
sess->rx_pool = pj_pool_create(sess->cfg->pf, name,
PJNATH_POOL_LEN_STUN_TDATA,
@@ -590,12 +595,12 @@ PJ_DEF(pj_status_t) pj_stun_session_set_lock( pj_stun_session *sess,
return PJ_SUCCESS;
}
-PJ_DEF(pj_status_t) pj_stun_session_set_server_name(pj_stun_session *sess,
- const pj_str_t *srv_name)
+PJ_DEF(pj_status_t) pj_stun_session_set_software_name(pj_stun_session *sess,
+ const pj_str_t *sw)
{
PJ_ASSERT_RETURN(sess, PJ_EINVAL);
- if (srv_name)
- pj_strdup(sess->pool, &sess->srv_name, srv_name);
+ if (sw && sw->slen)
+ pj_strdup(sess->pool, &sess->srv_name, sw);
else
sess->srv_name.slen = 0;
return PJ_SUCCESS;
diff --git a/pjnath/src/pjnath/turn_session.c b/pjnath/src/pjnath/turn_session.c
index 79cba249..7c970421 100644
--- a/pjnath/src/pjnath/turn_session.c
+++ b/pjnath/src/pjnath/turn_session.c
@@ -28,11 +28,14 @@
#include <pj/log.h>
#include <pj/os.h>
#include <pj/pool.h>
+#include <pj/rand.h>
#include <pj/sock.h>
#define PJ_TURN_CHANNEL_MIN 0x4000
-#define PJ_TURN_CHANNEL_MAX 0xFFFE /* inclusive */
-#define PJ_TURN_PEER_HTABLE_SIZE 8
+#define PJ_TURN_CHANNEL_MAX 0x7FFF /* inclusive */
+#define PJ_TURN_CHANNEL_HTABLE_SIZE 8
+#define PJ_TURN_PERM_HTABLE_SIZE 8
+#define PJ_TURN_RENEWAL_BEFORE 10 /* seconds before renewals */
static const char *state_names[] =
{
@@ -53,15 +56,56 @@ enum timer_id_t
TIMER_DESTROY
};
-
-struct peer
+/* This structure describes a channel binding. A channel binding is index by
+ * the channel number or IP address and port number of the peer.
+ */
+struct ch_t
{
- pj_uint16_t ch_id;
+ /* The channel number */
+ pj_uint16_t num;
+
+ /* PJ_TRUE if we've received successful response to ChannelBind request
+ * for this channel.
+ */
pj_bool_t bound;
+
+ /* The peer IP address and port */
pj_sockaddr addr;
+
+ /* The channel binding expiration */
+ pj_time_val expiry;
+};
+
+
+/* This structure describes a permission. A permission is identified by the
+ * IP address only.
+ */
+struct perm_t
+{
+ /* Cache of hash value to speed-up lookup */
+ pj_uint32_t hval;
+
+ /* The permission IP address. The port number MUST be zero */
+ pj_sockaddr addr;
+
+ /* Number of peers that uses this permission. */
+ unsigned peer_cnt;
+
+ /* Automatically renew this permission once it expires? */
+ pj_bool_t renew;
+
+ /* The permission expiration */
pj_time_val expiry;
+
+ /* Arbitrary/random pointer value (token) to map this perm with the
+ * request to create it. It is used to invalidate this perm when the
+ * request fails.
+ */
+ void *req_token;
};
+
+/* The TURN client session structure */
struct pj_turn_session
{
pj_pool_t *pool;
@@ -102,7 +146,8 @@ struct pj_turn_session
pj_sockaddr mapped_addr;
pj_sockaddr relay_addr;
- pj_hash_table_t *peer_table;
+ pj_hash_table_t *ch_table;
+ pj_hash_table_t *perm_table;
pj_uint32_t send_ind_tsx_id[3];
/* tx_pkt must be 16bit aligned */
@@ -142,13 +187,19 @@ static pj_status_t stun_on_rx_indication(pj_stun_session *sess,
static void dns_srv_resolver_cb(void *user_data,
pj_status_t status,
const pj_dns_srv_record *rec);
-static struct peer *lookup_peer_by_addr(pj_turn_session *sess,
- const pj_sockaddr_t *addr,
- unsigned addr_len,
- pj_bool_t update,
- pj_bool_t bind_channel);
-static struct peer *lookup_peer_by_chnum(pj_turn_session *sess,
- pj_uint16_t chnum);
+static struct ch_t *lookup_ch_by_addr(pj_turn_session *sess,
+ const pj_sockaddr_t *addr,
+ unsigned addr_len,
+ pj_bool_t update,
+ pj_bool_t bind_channel);
+static struct ch_t *lookup_ch_by_chnum(pj_turn_session *sess,
+ pj_uint16_t chnum);
+static struct perm_t *lookup_perm(pj_turn_session *sess,
+ const pj_sockaddr_t *addr,
+ unsigned addr_len,
+ pj_bool_t update);
+static void invalidate_perm(pj_turn_session *sess,
+ struct perm_t *perm);
static void on_timer_event(pj_timer_heap_t *th, pj_timer_entry *e);
@@ -224,7 +275,10 @@ PJ_DEF(pj_status_t) pj_turn_session_create( const pj_stun_config *cfg,
pj_memcpy(&sess->cb, cb, sizeof(*cb));
/* Peer hash table */
- sess->peer_table = pj_hash_create(pool, PJ_TURN_PEER_HTABLE_SIZE);
+ sess->ch_table = pj_hash_create(pool, PJ_TURN_CHANNEL_HTABLE_SIZE);
+
+ /* Permission hash table */
+ sess->perm_table = pj_hash_create(pool, PJ_TURN_PERM_HTABLE_SIZE);
/* Session lock */
status = pj_lock_create_recursive_mutex(pool, sess->obj_name,
@@ -482,6 +536,22 @@ PJ_DEF(void) pj_turn_session_set_log( pj_turn_session *sess,
}
+/*
+ * Set software name
+ */
+PJ_DEF(pj_status_t) pj_turn_session_set_software_name( pj_turn_session *sess,
+ const pj_str_t *sw)
+{
+ pj_status_t status;
+
+ pj_lock_acquire(sess->lock);
+ status = pj_stun_session_set_software_name(sess->stun, sw);
+ pj_lock_release(sess->lock);
+
+ return status;
+}
+
+
/**
* Set the server or domain name of the server.
*/
@@ -704,6 +774,101 @@ PJ_DEF(pj_status_t) pj_turn_session_alloc(pj_turn_session *sess,
/*
+ * Install or renew permissions
+ */
+PJ_DEF(pj_status_t) pj_turn_session_set_perm( pj_turn_session *sess,
+ unsigned addr_cnt,
+ const pj_sockaddr addr[],
+ unsigned options)
+{
+ pj_stun_tx_data *tdata;
+ pj_hash_iterator_t it_buf, *it;
+ void *req_token;
+ unsigned i, attr_added=0;
+ pj_status_t status;
+
+ PJ_ASSERT_RETURN(sess && addr_cnt && addr, PJ_EINVAL);
+
+ pj_lock_acquire(sess->lock);
+
+ /* Create a bare CreatePermission request */
+ status = pj_stun_session_create_req(sess->stun,
+ PJ_STUN_CREATE_PERM_REQUEST,
+ PJ_STUN_MAGIC, NULL, &tdata);
+ if (status != PJ_SUCCESS) {
+ pj_lock_release(sess->lock);
+ return status;
+ }
+
+ /* Create request token to map the request to the perm structures
+ * which the request belongs.
+ */
+ req_token = (void*)(long)pj_rand();
+
+ /* Process the addresses */
+ for (i=0; i<addr_cnt; ++i) {
+ struct perm_t *perm;
+
+ /* Lookup the perm structure and create if it doesn't exist */
+ perm = lookup_perm(sess, &addr[i], pj_sockaddr_get_len(&addr[i]),
+ PJ_TRUE);
+ perm->renew = (options & 0x01);
+
+ /* Only add to the request if the request doesn't contain this
+ * address yet.
+ */
+ if (perm->req_token != req_token) {
+ perm->req_token = req_token;
+
+ /* Add XOR-PEER-ADDRESS */
+ status = pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg,
+ PJ_STUN_ATTR_XOR_PEER_ADDR,
+ PJ_TRUE,
+ &addr[i],
+ sizeof(addr[i]));
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ ++attr_added;
+ }
+ }
+
+ pj_assert(attr_added != 0);
+
+ /* Send the request */
+ status = pj_stun_session_send_msg(sess->stun, req_token, PJ_FALSE,
+ (sess->conn_type==PJ_TURN_TP_UDP),
+ sess->srv_addr,
+ pj_sockaddr_get_len(sess->srv_addr),
+ tdata);
+ if (status != PJ_SUCCESS) {
+ /* tdata is already destroyed */
+ tdata = NULL;
+ goto on_error;
+ }
+
+ pj_lock_release(sess->lock);
+ return PJ_SUCCESS;
+
+on_error:
+ /* destroy tdata */
+ if (tdata) {
+ pj_stun_msg_destroy_tdata(sess->stun, tdata);
+ }
+ /* invalidate perm structures associated with this request */
+ it = pj_hash_first(sess->perm_table, &it_buf);
+ while (it) {
+ struct perm_t *perm = (struct perm_t*)
+ pj_hash_this(sess->perm_table, it);
+ it = pj_hash_next(sess->perm_table, it);
+ if (perm->req_token == req_token)
+ invalidate_perm(sess, perm);
+ }
+ pj_lock_release(sess->lock);
+ return status;
+}
+
+/*
* Send REFRESH
*/
static void send_refresh(pj_turn_session *sess, int lifetime)
@@ -757,7 +922,8 @@ PJ_DEF(pj_status_t) pj_turn_session_sendto( pj_turn_session *sess,
const pj_sockaddr_t *addr,
unsigned addr_len)
{
- struct peer *peer;
+ struct ch_t *ch;
+ struct perm_t *perm;
pj_status_t status;
PJ_ASSERT_RETURN(sess && pkt && pkt_len && addr && addr_len,
@@ -771,14 +937,29 @@ PJ_DEF(pj_status_t) pj_turn_session_sendto( pj_turn_session *sess,
/* Lock session now */
pj_lock_acquire(sess->lock);
- /* Lookup peer to see whether we've assigned a channel number
- * to this peer.
- */
- peer = lookup_peer_by_addr(sess, addr, addr_len, PJ_TRUE, PJ_FALSE);
- pj_assert(peer != NULL);
+ /* Lookup permission first */
+ perm = lookup_perm(sess, addr, pj_sockaddr_get_len(addr), PJ_FALSE);
+ if (perm == NULL) {
+ /* Permission doesn't exist, install it first */
+ char ipstr[PJ_INET6_ADDRSTRLEN+2];
+
+ PJ_LOG(4,(sess->obj_name,
+ "sendto(): IP %s has no permission, requesting it first..",
+ pj_sockaddr_print(addr, ipstr, sizeof(ipstr), 2)));
- if (peer->ch_id != PJ_TURN_INVALID_CHANNEL && peer->bound) {
- /* Peer is assigned Channel number, we can use ChannelData */
+ status = pj_turn_session_set_perm(sess, 1, (const pj_sockaddr*)addr,
+ 0);
+ if (status != PJ_SUCCESS) {
+ pj_lock_release(sess->lock);
+ return status;
+ }
+ }
+
+ /* See if the peer is bound to a channel number */
+ ch = lookup_ch_by_addr(sess, addr, pj_sockaddr_get_len(addr),
+ PJ_FALSE, PJ_FALSE);
+ if (ch && ch->num != PJ_TURN_INVALID_CHANNEL && ch->bound) {
+ /* Peer is assigned a channel number, we can use ChannelData */
pj_turn_channel_data *cd = (pj_turn_channel_data*)sess->tx_pkt;
pj_assert(sizeof(*cd)==4);
@@ -788,7 +969,7 @@ PJ_DEF(pj_status_t) pj_turn_session_sendto( pj_turn_session *sess,
goto on_return;
}
- cd->ch_number = pj_htons((pj_uint16_t)peer->ch_id);
+ cd->ch_number = pj_htons((pj_uint16_t)ch->num);
cd->length = pj_htons((pj_uint16_t)pkt_len);
pj_memcpy(cd+1, pkt, pkt_len);
@@ -799,9 +980,7 @@ PJ_DEF(pj_status_t) pj_turn_session_sendto( pj_turn_session *sess,
pj_sockaddr_get_len(sess->srv_addr));
} else {
- /* Peer has not been assigned Channel number, must use Send
- * Indication.
- */
+ /* Use Send Indication. */
pj_stun_sockaddr_attr peer_attr;
pj_stun_binary_attr data_attr;
pj_stun_msg send_ind;
@@ -817,8 +996,8 @@ PJ_DEF(pj_status_t) pj_turn_session_sendto( pj_turn_session *sess,
if (status != PJ_SUCCESS)
goto on_return;
- /* Add PEER-ADDRESS */
- pj_stun_sockaddr_attr_init(&peer_attr, PJ_STUN_ATTR_PEER_ADDR,
+ /* Add XOR-PEER-ADDRESS */
+ pj_stun_sockaddr_attr_init(&peer_attr, PJ_STUN_ATTR_XOR_PEER_ADDR,
PJ_TRUE, addr, addr_len);
pj_stun_msg_add_attr(&send_ind, (pj_stun_attr_hdr*)&peer_attr);
@@ -854,7 +1033,7 @@ PJ_DEF(pj_status_t) pj_turn_session_bind_channel(pj_turn_session *sess,
const pj_sockaddr_t *peer_adr,
unsigned addr_len)
{
- struct peer *peer;
+ struct ch_t *ch;
pj_stun_tx_data *tdata;
pj_uint16_t ch_num;
pj_status_t status;
@@ -871,17 +1050,18 @@ PJ_DEF(pj_status_t) pj_turn_session_bind_channel(pj_turn_session *sess,
if (status != PJ_SUCCESS)
goto on_return;
- /* Lookup peer */
- peer = lookup_peer_by_addr(sess, peer_adr, addr_len, PJ_TRUE, PJ_FALSE);
- pj_assert(peer);
+ /* Lookup if this peer has already been assigned a number */
+ ch = lookup_ch_by_addr(sess, peer_adr, pj_sockaddr_get_len(peer_adr),
+ PJ_TRUE, PJ_FALSE);
+ pj_assert(ch);
- if (peer->ch_id != PJ_TURN_INVALID_CHANNEL) {
+ if (ch->num != PJ_TURN_INVALID_CHANNEL) {
/* Channel is already bound. This is a refresh request. */
- ch_num = peer->ch_id;
+ ch_num = ch->num;
} else {
PJ_ASSERT_ON_FAIL(sess->next_ch <= PJ_TURN_CHANNEL_MAX,
{status=PJ_ETOOMANY; goto on_return;});
- peer->ch_id = ch_num = sess->next_ch++;
+ ch->num = ch_num = sess->next_ch++;
}
/* Add CHANNEL-NUMBER attribute */
@@ -889,15 +1069,15 @@ PJ_DEF(pj_status_t) pj_turn_session_bind_channel(pj_turn_session *sess,
PJ_STUN_ATTR_CHANNEL_NUMBER,
PJ_STUN_SET_CH_NB(ch_num));
- /* Add PEER-ADDRESS attribute */
+ /* Add XOR-PEER-ADDRESS attribute */
pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg,
- PJ_STUN_ATTR_PEER_ADDR, PJ_TRUE,
+ PJ_STUN_ATTR_XOR_PEER_ADDR, PJ_TRUE,
peer_adr, addr_len);
/* Send the request, associate peer data structure with tdata
* for future reference when we receive the ChannelBind response.
*/
- status = pj_stun_session_send_msg(sess->stun, peer, PJ_FALSE,
+ status = pj_stun_session_send_msg(sess->stun, ch, PJ_FALSE,
(sess->conn_type==PJ_TURN_TP_UDP),
sess->srv_addr,
pj_sockaddr_get_len(sess->srv_addr),
@@ -949,7 +1129,7 @@ PJ_DEF(pj_status_t) pj_turn_session_on_rx_pkt(pj_turn_session *sess,
} else {
/* This must be ChannelData. */
pj_turn_channel_data cd;
- struct peer *peer;
+ struct ch_t *ch;
if (pkt_len < 4) {
if (parsed_len) *parsed_len = 0;
@@ -980,9 +1160,9 @@ PJ_DEF(pj_status_t) pj_turn_session_on_rx_pkt(pj_turn_session *sess,
}
}
- /* Lookup peer */
- peer = lookup_peer_by_chnum(sess, cd.ch_number);
- if (!peer || !peer->bound) {
+ /* Lookup channel */
+ ch = lookup_ch_by_chnum(sess, cd.ch_number);
+ if (!ch || !ch->bound) {
status = PJ_ENOTFOUND;
goto on_return;
}
@@ -990,8 +1170,8 @@ PJ_DEF(pj_status_t) pj_turn_session_on_rx_pkt(pj_turn_session *sess,
/* Notify application */
if (sess->cb.on_rx_data) {
(*sess->cb.on_rx_data)(sess, ((pj_uint8_t*)pkt)+sizeof(cd),
- cd.length, &peer->addr,
- pj_sockaddr_get_len(&peer->addr));
+ cd.length, &ch->addr,
+ pj_sockaddr_get_len(&ch->addr));
}
status = PJ_SUCCESS;
@@ -1089,7 +1269,7 @@ static void on_allocate_success(pj_turn_session *sess,
const pj_stun_msg *msg)
{
const pj_stun_lifetime_attr *lf_attr;
- const pj_stun_relayed_addr_attr *raddr_attr;
+ const pj_stun_xor_relayed_addr_attr *raddr_attr;
const pj_stun_sockaddr_attr *mapped_attr;
pj_str_t s;
pj_time_val timeout;
@@ -1137,8 +1317,8 @@ static void on_allocate_success(pj_turn_session *sess,
/* Check that relayed transport address contains correct
* address family.
*/
- raddr_attr = (const pj_stun_relayed_addr_attr*)
- pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_RELAYED_ADDR, 0);
+ raddr_attr = (const pj_stun_xor_relayed_addr_attr*)
+ pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_XOR_RELAYED_ADDR, 0);
if (raddr_attr == NULL && method==PJ_STUN_ALLOCATE_METHOD) {
on_session_fail(sess, method, PJNATH_EINSTUNMSG,
pj_cstr(&s, "Error: Received ALLOCATE without "
@@ -1301,15 +1481,15 @@ static void stun_on_request_complete(pj_stun_session *stun,
PJ_STUN_IS_SUCCESS_RESPONSE(response->hdr.type))
{
/* Successful ChannelBind response */
- struct peer *peer = (struct peer*)token;
+ struct ch_t *ch = (struct ch_t*)token;
- pj_assert(peer->ch_id != PJ_TURN_INVALID_CHANNEL);
- peer->bound = PJ_TRUE;
+ pj_assert(ch->num != PJ_TURN_INVALID_CHANNEL);
+ ch->bound = PJ_TRUE;
/* Update hash table */
- lookup_peer_by_addr(sess, &peer->addr,
- pj_sockaddr_get_len(&peer->addr),
- PJ_TRUE, PJ_TRUE);
+ lookup_ch_by_addr(sess, &ch->addr,
+ pj_sockaddr_get_len(&ch->addr),
+ PJ_TRUE, PJ_TRUE);
} else {
/* Failed ChannelBind response */
@@ -1332,6 +1512,58 @@ static void stun_on_request_complete(pj_stun_session *stun,
(int)err_msg.slen, err_msg.ptr));
}
+ } else if (method == PJ_STUN_CREATE_PERM_METHOD) {
+ /* Handle CreatePermission response */
+ if (status==PJ_SUCCESS &&
+ PJ_STUN_IS_SUCCESS_RESPONSE(response->hdr.type))
+ {
+ /* No special handling when the request is successful. */
+ } else {
+ /* Iterate the permission table and invalidate all permissions
+ * that are related to this request.
+ */
+ pj_hash_iterator_t it_buf, *it;
+ char ipstr[PJ_INET6_ADDRSTRLEN+10];
+ int err_code;
+ char errbuf[PJ_ERR_MSG_SIZE];
+ pj_str_t reason;
+
+ if (status != PJ_SUCCESS) {
+ err_code = status;
+ reason = pj_strerror(status, errbuf, sizeof(errbuf));
+ } else {
+ const pj_stun_errcode_attr *eattr;
+
+ eattr = (const pj_stun_errcode_attr*)
+ pj_stun_msg_find_attr(response,
+ PJ_STUN_ATTR_ERROR_CODE, 0);
+ if (eattr) {
+ err_code = eattr->err_code;
+ reason = eattr->reason;
+ } else {
+ err_code = -1;
+ reason = pj_str("?");
+ }
+ }
+
+ it = pj_hash_first(sess->perm_table, &it_buf);
+ while (it) {
+ struct perm_t *perm = (struct perm_t*)
+ pj_hash_this(sess->perm_table, it);
+ it = pj_hash_next(sess->perm_table, it);
+
+ if (perm->req_token == token) {
+ PJ_LOG(1,(sess->obj_name,
+ "CreatePermission failed for IP %s: %d/%.*s",
+ pj_sockaddr_print(&perm->addr, ipstr,
+ sizeof(ipstr), 2),
+ err_code, (int)reason.slen, reason.ptr));
+
+ invalidate_perm(sess, perm);
+ }
+ }
+ }
+
} else {
PJ_LOG(4,(sess->obj_name, "Unexpected STUN %s response",
pj_stun_get_method_name(response->hdr.type)));
@@ -1352,7 +1584,7 @@ static pj_status_t stun_on_rx_indication(pj_stun_session *stun,
unsigned src_addr_len)
{
pj_turn_session *sess;
- pj_stun_peer_addr_attr *peer_attr;
+ pj_stun_xor_peer_addr_attr *peer_attr;
pj_stun_icmp_attr *icmp;
pj_stun_data_attr *data_attr;
@@ -1379,15 +1611,15 @@ static pj_status_t stun_on_rx_indication(pj_stun_session *stun,
return PJ_SUCCESS;
}
- /* Get PEER-ADDRESS attribute */
- peer_attr = (pj_stun_peer_addr_attr*)
- pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_PEER_ADDR, 0);
+ /* Get XOR-PEER-ADDRESS attribute */
+ peer_attr = (pj_stun_xor_peer_addr_attr*)
+ pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_XOR_PEER_ADDR, 0);
/* Get DATA attribute */
data_attr = (pj_stun_data_attr*)
pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_DATA, 0);
- /* Must have both PEER-ADDRESS and DATA attributes */
+ /* Must have both XOR-PEER-ADDRESS and DATA attributes */
if (!peer_attr || !data_attr) {
PJ_LOG(4,(sess->obj_name,
"Received Data indication with missing attributes"));
@@ -1472,63 +1704,110 @@ static void dns_srv_resolver_cb(void *user_data,
/*
* Lookup peer descriptor from its address.
*/
-static struct peer *lookup_peer_by_addr(pj_turn_session *sess,
- const pj_sockaddr_t *addr,
- unsigned addr_len,
- pj_bool_t update,
- pj_bool_t bind_channel)
+static struct ch_t *lookup_ch_by_addr(pj_turn_session *sess,
+ const pj_sockaddr_t *addr,
+ unsigned addr_len,
+ pj_bool_t update,
+ pj_bool_t bind_channel)
{
- unsigned hval = 0;
- struct peer *peer;
+ pj_uint32_t hval = 0;
+ struct ch_t *ch;
- peer = (struct peer*) pj_hash_get(sess->peer_table, addr, addr_len, &hval);
- if (peer == NULL && update) {
- peer = PJ_POOL_ZALLOC_T(sess->pool, struct peer);
- peer->ch_id = PJ_TURN_INVALID_CHANNEL;
- pj_memcpy(&peer->addr, addr, addr_len);
+ ch = (struct ch_t*)
+ pj_hash_get(sess->ch_table, addr, addr_len, &hval);
+ if (ch == NULL && update) {
+ ch = PJ_POOL_ZALLOC_T(sess->pool, struct ch_t);
+ ch->num = PJ_TURN_INVALID_CHANNEL;
+ pj_memcpy(&ch->addr, addr, addr_len);
/* Register by peer address */
- pj_hash_set(sess->pool, sess->peer_table, &peer->addr, addr_len,
- hval, peer);
+ pj_hash_set(sess->pool, sess->ch_table, &ch->addr, addr_len,
+ hval, ch);
}
- if (peer && update) {
- pj_gettimeofday(&peer->expiry);
- if (peer->bound) {
- peer->expiry.sec += PJ_TURN_CHANNEL_TIMEOUT - 10;
- } else {
- peer->expiry.sec += PJ_TURN_PERM_TIMEOUT - 10;
- }
+ if (ch && update) {
+ pj_gettimeofday(&ch->expiry);
+ ch->expiry.sec += PJ_TURN_PERM_TIMEOUT - PJ_TURN_RENEWAL_BEFORE;
if (bind_channel) {
pj_uint32_t hval = 0;
/* Register by channel number */
- pj_assert(peer->ch_id != PJ_TURN_INVALID_CHANNEL && peer->bound);
+ pj_assert(ch->num != PJ_TURN_INVALID_CHANNEL && ch->bound);
- if (pj_hash_get(sess->peer_table, &peer->ch_id,
- sizeof(peer->ch_id), &hval)==0) {
- pj_hash_set(sess->pool, sess->peer_table, &peer->ch_id,
- sizeof(peer->ch_id), hval, peer);
+ if (pj_hash_get(sess->ch_table, &ch->num,
+ sizeof(ch->num), &hval)==0) {
+ pj_hash_set(sess->pool, sess->ch_table, &ch->num,
+ sizeof(ch->num), hval, ch);
}
}
}
- return peer;
+ return ch;
}
/*
- * Lookup peer descriptor from its channel number.
+ * Lookup channel descriptor from its channel number.
*/
-static struct peer *lookup_peer_by_chnum(pj_turn_session *sess,
+static struct ch_t *lookup_ch_by_chnum(pj_turn_session *sess,
pj_uint16_t chnum)
{
- return (struct peer*) pj_hash_get(sess->peer_table, &chnum,
+ return (struct ch_t*) pj_hash_get(sess->ch_table, &chnum,
sizeof(chnum), NULL);
}
/*
+ * Lookup permission and optionally create if it doesn't exist.
+ */
+static struct perm_t *lookup_perm(pj_turn_session *sess,
+ const pj_sockaddr_t *addr,
+ unsigned addr_len,
+ pj_bool_t update)
+{
+ pj_uint32_t hval = 0;
+ pj_sockaddr perm_addr;
+ struct perm_t *perm;
+
+ /* make sure port number if zero */
+ if (pj_sockaddr_get_port(addr) != 0) {
+ pj_memcpy(&perm_addr, addr, addr_len);
+ pj_sockaddr_set_port(&perm_addr, 0);
+ addr = &perm_addr;
+ }
+
+ /* lookup and create if it doesn't exist and wanted */
+ perm = (struct perm_t*)
+ pj_hash_get(sess->perm_table, addr, addr_len, &hval);
+ if (perm == NULL && update) {
+ perm = PJ_POOL_ZALLOC_T(sess->pool, struct perm_t);
+ pj_memcpy(&perm->addr, addr, addr_len);
+ perm->hval = hval;
+
+ pj_hash_set(sess->pool, sess->perm_table, &perm->addr, addr_len,
+ perm->hval, perm);
+ }
+
+ if (perm && update) {
+ pj_gettimeofday(&perm->expiry);
+ perm->expiry.sec += PJ_TURN_PERM_TIMEOUT - PJ_TURN_RENEWAL_BEFORE;
+
+ }
+
+ return perm;
+}
+
+/*
+ * Delete permission
+ */
+static void invalidate_perm(pj_turn_session *sess,
+ struct perm_t *perm)
+{
+ pj_hash_set(NULL, sess->perm_table, &perm->addr,
+ pj_sockaddr_get_len(&perm->addr), perm->hval, NULL);
+}
+
+/*
* Timer event.
*/
static void on_timer_event(pj_timer_heap_t *th, pj_timer_entry *e)
@@ -1564,21 +1843,21 @@ static void on_timer_event(pj_timer_heap_t *th, pj_timer_entry *e)
}
/* Scan hash table to refresh bound channels */
- it = pj_hash_first(sess->peer_table, &itbuf);
+ it = pj_hash_first(sess->ch_table, &itbuf);
while (it) {
- struct peer *peer = (struct peer*)
- pj_hash_this(sess->peer_table, it);
- if (peer->bound && PJ_TIME_VAL_LTE(peer->expiry, now)) {
+ struct ch_t *ch = (struct ch_t*)
+ pj_hash_this(sess->ch_table, it);
+ if (ch->bound && PJ_TIME_VAL_LTE(ch->expiry, now)) {
/* Send ChannelBind to refresh channel binding and
* permission.
*/
- pj_turn_session_bind_channel(sess, &peer->addr,
- pj_sockaddr_get_len(&peer->addr));
+ pj_turn_session_bind_channel(sess, &ch->addr,
+ pj_sockaddr_get_len(&ch->addr));
pkt_sent = PJ_TRUE;
}
- it = pj_hash_next(sess->peer_table, it);
+ it = pj_hash_next(sess->ch_table, it);
}
/* If no packet is sent, send a blank Send indication to
diff --git a/pjnath/src/pjnath/turn_sock.c b/pjnath/src/pjnath/turn_sock.c
index 72e3e3bf..00bcfbac 100644
--- a/pjnath/src/pjnath/turn_sock.c
+++ b/pjnath/src/pjnath/turn_sock.c
@@ -387,6 +387,20 @@ PJ_DEF(pj_status_t) pj_turn_sock_alloc(pj_turn_sock *turn_sock,
}
/*
+ * Install permission
+ */
+PJ_DEF(pj_status_t) pj_turn_sock_set_perm( pj_turn_sock *turn_sock,
+ unsigned addr_cnt,
+ const pj_sockaddr addr[],
+ unsigned options)
+{
+ if (turn_sock->sess == NULL)
+ return PJ_EINVALIDOP;
+
+ return pj_turn_session_set_perm(turn_sock->sess, addr_cnt, addr, options);
+}
+
+/*
* Send packet.
*/
PJ_DEF(pj_status_t) pj_turn_sock_sendto( pj_turn_sock *turn_sock,
diff --git a/pjnath/src/pjturn-client/client_main.c b/pjnath/src/pjturn-client/client_main.c
index 36de03f5..0fbe85dd 100644
--- a/pjnath/src/pjturn-client/client_main.c
+++ b/pjnath/src/pjturn-client/client_main.c
@@ -138,6 +138,7 @@ static int init()
pj_stun_sock_cb stun_sock_cb;
char name[] = "peer0";
pj_uint16_t port;
+ pj_stun_sock_cfg ss_cfg;
pj_str_t server;
pj_bzero(&stun_sock_cb, sizeof(stun_sock_cb));
@@ -146,9 +147,15 @@ static int init()
g.peer[i].mapped_addr.addr.sa_family = pj_AF_INET();
+ pj_stun_sock_cfg_default(&ss_cfg);
+#if 1
+ /* make reading the log easier */
+ ss_cfg.ka_interval = 300;
+#endif
+
name[strlen(name)-1] = '0'+i;
status = pj_stun_sock_create(&g.stun_config, name, pj_AF_INET(),
- &stun_sock_cb, NULL,
+ &stun_sock_cb, &ss_cfg,
&g.peer[i], &g.peer[i].stun_sock);
if (status != PJ_SUCCESS) {
my_perror("pj_stun_sock_create()", status);
@@ -415,22 +422,23 @@ static void menu(void)
puts("\n");
- puts("+====================================================================+");
- puts("| CLIENT | PEER-0 |");
- puts("| | |");
- printf("| State : %-12s | Address: %-21s |\n",
+ puts("+=====================================================================+");
+ puts("| CLIENT | PEER-0 |");
+ puts("| | |");
+ printf("| State : %-12s | Address: %-21s |\n",
client_state, peer0_addr);
- printf("| Relay addr: %-21s | |\n",
+ printf("| Relay addr: %-21s | |\n",
relay_addr);
- puts("| | 0 Send data to relay address |");
- puts("| a Allocate relay +--------------------------------+ ");
- puts("| s,ss Send data to peer 0/1 | PEER-1 |");
- puts("| b,bb BindChannel to peer 0/1 | |");
- printf("| x Delete allocation | Address: %-21s |\n",
+ puts("| | 0 Send data to relay address |");
+ puts("| a Allocate relay | |");
+ puts("| p,pp Set permission for peer 0/1 +--------------------------------+");
+ puts("| s,ss Send data to peer 0/1 | PEER-1 |");
+ puts("| b,bb BindChannel to peer 0/1 | |");
+ printf("| x Delete allocation | Address: %-21s |\n",
peer1_addr);
- puts("+-----------------------------------+ |");
- puts("| q Quit d Dump | 1 Send data to relay adderss |");
- puts("+-----------------------------------+--------------------------------+");
+ puts("+------------------------------------+ |");
+ puts("| q Quit d Dump | 1 Send data to relay adderss |");
+ puts("+------------------------------------+--------------------------------+");
printf(">>> ");
fflush(stdout);
}
@@ -488,6 +496,20 @@ static void console_main(void)
if (status != PJ_SUCCESS)
my_perror("turn_udp_bind_channel() failed", status);
break;
+ case 'p':
+ if (g.relay == NULL) {
+ puts("Error: no relay");
+ continue;
+ }
+ if (input[1]!='p')
+ peer = &g.peer[0];
+ else
+ peer = &g.peer[1];
+
+ status = pj_turn_sock_set_perm(g.relay, 1, &peer->mapped_addr, 1);
+ if (status != PJ_SUCCESS)
+ my_perror("pj_turn_sock_set_perm() failed", status);
+ break;
case 'x':
if (g.relay == NULL) {
puts("Error: no relay");
diff --git a/pjnath/src/pjturn-srv/allocation.c b/pjnath/src/pjturn-srv/allocation.c
index 6fb606aa..930a3532 100644
--- a/pjnath/src/pjturn-srv/allocation.c
+++ b/pjnath/src/pjturn-srv/allocation.c
@@ -109,7 +109,6 @@ static pj_status_t parse_allocate_req(alloc_request *cfg,
pj_stun_bandwidth_attr *attr_bw;
pj_stun_req_transport_attr *attr_req_tp;
pj_stun_res_token_attr *attr_res_token;
- pj_stun_req_props_attr *attr_rpp;
pj_stun_lifetime_attr *attr_lifetime;
pj_bzero(cfg, sizeof(*cfg));
@@ -164,18 +163,6 @@ static pj_status_t parse_allocate_req(alloc_request *cfg,
return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_BAD_REQUEST);
}
- /* Get REQUESTED-PROPS attribute, if any */
- attr_rpp = (pj_stun_req_props_attr*)
- pj_stun_msg_find_attr(req, PJ_STUN_ATTR_REQ_PROPS, 0);
- if (attr_rpp) {
- /* We don't support REQUESTED-PROPS for now */
- pj_stun_session_respond(sess, rdata,
- PJ_STUN_SC_BAD_REQUEST,
- "REQUESTED-PROPS is not supported",
- NULL, PJ_TRUE, src_addr, src_addr_len);
- return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_BAD_REQUEST);
- }
-
/* Get LIFETIME attribute */
attr_lifetime = (pj_stun_uint_attr*)
pj_stun_msg_find_attr(req, PJ_STUN_ATTR_LIFETIME, 0);
@@ -211,9 +198,9 @@ static pj_status_t send_allocate_response(pj_turn_allocation *alloc,
if (status != PJ_SUCCESS)
return status;
- /* Add RELAYED-ADDRESS attribute */
+ /* Add XOR-RELAYED-ADDRESS attribute */
pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg,
- PJ_STUN_ATTR_RELAYED_ADDR, PJ_TRUE,
+ PJ_STUN_ATTR_XOR_RELAYED_ADDR, PJ_TRUE,
&alloc->relay.hkey.addr,
pj_sockaddr_get_len(&alloc->relay.hkey.addr));
@@ -1070,7 +1057,7 @@ static void handle_peer_pkt(pj_turn_allocation *alloc,
}
pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg,
- PJ_STUN_ATTR_PEER_ADDR, PJ_TRUE,
+ PJ_STUN_ATTR_XOR_PEER_ADDR, PJ_TRUE,
src_addr, pj_sockaddr_get_len(src_addr));
pj_stun_msg_add_binary_attr(tdata->pool, tdata->msg,
PJ_STUN_ATTR_DATA,
@@ -1230,13 +1217,13 @@ static pj_status_t stun_on_rx_request(pj_stun_session *sess,
* ChannelBind request.
*/
pj_stun_channel_number_attr *ch_attr;
- pj_stun_peer_addr_attr *peer_attr;
+ pj_stun_xor_peer_addr_attr *peer_attr;
pj_turn_permission *p1, *p2;
ch_attr = (pj_stun_channel_number_attr*)
pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_CHANNEL_NUMBER, 0);
- peer_attr = (pj_stun_peer_addr_attr*)
- pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_PEER_ADDR, 0);
+ peer_attr = (pj_stun_xor_peer_addr_attr*)
+ pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_XOR_PEER_ADDR, 0);
if (!ch_attr || !peer_attr) {
send_reply_err(alloc, rdata, PJ_TRUE,
@@ -1333,7 +1320,7 @@ static pj_status_t stun_on_rx_indication(pj_stun_session *sess,
const pj_sockaddr_t *src_addr,
unsigned src_addr_len)
{
- pj_stun_peer_addr_attr *peer_attr;
+ pj_stun_xor_peer_addr_attr *peer_attr;
pj_stun_data_attr *data_attr;
pj_turn_allocation *alloc;
pj_turn_permission *perm;
@@ -1353,11 +1340,11 @@ static pj_status_t stun_on_rx_indication(pj_stun_session *sess,
return PJ_SUCCESS;
}
- /* Get PEER-ADDRESS attribute */
- peer_attr = (pj_stun_peer_addr_attr*)
- pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_PEER_ADDR, 0);
+ /* Get XOR-PEER-ADDRESS attribute */
+ peer_attr = (pj_stun_xor_peer_addr_attr*)
+ pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_XOR_PEER_ADDR, 0);
- /* MUST have PEER-ADDRESS attribute */
+ /* MUST have XOR-PEER-ADDRESS attribute */
if (!peer_attr)
return PJ_SUCCESS;