diff options
Diffstat (limited to 'pjlib-util')
-rw-r--r-- | pjlib-util/include/pjlib-util/stun_msg.h | 57 | ||||
-rw-r--r-- | pjlib-util/include/pjlib-util/stun_session.h | 98 | ||||
-rw-r--r-- | pjlib-util/src/pjlib-util/stun_auth.c | 18 | ||||
-rw-r--r-- | pjlib-util/src/pjlib-util/stun_msg.c | 96 | ||||
-rw-r--r-- | pjlib-util/src/pjlib-util/stun_msg_dump.c | 2 | ||||
-rw-r--r-- | pjlib-util/src/pjlib-util/stun_session.c | 81 | ||||
-rw-r--r-- | pjlib-util/src/pjstun-client/client_main.c | 75 | ||||
-rw-r--r-- | pjlib-util/src/pjstun-srv-test/turn_usage.c | 977 |
8 files changed, 992 insertions, 412 deletions
diff --git a/pjlib-util/include/pjlib-util/stun_msg.h b/pjlib-util/include/pjlib-util/stun_msg.h index 7a38c9d4..b9b9275d 100644 --- a/pjlib-util/include/pjlib-util/stun_msg.h +++ b/pjlib-util/include/pjlib-util/stun_msg.h @@ -334,7 +334,7 @@ typedef enum pj_stun_attr_type PJ_STUN_ATTR_REFLECTED_FROM = 0x000B,/**< REFLECTED-FROM (deprecatd)*/ PJ_STUN_ATTR_LIFETIME = 0x000D,/**< LIFETIME attribute. */ PJ_STUN_ATTR_BANDWIDTH = 0x0010,/**< BANDWIDTH attribute */ - PJ_STUN_ATTR_REMOTE_ADDRESS = 0x0012,/**< REMOTE-ADDRESS attribute */ + PJ_STUN_ATTR_REMOTE_ADDR = 0x0012,/**< REMOTE-ADDRESS attribute */ PJ_STUN_ATTR_DATA = 0x0013,/**< DATA attribute. */ PJ_STUN_ATTR_REALM = 0x0014,/**< REALM attribute. */ PJ_STUN_ATTR_NONCE = 0x0015,/**< NONCE attribute. */ @@ -354,10 +354,10 @@ typedef enum pj_stun_attr_type PJ_STUN_ATTR_START_EXTENDED_ATTR= 0x8021, - PJ_STUN_ATTR_FINGERPRINT = 0x8021,/**< FINGERPRINT attribute. */ PJ_STUN_ATTR_SERVER = 0x8022,/**< SERVER attribute. */ PJ_STUN_ATTR_ALTERNATE_SERVER = 0x8023,/**< ALTERNATE-SERVER. */ PJ_STUN_ATTR_REFRESH_INTERVAL = 0x8024,/**< REFRESH-INTERVAL. */ + PJ_STUN_ATTR_FINGERPRINT = 0x8028,/**< FINGERPRINT attribute. */ PJ_STUN_ATTR_END_EXTENDED_ATTR @@ -369,31 +369,34 @@ typedef enum pj_stun_attr_type */ typedef enum pj_stun_status { - PJ_STUN_STATUS_TRY_ALTERNATE = 300, /**< Try Alternate */ - PJ_STUN_STATUS_BAD_REQUEST = 400, /**< Bad Request */ - PJ_STUN_STATUS_UNAUTHORIZED = 401, /**< Unauthorized */ - PJ_STUN_STATUS_UNKNOWN_ATTRIBUTE = 420, /**< Unknown Attribute */ - PJ_STUN_STATUS_STALE_CREDENTIALS = 430, /**< Stale Credentials */ - PJ_STUN_STATUS_INTEGRITY_CHECK_FAILURE = 431, /**< Integrity Chk Fail */ - PJ_STUN_STATUS_MISSING_USERNAME = 432, /**< Missing Username */ - PJ_STUN_STATUS_USE_TLS = 433, /**< Use TLS */ - PJ_STUN_STATUS_MISSING_REALM = 434, /**< Missing Realm */ - PJ_STUN_STATUS_MISSING_NONCE = 435, /**< Missing Nonce */ - PJ_STUN_STATUS_UNKNOWN_USERNAME = 436, /**< Unknown Username */ - PJ_STUN_STATUS_NO_BINDING = 437, /**< No Binding. */ - PJ_STUN_STATUS_STALE_NONCE = 438, /**< Stale Nonce */ - PJ_STUN_STATUS_TRANSITIONING = 439, /**< Transitioning. */ - PJ_STUN_STATUS_WRONG_USERNAME = 441, /**< Wrong Username. */ - PJ_STUN_STATUS_UNSUPP_TRANSPORT_PROTO = 442, /**< Unsupported Transport Protocol */ - PJ_STUN_STATUS_INVALID_IP_ADDR = 443, /**< Invalid IP Address */ - PJ_STUN_STATUS_INVALID_PORT = 444, /**< Invalid Port */ - PJ_STUN_STATUS_OPER_TCP_ONLY = 445, /**< Operation for TCP Only */ - PJ_STUN_STATUS_CONNECTION_FAILURE = 446, /**< Connection Failure */ - PJ_STUN_STATUS_CONNECTION_TIMEOUT = 447, /**< Connection Timeout */ - PJ_STUN_STATUS_ALLOCATION_QUOTA_REACHED = 486, /**< Allocation Quota Reached (TURN) */ - PJ_STUN_STATUS_SERVER_ERROR = 500, /**< Server Error */ - PJ_STUN_STATUS_INSUFFICIENT_CAPACITY = 507, /**< Insufficient Capacity (TURN) */ - PJ_STUN_STATUS_GLOBAL_FAILURE = 600 /**< Global Failure */ + 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_UNKNOWN_ATTRIBUTE = 420, /**< Unknown Attribute */ + PJ_STUN_SC_STALE_CREDENTIALS = 430, /**< Stale Credentials */ + PJ_STUN_SC_INTEGRITY_CHECK_FAILURE = 431, /**< Integrity Chk Fail */ + PJ_STUN_SC_MISSING_USERNAME = 432, /**< Missing Username */ + PJ_STUN_SC_USE_TLS = 433, /**< Use TLS */ + PJ_STUN_SC_MISSING_REALM = 434, /**< Missing Realm */ + PJ_STUN_SC_MISSING_NONCE = 435, /**< Missing Nonce */ + PJ_STUN_SC_UNKNOWN_USERNAME = 436, /**< Unknown Username */ + PJ_STUN_SC_NO_BINDING = 437, /**< No Binding. */ + PJ_STUN_SC_STALE_NONCE = 438, /**< Stale Nonce */ + PJ_STUN_SC_TRANSITIONING = 439, /**< Transitioning. */ + PJ_STUN_SC_WRONG_USERNAME = 441, /**< Wrong Username. */ + PJ_STUN_SC_UNSUPP_TRANSPORT_PROTO = 442, /**< Unsupported Transport or + Protocol */ + PJ_STUN_SC_INVALID_IP_ADDR = 443, /**< Invalid IP Address */ + PJ_STUN_SC_INVALID_PORT = 444, /**< Invalid Port */ + 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 */ + PJ_STUN_SC_ALLOCATION_QUOTA_REACHED = 486, /**< Allocation Quota Reached + (TURN) */ + PJ_STUN_SC_SERVER_ERROR = 500, /**< Server Error */ + PJ_STUN_SC_INSUFFICIENT_CAPACITY = 507, /**< Insufficient Capacity + (TURN) */ + PJ_STUN_SC_GLOBAL_FAILURE = 600 /**< Global Failure */ } pj_stun_status; diff --git a/pjlib-util/include/pjlib-util/stun_session.h b/pjlib-util/include/pjlib-util/stun_session.h index 8fe7b1ca..81fc5640 100644 --- a/pjlib-util/include/pjlib-util/stun_session.h +++ b/pjlib-util/include/pjlib-util/stun_session.h @@ -138,7 +138,6 @@ struct pj_stun_tx_data pj_pool_t *pool; /**< Pool. */ pj_stun_session *sess; /**< The STUN session. */ pj_stun_msg *msg; /**< The STUN message. */ - void *user_data; /**< Arbitrary user data. */ pj_stun_client_tsx *client_tsx; /**< Client STUN transaction. */ pj_uint32_t msg_magic; /**< Message magic. */ @@ -232,104 +231,39 @@ PJ_DECL(void) pj_stun_session_set_credential(pj_stun_session *sess, const pj_stun_auth_cred *cred); /** - * Create a STUN Bind request message. After the message has been - * successfully created, application can send the message by calling + * Create a STUN request message. After the message has been successfully + * created, application can send the message by calling * pj_stun_session_send_msg(). * * @param sess The STUN session instance. + * @param msg_type The STUN request message type, from pj_stun_method_e or + * from pj_stun_msg_type. * @param p_tdata Pointer to receive STUN transmit data instance containing * the request. * * @return PJ_SUCCESS on success, or the appropriate error code. */ -PJ_DECL(pj_status_t) pj_stun_session_create_bind_req(pj_stun_session *sess, - pj_stun_tx_data **p_tdata); +PJ_DECL(pj_status_t) pj_stun_session_create_req(pj_stun_session *sess, + int msg_type, + pj_stun_tx_data **p_tdata); /** - * Create a STUN Allocate request message. After the message has been - * successfully created, application can send the message by calling - * pj_stun_session_send_msg(). - * - * @param sess The STUN session instance. - * @param p_tdata Pointer to receive STUN transmit data instance containing - * the request. - * - * @return PJ_SUCCESS on success, or the appropriate error code. - */ -PJ_DECL(pj_status_t) pj_stun_session_create_allocate_req(pj_stun_session *sess, - pj_stun_tx_data **p_tdata); - -/** - * Create a STUN Set Active Destination request message. After the message - * has been successfully created, application can send the message by calling - * pj_stun_session_send_msg(). - * - * @param sess The STUN session instance. - * @param p_tdata Pointer to receive STUN transmit data instance containing - * the request. - * - * @return PJ_SUCCESS on success, or the appropriate error code. - */ -PJ_DECL(pj_status_t) -pj_stun_session_create_set_active_destination_req(pj_stun_session *sess, - pj_stun_tx_data **p_tdata); - -/** - * Create a STUN Connect request message. After the message has been - * successfully created, application can send the message by calling - * pj_stun_session_send_msg(). - * - * @param sess The STUN session instance. - * @param p_tdata Pointer to receive STUN transmit data instance containing - * the request. - * - * @return PJ_SUCCESS on success, or the appropriate error code. - */ -PJ_DECL(pj_status_t) pj_stun_session_create_connect_req(pj_stun_session *sess, - pj_stun_tx_data **p_tdata); - -/** - * Create a STUN Connection Status Indication message. After the message - * has been successfully created, application can send the message by calling - * pj_stun_session_send_msg(). - * - * @param sess The STUN session instance. - * @param p_tdata Pointer to receive STUN transmit data instance containing - * the message. - * - * @return PJ_SUCCESS on success, or the appropriate error code. - */ -PJ_DECL(pj_status_t) -pj_stun_session_create_connection_status_ind(pj_stun_session *sess, - pj_stun_tx_data **p_tdata); - -/** - * Create a STUN Send Indication message. After the message has been - * successfully created, application can send the message by calling - * pj_stun_session_send_msg(). - * - * @param sess The STUN session instance. - * @param p_tdata Pointer to receive STUN transmit data instance containing - * the message. - * - * @return PJ_SUCCESS on success, or the appropriate error code. - */ -PJ_DECL(pj_status_t) pj_stun_session_create_send_ind(pj_stun_session *sess, - pj_stun_tx_data **p_tdata); - -/** - * Create a STUN Data Indication message. After the message has been - * successfully created, application can send the message by calling + * Create a STUN Indication message. After the message has been successfully + * created, application can send the message by calling * pj_stun_session_send_msg(). * * @param sess The STUN session instance. + * @param msg_type The STUN request message type, from pj_stun_method_e or + * from pj_stun_msg_type. This function will add the + * indication bit as necessary. * @param p_tdata Pointer to receive STUN transmit data instance containing * the message. * * @return PJ_SUCCESS on success, or the appropriate error code. */ -PJ_DECL(pj_status_t) pj_stun_session_create_data_ind(pj_stun_session *sess, - pj_stun_tx_data **p_tdata); +PJ_DECL(pj_status_t) pj_stun_session_create_ind(pj_stun_session *sess, + int msg_type, + pj_stun_tx_data **p_tdata); /** * Create a STUN response message. After the message has been @@ -399,7 +333,7 @@ PJ_DECL(pj_status_t) pj_stun_session_send_msg(pj_stun_session *sess, * @param sess The STUN session instance. * @param packet The packet containing STUN message. * @param pkt_size Size of the packet. - * @param options Options, from pj_stun_options. + * @param options Options, from #pj_stun_decode_options. * @param parsed_len Optional pointer to receive the size of the parsed * STUN message (useful if packet is received via a * stream oriented protocol). diff --git a/pjlib-util/src/pjlib-util/stun_auth.c b/pjlib-util/src/pjlib-util/stun_auth.c index abf61b6e..9a94fe0d 100644 --- a/pjlib-util/src/pjlib-util/stun_auth.c +++ b/pjlib-util/src/pjlib-util/stun_auth.c @@ -156,7 +156,7 @@ PJ_DEF(pj_status_t) pj_stun_verify_credential( const pj_uint8_t *pkt, pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_MESSAGE_INTEGRITY, 0); if (amsgi == NULL) { if (p_response) { - create_challenge(pool, msg, PJ_STUN_STATUS_UNAUTHORIZED, NULL, + create_challenge(pool, msg, PJ_STUN_SC_UNAUTHORIZED, NULL, &realm, &nonce, p_response); } return PJLIB_UTIL_ESTUNMSGINT; @@ -167,7 +167,7 @@ PJ_DEF(pj_status_t) pj_stun_verify_credential( const pj_uint8_t *pkt, pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_USERNAME, 0); if (auser == NULL) { if (p_response) { - create_challenge(pool, msg, PJ_STUN_STATUS_MISSING_USERNAME, NULL, + create_challenge(pool, msg, PJ_STUN_SC_MISSING_USERNAME, NULL, &realm, &nonce, p_response); } return PJLIB_UTIL_ESTUNNOUSERNAME; @@ -198,7 +198,7 @@ PJ_DEF(pj_status_t) pj_stun_verify_credential( const pj_uint8_t *pkt, if (!username_ok) { /* Username mismatch */ if (p_response) { - create_challenge(pool, msg, PJ_STUN_STATUS_UNKNOWN_USERNAME, NULL, + create_challenge(pool, msg, PJ_STUN_SC_UNKNOWN_USERNAME, NULL, &realm, &nonce, p_response); } return PJLIB_UTIL_ESTUNUSERNAME; @@ -213,7 +213,7 @@ PJ_DEF(pj_status_t) pj_stun_verify_credential( const pj_uint8_t *pkt, if (realm.slen != 0 && arealm == NULL) { /* Long term credential is required and REALM is not present */ if (p_response) { - create_challenge(pool, msg, PJ_STUN_STATUS_MISSING_REALM, NULL, + create_challenge(pool, msg, PJ_STUN_SC_MISSING_REALM, NULL, &realm, &nonce, p_response); } return PJLIB_UTIL_ESTUNNOREALM; @@ -224,7 +224,7 @@ PJ_DEF(pj_status_t) pj_stun_verify_credential( const pj_uint8_t *pkt, /* NONCE must be present. */ if (anonce == NULL) { if (p_response) { - create_challenge(pool, msg, PJ_STUN_STATUS_MISSING_NONCE, + create_challenge(pool, msg, PJ_STUN_SC_MISSING_NONCE, NULL, &realm, &nonce, p_response); } return PJLIB_UTIL_ESTUNNONCE; @@ -234,7 +234,7 @@ PJ_DEF(pj_status_t) pj_stun_verify_credential( const pj_uint8_t *pkt, if (pj_stricmp(&arealm->value, &realm)) { /* REALM doesn't match */ if (p_response) { - create_challenge(pool, msg, PJ_STUN_STATUS_MISSING_REALM, + create_challenge(pool, msg, PJ_STUN_SC_MISSING_REALM, NULL, &realm, &nonce, p_response); } return PJLIB_UTIL_ESTUNNOREALM; @@ -257,7 +257,7 @@ PJ_DEF(pj_status_t) pj_stun_verify_credential( const pj_uint8_t *pkt, /* Application MAY request NONCE to be supplied */ if (nonce.slen != 0) { if (p_response) { - create_challenge(pool, msg, PJ_STUN_STATUS_MISSING_NONCE, + create_challenge(pool, msg, PJ_STUN_SC_MISSING_NONCE, NULL, &realm, &nonce, p_response); } return PJLIB_UTIL_ESTUNNONCE; @@ -283,7 +283,7 @@ PJ_DEF(pj_status_t) pj_stun_verify_credential( const pj_uint8_t *pkt, if (!ok) { if (p_response) { - create_challenge(pool, msg, PJ_STUN_STATUS_STALE_NONCE, + create_challenge(pool, msg, PJ_STUN_SC_STALE_NONCE, NULL, &realm, &nonce, p_response); } return PJLIB_UTIL_ESTUNNONCE; @@ -328,7 +328,7 @@ PJ_DEF(pj_status_t) pj_stun_verify_credential( const pj_uint8_t *pkt, if (pj_memcmp(amsgi->hmac, digest, 20)) { /* HMAC value mismatch */ if (p_response) { - create_challenge(pool, msg, PJ_STUN_STATUS_INTEGRITY_CHECK_FAILURE, + create_challenge(pool, msg, PJ_STUN_SC_INTEGRITY_CHECK_FAILURE, NULL, &realm, &nonce, p_response); } return PJLIB_UTIL_ESTUNMSGINT; diff --git a/pjlib-util/src/pjlib-util/stun_msg.c b/pjlib-util/src/pjlib-util/stun_msg.c index 00158ab4..1887b009 100644 --- a/pjlib-util/src/pjlib-util/stun_msg.c +++ b/pjlib-util/src/pjlib-util/stun_msg.c @@ -50,31 +50,31 @@ static struct const char *err_msg; } stun_err_msg_map[] = { - { PJ_STUN_STATUS_TRY_ALTERNATE, "Try Alternate"}, - { PJ_STUN_STATUS_BAD_REQUEST, "Bad Request"}, - { PJ_STUN_STATUS_UNAUTHORIZED, "Unauthorized"}, - { PJ_STUN_STATUS_UNKNOWN_ATTRIBUTE, "Unknown Attribute"}, - { PJ_STUN_STATUS_STALE_CREDENTIALS, "Stale Credentials"}, - { PJ_STUN_STATUS_INTEGRITY_CHECK_FAILURE,"Integrity Check Failure"}, - { PJ_STUN_STATUS_MISSING_USERNAME, "Missing Username"}, - { PJ_STUN_STATUS_USE_TLS, "Use TLS"}, - { PJ_STUN_STATUS_MISSING_REALM, "Missing Realm"}, - { PJ_STUN_STATUS_MISSING_NONCE, "Missing Nonce"}, - { PJ_STUN_STATUS_UNKNOWN_USERNAME, "Unknown Username"}, - { PJ_STUN_STATUS_NO_BINDING, "No Binding"}, - { PJ_STUN_STATUS_STALE_NONCE, "Stale Nonce"}, - { PJ_STUN_STATUS_TRANSITIONING, "Transitioning"}, - { PJ_STUN_STATUS_WRONG_USERNAME, "Wrong Username"}, - { PJ_STUN_STATUS_UNSUPP_TRANSPORT_PROTO,"Unsupported Transport Protocol"}, - { PJ_STUN_STATUS_INVALID_IP_ADDR, "Invalid IP Address"}, - { PJ_STUN_STATUS_INVALID_PORT, "Invalid Port"}, - { PJ_STUN_STATUS_OPER_TCP_ONLY, "Operation for TCP Only"}, - { PJ_STUN_STATUS_CONNECTION_FAILURE, "Connection Failure"}, - { PJ_STUN_STATUS_CONNECTION_TIMEOUT, "Connection Timeout"}, - { PJ_STUN_STATUS_ALLOCATION_QUOTA_REACHED, "Allocation Quota Reached"}, - { PJ_STUN_STATUS_SERVER_ERROR, "Server Error"}, - { PJ_STUN_STATUS_INSUFFICIENT_CAPACITY, "Insufficient Capacity"}, - { PJ_STUN_STATUS_GLOBAL_FAILURE, "Global Failure"} + { PJ_STUN_SC_TRY_ALTERNATE, "Try Alternate"}, + { PJ_STUN_SC_BAD_REQUEST, "Bad Request"}, + { PJ_STUN_SC_UNAUTHORIZED, "Unauthorized"}, + { PJ_STUN_SC_UNKNOWN_ATTRIBUTE, "Unknown Attribute"}, + { PJ_STUN_SC_STALE_CREDENTIALS, "Stale Credentials"}, + { PJ_STUN_SC_INTEGRITY_CHECK_FAILURE, "Integrity Check Failure"}, + { PJ_STUN_SC_MISSING_USERNAME, "Missing Username"}, + { PJ_STUN_SC_USE_TLS, "Use TLS"}, + { PJ_STUN_SC_MISSING_REALM, "Missing Realm"}, + { PJ_STUN_SC_MISSING_NONCE, "Missing Nonce"}, + { PJ_STUN_SC_UNKNOWN_USERNAME, "Unknown Username"}, + { PJ_STUN_SC_NO_BINDING, "No Binding"}, + { PJ_STUN_SC_STALE_NONCE, "Stale Nonce"}, + { PJ_STUN_SC_TRANSITIONING, "Active Destination Already Set"}, + { PJ_STUN_SC_WRONG_USERNAME, "Wrong Username"}, + { 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"}, + { PJ_STUN_SC_ALLOCATION_QUOTA_REACHED, "Allocation Quota Reached"}, + { PJ_STUN_SC_SERVER_ERROR, "Server Error"}, + { PJ_STUN_SC_INSUFFICIENT_CAPACITY, "Insufficient Capacity"}, + { PJ_STUN_SC_GLOBAL_FAILURE, "Global Failure"} }; @@ -381,10 +381,10 @@ struct attr_desc mandatory_attr_desc[] = static struct attr_desc extended_attr_desc[] = { { - /* PJ_STUN_ATTR_FINGERPRINT, */ - "FINGERPRINT", - &decode_uint_attr, - &encode_uint_attr + /* ID 0x8021 is not assigned */ + NULL, + NULL, + NULL }, { /* PJ_STUN_ATTR_SERVER, */ @@ -404,6 +404,30 @@ static struct attr_desc extended_attr_desc[] = &decode_uint_attr, &encode_uint_attr }, + { + /* Unassigned, 0x8025 */ + NULL, + NULL, + NULL + }, + { + /* Padding, 0x8026 */ + NULL, + NULL, + NULL + }, + { + /* Padding, 0x8027 */ + NULL, + NULL, + NULL + }, + { + /* PJ_STUN_ATTR_FINGERPRINT, */ + "FINGERPRINT", + &decode_uint_attr, + &encode_uint_attr + } }; @@ -1649,7 +1673,7 @@ PJ_DEF(pj_status_t) pj_stun_msg_decode(pj_pool_t *pool, if (p_response) { pj_stun_msg_create_response(pool, msg, - PJ_STUN_STATUS_BAD_REQUEST, + PJ_STUN_SC_BAD_REQUEST, &err_msg, p_response); } return PJLIB_UTIL_ESTUNINATTRLEN; @@ -1670,7 +1694,7 @@ PJ_DEF(pj_status_t) pj_stun_msg_decode(pj_pool_t *pool, * if we don't understand the attribute. */ if (p_response) { - unsigned err_code = PJ_STUN_STATUS_UNKNOWN_ATTRIBUTE; + unsigned err_code = PJ_STUN_SC_UNKNOWN_ATTRIBUTE; status = pj_stun_msg_create_response(pool, msg, err_code, NULL, @@ -1705,7 +1729,7 @@ PJ_DEF(pj_status_t) pj_stun_msg_decode(pj_pool_t *pool, pj_stun_get_attr_name(attr_type)); pj_stun_msg_create_response(pool, msg, - PJ_STUN_STATUS_BAD_REQUEST, + PJ_STUN_SC_BAD_REQUEST, &e, p_response); } @@ -1726,7 +1750,7 @@ PJ_DEF(pj_status_t) pj_stun_msg_decode(pj_pool_t *pool, pj_str_t e; e = pj_str("MESSAGE-INTEGRITY already present"); pj_stun_msg_create_response(pool, msg, - PJ_STUN_STATUS_BAD_REQUEST, + PJ_STUN_SC_BAD_REQUEST, NULL, p_response); } return PJLIB_UTIL_ESTUNDUPATTR; @@ -1740,7 +1764,7 @@ PJ_DEF(pj_status_t) pj_stun_msg_decode(pj_pool_t *pool, pj_str_t e; e = pj_str("FINGERPRINT already present"); pj_stun_msg_create_response(pool, msg, - PJ_STUN_STATUS_BAD_REQUEST, + PJ_STUN_SC_BAD_REQUEST, NULL, p_response); } return PJLIB_UTIL_ESTUNDUPATTR; @@ -1754,7 +1778,7 @@ PJ_DEF(pj_status_t) pj_stun_msg_decode(pj_pool_t *pool, pj_str_t e; e = pj_str("Invalid attribute order"); pj_stun_msg_create_response(pool, msg, - PJ_STUN_STATUS_BAD_REQUEST, + PJ_STUN_SC_BAD_REQUEST, NULL, p_response); } return has_fingerprint ? PJLIB_UTIL_ESTUNFINGERPOS : @@ -1769,7 +1793,7 @@ PJ_DEF(pj_status_t) pj_stun_msg_decode(pj_pool_t *pool, e = pj_str("Too many attributes"); pj_stun_msg_create_response(pool, msg, - PJ_STUN_STATUS_BAD_REQUEST, + PJ_STUN_SC_BAD_REQUEST, &e, p_response); } return PJLIB_UTIL_ESTUNTOOMANYATTR; diff --git a/pjlib-util/src/pjlib-util/stun_msg_dump.c b/pjlib-util/src/pjlib-util/stun_msg_dump.c index a886dd3f..e599856b 100644 --- a/pjlib-util/src/pjlib-util/stun_msg_dump.c +++ b/pjlib-util/src/pjlib-util/stun_msg_dump.c @@ -68,7 +68,7 @@ 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_REMOTE_ADDRESS: + case PJ_STUN_ATTR_REMOTE_ADDR: case PJ_STUN_ATTR_RELAY_ADDR: case PJ_STUN_ATTR_XOR_MAPPED_ADDR: case PJ_STUN_ATTR_REQ_IP: diff --git a/pjlib-util/src/pjlib-util/stun_session.c b/pjlib-util/src/pjlib-util/stun_session.c index 903be9e6..b4e7356c 100644 --- a/pjlib-util/src/pjlib-util/stun_session.c +++ b/pjlib-util/src/pjlib-util/stun_session.c @@ -113,7 +113,6 @@ static pj_stun_tx_data* tsx_lookup(pj_stun_session *sess, } static pj_status_t create_tdata(pj_stun_session *sess, - void *user_data, pj_stun_tx_data **p_tdata) { pj_pool_t *pool; @@ -127,7 +126,6 @@ static pj_status_t create_tdata(pj_stun_session *sess, tdata = PJ_POOL_ZALLOC_T(pool, pj_stun_tx_data); tdata->pool = pool; tdata->sess = sess; - tdata->user_data = user_data; *p_tdata = tdata; @@ -136,13 +134,12 @@ static pj_status_t create_tdata(pj_stun_session *sess, static pj_status_t create_request_tdata(pj_stun_session *sess, unsigned msg_type, - void *user_data, pj_stun_tx_data **p_tdata) { pj_status_t status; pj_stun_tx_data *tdata; - status = create_tdata(sess, user_data, &tdata); + status = create_tdata(sess, &tdata); if (status != PJ_SUCCESS) return status; @@ -405,16 +402,16 @@ PJ_DEF(void) pj_stun_session_set_credential(pj_stun_session *sess, } -PJ_DEF(pj_status_t) pj_stun_session_create_bind_req(pj_stun_session *sess, - pj_stun_tx_data **p_tdata) +PJ_DEF(pj_status_t) pj_stun_session_create_req(pj_stun_session *sess, + int method, + pj_stun_tx_data **p_tdata) { pj_stun_tx_data *tdata = NULL; pj_status_t status; PJ_ASSERT_RETURN(sess && p_tdata, PJ_EINVAL); - status = create_request_tdata(sess, PJ_STUN_BINDING_REQUEST, NULL, - &tdata); + status = create_request_tdata(sess, method, &tdata); if (status != PJ_SUCCESS) return status; @@ -422,58 +419,32 @@ PJ_DEF(pj_status_t) pj_stun_session_create_bind_req(pj_stun_session *sess, return PJ_SUCCESS; } -PJ_DEF(pj_status_t) pj_stun_session_create_allocate_req(pj_stun_session *sess, - pj_stun_tx_data **p_tdata) +PJ_DEF(pj_status_t) pj_stun_session_create_ind(pj_stun_session *sess, + int msg_type, + pj_stun_tx_data **p_tdata) { - PJ_UNUSED_ARG(sess); - PJ_UNUSED_ARG(p_tdata); - PJ_ASSERT_RETURN(PJ_FALSE, PJ_ENOTSUP); -} - - -PJ_DEF(pj_status_t) -pj_stun_session_create_set_active_destination_req(pj_stun_session *sess, - pj_stun_tx_data **p_tdata) -{ - PJ_UNUSED_ARG(sess); - PJ_UNUSED_ARG(p_tdata); - PJ_ASSERT_RETURN(PJ_FALSE, PJ_ENOTSUP); -} + pj_stun_tx_data *tdata = NULL; + pj_status_t status; -PJ_DEF(pj_status_t) pj_stun_session_create_connect_req( pj_stun_session *sess, - pj_stun_tx_data **p_tdata) -{ - PJ_UNUSED_ARG(sess); - PJ_UNUSED_ARG(p_tdata); - PJ_ASSERT_RETURN(PJ_FALSE, PJ_ENOTSUP); -} + PJ_ASSERT_RETURN(sess && p_tdata, PJ_EINVAL); -PJ_DEF(pj_status_t) -pj_stun_session_create_connection_status_ind(pj_stun_session *sess, - pj_stun_tx_data **p_tdata) -{ - PJ_UNUSED_ARG(sess); - PJ_UNUSED_ARG(p_tdata); - PJ_ASSERT_RETURN(PJ_FALSE, PJ_ENOTSUP); -} + status = create_tdata(sess, &tdata); + if (status != PJ_SUCCESS) + return status; -PJ_DEF(pj_status_t) pj_stun_session_create_send_ind( pj_stun_session *sess, - pj_stun_tx_data **p_tdata) -{ - PJ_UNUSED_ARG(sess); - PJ_UNUSED_ARG(p_tdata); - PJ_ASSERT_RETURN(PJ_FALSE, PJ_ENOTSUP); -} + /* Create STUN message */ + msg_type |= PJ_STUN_INDICATION_BIT; + status = pj_stun_msg_create(tdata->pool, msg_type, PJ_STUN_MAGIC, + NULL, &tdata->msg); + if (status != PJ_SUCCESS) { + pj_pool_release(tdata->pool); + return status; + } -PJ_DEF(pj_status_t) pj_stun_session_create_data_ind( pj_stun_session *sess, - pj_stun_tx_data **p_tdata) -{ - PJ_UNUSED_ARG(sess); - PJ_UNUSED_ARG(p_tdata); - PJ_ASSERT_RETURN(PJ_FALSE, PJ_ENOTSUP); + *p_tdata = tdata; + return PJ_SUCCESS; } - /* * Create a STUN response message. */ @@ -486,7 +457,7 @@ PJ_DEF(pj_status_t) pj_stun_session_create_response( pj_stun_session *sess, pj_status_t status; pj_stun_tx_data *tdata = NULL; - status = create_tdata(sess, NULL, &tdata); + status = create_tdata(sess, &tdata); if (status != PJ_SUCCESS) return status; @@ -810,7 +781,7 @@ static pj_status_t on_incoming_request(pj_stun_session *sess, pj_stun_msg *response; status = pj_stun_msg_create_response(tmp_pool, msg, - PJ_STUN_STATUS_BAD_REQUEST, NULL, + PJ_STUN_SC_BAD_REQUEST, NULL, &response); if (status == PJ_SUCCESS && response) { status = send_response(sess, tmp_pool, response, diff --git a/pjlib-util/src/pjstun-client/client_main.c b/pjlib-util/src/pjstun-client/client_main.c index fd1b9e39..f412a4ce 100644 --- a/pjlib-util/src/pjstun-client/client_main.c +++ b/pjlib-util/src/pjstun-client/client_main.c @@ -236,11 +236,46 @@ static int shutdown() return PJ_SUCCESS; } +static void send_bind_request(void) +{ + pj_stun_tx_data *tdata; + pj_status_t rc; + + rc = pj_stun_session_create_req(g.sess, PJ_STUN_BINDING_REQUEST, &tdata); + pj_assert(rc == PJ_SUCCESS); + + rc = pj_stun_session_send_msg(g.sess, PJ_FALSE, + &g.dst_addr, sizeof(g.dst_addr), + tdata); + if (rc != PJ_SUCCESS) + my_perror("Error sending STUN request", rc); +} + +static void send_allocate_request(void) +{ +} + +static void send_sad_request(void) +{ +} + +static void send_send_ind(void) +{ +} + +static void send_raw_data(void) +{ +} + static void menu(void) { puts("Menu:"); - puts(" b Send Bind request"); - puts(" q Quit"); + puts(" br Send Bind request"); + puts(" ar Send Allocate request"); + puts(" sr Send Set Active Indication request"); + puts(" si Send Send Indication"); + puts(" rw Send raw data"); + puts(" q Quit"); puts(""); printf("Choice: "); } @@ -254,27 +289,23 @@ static void console_main(void) fgets(input, sizeof(input), stdin); - switch (input[0]) { - case 'b': - { - pj_stun_tx_data *tdata; - pj_status_t rc; - - rc = pj_stun_session_create_bind_req(g.sess, &tdata); - pj_assert(rc == PJ_SUCCESS); - - rc = pj_stun_session_send_msg(g.sess, PJ_FALSE, - &g.dst_addr, sizeof(g.dst_addr), - tdata); - if (rc != PJ_SUCCESS) - my_perror("Error sending STUN request", rc); - } - break; - case 'q': + if (input[0]=='b' && input[1]=='r') { + send_bind_request(); + + } else if (input[0]=='a' && input[1]=='r') { + send_allocate_request(); + + } else if (input[0]=='s' && input[1]=='r') { + send_sad_request(); + + } else if (input[0]=='s' && input[1]=='i') { + send_send_ind(); + + } else if (input[0]=='r' && input[1]=='w') { + send_raw_data(); + + } else if (input[0]=='q') { g.quit = 1; - break; - default: - break; } } } diff --git a/pjlib-util/src/pjstun-srv-test/turn_usage.c b/pjlib-util/src/pjstun-srv-test/turn_usage.c index 2351005a..a9f2fdc7 100644 --- a/pjlib-util/src/pjstun-srv-test/turn_usage.c +++ b/pjlib-util/src/pjstun-srv-test/turn_usage.c @@ -21,28 +21,44 @@ #define THIS_FILE "turn_usage.c" #define MAX_CLIENTS 8000 -#define MAX_PEER_PER_CLIENTS 16 +#define MAX_PEER_PER_CLIENT 16 #define START_PORT 2000 #define END_PORT 65530 -static void tu_on_rx_data(pj_stun_usage *usage, - void *pkt, - pj_size_t pkt_size, - const pj_sockaddr_t *src_addr, - unsigned src_addr_len); -static void tu_on_destroy(pj_stun_usage *usage); - -static pj_status_t sess_on_send_msg(pj_stun_session *sess, - const void *pkt, - pj_size_t pkt_size, - const pj_sockaddr_t *dst_addr, - unsigned addr_len); -static pj_status_t sess_on_rx_request(pj_stun_session *sess, - const pj_uint8_t *pkt, - unsigned pkt_len, - const pj_stun_msg *msg, - const pj_sockaddr_t *src_addr, - unsigned src_addr_len); +/* + * Forward declarations. + */ +struct turn_usage; +struct turn_client; + +static void tu_on_rx_data(pj_stun_usage *usage, + void *pkt, + pj_size_t pkt_size, + const pj_sockaddr_t *src_addr, + unsigned src_addr_len); +static void tu_on_destroy(pj_stun_usage *usage); +static pj_status_t tu_sess_on_send_msg(pj_stun_session *sess, + const void *pkt, + pj_size_t pkt_size, + const pj_sockaddr_t *dst_addr, + unsigned addr_len); +static pj_status_t tu_sess_on_rx_request(pj_stun_session *sess, + const pj_uint8_t *pkt, + unsigned pkt_len, + const pj_stun_msg *msg, + const pj_sockaddr_t *src_addr, + unsigned src_addr_len); + +static pj_status_t client_create(struct turn_usage *tu, + const pj_sockaddr_t *src_addr, + unsigned src_addr_len, + struct turn_client **p_client); +static pj_status_t client_destroy(struct turn_client *client, + pj_status_t reason); +static pj_status_t client_handle_stun_msg(struct turn_client *client, + const pj_stun_msg *msg, + const pj_sockaddr_t *src_addr, + unsigned src_addr_len); struct turn_usage @@ -50,12 +66,13 @@ struct turn_usage pj_pool_factory *pf; pj_stun_endpoint *endpt; pj_ioqueue_t *ioqueue; + pj_timer_heap_t *timer_heap; pj_pool_t *pool; + pj_mutex_t *mutex; pj_stun_usage *usage; int type; pj_stun_session *default_session; pj_hash_table_t *client_htable; - pj_hash_table_t *peer_htable; unsigned max_bw_kbps; unsigned max_lifetime; @@ -67,25 +84,38 @@ struct peer; struct turn_client { + char obj_name[PJ_MAX_OBJ_NAME]; struct turn_usage *tu; pj_pool_t *pool; - pj_sockaddr_in addr; pj_stun_session *session; + pj_mutex_t *mutex; + int sock_type; + + /* Socket and socket address of the allocated port */ + pj_sock_t sock; + pj_ioqueue_key_t *key; + pj_sockaddr_in client_addr; + /* Allocation properties */ unsigned bw_kbps; unsigned lifetime; + pj_timer_entry expiry_timer; - pj_sock_t sock; - pj_ioqueue_key_t *key; - char packet[4000]; - pj_sockaddr_in src_addr; - int src_addr_len; - pj_ioqueue_op_key_t read_key; - pj_ioqueue_op_key_t write_key; - - pj_bool_t has_ad; - pj_bool_t ad_is_pending; - pj_sockaddr_in ad; + + /* Hash table to keep all peers, key-ed by their address */ + pj_hash_table_t *peer_htable; + + /* Active destination, or sin_addr.s_addr will be zero if + * no active destination is set. + */ + struct peer *active_peer; + + /* Current packet received/sent from/to the allocated port */ + pj_uint8_t pkt[4000]; + pj_sockaddr_in pkt_src_addr; + int pkt_src_addr_len; + pj_ioqueue_op_key_t pkt_read_key; + pj_ioqueue_op_key_t pkt_write_key; }; struct peer @@ -101,6 +131,9 @@ struct session_data }; +/* + * This is the only public API, to create and start the TURN usage. + */ PJ_DEF(pj_status_t) pj_stun_turn_usage_create(pj_stun_server *srv, int type, const pj_str_t *ip_addr, @@ -126,6 +159,7 @@ PJ_DEF(pj_status_t) pj_stun_turn_usage_create(pj_stun_server *srv, tu->type = type; tu->pf = si->pf; tu->endpt = si->endpt; + tu->timer_heap = si->timer_heap; tu->next_port = START_PORT; status = pj_sockaddr_in_init(&local_addr, ip_addr, (pj_uint16_t)port); @@ -148,12 +182,11 @@ PJ_DEF(pj_status_t) pj_stun_turn_usage_create(pj_stun_server *srv, /* Init hash tables */ tu->client_htable = pj_hash_create(tu->pool, MAX_CLIENTS); - tu->peer_htable = pj_hash_create(tu->pool, MAX_CLIENTS); /* Create default session */ pj_bzero(&sess_cb, sizeof(sess_cb)); - sess_cb.on_send_msg = &sess_on_send_msg; - sess_cb.on_rx_request = &sess_on_rx_request; + sess_cb.on_send_msg = &tu_sess_on_send_msg; + sess_cb.on_rx_request = &tu_sess_on_rx_request; status = pj_stun_session_create(si->endpt, "turns%p", &sess_cb, PJ_FALSE, &tu->default_session); if (status != PJ_SUCCESS) { @@ -165,22 +198,52 @@ PJ_DEF(pj_status_t) pj_stun_turn_usage_create(pj_stun_server *srv, sd->tu = tu; pj_stun_session_set_user_data(tu->default_session, sd); + /* Create mutex */ + status = pj_mutex_create_recursive(pool, "turn%p", &tu->mutex); + if (status != PJ_SUCCESS) { + pj_stun_usage_destroy(tu->usage); + return status; + } + *p_bu = tu->usage; return PJ_SUCCESS; } +/* + * This is a callback called by usage.c when the particular STUN usage + * is to be destroyed. + */ static void tu_on_destroy(pj_stun_usage *usage) { struct turn_usage *tu; + pj_hash_iterator_t hit, *it; tu = (struct turn_usage*) pj_stun_usage_get_user_data(usage); - PJ_TODO(DESTROY_ALL_CLIENTS); + + /* Destroy all clients */ + it = pj_hash_first(tu->client_htable, &hit); + while (it) { + struct turn_client *client; + + client = (struct turn_client *)pj_hash_this(tu->client_htable, it); + client_destroy(client, PJ_SUCCESS); + + it = pj_hash_next(tu->client_htable, it); + } + + pj_stun_session_destroy(tu->default_session); + pj_mutex_destroy(tu->mutex); pj_pool_release(tu->pool); } +/* + * This is a callback called by the usage.c to notify the TURN usage, + * that incoming packet (may or may not be a STUN packet) is received + * on the port where the TURN usage is listening. + */ static void tu_on_rx_data(pj_stun_usage *usage, void *pkt, pj_size_t pkt_size, @@ -188,7 +251,6 @@ static void tu_on_rx_data(pj_stun_usage *usage, unsigned src_addr_len) { struct turn_usage *tu; - pj_stun_session *session; struct turn_client *client; unsigned flags; pj_status_t status; @@ -200,26 +262,50 @@ static void tu_on_rx_data(pj_stun_usage *usage, client = (struct turn_client*) pj_hash_get(tu->client_htable, src_addr, src_addr_len, NULL); - if (client == NULL) { - session = tu->default_session; - } else { - session = client->session; - } - - /* Handle packet to session */ - flags = PJ_STUN_CHECK_PACKET; + /* STUN message decoding flag */ + flags = 0; if (tu->type == PJ_SOCK_DGRAM) flags |= PJ_STUN_IS_DATAGRAM; + - status = pj_stun_session_on_rx_pkt(session, (pj_uint8_t*)pkt, pkt_size, - flags, NULL, src_addr, src_addr_len); - if (status != PJ_SUCCESS) { - pj_stun_perror(THIS_FILE, "Error handling incoming packet", status); - return; + if (client) { + status = pj_stun_msg_check(pkt, pkt_size, flags); + + if (status == PJ_SUCCESS) { + /* Received STUN message */ + status = pj_stun_session_on_rx_pkt(client->session, + (pj_uint8_t*)pkt, pkt_size, + flags, NULL, + src_addr, src_addr_len); + } else if (client->active_peer) { + /* Received non-STUN message and client has active destination */ + pj_ssize_t sz = pkt_size; + pj_ioqueue_sendto(client->key, &client->pkt_write_key, + pkt, &sz, 0, + &client->active_peer->addr, + sizeof(client->active_peer->addr)); + } else { + /* Received non-STUN message and client doesn't have active + * destination. + */ + /* Ignore */ + } + + } else { + /* Received packet (could be STUN or no) from new source */ + flags |= PJ_STUN_CHECK_PACKET; + pj_stun_session_on_rx_pkt(tu->default_session, (pj_uint8_t*)pkt, + pkt_size, flags, NULL, + src_addr, src_addr_len); } } +/* + * This is a utility function provided by TU (Turn Usage) to reserve + * or allocate internal port/socket. The allocation needs to be + * coordinated to minimize bind() collissions. + */ static pj_status_t tu_alloc_port(struct turn_usage *tu, int type, unsigned rpp_bits, @@ -235,7 +321,7 @@ static pj_status_t tu_alloc_port(struct turn_usage *tu, if (req_addr && req_addr->sin_port != 0) { - *err_code = PJ_STUN_STATUS_INVALID_PORT; + *err_code = PJ_STUN_SC_INVALID_PORT; /* Allocate specific port */ status = pj_sock_socket(PJ_AF_INET, type, 0, &sock); @@ -254,10 +340,11 @@ static pj_status_t tu_alloc_port(struct turn_usage *tu, return PJ_SUCCESS; } else { - *err_code = PJ_STUN_STATUS_INSUFFICIENT_CAPACITY; + status = -1; + *err_code = PJ_STUN_SC_INSUFFICIENT_CAPACITY; if (req_addr && req_addr->sin_addr.s_addr) { - *err_code = PJ_STUN_STATUS_INVALID_IP_ADDR; + *err_code = PJ_STUN_SC_INVALID_IP_ADDR; pj_memcpy(&addr, req_addr, sizeof(pj_sockaddr_in)); } else { pj_sockaddr_in_init(&addr, NULL, 0); @@ -308,10 +395,167 @@ static pj_status_t tu_alloc_port(struct turn_usage *tu, } } + +/* + * This callback is called by the TU's STUN session when it receives + * a valid STUN message. This is called from tu_on_rx_data above. + */ +static pj_status_t tu_sess_on_rx_request(pj_stun_session *sess, + const pj_uint8_t *pkt, + unsigned pkt_len, + const pj_stun_msg *msg, + const pj_sockaddr_t *src_addr, + unsigned src_addr_len) +{ + struct session_data *sd; + struct turn_client *client; + pj_stun_tx_data *tdata; + pj_status_t status; + + PJ_UNUSED_ARG(pkt); + PJ_UNUSED_ARG(pkt_len); + + sd = (struct session_data*) pj_stun_session_get_user_data(sess); + + pj_assert(sd->client == NULL); + + if (msg->hdr.type != PJ_STUN_ALLOCATE_REQUEST) { + if (PJ_STUN_IS_REQUEST(msg->hdr.type)) { + status = pj_stun_session_create_response(sess, msg, + PJ_STUN_SC_NO_BINDING, + NULL, &tdata); + if (status==PJ_SUCCESS) { + status = pj_stun_session_send_msg(sess, PJ_FALSE, + src_addr, src_addr_len, + tdata); + } + } else { + PJ_LOG(4,(THIS_FILE, + "Received %s %s without matching Allocation, " + "ignored", pj_stun_get_method_name(msg->hdr.type), + pj_stun_get_class_name(msg->hdr.type))); + } + return PJ_SUCCESS; + } + + status = client_create(sd->tu, src_addr, src_addr_len, &client); + if (status != PJ_SUCCESS) { + pj_stun_perror(THIS_FILE, "Error creating new TURN client", + status); + return status; + } + + + /* Hand over message to client */ + pj_mutex_lock(client->mutex); + status = client_handle_stun_msg(client, msg, src_addr, src_addr_len); + pj_mutex_unlock(client->mutex); + + return status; +} + + +/* + * This callback is called by STUN session when it needs to send packet + * to the network. + */ +static pj_status_t tu_sess_on_send_msg(pj_stun_session *sess, + const void *pkt, + pj_size_t pkt_size, + const pj_sockaddr_t *dst_addr, + unsigned addr_len) +{ + struct session_data *sd; + + sd = (struct session_data*) pj_stun_session_get_user_data(sess); + + if (sd->tu->type == PJ_SOCK_DGRAM) { + return pj_stun_usage_sendto(sd->tu->usage, pkt, pkt_size, 0, + dst_addr, addr_len); + } else { + return PJ_ENOTSUP; + } +} + + /****************************************************************************/ +/* + * TURN client operations. + */ + +/* Function prototypes */ +static pj_status_t client_create_relay(struct turn_client *client); +static pj_status_t client_destroy_relay(struct turn_client *client); +static void client_on_expired(pj_timer_heap_t *th, pj_timer_entry *e); +static void client_on_read_complete(pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key, + pj_ssize_t bytes_read); +static pj_status_t client_respond(struct turn_client *client, + const pj_stun_msg *msg, + int err_code, + const char *err_msg, + const pj_sockaddr_t *dst_addr, + int dst_addr_len); +static struct peer* client_get_peer(struct turn_client *client, + const pj_sockaddr_in *peer_addr, + pj_uint32_t *hval); +static struct peer* client_add_peer(struct turn_client *client, + const pj_sockaddr_in *peer_addr, + pj_uint32_t hval); + + +/* + * This callback is called when incoming STUN message is received + * in the TURN usage. This is called from by tu_on_rx_data() when + * the packet is handed over to the client. + */ +static pj_status_t client_sess_on_rx_request(pj_stun_session *sess, + const pj_uint8_t *pkt, + unsigned pkt_len, + const pj_stun_msg *msg, + const pj_sockaddr_t *src_addr, + unsigned src_addr_len) +{ + struct session_data *sd; + + PJ_UNUSED_ARG(pkt); + PJ_UNUSED_ARG(pkt_len); + + sd = (struct session_data*) pj_stun_session_get_user_data(sess); + pj_assert(sd->client != PJ_SUCCESS); + + return client_handle_stun_msg(sd->client, msg, src_addr, src_addr_len); +} + + +/* + * This callback is called by client's STUN session to send outgoing + * STUN packet. It's called when client calls pj_stun_session_send_msg() + * function. + */ +static pj_status_t client_sess_on_send_msg(pj_stun_session *sess, + const void *pkt, + pj_size_t pkt_size, + const pj_sockaddr_t *dst_addr, + unsigned addr_len) +{ + struct session_data *sd; + + sd = (struct session_data*) pj_stun_session_get_user_data(sess); + + if (sd->tu->type == PJ_SOCK_DGRAM) { + return pj_stun_usage_sendto(sd->tu->usage, pkt, pkt_size, 0, + dst_addr, addr_len); + } else { + return PJ_ENOTSUP; + } +} + +/* + * Create a new TURN client for the specified source address. + */ static pj_status_t client_create(struct turn_usage *tu, - const pj_stun_msg *msg, const pj_sockaddr_t *src_addr, unsigned src_addr_len, struct turn_client **p_client) @@ -322,18 +566,25 @@ static pj_status_t client_create(struct turn_usage *tu, struct session_data *sd; pj_status_t status; - PJ_UNUSED_ARG(msg); - pool = pj_pool_create(tu->pf, "turnc%p", 4000, 4000, NULL); client = PJ_POOL_ZALLOC_T(pool, struct turn_client); client->pool = pool; client->tu = tu; client->sock = PJ_INVALID_SOCKET; + if (src_addr) { + const pj_sockaddr_in *a4 = (const pj_sockaddr_in *)src_addr; + pj_ansi_snprintf(client->obj_name, sizeof(client->obj_name), + "%s:%d", + pj_inet_ntoa(a4->sin_addr), + (int)pj_ntohs(a4->sin_port)); + client->obj_name[sizeof(client->obj_name)-1] = '\0'; + } + /* Create session */ pj_bzero(&sess_cb, sizeof(sess_cb)); - sess_cb.on_send_msg = &sess_on_send_msg; - sess_cb.on_rx_request = &sess_on_rx_request; + sess_cb.on_send_msg = &client_sess_on_send_msg; + sess_cb.on_rx_request = &client_sess_on_rx_request; status = pj_stun_session_create(tu->endpt, "turnc%p", &sess_cb, PJ_FALSE, &client->session); if (status != PJ_SUCCESS) { @@ -346,29 +597,98 @@ static pj_status_t client_create(struct turn_usage *tu, sd->client = client; pj_stun_session_set_user_data(client->session, sd); + /* Mutex */ + status = pj_mutex_create_recursive(client->pool, pool->obj_name, + &client->mutex); + if (status != PJ_SUCCESS) { + client_destroy(client, status); + return status; + } + + /* Create hash table */ + client->peer_htable = pj_hash_create(client->pool, MAX_PEER_PER_CLIENT); + if (client->peer_htable == NULL) { + client_destroy(client, status); + return PJ_ENOMEM; + } + + /* Init timer entry */ + client->expiry_timer.user_data = client; + client->expiry_timer.cb = &client_on_expired; + client->expiry_timer.id = 0; + /* Register to hash table */ + pj_mutex_lock(tu->mutex); pj_hash_set(pool, tu->client_htable, src_addr, src_addr_len, 0, client); + pj_mutex_unlock(tu->mutex); + /* Done */ *p_client = client; + + PJ_LOG(4,(THIS_FILE, "TURN client %s created", client->obj_name)); + return PJ_SUCCESS; } -static pj_status_t client_destroy(struct turn_client *client) -{ -} -static void client_on_read_complete(pj_ioqueue_key_t *key, - pj_ioqueue_op_key_t *op_key, - pj_ssize_t bytes_read) +/* + * Destroy TURN client. + */ +static pj_status_t client_destroy(struct turn_client *client, + pj_status_t reason) { -} + struct turn_usage *tu = client->tu; + char name[PJ_MAX_OBJ_NAME]; -static void client_on_write_complete(pj_ioqueue_key_t *key, - pj_ioqueue_op_key_t *op_key, - pj_ssize_t bytes_sent) -{ + pj_assert(sizeof(name)==sizeof(client->obj_name)); + pj_memcpy(name, client->obj_name, sizeof(name)); + + /* Kill timer if it's active */ + if (client->expiry_timer.id != 0) { + pj_timer_heap_cancel(tu->timer_heap, &client->expiry_timer); + client->expiry_timer.id = PJ_FALSE; + } + + /* Destroy relay */ + client_destroy_relay(client); + + /* Unregister client from hash table */ + pj_mutex_lock(tu->mutex); + pj_hash_set(NULL, tu->client_htable, + &client->client_addr, sizeof(client->client_addr), 0, NULL); + pj_mutex_unlock(tu->mutex); + + /* Destroy STUN session */ + if (client->session) { + pj_stun_session_destroy(client->session); + client->session = NULL; + } + + /* Mutex */ + if (client->mutex) { + pj_mutex_destroy(client->mutex); + client->mutex = NULL; + } + + /* Finally destroy pool */ + if (client->pool) { + pj_pool_t *pool = client->pool; + client->pool = NULL; + pj_pool_release(pool); + } + + if (reason == PJ_SUCCESS) { + PJ_LOG(4,(THIS_FILE, "TURN client %s destroyed", name)); + } + + return PJ_SUCCESS; } + +/* + * This utility function is used to setup relay (with ioqueue) after + * socket has been allocated for the TURN client. + */ static pj_status_t client_create_relay(struct turn_client *client) { pj_ioqueue_callback client_ioq_cb; @@ -377,7 +697,6 @@ static pj_status_t client_create_relay(struct turn_client *client) /* Register to ioqueue */ pj_bzero(&client_ioq_cb, sizeof(client_ioq_cb)); client_ioq_cb.on_read_complete = &client_on_read_complete; - client_ioq_cb.on_write_complete = &client_on_write_complete; status = pj_ioqueue_register_sock(client->pool, client->tu->ioqueue, client->sock, client, &client_ioq_cb, &client->key); @@ -387,18 +706,112 @@ static pj_status_t client_create_relay(struct turn_client *client) return status; } - pj_ioqueue_op_key_init(&client->read_key, sizeof(client->read_key)); - pj_ioqueue_op_key_init(&client->write_key, sizeof(client->write_key)); + pj_ioqueue_op_key_init(&client->pkt_read_key, + sizeof(client->pkt_read_key)); + pj_ioqueue_op_key_init(&client->pkt_write_key, + sizeof(client->pkt_write_key)); /* Trigger the first read */ - client_on_read_complete(client->key, &client->read_key, 0); + client_on_read_complete(client->key, &client->pkt_read_key, 0); + + return PJ_SUCCESS; +} + +/* + * This utility function is used to destroy the port allocated for + * the TURN client. + */ +static pj_status_t client_destroy_relay(struct turn_client *client) +{ + /* Close socket */ + if (client->key) { + pj_ioqueue_unregister(client->key); + client->key = NULL; + client->sock = PJ_INVALID_SOCKET; + } else if (client->sock && client->sock != PJ_INVALID_SOCKET) { + pj_sock_close(client->sock); + client->sock = PJ_INVALID_SOCKET; + } + + PJ_LOG(4,(THIS_FILE, "TURN client %s: relay allocation %s:%d destroyed", + client->obj_name, + pj_inet_ntoa(client->client_addr.sin_addr), + (int)pj_ntohs(client->client_addr.sin_port))); return PJ_SUCCESS; } + +/* + * From the source packet address, get the peer instance from hash table. + */ +static struct peer* client_get_peer(struct turn_client *client, + const pj_sockaddr_in *peer_addr, + pj_uint32_t *hval) +{ + return (struct peer*) + pj_hash_get(client->peer_htable, peer_addr, sizeof(*peer_addr), hval); +} + + +/* + * Add a peer instance to the peer hash table. + */ +static struct peer* client_add_peer(struct turn_client *client, + const pj_sockaddr_in *peer_addr, + unsigned hval) +{ + struct peer *peer; + + peer = PJ_POOL_ZALLOC_T(client->pool, struct peer); + peer->client = client; + pj_memcpy(&peer->addr, peer_addr, sizeof(*peer_addr)); + + pj_hash_set(client->pool, client->peer_htable, + peer_addr, sizeof(*peer_addr), hval, peer); + + PJ_LOG(4,(THIS_FILE, "TURN client %s: peer %s:%s:%d added", + client->obj_name, "udp", pj_inet_ntoa(peer_addr->sin_addr), + (int)pj_ntohs(peer_addr->sin_port))); + + return peer; +} + + +/* + * Utility to send STUN response message (normally to send error response). + */ +static pj_status_t client_respond(struct turn_client *client, + const pj_stun_msg *msg, + int err_code, + const char *custom_msg, + const pj_sockaddr_t *dst_addr, + int dst_addr_len) +{ + pj_str_t err_msg; + pj_str_t *p_err_msg = NULL; + pj_stun_tx_data *response; + pj_status_t status; + + if (custom_msg) + pj_cstr(&err_msg, custom_msg), p_err_msg = &err_msg; + + status = pj_stun_session_create_response(client->session, msg, + err_code, p_err_msg, + &response); + if (status == PJ_SUCCESS) + status = pj_stun_session_send_msg(client->session, PJ_TRUE, + dst_addr, dst_addr_len, response); + + return status; +} + + +/* + * Handle incoming initial or subsequent Allocate Request. + * This function is called by client_handle_stun_msg() below. + */ static pj_status_t client_handle_allocate_req(struct turn_client *client, - const pj_uint8_t *pkt, - unsigned pkt_len, const pj_stun_msg *msg, const pj_sockaddr_t *src_addr, unsigned src_addr_len) @@ -411,8 +824,8 @@ static pj_status_t client_handle_allocate_req(struct turn_client *client, pj_stun_tx_data *response; pj_sockaddr_in req_addr; int addr_len; - unsigned type; unsigned rpp_bits; + pj_time_val timeout; pj_status_t status; a_bw = (const pj_stun_bandwidth_attr *) @@ -431,14 +844,9 @@ static pj_status_t client_handle_allocate_req(struct turn_client *client, /* Process BANDWIDTH attribute */ if (a_bw && a_bw->value > client->tu->max_bw_kbps) { - status = pj_stun_session_create_response(client->session, msg, - PJ_STUN_STATUS_INSUFFICIENT_CAPACITY, - NULL, &response); - if (status == PJ_SUCCESS && response) { - pj_stun_session_send_msg(client->session, PJ_TRUE, - src_addr, src_addr_len, response); - } - return -1; + client_respond(client, msg, PJ_STUN_SC_INSUFFICIENT_CAPACITY, NULL, + src_addr, src_addr_len); + return PJ_SUCCESS; } else if (a_bw) { client->bw_kbps = a_bw->value; } else { @@ -447,30 +855,20 @@ static pj_status_t client_handle_allocate_req(struct turn_client *client, /* Process REQUESTED-TRANSPORT attribute */ if (a_rt && a_rt->value != 0) { - status = pj_stun_session_create_response(client->session, msg, - PJ_STUN_STATUS_UNSUPP_TRANSPORT_PROTO, - NULL, &response); - if (status == PJ_SUCCESS && response) { - pj_stun_session_send_msg(client->session, PJ_TRUE, - src_addr, src_addr_len, response); - } - return -1; + client_respond(client, msg, PJ_STUN_SC_UNSUPP_TRANSPORT_PROTO, NULL, + src_addr, src_addr_len); + return PJ_SUCCESS; } else if (a_rt) { - type = a_rt->value ? PJ_SOCK_STREAM : PJ_SOCK_DGRAM; + client->sock_type = a_rt->value ? PJ_SOCK_STREAM : PJ_SOCK_DGRAM; } else { - type = client->tu->type;; + client->sock_type = client->tu->type;; } /* Process REQUESTED-IP attribute */ if (a_rip && a_rip->addr.addr.sa_family != PJ_AF_INET) { - status = pj_stun_session_create_response(client->session, msg, - PJ_STUN_STATUS_INVALID_IP_ADDR, - NULL, &response); - if (status == PJ_SUCCESS && response) { - pj_stun_session_send_msg(client->session, PJ_TRUE, - src_addr, src_addr_len, response); - } - return -1; + client_respond(client, msg, PJ_STUN_SC_INVALID_IP_ADDR, NULL, + src_addr, src_addr_len); + return PJ_SUCCESS; } else if (a_rip) { req_addr.sin_addr.s_addr = a_rip->addr.ipv4.sin_addr.s_addr; @@ -497,44 +895,60 @@ static pj_status_t client_handle_allocate_req(struct turn_client *client, } /* Allocate socket if we don't have one */ - if (client->sock == PJ_INVALID_SOCKET) { + if (client->key == NULL) { int err_code; - status = tu_alloc_port(client->tu, type, rpp_bits, &req_addr, - &client->sock, &err_code); + PJ_LOG(4,(THIS_FILE, "TURN client %s: received initial Allocate " + "request, requested type:addr:port=%d:%s:%d, rpp " + "bits=%d", + client->obj_name, client->sock_type, + pj_inet_ntoa(req_addr.sin_addr), pj_ntohs(req_addr.sin_port), + rpp_bits)); + + status = tu_alloc_port(client->tu, client->sock_type, rpp_bits, + &req_addr, &client->sock, &err_code); if (status != PJ_SUCCESS) { + char errmsg[PJ_ERR_MSG_SIZE]; - status = pj_stun_session_create_response(client->session, msg, - err_code, NULL, - &response); - if (status == PJ_SUCCESS && response) { - pj_stun_session_send_msg(client->session, PJ_TRUE, - src_addr, src_addr_len, response); - } - return -1; + pj_strerror(status, errmsg, sizeof(errmsg)); + PJ_LOG(4,(THIS_FILE, "TURN client %s: error allocating relay port" + ": %s", + client->obj_name, errmsg)); + + client_respond(client, msg, err_code, NULL, + src_addr, src_addr_len); + + return status; } status = client_create_relay(client); if (status != PJ_SUCCESS) { - status = pj_stun_session_create_response(client->session, msg, - PJ_STUN_STATUS_SERVER_ERROR, - NULL, &response); - if (status == PJ_SUCCESS && response) { - pj_stun_session_send_msg(client->session, PJ_TRUE, - src_addr, src_addr_len, response); - } - return -1; + client_respond(client, msg, PJ_STUN_SC_SERVER_ERROR, NULL, + src_addr, src_addr_len); + return status; } } else { /* Otherwise check if the port parameter stays the same */ /* TODO */ + PJ_LOG(4,(THIS_FILE, "TURN client %s: received Allocate refresh", + client->obj_name)); + } + + /* Refresh timer */ + if (client->expiry_timer.id != PJ_FALSE) { + pj_timer_heap_cancel(client->tu->timer_heap, &client->expiry_timer); + client->expiry_timer.id = PJ_FALSE; } + timeout.sec = client->lifetime; + timeout.msec = 0; + pj_timer_heap_schedule(client->tu->timer_heap, &client->expiry_timer, &timeout); + /* Done successfully, create and send success response */ status = pj_stun_session_create_response(client->session, msg, 0, NULL, &response); if (status != PJ_SUCCESS) { - return -1; + return status; } pj_stun_msg_add_uint_attr(response->pool, response->msg, @@ -554,31 +968,157 @@ static pj_status_t client_handle_allocate_req(struct turn_client *client, PJ_STUN_ATTR_RELAY_ADDR, PJ_FALSE, &req_addr, addr_len); + PJ_LOG(4,(THIS_FILE, "TURN client %s: relay allocated or refreshed, " + "internal address is %s:%d", + client->obj_name, + pj_inet_ntoa(req_addr.sin_addr), + (int)pj_ntohs(req_addr.sin_port))); + return pj_stun_session_send_msg(client->session, PJ_TRUE, src_addr, src_addr_len, response); } + +/* + * client handling incoming STUN Set Active Destination request + * This function is called by client_handle_stun_msg() below. + */ +static pj_status_t client_handle_sad(struct turn_client *client, + const pj_stun_msg *msg, + const pj_sockaddr_t *src_addr, + unsigned src_addr_len) +{ + pj_stun_remote_addr_attr *a_raddr; + + a_raddr = (pj_stun_remote_addr_attr*) + pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_REMOTE_ADDR, 0); + if (!a_raddr) { + /* Remote active destination needs to be cleared */ + client->active_peer = NULL; + + } else if (a_raddr->addr.addr.sa_family != PJ_AF_INET) { + /* Bad request (not IPv4) */ + client_respond(client, msg, PJ_STUN_SC_BAD_REQUEST, NULL, + src_addr, src_addr_len); + return PJ_SUCCESS; + + } else if (client->active_peer) { + /* Client tries to set new active destination without clearing + * it first. Reject with 439. + */ + client_respond(client, msg, PJ_STUN_SC_TRANSITIONING, NULL, + src_addr, src_addr_len); + return PJ_SUCCESS; + + } else { + struct peer *peer; + pj_uint32_t hval = 0; + + /* Add a new peer/permission if we don't have one for this address */ + peer = client_get_peer(client, &a_raddr->addr.ipv4, &hval); + if (peer==NULL) { + peer = client_add_peer(client, &a_raddr->addr.ipv4, hval); + } + + /* Set active destination */ + client->active_peer = peer; + } + + PJ_LOG(4,(THIS_FILE, "TURN client %s: active destination set to %s:%d", + client->obj_name, + pj_inet_ntoa(client->active_peer->addr.sin_addr), + (int)pj_ntohs(client->active_peer->addr.sin_port))); + + /* Respond with successful response */ + client_respond(client, msg, 0, NULL, src_addr, src_addr_len); + + return PJ_SUCCESS; +} + + +/* + * client handling incoming STUN Send Indication + * This function is called by client_handle_stun_msg() below. + */ static pj_status_t client_handle_send_ind(struct turn_client *client, - const pj_uint8_t *pkt, - unsigned pkt_len, - const pj_stun_msg *msg, - const pj_sockaddr_t *src_addr, - unsigned src_addr_len) + const pj_stun_msg *msg) { + pj_stun_remote_addr_attr *a_raddr; + pj_stun_data_attr *a_data; + pj_uint32_t hval = 0; + const pj_uint8_t *data; + pj_ssize_t datalen; + + /* Get REMOTE-ADDRESS attribute */ + a_raddr = (pj_stun_remote_addr_attr*) + pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_REMOTE_ADDR, 0); + if (!a_raddr) { + /* REMOTE-ADDRESS not present, discard packet */ + return PJ_SUCCESS; + + } else if (a_raddr->addr.addr.sa_family != PJ_AF_INET) { + /* REMOTE-ADDRESS present but not IPv4, discard packet */ + return PJ_SUCCESS; + + } + + /* Get the DATA attribute */ + a_data = (pj_stun_data_attr*) + pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_DATA, 0); + if (a_data) { + data = (const pj_uint8_t *)a_data->data; + datalen = a_data->length; + + } else if (client->sock_type == PJ_SOCK_STREAM) { + /* Discard if no Data and Allocation type is TCP */ + return PJ_SUCCESS; + + } else { + data = (const pj_uint8_t *)""; + datalen = 0; + } + + /* Add to peer table if necessary */ + if (client_get_peer(client, &a_raddr->addr.ipv4, &hval)==NULL) + client_add_peer(client, &a_raddr->addr.ipv4, hval); + + /* Send the packet */ + pj_ioqueue_sendto(client->key, &client->pkt_write_key, + data, &datalen, 0, + &a_raddr->addr.ipv4, sizeof(a_raddr->addr.ipv4)); + + return PJ_SUCCESS; } + +/* + * client handling unknown incoming STUN message. + * This function is called by client_handle_stun_msg() below. + */ static pj_status_t client_handle_unknown_msg(struct turn_client *client, - const pj_uint8_t *pkt, - unsigned pkt_len, const pj_stun_msg *msg, const pj_sockaddr_t *src_addr, unsigned src_addr_len) { + PJ_LOG(4,(THIS_FILE, "TURN client %s: unhandled %s %s", + client->obj_name, pj_stun_get_method_name(msg->hdr.type), + pj_stun_get_class_name(msg->hdr.type))); + + if (PJ_STUN_IS_REQUEST(msg->hdr.type)) { + return client_respond(client, msg, PJ_STUN_SC_BAD_REQUEST, NULL, + src_addr, src_addr_len); + } else { + /* Ignore */ + return PJ_SUCCESS; + } } + +/* + * Main entry for handling STUN messages arriving on the main TURN port, + * for this client + */ static pj_status_t client_handle_stun_msg(struct turn_client *client, - const pj_uint8_t *pkt, - unsigned pkt_len, const pj_stun_msg *msg, const pj_sockaddr_t *src_addr, unsigned src_addr_len) @@ -586,77 +1126,154 @@ static pj_status_t client_handle_stun_msg(struct turn_client *client, pj_status_t status; switch (msg->hdr.type) { - case PJ_STUN_ALLOCATE_REQUEST: - return client_handle_allocate_req(client, pkt, pkt_len, msg, - src_addr, src_addr_len); - case PJ_STUN_SEND_INDICATION: - return client_handle_send_ind(client, pkt, pkt_len, msg, - src_addr, src_addr_len); + status = client_handle_send_ind(client, msg); + + case PJ_STUN_SET_ACTIVE_DESTINATION_REQUEST: + status = client_handle_sad(client, msg, + src_addr, src_addr_len); + case PJ_STUN_ALLOCATE_REQUEST: + status = client_handle_allocate_req(client, msg, + src_addr, src_addr_len); default: - return client_handle_unknown_msg(client, pkt, pkt_len, msg, - src_addr, src_addr_len); + status = client_handle_unknown_msg(client, msg, + src_addr, src_addr_len); } + + return status; } -static pj_status_t sess_on_rx_request(pj_stun_session *sess, - const pj_uint8_t *pkt, - unsigned pkt_len, - const pj_stun_msg *msg, - const pj_sockaddr_t *src_addr, - unsigned src_addr_len) +PJ_INLINE(pj_uint32_t) GET_VAL32(const pj_uint8_t *pdu, unsigned pos) { - struct session_data *sd; - struct turn_client *client; - pj_stun_tx_data *tdata; - pj_status_t status; + return (pdu[pos+0] << 24) + + (pdu[pos+1] << 16) + + (pdu[pos+2] << 8) + + (pdu[pos+3]); +} - sd = (struct session_data*) pj_stun_session_get_user_data(sess); - if (sd->client == NULL) { - /* No client is associated with this source address. Create a new - * one if this is an Allocate request. - */ - if (msg->hdr.type != PJ_STUN_ALLOCATE_REQUEST) { - PJ_LOG(4,(THIS_FILE, "Received first packet not Allocate request")); - return PJ_SUCCESS; - } +/* + * Handle incoming data from peer + * This function is called by client_on_read_complete() below. + */ +static void client_handle_peer_data(struct turn_client *client, + unsigned bytes_read) +{ + struct peer *peer; + pj_bool_t has_magic_cookie; + pj_status_t status; - PJ_TODO(SUPPORT_MOVE); + /* Has the sender been registered as peer? */ + peer = client_get_peer(client, &client->pkt_src_addr, NULL); + if (peer == NULL) { + /* Nope. Discard packet */ + return; + } - status = client_create(sd->tu, msg, src_addr, src_addr_len, &client); - if (status != PJ_SUCCESS) { - pj_stun_perror(THIS_FILE, "Error creating new TURN client", status); - return status; - } + /* Check if packet has STUN magic cookie */ + has_magic_cookie = (GET_VAL32(client->pkt, 4) == PJ_STUN_MAGIC); + /* If this is the Active Destination and the packet doesn't have + * STUN magic cookie, send the packet to client as is. + */ + if (peer == client->active_peer && !has_magic_cookie) { + pj_stun_usage_sendto(client->tu->usage, client->pkt, bytes_read, 0, + &client->pkt_src_addr, client->pkt_src_addr_len); } else { - client = sd->client; - } + /* Otherwise wrap in Data Indication */ + pj_stun_tx_data *data_ind; - return client_handle_stun_msg(client, pkt, pkt_len, msg, - src_addr, src_addr_len); + status = pj_stun_session_create_ind(client->session, + PJ_STUN_DATA_INDICATION, + &data_ind); + if (status != PJ_SUCCESS) + return; + + pj_stun_msg_add_ip_addr_attr(data_ind->pool, data_ind->msg, + PJ_STUN_ATTR_REMOTE_ADDR, PJ_FALSE, + &client->pkt_src_addr, + client->pkt_src_addr_len); + pj_stun_msg_add_binary_attr(data_ind->pool, data_ind->msg, + PJ_STUN_ATTR_DATA, + client->pkt, bytes_read); + + + pj_stun_session_send_msg(client->session, PJ_FALSE, + &client->pkt_src_addr, + client->pkt_src_addr_len, + data_ind); + } } -static pj_status_t sess_on_send_msg(pj_stun_session *sess, - const void *pkt, - pj_size_t pkt_size, - const pj_sockaddr_t *dst_addr, - unsigned addr_len) + +/* + * This callback is called by the ioqueue when read operation has + * completed on the allocated relay port. + */ +static void client_on_read_complete(pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key, + pj_ssize_t bytes_read) { - struct session_data *sd; + enum { MAX_LOOP = 10 }; + struct turn_client *client; + unsigned count; + pj_status_t status; - sd = (struct session_data*) pj_stun_session_get_user_data(sess); + PJ_UNUSED_ARG(op_key); - if (sd->tu->type == PJ_SOCK_DGRAM) { - return pj_stun_usage_sendto(sd->tu->usage, pkt, pkt_size, 0, - dst_addr, addr_len); - } else { - return PJ_ENOTSUP; + client = pj_ioqueue_get_user_data(key); + + /* Lock client */ + pj_mutex_lock(client->mutex); + + for (count=0; ; ++count) { + unsigned flags; + + if (bytes_read > 0) { + /* Received data from peer! */ + client_handle_peer_data(client, bytes_read); + + } else if (bytes_read < 0) { + char errmsg[PJ_ERR_MSG_SIZE]; + pj_strerror(-bytes_read, errmsg, sizeof(errmsg)); + PJ_LOG(4,(THIS_FILE, "TURN client %s: error reading data " + "from allocated relay port: %s", + client->obj_name, errmsg)); + } + + bytes_read = sizeof(client->pkt); + flags = (count >= MAX_LOOP) ? PJ_IOQUEUE_ALWAYS_ASYNC : 0; + client->pkt_src_addr_len = sizeof(client->pkt_src_addr); + status = pj_ioqueue_recvfrom(client->key, + &client->pkt_read_key, + client->pkt, &bytes_read, flags, + &client->pkt_src_addr, + &client->pkt_src_addr_len); + if (status == PJ_EPENDING) + break; } + + /* Unlock client */ + pj_mutex_unlock(client->mutex); } +/* On Allocation timer timeout (i.e. we don't receive new Allocate request + * to refresh the allocation in time) + */ +static void client_on_expired(pj_timer_heap_t *th, pj_timer_entry *e) +{ + struct turn_client *client; + + PJ_UNUSED_ARG(th); + + client = (struct turn_client*) e->user_data; + + PJ_LOG(4,(THIS_FILE, "TURN client %s: allocation timer timeout, " + "destroying client", + client->obj_name)); + client_destroy(client, PJ_SUCCESS); +} |