From 0efc01a54a538cb2ecaf03dcc77e12d78b346c39 Mon Sep 17 00:00:00 2001 From: Benny Prijono Date: Mon, 13 Apr 2009 08:54:10 +0000 Subject: 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 --- pjnath/include/pjnath/stun_msg.h | 137 +++++----- pjnath/include/pjnath/stun_session.h | 12 +- pjnath/include/pjnath/turn_session.h | 39 +++ pjnath/include/pjnath/turn_sock.h | 23 ++ pjnath/include/pjnath/types.h | 12 +- pjnath/src/pjnath-test/server.c | 4 +- pjnath/src/pjnath/stun_msg.c | 28 +- pjnath/src/pjnath/stun_msg_dump.c | 7 +- pjnath/src/pjnath/stun_session.c | 19 +- pjnath/src/pjnath/turn_session.c | 469 ++++++++++++++++++++++++++------- pjnath/src/pjnath/turn_sock.c | 14 + pjnath/src/pjturn-client/client_main.c | 50 +++- pjnath/src/pjturn-srv/allocation.c | 35 +-- 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 @@ -82,6 +82,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 */ @@ -253,6 +258,22 @@ typedef enum pj_stun_msg_type PJ_STUN_DATA_INDICATION = 0x0017, + /** + * 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 */ @@ -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 @@ -436,6 +436,21 @@ PJ_DECL(void) pj_turn_session_set_log(pj_turn_session *sess, unsigned flags); +/** + * 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 @@ -518,6 +533,30 @@ PJ_DECL(pj_status_t) pj_turn_session_alloc(pj_turn_session *sess, 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 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 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 @@ -247,6 +247,29 @@ PJ_DECL(pj_status_t) pj_turn_sock_alloc(pj_turn_sock *turn_sock, const pj_stun_auth_cred *cred, 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 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: - - - draft-ietf-behave-turn-09: Obtaining Relay Addresses +Features: + - + draft-ietf-behave-turn-13: 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 #include #include +#include #include #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. */ @@ -703,6 +773,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; irenew = (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 */ @@ -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,62 +1704,109 @@ 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. */ @@ -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 @@ -386,6 +386,20 @@ PJ_DEF(pj_status_t) pj_turn_sock_alloc(pj_turn_sock *turn_sock, return PJ_SUCCESS; } +/* + * 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. */ 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; -- cgit v1.2.3