Changeset 1862


Ignore:
Timestamp:
Mar 12, 2008 8:52:16 PM (16 years ago)
Author:
bennylp
Message:

More ticket #485: implementation of TURN UDP client session

Location:
pjproject/trunk/pjnath
Files:
2 added
6 edited

Legend:

Unmodified
Added
Removed
  • pjproject/trunk/pjnath/include/pjnath/errno.h

    r1501 r1862  
    5151/** 
    5252 * @hideinitializer 
     53 * Invalid STUN message 
     54 */ 
     55#define PJNATH_EINSTUNMSG           (PJNATH_ERRNO_START+1)  /* 370001 */ 
     56/** 
     57 * @hideinitializer 
    5358 * Invalid STUN message length. 
    5459 */ 
    55 #define PJNATH_EINSTUNMSGLEN        (PJNATH_ERRNO_START+1)  /* 370001 */ 
     60#define PJNATH_EINSTUNMSGLEN        (PJNATH_ERRNO_START+2)  /* 370002 */ 
    5661/** 
    5762 * @hideinitializer 
    5863 * Invalid or unexpected STUN message type 
    5964 */ 
    60 #define PJNATH_EINSTUNMSGTYPE       (PJNATH_ERRNO_START+2)  /* 370002 */ 
     65#define PJNATH_EINSTUNMSGTYPE       (PJNATH_ERRNO_START+3)  /* 370003 */ 
    6166/** 
    6267 * @hideinitializer 
    6368 * STUN transaction has timed out 
    6469 */ 
    65 #define PJNATH_ESTUNTIMEDOUT        (PJNATH_ERRNO_START+3)  /* 370003 */ 
    66  
     70#define PJNATH_ESTUNTIMEDOUT        (PJNATH_ERRNO_START+4)  /* 370004 */ 
    6771 
    6872 
     
    174178 
    175179 
     180/************************************************************ 
     181 * TURN ERROR CODES 
     182 ***********************************************************/ 
     183/** 
     184 * @hideinitializer 
     185 * Invalid or unsupported TURN transport. 
     186 */ 
     187#define PJNATH_ETURNINTP            (PJNATH_ERRNO_START+120) /* 370120 */ 
     188 
     189 
     190 
    176191/** 
    177192 * @} 
  • pjproject/trunk/pjnath/include/pjnath/stun_session.h

    r1852 r1862  
    113113     *                      response may contain non-NULL value if the  
    114114     *                      response contains STUN ERROR-CODE attribute. 
    115      * @param request       The original STUN request. 
     115     * @param tdata         The original STUN request. 
    116116     * @param response      The response message, on successful transaction, 
    117117     *                      or otherwise MAY BE NULL if status is not success. 
     
    223223 */ 
    224224PJ_DECL(void*) pj_stun_session_get_user_data(pj_stun_session *sess); 
     225 
     226/** 
     227 * Change the lock object used by the STUN session. By default, the STUN 
     228 * session uses a mutex to protect its internal data. If application already 
     229 * protects access to STUN session with higher layer lock, it may disable 
     230 * the mutex protection in the STUN session by changing the STUN session 
     231 * lock to a NULL mutex. 
     232 * 
     233 * @param sess      The STUN session instance. 
     234 * @param lock      New lock instance to be used by the STUN session. 
     235 * @param auto_del  Specify whether STUN session should destroy this 
     236 *                  lock instance when it's destroyed. 
     237 */ 
     238PJ_DECL(pj_status_t) pj_stun_session_set_lock(pj_stun_session *sess, 
     239                                              pj_lock_t *lock, 
     240                                              pj_bool_t auto_del); 
    225241 
    226242/** 
  • pjproject/trunk/pjnath/include/pjnath/turn_session.h

    r1854 r1862  
    4949#define PJ_TURN_NO_TIMEOUT          ((long)0x7FFFFFFF) 
    5050#define PJ_TURN_MAX_PKT_LEN         3000 
    51 #define PJ_TURN_PERM_TIMEOUT        300 
    52 #define PJ_TURN_CHANNEL_TIMEOUT     600 
    53  
    54  
    55 /** Transport types */ 
    56 enum { 
     51#define PJ_TURN_PERM_TIMEOUT        300 /* Must be greater than REFRESH_SEC_BEFORE */ 
     52#define PJ_TURN_CHANNEL_TIMEOUT     600 /* Must be greater than REFRESH_SEC_BEFORE */ 
     53#define PJ_TURN_REFRESH_SEC_BEFORE  60 
     54#define PJ_TURN_KEEP_ALIVE_SEC      15 
     55#define PJ_TURN_PEER_HTABLE_SIZE    8 
     56 
     57 
     58/** TURN transport types */ 
     59typedef enum pj_turn_tp_type 
     60{ 
    5761    PJ_TURN_TP_UDP = 16,    /**< UDP.   */ 
    58     PJ_TURN_TP_TCP = 6      /**< TCP.   */ 
    59 }; 
     62    PJ_TURN_TP_TCP = 6,     /**< TCP.   */ 
     63    PJ_TURN_TP_TLS = 256    /**< TLS.   */ 
     64} pj_turn_tp_type; 
     65 
     66 
     67/** TURN session state */ 
     68typedef enum pj_turn_state_t 
     69{ 
     70    /** 
     71     * TURN session has just been created. 
     72     */ 
     73    PJ_TURN_STATE_NULL, 
     74 
     75    /** 
     76     * TURN server has been configured and now is being resolved via 
     77     * DNS SRV resolution. 
     78     */ 
     79    PJ_TURN_STATE_RESOLVING, 
     80 
     81    /** 
     82     * TURN server has been resolved. If there is pending allocation to 
     83     * be done, it will be invoked immediately. 
     84     */ 
     85    PJ_TURN_STATE_RESOLVED, 
     86 
     87    /** 
     88     * TURN session has issued ALLOCATE request and is waiting for response 
     89     * from the TURN server. 
     90     */ 
     91    PJ_TURN_STATE_ALLOCATING, 
     92 
     93    /** 
     94     * TURN session has successfully allocated relay resoruce and now is 
     95     * ready to be used. 
     96     */ 
     97    PJ_TURN_STATE_READY, 
     98 
     99    /** 
     100     * TURN session has issued deallocate request and is waiting for a 
     101     * response from the TURN server. 
     102     */ 
     103    PJ_TURN_STATE_DEALLOCATING, 
     104 
     105    /** 
     106     * Deallocate response has been received. Normally the session will 
     107     * proceed to DESTROYING state immediately. 
     108     */ 
     109    PJ_TURN_STATE_DEALLOCATED, 
     110 
     111    /** 
     112     * TURN session is being destroyed. 
     113     */ 
     114    PJ_TURN_STATE_DESTROYING 
     115 
     116} pj_turn_state_t; 
     117 
    60118 
    61119/* ChannelData header */ 
     
    74132{ 
    75133    /** 
    76      * Callback to send outgoing packet. This callback is mandatory. 
     134     * This callback will be called by the TURN session whenever it 
     135     * needs to send outgoing message. Since the TURN session doesn't 
     136     * have a socket on its own, this callback must be implemented. 
    77137     */ 
    78138    pj_status_t (*on_send_pkt)(pj_turn_session *sess, 
     
    83143 
    84144    /** 
    85      * Notification when allocation completes, either successfully or 
    86      * with failure. 
    87      */ 
    88     void (*on_allocate_complete)(pj_turn_session *sess, 
    89                                  pj_status_t status); 
    90  
    91     /** 
    92      * Notification when data is received. 
     145     * Notification when peer address has been bound successfully to  
     146     * a channel number. 
     147     * 
     148     * This callback is optional. 
     149     */ 
     150    void (*on_channel_bound)(pj_turn_session *sess, 
     151                             const pj_sockaddr_t *peer_addr, 
     152                             unsigned addr_len, 
     153                             unsigned ch_num); 
     154 
     155    /** 
     156     * Notification when incoming data has been received, either through 
     157     * Data indication or ChannelData message from the TURN server. 
     158     * 
     159     * This callback is optional. 
    93160     */ 
    94161    void (*on_rx_data)(pj_turn_session *sess, 
     
    99166 
    100167    /** 
    101      * Notification when session has been destroyed. 
    102      */ 
    103     void (*on_destroyed)(pj_turn_session *sess); 
     168     * Notification when TURN session state has changed. Application should 
     169     * implement this callback at least to know that the TURN session is 
     170     * going to be destroyed. 
     171     */ 
     172    void (*on_state)(pj_turn_session *sess, pj_turn_state_t old_state, 
     173                     pj_turn_state_t new_state); 
    104174 
    105175} pj_turn_session_cb; 
     
    113183    int     bandwidth; 
    114184    int     lifetime; 
     185    int     ka_interval; 
    115186} pj_turn_alloc_param; 
    116187 
     
    121192typedef struct pj_turn_session_info 
    122193{ 
     194    /** 
     195     * The relay address 
     196     */ 
     197    pj_sockaddr     relay_addr; 
     198 
     199    /** 
     200     * The TURN server address for informational purpose. 
     201     */ 
    123202    pj_sockaddr     server; 
     203 
    124204} pj_turn_session_info; 
    125205 
    126206 
    127207/** 
     208 * Get TURN state name. 
     209 */ 
     210PJ_DECL(const char*) pj_turn_state_name(pj_turn_state_t state); 
     211 
     212 
     213/** 
    128214 * Create TURN client session. 
    129215 */ 
    130216PJ_DECL(pj_status_t) pj_turn_session_create(pj_stun_config *cfg, 
     217                                            const char *name, 
     218                                            int af, 
     219                                            pj_turn_tp_type conn_type, 
    131220                                            const pj_turn_session_cb *cb, 
     221                                            void *user_data, 
     222                                            unsigned options, 
    132223                                            pj_turn_session **p_sess); 
    133224 
     
    138229PJ_DECL(pj_status_t) pj_turn_session_destroy(pj_turn_session *sess); 
    139230 
     231 
     232/** 
     233 * Re-assign user data. 
     234 */ 
     235PJ_DECL(pj_status_t) pj_turn_session_set_user_data(pj_turn_session *sess, 
     236                                                   void *user_data); 
     237 
     238/** 
     239 * Retrieve user data. 
     240 */ 
     241PJ_DECL(void*) pj_turn_session_get_user_data(pj_turn_session *sess); 
    140242 
    141243/** 
     
    144246PJ_DECL(pj_status_t) pj_turn_session_set_server(pj_turn_session *sess, 
    145247                                                const pj_str_t *domain, 
    146                                                 const pj_str_t *res_name, 
    147248                                                int default_port, 
    148249                                                pj_dns_resolver *resolver); 
  • pjproject/trunk/pjnath/src/pjnath/errno.c

    r1501 r1862  
    3636{ 
    3737    /* STUN related error codes */ 
     38    PJ_BUILD_ERR( PJNATH_EINSTUNMSG,        "Invalid STUN message"), 
    3839    PJ_BUILD_ERR( PJNATH_EINSTUNMSGLEN,     "Invalid STUN message length"), 
    3940    PJ_BUILD_ERR( PJNATH_EINSTUNMSGTYPE,    "Invalid or unexpected STUN message type"), 
     
    6263    PJ_BUILD_ERR( PJNATH_EICEINCANDSDP,     "Invalid SDP \"candidate\" attribute"), 
    6364    PJ_BUILD_ERR( PJNATH_EICENOHOSTCAND,    "No host candidate associated with srflx"), 
     65 
     66    /* TURN related errors */ 
     67    PJ_BUILD_ERR( PJNATH_ETURNINTP,         "Invalid/unsupported transport"), 
    6468 
    6569}; 
  • pjproject/trunk/pjnath/src/pjnath/stun_session.c

    r1852 r1862  
    2424    pj_stun_config      *cfg; 
    2525    pj_pool_t           *pool; 
    26     pj_mutex_t          *mutex; 
     26    pj_lock_t           *lock; 
     27    pj_bool_t            delete_lock; 
    2728    pj_stun_session_cb   cb; 
    2829    void                *user_data; 
     
    403404    pj_list_init(&sess->cached_response_list); 
    404405 
    405     status = pj_mutex_create_recursive(pool, name, &sess->mutex); 
     406    status = pj_lock_create_recursive_mutex(pool, name, &sess->lock); 
    406407    if (status != PJ_SUCCESS) { 
    407408        pj_pool_release(pool); 
    408409        return status; 
    409410    } 
     411    sess->delete_lock = PJ_TRUE; 
    410412 
    411413    *p_sess = sess; 
     
    418420    PJ_ASSERT_RETURN(sess, PJ_EINVAL); 
    419421 
    420     pj_mutex_lock(sess->mutex); 
     422    pj_lock_acquire(sess->lock); 
    421423    while (!pj_list_empty(&sess->pending_request_list)) { 
    422424        pj_stun_tx_data *tdata = sess->pending_request_list.next; 
     
    427429        destroy_tdata(tdata); 
    428430    } 
    429     pj_mutex_unlock(sess->mutex); 
    430  
    431     pj_mutex_destroy(sess->mutex); 
     431    pj_lock_release(sess->lock); 
     432 
     433    if (sess->delete_lock) { 
     434        pj_lock_destroy(sess->lock); 
     435    } 
     436 
    432437    pj_pool_release(sess->pool); 
    433438 
     
    440445{ 
    441446    PJ_ASSERT_RETURN(sess, PJ_EINVAL); 
    442     pj_mutex_lock(sess->mutex); 
     447    pj_lock_acquire(sess->lock); 
    443448    sess->user_data = user_data; 
    444     pj_mutex_unlock(sess->mutex); 
     449    pj_lock_release(sess->lock); 
    445450    return PJ_SUCCESS; 
    446451} 
     
    450455    PJ_ASSERT_RETURN(sess, NULL); 
    451456    return sess->user_data; 
     457} 
     458 
     459PJ_DEF(pj_status_t) pj_stun_session_set_lock( pj_stun_session *sess, 
     460                                              pj_lock_t *lock, 
     461                                              pj_bool_t auto_del) 
     462{ 
     463    pj_lock_t *old_lock = sess->lock; 
     464    pj_bool_t old_del; 
     465 
     466    PJ_ASSERT_RETURN(sess && lock, PJ_EINVAL); 
     467 
     468    pj_lock_acquire(old_lock); 
     469    sess->lock = lock; 
     470    old_del = sess->delete_lock; 
     471    sess->delete_lock = auto_del; 
     472    pj_lock_release(old_lock); 
     473 
     474    if (old_lock) 
     475        pj_lock_destroy(old_lock); 
     476 
     477    return PJ_SUCCESS; 
    452478} 
    453479 
     
    603629 
    604630    /* Start locking the session now */ 
    605     pj_mutex_lock(sess->mutex); 
     631    pj_lock_acquire(sess->lock); 
    606632 
    607633    /* Apply options */ 
     
    609635    if (status != PJ_SUCCESS) { 
    610636        pj_stun_msg_destroy_tdata(sess, tdata); 
    611         pj_mutex_unlock(sess->mutex); 
     637        pj_lock_release(sess->lock); 
    612638        LOG_ERR_(sess, "Error applying options", status); 
    613639        return status; 
     
    617643    if (status != PJ_SUCCESS) { 
    618644        pj_stun_msg_destroy_tdata(sess, tdata); 
    619         pj_mutex_unlock(sess->mutex); 
     645        pj_lock_release(sess->lock); 
    620646        LOG_ERR_(sess, "Error getting creadential's key", status); 
    621647        return status; 
     
    629655    if (status != PJ_SUCCESS) { 
    630656        pj_stun_msg_destroy_tdata(sess, tdata); 
    631         pj_mutex_unlock(sess->mutex); 
     657        pj_lock_release(sess->lock); 
    632658        LOG_ERR_(sess, "STUN encode() error", status); 
    633659        return status; 
     
    657683        if (status != PJ_SUCCESS && status != PJ_EPENDING) { 
    658684            pj_stun_msg_destroy_tdata(sess, tdata); 
    659             pj_mutex_unlock(sess->mutex); 
     685            pj_lock_release(sess->lock); 
    660686            LOG_ERR_(sess, "Error sending STUN request", status); 
    661687            return status; 
     
    685711            if (status != PJ_SUCCESS) { 
    686712                pj_stun_msg_destroy_tdata(sess, tdata); 
    687                 pj_mutex_unlock(sess->mutex); 
     713                pj_lock_release(sess->lock); 
    688714                LOG_ERR_(sess, "Error scheduling response timer", status); 
    689715                return status; 
     
    708734 
    709735 
    710     pj_mutex_unlock(sess->mutex); 
     736    pj_lock_release(sess->lock); 
    711737    return status; 
    712738} 
     
    750776    PJ_ASSERT_RETURN(PJ_STUN_IS_REQUEST(tdata->msg->hdr.type), PJ_EINVAL); 
    751777 
    752     pj_mutex_lock(sess->mutex); 
     778    pj_lock_acquire(sess->lock); 
    753779 
    754780    if (notify) { 
     
    760786    pj_stun_msg_destroy_tdata(sess, tdata); 
    761787 
    762     pj_mutex_unlock(sess->mutex); 
     788    pj_lock_release(sess->lock); 
    763789    return PJ_SUCCESS; 
    764790} 
     
    775801    PJ_ASSERT_RETURN(PJ_STUN_IS_REQUEST(tdata->msg->hdr.type), PJ_EINVAL); 
    776802 
    777     pj_mutex_lock(sess->mutex); 
     803    pj_lock_acquire(sess->lock); 
    778804 
    779805    status = pj_stun_client_tsx_retransmit(tdata->client_tsx); 
    780806 
    781     pj_mutex_unlock(sess->mutex); 
     807    pj_lock_release(sess->lock); 
    782808 
    783809    return status; 
     
    10541080              pj_stun_msg_dump(msg, dump, PJ_STUN_MAX_PKT_LEN, NULL))); 
    10551081 
    1056     pj_mutex_lock(sess->mutex); 
     1082    pj_lock_acquire(sess->lock); 
    10571083 
    10581084    /* For requests, check if we have cached response */ 
     
    10891115 
    10901116on_return: 
    1091     pj_mutex_unlock(sess->mutex); 
     1117    pj_lock_release(sess->lock); 
    10921118 
    10931119    pj_pool_release(tmp_pool); 
  • pjproject/trunk/pjnath/src/pjnath/turn_session.c

    r1854 r1862  
    1818 */ 
    1919#include <pjnath/turn_session.h> 
     20#include <pjnath/errno.h> 
    2021#include <pjlib-util/srv_resolver.h> 
    2122#include <pj/addr_resolv.h> 
    2223#include <pj/assert.h> 
    2324#include <pj/errno.h> 
     25#include <pj/hash.h> 
     26#include <pj/lock.h> 
    2427#include <pj/log.h> 
     28#include <pj/os.h> 
    2529#include <pj/pool.h> 
    2630#include <pj/sock.h> 
    2731 
    28  
    29 enum state_t 
    30 { 
    31     STATE_NULL, 
    32     STATE_RESOLVING, 
    33     STATE_RESOLVED, 
    34     STATE_ALLOCATING, 
    35     STATE_READY 
     32#define MAX_SRV_CNT         4 
     33#define REFRESH_SEC_BEFORE  60 
     34 
     35static const char *state_names[] =  
     36{ 
     37    "Null", 
     38    "Resolving", 
     39    "Resolved", 
     40    "Allocating", 
     41    "Ready", 
     42    "Deallocating", 
     43    "Deallocated", 
     44    "Destroying" 
    3645}; 
    3746 
     47enum timer_id_t 
     48{ 
     49    TIMER_NONE, 
     50    TIMER_KEEP_ALIVE, 
     51    TIMER_DESTROY 
     52}; 
     53 
     54 
    3855struct peer 
    3956{ 
    40     unsigned        ch_id; 
    41     pj_sockaddr     peer_addr; 
     57    pj_uint16_t     ch_id; 
     58    pj_bool_t       bound; 
     59    pj_sockaddr     addr; 
    4260    pj_time_val     expiry; 
    43     pj_uint8_t      tsx_id[12]; /* Pending ChannelBind request */ 
    4461}; 
    4562 
     
    4966    const char          *obj_name; 
    5067    pj_turn_session_cb   cb; 
    51  
    52     enum state_t         state; 
     68    void                *user_data; 
     69 
     70    pj_lock_t           *lock; 
     71    int                  busy; 
     72 
     73    pj_turn_state_t      state; 
     74    pj_bool_t            pending_destroy; 
     75    pj_bool_t            destroy_notified; 
    5376 
    5477    pj_stun_session     *stun; 
    5578 
     79    unsigned             lifetime; 
     80    int                  ka_interval; 
     81    pj_time_val          expiry; 
     82 
     83    pj_timer_heap_t     *timer_heap; 
     84    pj_timer_entry       timer; 
     85 
    5686    pj_dns_async_query  *dns_async; 
    57  
    58     unsigned             srv_addr_cnt; 
     87    pj_uint16_t          default_port; 
     88 
     89    pj_uint16_t          af; 
     90    pj_turn_tp_type      tp_type; 
     91    pj_uint16_t          srv_addr_cnt; 
    5992    pj_sockaddr         *srv_addr_list; 
    6093    pj_sockaddr         *srv_addr; 
     
    6396    pj_turn_alloc_param  alloc_param; 
    6497 
     98    pj_hash_table_t     *peer_table; 
     99 
    65100    /* tx_pkt must be 16bit aligned */ 
    66101    pj_uint8_t           tx_pkt[PJ_TURN_MAX_PKT_LEN]; 
     
    73108 * Prototypes. 
    74109 */ 
     110static void sess_shutdown(pj_turn_session *sess, 
     111                          pj_bool_t notify, 
     112                          pj_status_t status); 
     113static void do_destroy(pj_turn_session *sess); 
     114static void send_refresh(pj_turn_session *sess, int lifetime); 
    75115static pj_status_t stun_on_send_msg(pj_stun_session *sess, 
    76116                                    const void *pkt, 
     
    93133                                pj_status_t status, 
    94134                                const pj_dns_srv_record *rec); 
    95 static void dns_a_resolver_cb(void *user_data, 
    96                               pj_status_t status, 
    97                               pj_dns_parsed_packet *response); 
    98135static struct peer *lookup_peer_by_addr(pj_turn_session *sess, 
    99136                                        const pj_sockaddr_t *addr, 
    100137                                        unsigned addr_len, 
    101                                         pj_bool_t update); 
     138                                        pj_bool_t update, 
     139                                        pj_bool_t bind_channel); 
    102140static struct peer *lookup_peer_by_chnum(pj_turn_session *sess, 
    103                                          unsigned chnum); 
    104  
     141                                         pj_uint16_t chnum); 
     142static void on_timer_event(pj_timer_heap_t *th, pj_timer_entry *e); 
     143 
     144 
     145/** 
     146 * Get TURN state name. 
     147 */ 
     148PJ_DEF(const char*) pj_turn_state_name(pj_turn_state_t state) 
     149{ 
     150    return state_names[state]; 
     151} 
    105152 
    106153/* 
     
    108155 */ 
    109156PJ_DEF(pj_status_t) pj_turn_session_create( pj_stun_config *cfg, 
     157                                            const char *name, 
     158                                            int af, 
     159                                            pj_turn_tp_type tp_type, 
    110160                                            const pj_turn_session_cb *cb, 
     161                                            void *user_data, 
     162                                            unsigned options, 
    111163                                            pj_turn_session **p_sess) 
    112164{ 
     
    114166    pj_turn_session *sess; 
    115167    pj_stun_session_cb stun_cb; 
     168    pj_lock_t *null_lock; 
    116169    pj_status_t status; 
    117170 
    118171    PJ_ASSERT_RETURN(cfg && cfg->pf && cb && p_sess, PJ_EINVAL); 
     172    PJ_ASSERT_RETURN(cb->on_send_pkt, PJ_EINVAL); 
     173 
     174    PJ_UNUSED_ARG(options); 
     175 
     176    if (name == NULL) 
     177        name = "turn%p"; 
    119178 
    120179    /* Allocate and create TURN session */ 
    121     pool = pj_pool_create(cfg->pf, "turn%p", 1000, 1000, NULL); 
     180    pool = pj_pool_create(cfg->pf, name, 1000, 1000, NULL); 
    122181    sess = PJ_POOL_ZALLOC_T(pool, pj_turn_session); 
    123182    sess->pool = pool; 
    124183    sess->obj_name = pool->obj_name; 
    125  
     184    sess->timer_heap = cfg->timer_heap; 
     185    sess->af = (pj_uint16_t)af; 
     186    sess->tp_type = tp_type; 
     187    sess->ka_interval = PJ_TURN_KEEP_ALIVE_SEC; 
     188    sess->user_data = user_data; 
     189 
     190    /* Copy callback */ 
    126191    pj_memcpy(&sess->cb, cb, sizeof(*cb)); 
     192 
     193    /* Peer hash table */ 
     194    sess->peer_table = pj_hash_create(pool, PJ_TURN_PEER_HTABLE_SIZE); 
     195 
     196    /* Session lock */ 
     197    status = pj_lock_create_recursive_mutex(pool, sess->obj_name,  
     198                                            &sess->lock); 
     199    if (status != PJ_SUCCESS) { 
     200        do_destroy(sess); 
     201        return status; 
     202    } 
     203 
     204    /* Timer */ 
     205    pj_timer_entry_init(&sess->timer, TIMER_NONE, sess, &on_timer_event); 
    127206 
    128207    /* Create STUN session */ 
     
    134213                                    &sess->stun); 
    135214    if (status != PJ_SUCCESS) { 
    136         pj_turn_session_destroy(sess); 
     215        do_destroy(sess); 
    137216        return status; 
    138217    } 
    139218 
    140     /* Done for now */ 
     219    /* Replace mutex in STUN session with a NULL mutex, since access to 
     220     * STUN session is serialized. 
     221     */ 
     222    status = pj_lock_create_null_mutex(pool, name, &null_lock); 
     223    if (status != PJ_SUCCESS) { 
     224        do_destroy(sess); 
     225        return status; 
     226    } 
     227    pj_stun_session_set_lock(sess->stun, null_lock, PJ_TRUE); 
     228 
     229    /* Done */ 
     230 
     231    PJ_LOG(4,(sess->obj_name, "TURN client session created")); 
     232 
    141233    *p_sess = sess; 
    142234    return PJ_SUCCESS; 
     
    144236 
    145237 
    146 /* 
    147  * Destroy TURN client session. 
     238/* Destroy */ 
     239static void do_destroy(pj_turn_session *sess) 
     240{ 
     241    /* Lock session */ 
     242    if (sess->lock) { 
     243        pj_lock_acquire(sess->lock); 
     244    } 
     245 
     246    /* Cancel pending timer, if any */ 
     247    if (sess->timer.id != TIMER_NONE) { 
     248        pj_timer_heap_cancel(sess->timer_heap, &sess->timer); 
     249        sess->timer.id = TIMER_NONE; 
     250    } 
     251 
     252    /* Destroy STUN session */ 
     253    if (sess->stun) { 
     254        pj_stun_session_destroy(sess->stun); 
     255        sess->stun = NULL; 
     256    } 
     257 
     258    /* Destroy lock */ 
     259    if (sess->lock) { 
     260        pj_lock_release(sess->lock); 
     261        pj_lock_destroy(sess->lock); 
     262        sess->lock = NULL; 
     263    } 
     264 
     265    /* Destroy pool */ 
     266    if (sess->pool) { 
     267        pj_pool_t *pool = sess->pool; 
     268 
     269        PJ_LOG(4,(sess->obj_name, "TURN client session destroyed")); 
     270 
     271        sess->pool = NULL; 
     272        pj_pool_release(pool); 
     273    } 
     274} 
     275 
     276 
     277/* Set session state */ 
     278static void set_state(pj_turn_session *sess, enum pj_turn_state_t state) 
     279{ 
     280    pj_turn_state_t old_state = sess->state; 
     281 
     282    PJ_LOG(4,(sess->obj_name, "State changed %s --> %s", 
     283              state_names[old_state], state_names[state])); 
     284    sess->state = state; 
     285 
     286    if (sess->cb.on_state) { 
     287        (*sess->cb.on_state)(sess, old_state, state); 
     288    } 
     289} 
     290 
     291/* 
     292 * Notify application and shutdown the TURN session. 
     293 */ 
     294static void sess_shutdown(pj_turn_session *sess, 
     295                          pj_bool_t notify, 
     296                          pj_status_t status) 
     297{ 
     298    pj_bool_t can_destroy = PJ_TRUE; 
     299 
     300    PJ_UNUSED_ARG(notify); 
     301 
     302    PJ_LOG(4,(sess->obj_name, "Request to shutdown in state %s, cause:%d", 
     303              state_names[sess->state], status)); 
     304 
     305    switch (sess->state) { 
     306    case PJ_TURN_STATE_NULL: 
     307        break; 
     308    case PJ_TURN_STATE_RESOLVING: 
     309        pj_assert(sess->dns_async != NULL); 
     310        pj_dns_resolver_cancel_query(sess->dns_async, PJ_FALSE); 
     311        sess->dns_async = NULL; 
     312        break; 
     313    case PJ_TURN_STATE_RESOLVED: 
     314        break; 
     315    case PJ_TURN_STATE_ALLOCATING: 
     316        /* We need to wait until allocation complete */ 
     317        sess->pending_destroy = PJ_TRUE; 
     318        can_destroy = PJ_FALSE; 
     319        break; 
     320    case PJ_TURN_STATE_READY: 
     321        /* Send REFRESH with LIFETIME=0 */ 
     322        can_destroy = PJ_FALSE; 
     323        sess->pending_destroy = PJ_TRUE; 
     324        break; 
     325    case PJ_TURN_STATE_DEALLOCATING: 
     326        can_destroy = PJ_FALSE; 
     327        /* This may recursively call this function again with 
     328         * state==PJ_TURN_STATE_DEALLOCATED. 
     329         */ 
     330        send_refresh(sess, 0); 
     331        break; 
     332    case PJ_TURN_STATE_DEALLOCATED: 
     333        break; 
     334    } 
     335 
     336    if (can_destroy) { 
     337        /* Schedule destroy */ 
     338        pj_time_val delay = {0, 0}; 
     339 
     340        if (sess->timer.id != TIMER_NONE) { 
     341            pj_timer_heap_cancel(sess->timer_heap, &sess->timer); 
     342            sess->timer.id = TIMER_NONE; 
     343        } 
     344 
     345        set_state(sess, PJ_TURN_STATE_DESTROYING); 
     346 
     347        sess->timer.id = TIMER_DESTROY; 
     348        pj_timer_heap_schedule(sess->timer_heap, &sess->timer, &delay); 
     349    } 
     350} 
     351 
     352 
     353/* 
     354 * Public API to destroy TURN client session. 
    148355 */ 
    149356PJ_DEF(pj_status_t) pj_turn_session_destroy(pj_turn_session *sess) 
     
    151358    PJ_ASSERT_RETURN(sess, PJ_EINVAL); 
    152359 
    153     /* TODO */ 
    154 } 
    155  
    156  
    157 /* 
    158  * Notify application and destroy the TURN session. 
    159  */ 
    160 static void destroy(pj_turn_session *sess, 
    161                     pj_bool_t notify, 
    162                     pj_status_t status) 
    163 { 
     360    pj_lock_acquire(sess->lock); 
     361 
     362    sess_shutdown(sess, PJ_FALSE, PJ_SUCCESS); 
     363 
     364    pj_lock_release(sess->lock); 
     365 
     366    return PJ_SUCCESS; 
     367} 
     368 
     369 
     370/* 
     371 * Re-assign user data. 
     372 */ 
     373PJ_DEF(pj_status_t) pj_turn_session_set_user_data( pj_turn_session *sess, 
     374                                                   void *user_data) 
     375{ 
     376    sess->user_data = user_data; 
     377    return PJ_SUCCESS; 
     378} 
     379 
     380 
     381/** 
     382 * Retrieve user data. 
     383 */ 
     384PJ_DEF(void*) pj_turn_session_get_user_data(pj_turn_session *sess) 
     385{ 
     386    return sess->user_data; 
    164387} 
    165388 
     
    170393PJ_DEF(pj_status_t) pj_turn_session_set_server( pj_turn_session *sess, 
    171394                                                const pj_str_t *domain, 
    172                                                 const pj_str_t *res_name, 
    173395                                                int default_port, 
    174396                                                pj_dns_resolver *resolver) 
     
    177399 
    178400    PJ_ASSERT_RETURN(sess && domain, PJ_EINVAL); 
    179  
    180     if (res_name) { 
    181         /* res_name is specified, resolve with DNS SRV resolution. 
    182          * Resolver must be specified in this case. 
     401    PJ_ASSERT_RETURN(sess->state == PJ_TURN_STATE_NULL, PJ_EINVALIDOP); 
     402 
     403    pj_lock_acquire(sess->lock); 
     404 
     405    if (resolver) { 
     406        /* Resolve with DNS SRV resolution, and fallback to DNS A resolution 
     407         * if default_port is specified. 
    183408         */ 
    184         PJ_ASSERT_RETURN(resolver, PJ_EINVAL); 
    185      
    186         sess->state = STATE_RESOLVING; 
    187         status = pj_dns_srv_resolve(domain, res_name, default_port, sess->pool, 
    188                                     resolver, PJ_DNS_SRV_FALLBACK_A, sess,  
     409        unsigned opt = 0; 
     410        pj_str_t res_name; 
     411 
     412        switch (sess->tp_type) { 
     413        case PJ_TURN_TP_UDP: 
     414            res_name = pj_str("_turn._udp."); 
     415            break; 
     416        case PJ_TURN_TP_TCP: 
     417            res_name = pj_str("_turn._tcp."); 
     418            break; 
     419        case PJ_TURN_TP_TLS: 
     420            res_name = pj_str("_turns._tcp."); 
     421            break; 
     422        default: 
     423            status = PJNATH_ETURNINTP; 
     424            goto on_return; 
     425        } 
     426 
     427        /* Fallback to DNS A only if default port is specified */ 
     428        if (default_port>0 && default_port<65536) { 
     429            opt = PJ_DNS_SRV_FALLBACK_A; 
     430            sess->default_port = (pj_uint16_t)default_port; 
     431        } 
     432 
     433        set_state(sess, PJ_TURN_STATE_RESOLVING); 
     434        status = pj_dns_srv_resolve(domain, &res_name, default_port,  
     435                                    sess->pool, resolver, opt, sess,  
    189436                                    &dns_srv_resolver_cb, &sess->dns_async); 
    190437        if (status != PJ_SUCCESS) { 
    191             sess->state = STATE_NULL; 
    192             return status; 
    193         } 
    194  
    195     } else if (resolver) { 
    196         /* res_name is not specified, but resolver is specified. 
    197          * Resolve domain as a hostname with DNS A resolution. 
     438            set_state(sess, PJ_TURN_STATE_NULL); 
     439            goto on_return; 
     440        } 
     441 
     442    } else { 
     443        /* Resolver is not specified, resolve with standard gethostbyname(). 
     444         * The default_port MUST be specified in this case. 
    198445         */ 
    199         sess->state = STATE_RESOLVING; 
    200         status = pj_dns_resolver_start_query(resolver, domain, PJ_DNS_TYPE_A, 
    201                                              0, &dns_a_resolver_cb, 
    202                                              sess, &sess->dns_async); 
    203         if (status != PJ_SUCCESS) { 
    204             sess->state = STATE_NULL; 
    205             return status; 
    206         } 
    207  
    208     } else { 
    209         /* Both res_name and resolver is not specified. 
    210          * Resolve with standard gethostbyname() 
    211          */ 
    212         pj_addrinfo ai[3]; 
    213         unsigned i, cnt = PJ_ARRAY_SIZE(ai); 
    214  
    215         status = pj_getaddrinfo(pj_AF_INET(), domain, &cnt, ai); 
     446        pj_addrinfo *ai; 
     447        unsigned i, cnt; 
     448 
     449        /* Default port must be specified */ 
     450        PJ_ASSERT_RETURN(default_port>0 && default_port<65536, PJ_EINVAL); 
     451        sess->default_port = (pj_uint16_t)default_port; 
     452 
     453        cnt = MAX_SRV_CNT; 
     454        ai = (pj_addrinfo*) 
     455             pj_pool_calloc(sess->pool, cnt, sizeof(pj_addrinfo)); 
     456 
     457        status = pj_getaddrinfo(sess->af, domain, &cnt, ai); 
    216458        if (status != PJ_SUCCESS) 
    217             return status; 
    218  
    219         sess->srv_addr_cnt = cnt; 
     459            goto on_return; 
     460 
     461        sess->srv_addr_cnt = (pj_uint16_t)cnt; 
    220462        sess->srv_addr_list = (pj_sockaddr*) 
    221463                              pj_pool_calloc(sess->pool, cnt,  
    222464                                             sizeof(pj_sockaddr)); 
    223465        for (i=0; i<cnt; ++i) { 
    224             pj_memcpy(&sess->srv_addr_list[i], &ai[i].ai_addr,  
    225                       sizeof(pj_sockaddr)); 
     466            pj_sockaddr *addr = &sess->srv_addr_list[i]; 
     467            pj_memcpy(addr, &ai[i].ai_addr, sizeof(pj_sockaddr)); 
     468            addr->addr.sa_family = sess->af; 
     469            addr->ipv4.sin_port = pj_htons(sess->default_port); 
    226470        } 
    227471 
    228472        sess->srv_addr = &sess->srv_addr_list[0]; 
    229         sess->state = STATE_RESOLVED; 
    230     } 
    231  
    232     return PJ_SUCCESS; 
     473        set_state(sess, PJ_TURN_STATE_RESOLVED); 
     474    } 
     475 
     476on_return: 
     477    pj_lock_release(sess->lock); 
     478    return status; 
    233479} 
    234480 
     
    241487{ 
    242488    PJ_ASSERT_RETURN(sess && cred, PJ_EINVAL); 
     489 
     490    pj_lock_acquire(sess->lock); 
     491 
    243492    pj_stun_session_set_credential(sess->stun, cred); 
     493 
     494    pj_lock_release(sess->lock); 
     495 
    244496    return PJ_SUCCESS; 
    245497} 
     
    256508 
    257509    PJ_ASSERT_RETURN(sess, PJ_EINVAL); 
    258     PJ_ASSERT_RETURN(sess->state <= STATE_RESOLVED, PJ_EINVALIDOP); 
    259  
    260     if (sess->state < STATE_RESOLVED) { 
    261         if (param) 
     510    PJ_ASSERT_RETURN(sess->state>PJ_TURN_STATE_NULL && sess->state<=PJ_TURN_STATE_RESOLVED,  
     511                     PJ_EINVALIDOP); 
     512 
     513    pj_lock_acquire(sess->lock); 
     514 
     515    if (sess->state < PJ_TURN_STATE_RESOLVED) { 
     516        if (param && param != &sess->alloc_param) 
    262517            pj_memcpy(&sess->alloc_param, param, sizeof(*param)); 
    263518        sess->pending_alloc = PJ_TRUE; 
     519 
     520        PJ_LOG(4,(sess->obj_name, "Pending ALLOCATE in state %s", 
     521                  state_names[sess->state])); 
     522 
     523        pj_lock_release(sess->lock); 
    264524        return PJ_SUCCESS; 
    265525 
     
    267527 
    268528    /* Ready to allocate */ 
    269     pj_assert(sess->state == STATE_RESOLVED); 
     529    pj_assert(sess->state == PJ_TURN_STATE_RESOLVED); 
    270530     
    271531    /* Create a bare request */ 
    272532    status = pj_stun_session_create_req(sess->stun, PJ_STUN_ALLOCATE_REQUEST, 
    273533                                        PJ_STUN_MAGIC, NULL, &tdata); 
    274     if (status != PJ_SUCCESS) 
     534    if (status != PJ_SUCCESS) { 
     535        pj_lock_release(sess->lock); 
    275536        return status; 
     537    } 
    276538 
    277539    /* MUST include REQUESTED-TRANSPORT attribute */ 
     
    294556    } 
    295557 
    296     /* Select server address */ 
     558    /* Server address must be set */ 
    297559    pj_assert(sess->srv_addr != NULL); 
    298560 
    299561    /* Send request */ 
    300     sess->state = STATE_ALLOCATING; 
     562    set_state(sess, PJ_TURN_STATE_ALLOCATING); 
    301563    status = pj_stun_session_send_msg(sess->stun, PJ_FALSE, sess->srv_addr, 
    302564                                      pj_sockaddr_get_len(sess->srv_addr),  
    303565                                      tdata); 
    304566    if (status != PJ_SUCCESS) { 
    305         sess->state = STATE_RESOLVED; 
    306     } 
    307  
     567        /* Set state back to RESOLVED. We don't want to destroy session now, 
     568         * let the application do it if it wants to. 
     569         */ 
     570        set_state(sess, PJ_TURN_STATE_RESOLVED); 
     571    } 
     572 
     573    pj_lock_release(sess->lock); 
    308574    return status; 
     575} 
     576 
     577 
     578/* 
     579 * Send REFRESH 
     580 */ 
     581static void send_refresh(pj_turn_session *sess, int lifetime) 
     582{ 
     583    pj_stun_tx_data *tdata; 
     584    pj_status_t status; 
     585 
     586    PJ_ASSERT_ON_FAIL(sess->state==PJ_TURN_STATE_READY, return); 
     587 
     588    /* Create a bare REFRESH request */ 
     589    status = pj_stun_session_create_req(sess->stun, PJ_STUN_REFRESH_REQUEST, 
     590                                        PJ_STUN_MAGIC, NULL, &tdata); 
     591    if (status != PJ_SUCCESS) 
     592        goto on_error; 
     593 
     594    /* Add LIFETIME */ 
     595    if (lifetime >= 0) { 
     596        pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg, 
     597                                  PJ_STUN_ATTR_LIFETIME, lifetime); 
     598    } 
     599 
     600    /* Send request */ 
     601    if (lifetime == 0) { 
     602        set_state(sess, PJ_TURN_STATE_DEALLOCATING); 
     603    } 
     604 
     605    status = pj_stun_session_send_msg(sess->stun, PJ_FALSE, sess->srv_addr, 
     606                                      pj_sockaddr_get_len(sess->srv_addr),  
     607                                      tdata); 
     608    if (status != PJ_SUCCESS) 
     609        goto on_error; 
     610 
     611    return; 
     612 
     613on_error: 
     614    if (lifetime == 0) { 
     615        set_state(sess, PJ_TURN_STATE_DEALLOCATED); 
     616        sess_shutdown(sess, PJ_FALSE, status); 
     617    } 
    309618} 
    310619 
     
    316625                                            const pj_uint8_t *pkt, 
    317626                                            unsigned pkt_len, 
    318                                             const pj_sockaddr_t *peer_addr, 
     627                                            const pj_sockaddr_t *addr, 
    319628                                            unsigned addr_len) 
    320629{ 
    321630    struct peer *peer; 
    322  
    323     PJ_ASSERT_RETURN(sess && pkt && pkt_len && peer_addr && addr_len,  
     631    pj_status_t status; 
     632 
     633    PJ_ASSERT_RETURN(sess && pkt && pkt_len && addr && addr_len,  
    324634                     PJ_EINVAL); 
    325635 
    326636    /* Return error if we're not ready */ 
    327     if (sess->state != STATE_READY) { 
     637    if (sess->state != PJ_TURN_STATE_READY) { 
    328638        return PJ_EIGNORED; 
    329639    } 
     640 
     641    /* Lock session now */ 
     642    pj_lock_acquire(sess->lock); 
    330643 
    331644    /* Lookup peer to see whether we've assigned a channel number 
    332645     * to this peer. 
    333646     */ 
    334     peer = lookup_peer_by_addr(sess, peer_addr, addr_len, PJ_TRUE); 
     647    peer = lookup_peer_by_addr(sess, addr, addr_len, PJ_TRUE, PJ_FALSE); 
    335648    pj_assert(peer != NULL); 
    336649 
    337     if (peer->ch_id != PJ_TURN_INVALID_CHANNEL) { 
     650    if (peer->ch_id != PJ_TURN_INVALID_CHANNEL && peer->bound) { 
    338651        /* Peer is assigned Channel number, we can use ChannelData */ 
    339652        pj_turn_channel_data *cd = (pj_turn_channel_data*)sess->tx_pkt; 
     
    341654        pj_assert(sizeof(*cd)==4); 
    342655 
    343         if (pkt_len > sizeof(sess->tx_pkt)-sizeof(*cd)) 
    344             return PJ_ETOOBIG; 
     656        if (pkt_len > sizeof(sess->tx_pkt)-sizeof(*cd)) { 
     657            status = PJ_ETOOBIG; 
     658            goto on_return; 
     659        } 
    345660 
    346661        cd->ch_number = pj_htons((pj_uint16_t)peer->ch_id); 
     
    350665        pj_assert(sess->srv_addr != NULL); 
    351666 
    352         return sess->cb.on_send_pkt(sess, sess->tx_pkt, pkt_len+sizeof(*cd), 
    353                                     sess->srv_addr, 
    354                                     pj_sockaddr_get_len(sess->srv_addr)); 
     667        status = sess->cb.on_send_pkt(sess, sess->tx_pkt, pkt_len+sizeof(*cd), 
     668                                      sess->srv_addr, 
     669                                      pj_sockaddr_get_len(sess->srv_addr)); 
    355670 
    356671    } else { 
     
    359674         */ 
    360675        pj_stun_tx_data *tdata; 
    361         pj_status_t status; 
    362676 
    363677        /* Create blank SEND-INDICATION */ 
     
    365679                                            PJ_STUN_SEND_INDICATION, &tdata); 
    366680        if (status != PJ_SUCCESS) 
    367             return status; 
     681            goto on_return; 
    368682 
    369683        /* Add PEER-ADDRESS */ 
    370684        pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg, 
    371685                                      PJ_STUN_ATTR_PEER_ADDR, PJ_TRUE, 
    372                                       peer_addr, addr_len); 
     686                                      addr, addr_len); 
    373687 
    374688        /* Add DATA attribute */ 
     
    377691 
    378692        /* Send the indication */ 
    379         return pj_stun_session_send_msg(sess->stun, PJ_FALSE, sess->srv_addr, 
    380                                         pj_sockaddr_get_len(sess->srv_addr), 
    381                                         tdata); 
    382     } 
     693        status = pj_stun_session_send_msg(sess->stun, PJ_FALSE, sess->srv_addr, 
     694                                          pj_sockaddr_get_len(sess->srv_addr), 
     695                                          tdata); 
     696    } 
     697 
     698on_return: 
     699    pj_lock_release(sess->lock); 
     700    return status; 
    383701} 
    384702 
     
    393711    struct peer *peer; 
    394712    pj_stun_tx_data *tdata; 
    395     unsigned ch_num; 
     713    pj_uint16_t ch_num; 
    396714    pj_status_t status; 
    397715 
    398     PJ_ASSERT_RETURN(sess && peer && addr_len, PJ_EINVAL); 
     716    PJ_ASSERT_RETURN(sess && peer_adr && addr_len, PJ_EINVAL); 
     717    PJ_ASSERT_RETURN(sess->state == PJ_TURN_STATE_READY, PJ_EINVALIDOP); 
     718 
     719    pj_lock_acquire(sess->lock); 
    399720 
    400721    /* Create blank ChannelBind request */ 
     
    403724                                        PJ_STUN_MAGIC, NULL, &tdata); 
    404725    if (status != PJ_SUCCESS) 
    405         return status; 
     726        goto on_return; 
    406727 
    407728    /* Lookup peer */ 
    408     peer = lookup_peer_by_addr(sess, peer_adr, addr_len, PJ_TRUE); 
     729    peer = lookup_peer_by_addr(sess, peer_adr, addr_len, PJ_TRUE, PJ_FALSE); 
    409730    pj_assert(peer); 
    410731 
     732    /* Associate peer data structure with tdata for future reference 
     733     * when we receive the ChannelBind response. 
     734     */ 
     735    tdata->user_data = peer; 
     736 
    411737    if (peer->ch_id != PJ_TURN_INVALID_CHANNEL) { 
     738        /* Channel is already bound. This is a refresh request. */ 
    412739        ch_num = peer->ch_id; 
    413740    } else { 
    414         PJ_ASSERT_RETURN(sess->next_ch <= PJ_TURN_CHANNEL_MAX, PJ_ETOOMANY); 
    415         ch_num = sess->next_ch++; 
     741        PJ_ASSERT_ON_FAIL(sess->next_ch <= PJ_TURN_CHANNEL_MAX,  
     742                            {status=PJ_ETOOMANY; goto on_return;}); 
     743        peer->ch_id = ch_num = sess->next_ch++; 
    416744    } 
    417745 
     
    426754                                  peer_adr, addr_len); 
    427755 
    428     /* Save transaction ID to peer */ 
    429     pj_memcpy(peer->tsx_id, tdata->msg->hdr.tsx_id, sizeof(peer->tsx_id)); 
    430  
    431756    /* Send the request */ 
    432     return pj_stun_session_send_msg(sess->stun, PJ_FALSE, sess->srv_addr, 
    433                                     pj_sockaddr_get_len(sess->srv_addr), 
    434                                     tdata); 
     757    status = pj_stun_session_send_msg(sess->stun, PJ_FALSE, sess->srv_addr, 
     758                                      pj_sockaddr_get_len(sess->srv_addr), 
     759                                      tdata); 
     760 
     761on_return: 
     762    pj_lock_release(sess->lock); 
     763    return status; 
    435764} 
    436765 
     
    446775{ 
    447776    pj_bool_t is_stun; 
     777    pj_status_t status; 
    448778 
    449779    /* Packet could be ChannelData or STUN message (response or 
    450780     * indication). 
    451781     */ 
     782 
     783    /* Start locking the session */ 
     784    pj_lock_acquire(sess->lock); 
     785 
    452786    /* Quickly check if this is STUN message */ 
    453787    is_stun = ((pkt[0] & 0xC0) == 0); 
     
    460794        if (is_datagram) 
    461795            options |= PJ_STUN_IS_DATAGRAM; 
    462         return pj_stun_session_on_rx_pkt(sess->stun, pkt, pkt_len, 
     796        status=pj_stun_session_on_rx_pkt(sess->stun, pkt, pkt_len, 
    463797                                         options, NULL, 
    464798                                         sess->srv_addr, 
    465799                                         pj_sockaddr_get_len(sess->srv_addr)); 
    466     } else { 
    467         /* This must be ChannelData */ 
     800 
     801    } else if (sess->cb.on_rx_data) { 
     802 
     803        /* This must be ChannelData. Only makes sense when on_rx_data() is 
     804         * implemented by application. 
     805         */ 
    468806        pj_turn_channel_data cd; 
    469807        struct peer *peer; 
    470808 
     809        PJ_ASSERT_RETURN(pkt_len >= 4, PJ_ETOOSMALL); 
     810 
    471811        /* Lookup peer */ 
    472812        pj_memcpy(&cd, pkt, sizeof(pj_turn_channel_data)); 
    473         peer = lookup_peer_by_chnum(sess, pj_ntohs(cd.ch_number)); 
    474         if (!peer) 
    475             return PJ_ENOTFOUND; 
     813        cd.ch_number = pj_ntohs(cd.ch_number); 
     814        cd.length = pj_ntohs(cd.length); 
     815        peer = lookup_peer_by_chnum(sess, cd.ch_number); 
     816        if (!peer || !peer->bound) { 
     817            status = PJ_ENOTFOUND; 
     818            goto on_return; 
     819        } 
     820 
     821        /* Check that size is correct, for UDP */ 
     822        if (pkt_len < cd.length+sizeof(cd)) { 
     823            status = PJ_ETOOSMALL; 
     824            goto on_return; 
     825        } 
    476826 
    477827        /* Notify application */ 
    478         if (sess->cb.on_rx_data) { 
    479             (*sess->cb.on_rx_data)(sess, pkt+sizeof(cd), pj_ntohs(cd.length), 
    480                                    &peer->peer_addr, 
    481                                    pj_sockaddr_get_len(&peer->peer_addr)); 
    482         } 
    483  
    484         return PJ_SUCCESS; 
    485     } 
     828        (*sess->cb.on_rx_data)(sess, pkt+sizeof(cd), cd.length, 
     829                               &peer->addr, 
     830                               pj_sockaddr_get_len(&peer->addr)); 
     831 
     832        status = PJ_SUCCESS; 
     833 
     834    } else { 
     835        /* This is ChannelData and application doesn't implement 
     836         * on_rx_data() callback. Just ignore the packet. 
     837         */ 
     838        status = PJ_SUCCESS; 
     839    } 
     840 
     841on_return: 
     842    pj_lock_release(sess->lock); 
     843    return status; 
    486844} 
    487845 
     
    503861} 
    504862 
     863 
     864/* 
     865 * Handle failed ALLOCATE or REFRESH request. This may switch to alternate 
     866 * server if we have one. 
     867 */ 
     868static void on_session_fail( pj_turn_session *sess,  
     869                             enum pj_stun_method_e method, 
     870                             pj_status_t status, 
     871                             const pj_str_t *reason) 
     872{ 
     873    do { 
     874        pj_str_t reason1; 
     875        char err_msg[PJ_ERR_MSG_SIZE]; 
     876 
     877        if (reason == NULL) { 
     878            pj_strerror(status, err_msg, sizeof(err_msg)); 
     879            reason1 = pj_str(err_msg); 
     880            reason = &reason1; 
     881        } 
     882 
     883        PJ_LOG(4,(sess->obj_name, "%s error: %.*s", 
     884                  pj_stun_get_method_name(method), 
     885                  (int)reason->slen, reason->ptr)); 
     886 
     887        /* If this is ALLOCATE response and we don't have more server  
     888         * addresses to try, notify application and destroy the TURN 
     889         * session. 
     890         */ 
     891        if (method==PJ_STUN_ALLOCATE_METHOD && 
     892            sess->srv_addr == &sess->srv_addr_list[sess->srv_addr_cnt-1])  
     893        { 
     894 
     895            set_state(sess, PJ_TURN_STATE_DEALLOCATED); 
     896            sess_shutdown(sess, PJ_TRUE, status); 
     897            return; 
     898        } 
     899 
     900        /* Otherwise if this is REFRESH response, notify application 
     901         * that session has been TERMINATED. 
     902         */ 
     903        if (method==PJ_STUN_REFRESH_METHOD) { 
     904            set_state(sess, PJ_TURN_STATE_DEALLOCATED); 
     905            sess_shutdown(sess, PJ_TRUE, status); 
     906            return; 
     907        } 
     908 
     909        /* Try next server */ 
     910        ++sess->srv_addr; 
     911        reason = NULL; 
     912 
     913        PJ_LOG(4,(sess->obj_name, "Trying next server")); 
     914 
     915        status = pj_turn_session_alloc(sess, NULL); 
     916 
     917    } while (status != PJ_SUCCESS); 
     918} 
     919 
     920 
     921/* 
     922 * Handle successful response to ALLOCATE or REFRESH request. 
     923 */ 
     924static void on_allocate_success(pj_turn_session *sess,  
     925                                enum pj_stun_method_e method, 
     926                                const pj_stun_msg *msg) 
     927{ 
     928    const pj_stun_lifetime_attr *lf_attr; 
     929    const pj_stun_relay_addr_attr *raddr_attr; 
     930    pj_str_t s; 
     931    pj_time_val timeout; 
     932 
     933    /* Must have LIFETIME attribute */ 
     934    lf_attr = (const pj_stun_lifetime_attr*) 
     935              pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_LIFETIME, 0); 
     936    if (lf_attr == NULL) { 
     937        on_session_fail(sess, method, PJNATH_EINSTUNMSG, 
     938                        pj_cstr(&s, "Error: Missing LIFETIME attribute")); 
     939        return; 
     940    } 
     941 
     942    /* If LIFETIME is zero, this is a deallocation */ 
     943    if (lf_attr->value == 0) { 
     944        pj_bool_t notify = sess->state < PJ_TURN_STATE_DEALLOCATING; 
     945        set_state(sess, PJ_TURN_STATE_DEALLOCATED); 
     946        sess_shutdown(sess, notify, PJ_SUCCESS); 
     947        return; 
     948    } 
     949 
     950    /* Update lifetime and keep-alive interval */ 
     951    sess->lifetime = lf_attr->value; 
     952    pj_gettimeofday(&sess->expiry); 
     953 
     954    if (sess->lifetime < PJ_TURN_KEEP_ALIVE_SEC) { 
     955        if (sess->lifetime <= 2) { 
     956            on_session_fail(sess, method, PJ_ETOOSMALL, 
     957                             pj_cstr(&s, "Error: LIFETIME too small")); 
     958            return; 
     959        } 
     960        sess->ka_interval = sess->lifetime - 2; 
     961        sess->expiry.sec += (sess->ka_interval-1); 
     962    } else { 
     963        int timeout; 
     964 
     965        sess->ka_interval = PJ_TURN_KEEP_ALIVE_SEC; 
     966 
     967        timeout = sess->lifetime - PJ_TURN_REFRESH_SEC_BEFORE; 
     968        if (timeout < sess->ka_interval) 
     969            timeout = sess->ka_interval - 1; 
     970 
     971        sess->expiry.sec += timeout; 
     972    } 
     973 
     974    /* Check that relayed transport address contains correct 
     975     * address family. 
     976     */ 
     977    raddr_attr = (const pj_stun_relay_addr_attr*) 
     978                 pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_RELAY_ADDR, 0); 
     979    if (raddr_attr == NULL && method==PJ_STUN_ALLOCATE_METHOD) { 
     980        on_session_fail(sess, method, PJNATH_EINSTUNMSG, 
     981                        pj_cstr(&s, "Error: Received ALLOCATE without " 
     982                                    "RELAY-ADDRESS attribute")); 
     983        return; 
     984    } 
     985    if (raddr_attr && raddr_attr->sockaddr.addr.sa_family != sess->af) { 
     986        on_session_fail(sess, method, PJNATH_EINSTUNMSG, 
     987                        pj_cstr(&s, "Error: RELAY-ADDRESS with non IPv4" 
     988                                    " address family is not supported " 
     989                                    "for now")); 
     990        return; 
     991    } 
     992     
     993 
     994    /* Success */ 
     995 
     996    /* Cancel existing keep-alive timer, if any */ 
     997    pj_assert(sess->timer.id != TIMER_DESTROY); 
     998 
     999    if (sess->timer.id != TIMER_NONE) { 
     1000        pj_timer_heap_cancel(sess->timer_heap, &sess->timer); 
     1001        sess->timer.id = TIMER_NONE; 
     1002    } 
     1003 
     1004    /* Start keep-alive timer once allocation succeeds */ 
     1005    timeout.sec = sess->ka_interval; 
     1006    timeout.msec = 0; 
     1007 
     1008    sess->timer.id = TIMER_KEEP_ALIVE; 
     1009    pj_timer_heap_schedule(sess->timer_heap, &sess->timer, &timeout); 
     1010 
     1011    set_state(sess, PJ_TURN_STATE_READY); 
     1012} 
    5051013 
    5061014/* 
     
    5151023{ 
    5161024    pj_turn_session *sess; 
    517     int method = PJ_STUN_GET_METHOD(response->hdr.type); 
     1025    int method = PJ_STUN_GET_METHOD(tdata->msg->hdr.type); 
     1026 
     1027    PJ_UNUSED_ARG(src_addr); 
     1028    PJ_UNUSED_ARG(src_addr_len); 
    5181029 
    5191030    sess = (pj_turn_session*)pj_stun_session_get_user_data(stun); 
     
    5211032    if (method == PJ_STUN_ALLOCATE_METHOD) { 
    5221033        /* Handle ALLOCATE response */ 
    523         if (PJ_STUN_IS_SUCCESS_RESPONSE(response->hdr.type)) { 
     1034        if (status==PJ_SUCCESS &&  
     1035            PJ_STUN_IS_SUCCESS_RESPONSE(response->hdr.type))  
     1036        { 
     1037 
    5241038            /* Successful Allocate response */ 
     1039            on_allocate_success(sess, method, response); 
    5251040 
    5261041        } else { 
    527             /* Error Allocate response */ 
    528  
     1042            /* Failed Allocate request */ 
     1043            const pj_str_t *err_msg = NULL; 
     1044 
     1045            if (status == PJ_SUCCESS) { 
     1046                const pj_stun_errcode_attr *err_attr; 
     1047                err_attr = (const pj_stun_errcode_attr*) 
     1048                           pj_stun_msg_find_attr(response, 
     1049                                                 PJ_STUN_ATTR_ERROR_CODE, 0); 
     1050                if (err_attr) { 
     1051                    status = PJ_STATUS_FROM_STUN_CODE(err_attr->err_code); 
     1052                    err_msg = &err_attr->reason; 
     1053                } else { 
     1054                    status = PJNATH_EINSTUNMSG; 
     1055                } 
     1056            } 
     1057 
     1058            on_session_fail(sess, method, status, err_msg); 
     1059        } 
     1060 
     1061    } else if (method == PJ_STUN_REFRESH_METHOD) { 
     1062        /* Handle Refresh response */ 
     1063        if (status==PJ_SUCCESS &&  
     1064            PJ_STUN_IS_SUCCESS_RESPONSE(response->hdr.type))  
     1065        { 
     1066            /* Success, schedule next refresh. */ 
     1067            on_allocate_success(sess, method, response); 
     1068 
     1069        } else { 
     1070            /* Failed Refresh request */ 
     1071            const pj_str_t *err_msg = NULL; 
     1072 
     1073            if (status == PJ_SUCCESS) { 
     1074                const pj_stun_errcode_attr *err_attr; 
     1075                err_attr = (const pj_stun_errcode_attr*) 
     1076                           pj_stun_msg_find_attr(response, 
     1077                                                 PJ_STUN_ATTR_ERROR_CODE, 0); 
     1078                if (err_attr) { 
     1079                    status = PJ_STATUS_FROM_STUN_CODE(err_attr->err_code); 
     1080                    err_msg = &err_attr->reason; 
     1081                } else { 
     1082                    status = PJNATH_EINSTUNMSG; 
     1083                } 
     1084            } 
     1085 
     1086            /* Notify and destroy */ 
     1087            on_session_fail(sess, method, status, err_msg); 
    5291088        } 
    5301089 
    5311090    } else if (method == PJ_STUN_CHANNEL_BIND_METHOD) { 
    5321091        /* Handle ChannelBind response */ 
    533         if (PJ_STUN_IS_SUCCESS_RESPONSE(response->hdr.type)) { 
     1092        if (status==PJ_SUCCESS &&  
     1093            PJ_STUN_IS_SUCCESS_RESPONSE(response->hdr.type))  
     1094        { 
    5341095            /* Successful ChannelBind response */ 
     1096            struct peer *peer = (struct peer*)tdata->user_data; 
     1097 
     1098            pj_assert(peer->ch_id != PJ_TURN_INVALID_CHANNEL); 
     1099            peer->bound = PJ_TRUE; 
     1100 
     1101            /* Update hash table */ 
     1102            lookup_peer_by_addr(sess, &peer->addr, 
     1103                                pj_sockaddr_get_len(&peer->addr), 
     1104                                PJ_TRUE, PJ_TRUE); 
    5351105 
    5361106        } else { 
    537             /* Error ChannelBind response */ 
    538  
     1107            /* Failed ChannelBind response */ 
     1108            pj_str_t err_msg = {"", 0}; 
     1109 
     1110            if (status == PJ_SUCCESS) { 
     1111                const pj_stun_errcode_attr *err_attr; 
     1112                err_attr = (const pj_stun_errcode_attr*) 
     1113                           pj_stun_msg_find_attr(response, 
     1114                                                 PJ_STUN_ATTR_ERROR_CODE, 0); 
     1115                if (err_attr) { 
     1116                    status = PJ_STATUS_FROM_STUN_CODE(err_attr->err_code); 
     1117                    err_msg = err_attr->reason; 
     1118                } else { 
     1119                    status = PJNATH_EINSTUNMSG; 
     1120                } 
     1121            } 
     1122 
     1123            PJ_LOG(4,(sess->obj_name, "ChannelBind failed: %.*s", 
     1124                      (int)err_msg.slen, err_msg.ptr)); 
    5391125        } 
    5401126 
     
    5611147    pj_stun_data_attr *data_attr; 
    5621148 
     1149    PJ_UNUSED_ARG(src_addr); 
     1150    PJ_UNUSED_ARG(src_addr_len); 
     1151 
    5631152    sess = (pj_turn_session*)pj_stun_session_get_user_data(stun); 
    5641153 
     
    6041193{ 
    6051194    pj_turn_session *sess = (pj_turn_session*) user_data; 
     1195    unsigned i, cnt; 
     1196 
     1197    /* Clear async resolver */ 
     1198    sess->dns_async = NULL; 
    6061199 
    6071200    /* Check failure */ 
    6081201    if (status != PJ_SUCCESS) { 
    609         destroy(sess, PJ_TRUE, status); 
     1202        sess_shutdown(sess, PJ_TRUE, status); 
    6101203        return; 
    6111204    } 
    6121205 
    6131206    /* Copy results to server entries */ 
    614  
    615     /* Set state to STATE_RESOLVED */ 
     1207    for (i=0, cnt=0; i<rec->count && cnt<MAX_SRV_CNT; ++i) { 
     1208        unsigned j; 
     1209 
     1210        for (j=0; j<rec->entry[i].server.addr_count && cnt<MAX_SRV_CNT; ++j) { 
     1211            pj_sockaddr_in *addr = &sess->srv_addr[cnt].ipv4; 
     1212 
     1213            addr->sin_family = sess->af; 
     1214            addr->sin_port = pj_htons(rec->entry[i].port); 
     1215            addr->sin_addr.s_addr = rec->entry[i].server.addr[j].s_addr; 
     1216 
     1217            ++cnt; 
     1218        } 
     1219    } 
     1220    sess->srv_addr_cnt = (pj_uint16_t)cnt; 
     1221 
     1222    /* Set current server */ 
     1223    sess->srv_addr = &sess->srv_addr[0]; 
     1224 
     1225    /* Set state to PJ_TURN_STATE_RESOLVED */ 
     1226    set_state(sess, PJ_TURN_STATE_RESOLVED); 
    6161227 
    6171228    /* Run pending allocation */ 
    618 } 
    619  
    620  
    621 /* 
    622  * Notification on completion of DNS A resolution. 
    623  */ 
    624 static void dns_a_resolver_cb(void *user_data, 
    625                               pj_status_t status, 
    626                               pj_dns_parsed_packet *response) 
    627 { 
     1229    if (sess->pending_alloc) { 
     1230        pj_turn_session_alloc(sess, NULL); 
     1231    } 
    6281232} 
    6291233 
     
    6351239                                        const pj_sockaddr_t *addr, 
    6361240                                        unsigned addr_len, 
    637                                         pj_bool_t update) 
    638 { 
     1241                                        pj_bool_t update, 
     1242                                        pj_bool_t bind_channel) 
     1243{ 
     1244    unsigned hval = 0; 
     1245    struct peer *peer; 
     1246 
     1247    peer = (struct peer*) pj_hash_get(sess->peer_table, addr, addr_len, &hval); 
     1248    if (peer == NULL && update) { 
     1249        peer = PJ_POOL_ZALLOC_T(sess->pool, struct peer); 
     1250        peer->ch_id = PJ_TURN_INVALID_CHANNEL; 
     1251        pj_memcpy(&peer->addr, addr, addr_len); 
     1252 
     1253        /* Register by peer address */ 
     1254        pj_hash_set(sess->pool, sess->peer_table, &peer->addr, addr_len, 
     1255                    hval, peer); 
     1256    } 
     1257 
     1258    if (peer && update) { 
     1259        pj_gettimeofday(&peer->expiry); 
     1260        if (peer->bound) { 
     1261            peer->expiry.sec += PJ_TURN_CHANNEL_TIMEOUT - 10; 
     1262        } else { 
     1263            peer->expiry.sec += PJ_TURN_PERM_TIMEOUT - 10; 
     1264        } 
     1265 
     1266        if (bind_channel) { 
     1267            /* Register by channel number */ 
     1268            pj_assert(peer->ch_id != PJ_TURN_INVALID_CHANNEL && peer->bound); 
     1269            pj_assert(pj_hash_get(sess->peer_table, &peer->ch_id,  
     1270                                  sizeof(peer->ch_id), NULL)==0); 
     1271 
     1272            pj_hash_set(sess->pool, sess->peer_table, &peer->ch_id, 
     1273                        sizeof(peer->ch_id), 0, peer); 
     1274        } 
     1275    } 
     1276 
     1277    return peer; 
    6391278} 
    6401279 
     
    6441283 */ 
    6451284static struct peer *lookup_peer_by_chnum(pj_turn_session *sess, 
    646                                          unsigned chnum) 
    647 { 
    648 } 
    649  
    650  
     1285                                         pj_uint16_t chnum) 
     1286{ 
     1287    return (struct peer*) pj_hash_get(sess->peer_table, &chnum,  
     1288                                      sizeof(chnum), NULL); 
     1289} 
     1290 
     1291 
     1292/* 
     1293 * Timer event. 
     1294 */ 
     1295static void on_timer_event(pj_timer_heap_t *th, pj_timer_entry *e) 
     1296{ 
     1297    pj_turn_session *sess = (pj_turn_session*)e->user_data; 
     1298    enum timer_id_t eid; 
     1299 
     1300    PJ_UNUSED_ARG(th); 
     1301 
     1302    pj_lock_acquire(sess->lock); 
     1303 
     1304    eid = e->id; 
     1305    e->id = TIMER_NONE; 
     1306     
     1307    if (eid == TIMER_KEEP_ALIVE) { 
     1308        pj_time_val now; 
     1309        pj_hash_iterator_t itbuf, *it; 
     1310        pj_bool_t resched = PJ_TRUE; 
     1311        pj_bool_t pkt_sent = PJ_FALSE; 
     1312 
     1313        pj_gettimeofday(&now); 
     1314 
     1315        /* Refresh allocation if it's time to do so */ 
     1316        if (PJ_TIME_VAL_LTE(sess->expiry, now)) { 
     1317            int lifetime = sess->alloc_param.lifetime; 
     1318 
     1319            if (lifetime == 0) 
     1320                lifetime = -1; 
     1321 
     1322            send_refresh(sess, lifetime); 
     1323            resched = PJ_FALSE; 
     1324            pkt_sent = PJ_TRUE; 
     1325        } 
     1326 
     1327        /* Scan hash table to refresh bound channels */ 
     1328        it = pj_hash_first(sess->peer_table, &itbuf); 
     1329        while (it) { 
     1330            struct peer *peer = (struct peer*)  
     1331                                pj_hash_this(sess->peer_table, it); 
     1332            if (peer->bound && PJ_TIME_VAL_LTE(peer->expiry, now)) { 
     1333 
     1334                /* Send ChannelBind to refresh channel binding and  
     1335                 * permission. 
     1336                 */ 
     1337                pj_turn_session_bind_channel(sess, &peer->addr, 
     1338                                             pj_sockaddr_get_len(&peer->addr)); 
     1339                pkt_sent = PJ_TRUE; 
     1340            } 
     1341 
     1342            it = pj_hash_next(sess->peer_table, it); 
     1343        } 
     1344 
     1345        /* If no packet is sent, send a blank Send indication to 
     1346         * refresh local NAT. 
     1347         */ 
     1348        if (!pkt_sent && sess->alloc_param.ka_interval > 0) { 
     1349            pj_stun_tx_data *tdata; 
     1350            pj_status_t rc; 
     1351 
     1352            /* Create blank SEND-INDICATION */ 
     1353            rc = pj_stun_session_create_ind(sess->stun,  
     1354                                            PJ_STUN_SEND_INDICATION, &tdata); 
     1355            if (rc == PJ_SUCCESS) { 
     1356                /* Add DATA attribute with zero length */ 
     1357                pj_stun_msg_add_binary_attr(tdata->pool, tdata->msg, 
     1358                                            PJ_STUN_ATTR_DATA, NULL, 0); 
     1359 
     1360                /* Send the indication */ 
     1361                pj_stun_session_send_msg(sess->stun, PJ_FALSE, sess->srv_addr, 
     1362                                        pj_sockaddr_get_len(sess->srv_addr), 
     1363                                        tdata); 
     1364            } 
     1365        } 
     1366 
     1367        /* Reshcedule timer */ 
     1368        if (resched) { 
     1369            pj_time_val delay; 
     1370 
     1371            delay.sec = sess->ka_interval; 
     1372            delay.msec = 0; 
     1373 
     1374            pj_timer_heap_schedule(sess->timer_heap, &sess->timer, &delay); 
     1375        } 
     1376 
     1377        pj_lock_release(sess->lock); 
     1378 
     1379    } else if (eid == TIMER_DESTROY) { 
     1380        /* Time to destroy */ 
     1381        pj_lock_release(sess->lock); 
     1382        do_destroy(sess); 
     1383    }     
     1384} 
     1385 
Note: See TracChangeset for help on using the changeset viewer.