Changeset 5987


Ignore:
Timestamp:
May 14, 2019 9:31:39 AM (5 years ago)
Author:
nanang
Message:

Close #2197: Support TURN extensions for TCP allocations (RFC 6062).

Location:
pjproject/trunk/pjnath
Files:
7 edited

Legend:

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

    r5850 r5987  
    221221#endif 
    222222 
     223/** 
     224 * Maximum number of TCP data connection to peer(s) that a TURN client can 
     225 * open/accept for each TURN allocation (or TURN control connection). 
     226 */ 
     227#ifndef PJ_TURN_MAX_TCP_CONN_CNT 
     228#   define PJ_TURN_MAX_TCP_CONN_CNT                 8 
     229#endif 
    223230 
    224231/* ************************************************************************** 
  • pjproject/trunk/pjnath/include/pjnath/stun_msg.h

    r5527 r5987  
    9494 
    9595    /** 
     96     * STUN/TURN Connect as defined by RFC 6062 
     97     */ 
     98    PJ_STUN_CONNECT_METHOD                  = 10, 
     99 
     100    /** 
     101     * STUN/TURN ConnectionBind as defined by RFC 6062 
     102     */ 
     103    PJ_STUN_CONNECTION_BIND_METHOD          = 11, 
     104 
     105    /** 
     106     * STUN/TURN ConnectionAttempt as defined by RFC 6062 
     107     */ 
     108    PJ_STUN_CONNECTION_ATTEMPT_METHOD       = 12, 
     109 
     110    /** 
    96111     * All known methods. 
    97112     */ 
     
    292307     * Error response to STUN ChannelBind request. 
    293308     */ 
    294     PJ_STUN_CHANNEL_BIND_ERROR_RESPONSE     = 0x0119 
     309    PJ_STUN_CHANNEL_BIND_ERROR_RESPONSE     = 0x0119, 
     310 
     311 
     312    /** 
     313     * STUN/TURN ConnectBind Request 
     314     */ 
     315    PJ_STUN_CONNECTION_BIND_REQUEST         = 0x000b, 
     316 
     317    /** 
     318     * TURN ConnectionAttempt indication 
     319     */ 
     320    PJ_STUN_CONNECTION_ATTEMPT_INDICATION   = 0x001c, 
    295321 
    296322} pj_stun_msg_type; 
     
    334360    PJ_STUN_ATTR_PRIORITY           = 0x0024,/**< PRIORITY                  */ 
    335361    PJ_STUN_ATTR_USE_CANDIDATE      = 0x0025,/**< USE-CANDIDATE             */ 
     362    PJ_STUN_ATTR_CONNECTION_ID      = 0x002a,/**< CONNECTION-ID             */ 
    336363    PJ_STUN_ATTR_ICMP               = 0x0030,/**< ICMP (TURN)               */ 
    337364 
  • pjproject/trunk/pjnath/include/pjnath/turn_session.h

    r5481 r5987  
    299299                     pj_turn_state_t new_state); 
    300300 
     301    /** 
     302     * Notification when TURN client received a ConnectionAttempt Indication 
     303     * from the TURN server, which indicates that peer initiates a TCP 
     304     * connection to allocated slot in the TURN server. Application must 
     305     * implement this callback if it uses RFC 6062 (TURN TCP allocations). 
     306     * 
     307     * After receiving this callback, application should establish a new TCP 
     308     * connection to the TURN server and send ConnectionBind request (using 
     309     * pj_turn_session_connection_bind()). After the connection binding 
     310     * succeeds, this new connection will become a data only connection. 
     311     * 
     312     * @param sess      The TURN session. 
     313     * @param conn_id   The connection ID assigned by TURN server. 
     314     * @param peer_addr Peer address that tried to connect to the TURN server. 
     315     * @param addr_len  Length of the peer address. 
     316     */ 
     317    void (*on_connection_attempt)(pj_turn_session *sess, 
     318                                  pj_uint32_t conn_id, 
     319                                  const pj_sockaddr_t *peer_addr, 
     320                                  unsigned addr_len); 
     321 
     322    /** 
     323     * Notification for ConnectionBind request sent using 
     324     * pj_turn_session_connection_bind(). 
     325     * 
     326     * @param sess      The TURN session. 
     327     * @param status    The status code. 
     328     * @param conn_id   The connection ID. 
     329     * @param peer_addr Peer address. 
     330     * @param addr_len  Length of the peer address. 
     331     */ 
     332    void (*on_connection_bind_status)(pj_turn_session *sess, 
     333                                      pj_status_t status, 
     334                                      pj_uint32_t conn_id, 
     335                                      const pj_sockaddr_t *peer_addr, 
     336                                      unsigned addr_len); 
     337 
    301338} pj_turn_session_cb; 
    302339 
     
    340377    int     af; 
    341378 
     379    /** 
     380     * Type of connection to from TURN server to peer. Supported values are 
     381     * PJ_TURN_TP_UDP (RFC 5766) and PJ_TURN_TP_TCP (RFC 6062) 
     382     * 
     383     * Default is PJ_TURN_TP_UDP. 
     384     */ 
     385    pj_turn_tp_type peer_conn_type; 
    342386 
    343387} pj_turn_alloc_param; 
     
    385429 
    386430} pj_turn_session_info; 
     431 
     432 
     433/** 
     434 * Parameters for function pj_turn_session_on_rx_pkt2(). 
     435 */ 
     436typedef struct pj_turn_session_on_rx_pkt_param 
     437{ 
     438    /** 
     439     * The packet as received from the TURN server. This should contain 
     440     * either STUN encapsulated message or a ChannelData packet. 
     441     */ 
     442    void                *pkt; 
     443 
     444    /** 
     445     * The length of the packet. 
     446     */ 
     447    pj_size_t            pkt_len; 
     448 
     449    /** 
     450     * The number of parsed or processed data from the packet. 
     451     */ 
     452    pj_size_t            parsed_len; 
     453 
     454    /** 
     455     * Source address where the packet is received from. 
     456     */ 
     457    const pj_sockaddr_t *src_addr; 
     458 
     459    /** 
     460     * Length of the source address. 
     461     */ 
     462    unsigned             src_addr_len; 
     463 
     464} pj_turn_session_on_rx_pkt_param; 
    387465 
    388466 
     
    742820                                               pj_size_t *parsed_len); 
    743821 
     822/** 
     823 * Notify TURN client session upon receiving a packet from server. Since 
     824 * the TURN session is transport independent, it does not read packet from 
     825 * any sockets, and rather relies on application giving it packets that 
     826 * are received from the TURN server. The session then processes this packet 
     827 * and decides whether it is part of TURN protocol exchange or if it is a 
     828 * data to be reported back to user, which in this case it will call the 
     829 * \a on_rx_data() callback. 
     830 * 
     831 * This function is variant of pj_turn_session_on_rx_pkt() with additional 
     832 * parameters such as source address. Source address will allow STUN/TURN 
     833 * session to resend the request (e.g: with updated authentication) to the 
     834 * provided source address which may be different to the initial connection, 
     835 * for example in RFC 6062 scenario that there can be some data connection 
     836 * and a control connection. 
     837 * 
     838 * @param sess          The TURN client session. 
     839 * @param prm           The function parameters, e.g: packet, source address. 
     840 * 
     841 * @return              The function may return non-PJ_SUCCESS if it receives 
     842 *                      non-STUN and non-ChannelData packet, or if the 
     843 *                      \a on_rx_data() returns non-PJ_SUCCESS; 
     844 */ 
     845PJ_DECL(pj_status_t) pj_turn_session_on_rx_pkt2( 
     846                                    pj_turn_session *sess, 
     847                                    pj_turn_session_on_rx_pkt_param *prm); 
     848 
     849/** 
     850 * Initiate connection binding to the specified peer using ConnectionBind 
     851 * request. Application must call this function when it uses RFC 6062 
     852 * (TURN TCP allocations) to establish a data connection with peer after 
     853 * opening/accepting connection to/from peer. The connection binding status 
     854 * will be notified via on_connection_bind_status callback. 
     855 * 
     856 * @param sess          The TURN session. 
     857 * @param pool          The memory pool. 
     858 * @param conn_id       The connection ID assigned by TURN server. 
     859 * @param peer_addr     Peer address. 
     860 * @param addr_len      Length of the peer address. 
     861 * 
     862 * @return              PJ_SUCCESS if the operation has been successfully 
     863 *                      issued, or the appropriate error code. Note that 
     864 *                      the operation itself will complete asynchronously. 
     865 */ 
     866PJ_DECL(pj_status_t) pj_turn_session_connection_bind( 
     867                                            pj_turn_session *sess, 
     868                                            pj_pool_t *pool, 
     869                                            pj_uint32_t conn_id, 
     870                                            const pj_sockaddr_t *peer_addr, 
     871                                            unsigned addr_len); 
    744872 
    745873/** 
  • pjproject/trunk/pjnath/include/pjnath/turn_sock.h

    r4606 r5987  
    9898                     pj_turn_state_t old_state, 
    9999                     pj_turn_state_t new_state); 
     100 
     101    /** 
     102     * Notification when TURN client received a ConnectionAttempt Indication 
     103     * from the TURN server, which indicates that peer initiates a TCP 
     104     * connection to allocated slot in the TURN server. Application should 
     105     * implement this callback if it uses RFC 6062 (TURN TCP allocations), 
     106     * otherwise TURN client will automatically accept it. 
     107     * 
     108     * If application accepts the peer connection attempt (i.e: by returning 
     109     * PJ_SUCCESS or not implementing this callback), the TURN socket will 
     110     * initiate a new connection to the TURN server and send ConnectionBind 
     111     * request, and eventually will notify application via 
     112     * on_connection_status callback, if implemented. 
     113     * 
     114     * @param turn_sock     The TURN client transport. 
     115     * @param conn_id       The connection ID assigned by TURN server. 
     116     * @param peer_addr     Peer address that tried to connect to the 
     117     *                      TURN server. 
     118     * @param addr_len      Length of the peer address. 
     119     * 
     120     * @return              The callback must return PJ_SUCCESS to accept 
     121     *                      the connection attempt. 
     122     */ 
     123    pj_status_t (*on_connection_attempt)(pj_turn_sock *turn_sock, 
     124                                         pj_uint32_t conn_id, 
     125                                         const pj_sockaddr_t *peer_addr, 
     126                                         unsigned addr_len); 
     127 
     128    /** 
     129     * Notification for initiated TCP data connection to peer (RFC 6062), 
     130     * for example after peer connection attempt is accepted. 
     131     * 
     132     * @param turn_sock     The TURN client transport. 
     133     * @param status        The status code. 
     134     * @param conn_id       The connection ID. 
     135     * @param peer_addr     Peer address. 
     136     * @param addr_len      Length of the peer address. 
     137     */ 
     138    void (*on_connection_status)(pj_turn_sock *turn_sock, 
     139                                 pj_status_t status, 
     140                                 pj_uint32_t conn_id, 
     141                                 const pj_sockaddr_t *peer_addr, 
     142                                 unsigned addr_len); 
    100143 
    101144} pj_turn_sock_cb; 
  • pjproject/trunk/pjnath/src/pjnath/stun_msg.c

    r5527 r5987  
    4646    "CreatePermission",         /* 8 */ 
    4747    "ChannelBind",              /* 9 */ 
     48    "Connect",                  /* 10 */ 
     49    "ConnectionBind",           /* 11 */ 
     50    "ConnectionAttempt",        /* 12 */ 
    4851}; 
    4952 
     
    477480    }, 
    478481    { 
    479         /* ID 0x002a is not assigned */ 
    480         NULL, 
    481         NULL, 
    482         NULL, 
    483         NULL 
     482        /* PJ_STUN_ATTR_CONNECTION_ID, */ 
     483        "CONNECTION-ID", 
     484        &decode_uint_attr, 
     485        &encode_uint_attr, 
     486        &clone_uint_attr 
    484487    }, 
    485488    { 
  • pjproject/trunk/pjnath/src/pjnath/turn_session.c

    r5983 r5987  
    104104}; 
    105105 
     106struct conn_bind_t 
     107{ 
     108    pj_uint32_t      id;                /* Connection ID.       */ 
     109    pj_sockaddr      peer_addr;         /* Peer address.        */ 
     110    unsigned         peer_addr_len; 
     111}; 
    106112 
    107113/* The TURN client session structure */ 
     
    209215{ 
    210216    pj_bzero(prm, sizeof(*prm)); 
     217    prm->peer_conn_type = PJ_TURN_TP_UDP; 
    211218} 
    212219 
     
    724731                     sess->state<=PJ_TURN_STATE_RESOLVED,  
    725732                     PJ_EINVALIDOP); 
     733    PJ_ASSERT_RETURN(param->peer_conn_type == PJ_TURN_TP_UDP || 
     734                     param->peer_conn_type == PJ_TURN_TP_TCP, 
     735                     PJ_EINVAL); 
    726736 
    727737    /* Verify address family in allocation param */ 
     
    761771    pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg, 
    762772                              PJ_STUN_ATTR_REQ_TRANSPORT,  
    763                               PJ_STUN_SET_RT_PROTO(PJ_TURN_TP_UDP)); 
     773                              PJ_STUN_SET_RT_PROTO(param->peer_conn_type)); 
    764774 
    765775    /* Include BANDWIDTH if requested */ 
     
    9991009    } 
    10001010 
     1011    /* If peer connection is TCP (RFC 6062), send it directly */ 
     1012    if (sess->alloc_param.peer_conn_type == PJ_TURN_TP_TCP) { 
     1013        status = sess->cb.on_send_pkt(sess, pkt, pkt_len, addr, addr_len); 
     1014        goto on_return; 
     1015    } 
     1016 
    10011017    /* See if the peer is bound to a channel number */ 
    10021018    ch = lookup_ch_by_addr(sess, addr, pj_sockaddr_get_len(addr),  
     
    11391155 
    11401156/** 
    1141  * Notify TURN client session upon receiving a packet from server. 
    1142  * The packet maybe a STUN packet or ChannelData packet. 
    1143  */ 
     1157 * Send ConnectionBind request. 
     1158 */ 
     1159PJ_DEF(pj_status_t) pj_turn_session_connection_bind( 
     1160                                            pj_turn_session *sess, 
     1161                                            pj_pool_t *pool, 
     1162                                            pj_uint32_t conn_id, 
     1163                                            const pj_sockaddr_t *peer_addr, 
     1164                                            unsigned addr_len) 
     1165{ 
     1166    pj_stun_tx_data *tdata; 
     1167    struct conn_bind_t *conn_bind; 
     1168    pj_status_t status; 
     1169 
     1170    PJ_ASSERT_RETURN(sess && pool && conn_id && peer_addr && addr_len, 
     1171                     PJ_EINVAL); 
     1172    PJ_ASSERT_RETURN(sess->state == PJ_TURN_STATE_READY, PJ_EINVALIDOP); 
     1173 
     1174    pj_grp_lock_acquire(sess->grp_lock); 
     1175 
     1176    /* Create blank ConnectionBind request */ 
     1177    status = pj_stun_session_create_req(sess->stun,  
     1178                                        PJ_STUN_CONNECTION_BIND_REQUEST, 
     1179                                        PJ_STUN_MAGIC, NULL, &tdata); 
     1180    if (status != PJ_SUCCESS) 
     1181        goto on_return; 
     1182 
     1183    /* Add CONNECTION_ID attribute */ 
     1184    pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg, 
     1185                              PJ_STUN_ATTR_CONNECTION_ID, 
     1186                              conn_id); 
     1187 
     1188    conn_bind = PJ_POOL_ZALLOC_T(pool, struct conn_bind_t); 
     1189    conn_bind->id = conn_id; 
     1190    pj_sockaddr_cp(&conn_bind->peer_addr, peer_addr); 
     1191    conn_bind->peer_addr_len = addr_len; 
     1192 
     1193    /* Send the request, associate connection data structure with tdata  
     1194     * for future reference when we receive the ConnectionBind response. 
     1195     */ 
     1196    status = pj_stun_session_send_msg(sess->stun, conn_bind, PJ_FALSE, 
     1197                                      PJ_FALSE, peer_addr, addr_len, tdata); 
     1198 
     1199on_return: 
     1200    pj_grp_lock_release(sess->grp_lock); 
     1201    return status; 
     1202} 
     1203 
    11441204PJ_DEF(pj_status_t) pj_turn_session_on_rx_pkt(pj_turn_session *sess, 
    11451205                                              void *pkt, 
     
    11471207                                              pj_size_t *parsed_len) 
    11481208{ 
     1209    pj_turn_session_on_rx_pkt_param prm; 
     1210    pj_status_t status; 
     1211     
     1212    pj_bzero(&prm, sizeof(prm)); 
     1213    prm.pkt = pkt; 
     1214    prm.pkt_len = pkt_len; 
     1215    status = pj_turn_session_on_rx_pkt2(sess, &prm); 
     1216    if (status == PJ_SUCCESS && parsed_len) 
     1217        *parsed_len = prm.parsed_len; 
     1218    return status; 
     1219} 
     1220 
     1221/** 
     1222 * Notify TURN client session upon receiving a packet from server. 
     1223 * The packet maybe a STUN packet or ChannelData packet. 
     1224 */ 
     1225PJ_DEF(pj_status_t) pj_turn_session_on_rx_pkt2( 
     1226                                pj_turn_session *sess, 
     1227                                pj_turn_session_on_rx_pkt_param *prm) 
     1228{ 
    11491229    pj_bool_t is_stun; 
    11501230    pj_status_t status; 
     
    11611241 
    11621242    /* Quickly check if this is STUN message */ 
    1163     is_stun = ((((pj_uint8_t*)pkt)[0] & 0xC0) == 0); 
     1243    is_stun = ((((pj_uint8_t*)prm->pkt)[0] & 0xC0) == 0); 
    11641244 
    11651245    if (is_stun) { 
    11661246        /* This looks like STUN, give it to the STUN session */ 
    11671247        unsigned options; 
     1248        const pj_sockaddr_t *src_addr = prm->src_addr? 
     1249                                        prm->src_addr:sess->srv_addr; 
     1250        unsigned src_addr_len = prm->src_addr_len? prm->src_addr_len: 
     1251                                pj_sockaddr_get_len(sess->srv_addr); 
    11681252 
    11691253        options = PJ_STUN_CHECK_PACKET | PJ_STUN_NO_FINGERPRINT_CHECK; 
    11701254        if (is_datagram) 
    11711255            options |= PJ_STUN_IS_DATAGRAM; 
    1172         status=pj_stun_session_on_rx_pkt(sess->stun, pkt, pkt_len, 
    1173                                          options, NULL, parsed_len, 
    1174                                          sess->srv_addr, 
    1175                                          pj_sockaddr_get_len(sess->srv_addr)); 
     1256        status=pj_stun_session_on_rx_pkt(sess->stun, prm->pkt, prm->pkt_len, 
     1257                                         options, NULL, &prm->parsed_len, 
     1258                                         src_addr, src_addr_len); 
    11761259 
    11771260    } else { 
     
    11801263        struct ch_t *ch; 
    11811264 
    1182         if (pkt_len < 4) { 
    1183             if (parsed_len) *parsed_len = 0; 
     1265        if (prm->pkt_len < 4) { 
     1266            prm->parsed_len = 0; 
    11841267            return PJ_ETOOSMALL; 
    11851268        } 
    11861269 
    11871270        /* Decode ChannelData packet */ 
    1188         pj_memcpy(&cd, pkt, sizeof(pj_turn_channel_data)); 
     1271        pj_memcpy(&cd, prm->pkt, sizeof(pj_turn_channel_data)); 
    11891272        cd.ch_number = pj_ntohs(cd.ch_number); 
    11901273        cd.length = pj_ntohs(cd.length); 
    11911274 
    11921275        /* Check that size is sane */ 
    1193         if (pkt_len < cd.length+sizeof(cd)) { 
    1194             if (parsed_len) { 
    1195                 if (is_datagram) { 
    1196                     /* Discard the datagram */ 
    1197                     *parsed_len = pkt_len; 
    1198                 } else { 
    1199                     /* Insufficient fragment */ 
    1200                     *parsed_len = 0; 
    1201                 } 
     1276        if (prm->pkt_len < cd.length+sizeof(cd)) { 
     1277            if (is_datagram) { 
     1278                /* Discard the datagram */ 
     1279                prm->parsed_len = prm->pkt_len; 
     1280            } else { 
     1281                /* Insufficient fragment */ 
     1282                prm->parsed_len = 0; 
    12021283            } 
    12031284            status = PJ_ETOOSMALL; 
    12041285            goto on_return; 
    12051286        } else { 
    1206             if (parsed_len) { 
    1207                 /* Apply padding too */ 
    1208                 *parsed_len = ((cd.length + 3) & (~3)) + sizeof(cd); 
    1209             } 
     1287            /* Apply padding too */ 
     1288            prm->parsed_len = ((cd.length + 3) & (~3)) + sizeof(cd); 
    12101289        } 
    12111290 
     
    12191298        /* Notify application */ 
    12201299        if (sess->cb.on_rx_data) { 
    1221             (*sess->cb.on_rx_data)(sess, ((pj_uint8_t*)pkt)+sizeof(cd),  
     1300            (*sess->cb.on_rx_data)(sess, ((pj_uint8_t*)prm->pkt)+sizeof(cd),  
    12221301                                   cd.length, &ch->addr, 
    12231302                                   pj_sockaddr_get_len(&ch->addr)); 
     
    16431722        } 
    16441723 
     1724    } else if (method == PJ_STUN_CONNECTION_BIND_METHOD) { 
     1725        /* Handle ConnectionBind response */ 
     1726        struct conn_bind_t *conn_bind = (struct conn_bind_t*)token; 
     1727 
     1728        if (status != PJ_SUCCESS || 
     1729            !PJ_STUN_IS_SUCCESS_RESPONSE(response->hdr.type))  
     1730        { 
     1731            pj_str_t reason = {0}; 
     1732            if (status == PJ_SUCCESS) { 
     1733                const pj_stun_errcode_attr *err_attr; 
     1734                err_attr = (const pj_stun_errcode_attr*) 
     1735                           pj_stun_msg_find_attr(response, 
     1736                                                 PJ_STUN_ATTR_ERROR_CODE, 0); 
     1737                if (err_attr) { 
     1738                    status = PJ_STATUS_FROM_STUN_CODE(err_attr->err_code); 
     1739                    reason = err_attr->reason; 
     1740                } else { 
     1741                    status = PJNATH_EINSTUNMSG; 
     1742                } 
     1743            } 
     1744            pj_perror(1, sess->obj_name, status, "ConnectionBind failed: %.*s", 
     1745                      (int)reason.slen, reason.ptr); 
     1746        } 
     1747 
     1748        /* Notify app */ 
     1749        if (sess->cb.on_connection_bind_status) { 
     1750            (*sess->cb.on_connection_bind_status) 
     1751                        (sess, status, conn_bind->id, 
     1752                        &conn_bind->peer_addr, conn_bind->peer_addr_len); 
     1753        } 
    16451754    } else { 
    16461755        PJ_LOG(4,(sess->obj_name, "Unexpected STUN %s response", 
     
    16751784    sess = (pj_turn_session*)pj_stun_session_get_user_data(stun); 
    16761785 
    1677     /* Expecting Data Indication only */ 
     1786    /* ConnectionAttempt Indication */ 
     1787    if (msg->hdr.type == PJ_STUN_CONNECTION_ATTEMPT_INDICATION) { 
     1788        pj_stun_uint_attr *connection_id_attr; 
     1789 
     1790        /* Get CONNECTION-ID attribute */ 
     1791        connection_id_attr = (pj_stun_uint_attr*) 
     1792            pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_CONNECTION_ID, 0); 
     1793 
     1794        /* Get XOR-PEER-ADDRESS attribute */ 
     1795        peer_attr = (pj_stun_xor_peer_addr_attr*) 
     1796            pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_XOR_PEER_ADDR, 0); 
     1797 
     1798        /* Must have both XOR-PEER-ADDRESS and CONNECTION-ID attributes */ 
     1799        if (!peer_attr || !connection_id_attr) { 
     1800            PJ_LOG(4,(sess->obj_name,  
     1801                      "Received ConnectionAttempt indication with missing " 
     1802                      "attributes")); 
     1803            return PJ_EINVALIDOP; 
     1804        } 
     1805 
     1806        /* Notify application */ 
     1807        if (sess->cb.on_connection_attempt) { 
     1808            (*sess->cb.on_connection_attempt) 
     1809                                (sess, 
     1810                                connection_id_attr->value, 
     1811                                &peer_attr->sockaddr, 
     1812                                pj_sockaddr_get_len(&peer_attr->sockaddr)); 
     1813        } 
     1814        return PJ_SUCCESS; 
     1815    } 
     1816 
     1817    /* Next, expecting Data Indication only */ 
    16781818    if (msg->hdr.type != PJ_STUN_DATA_INDICATION) { 
    16791819        PJ_LOG(4,(sess->obj_name, "Unexpected STUN %s indication", 
  • pjproject/trunk/pjnath/src/pjnath/turn_sock.c

    r5983 r5987  
    3939#define INIT    0x1FFFFFFF 
    4040 
     41enum { 
     42    DATACONN_STATE_NULL, 
     43    DATACONN_STATE_INITSOCK, 
     44    DATACONN_STATE_CONN_BINDING, 
     45    DATACONN_STATE_READY, 
     46}; 
     47 
     48/* This structure describe data connection of TURN TCP allocations 
     49 * (RFC 6062). 
     50 */ 
     51typedef struct tcp_data_conn_t 
     52{ 
     53    pj_pool_t            *pool; 
     54 
     55    pj_uint32_t          id;            /* Connection ID.               */ 
     56    int                  state;         /* Connection state.            */ 
     57    pj_sockaddr          peer_addr;     /* Peer address (mapped).       */ 
     58    unsigned             peer_addr_len; 
     59 
     60    pj_activesock_t     *asock;         /* Active socket.               */ 
     61    pj_ioqueue_op_key_t  send_key; 
     62 
     63    pj_turn_sock        *turn_sock;     /* TURN socket parent.          */ 
     64} tcp_data_conn_t; 
     65 
     66 
    4167struct pj_turn_sock 
    4268{ 
     
    6086    pj_activesock_t     *active_sock; 
    6187    pj_ioqueue_op_key_t  send_key; 
     88 
     89    /* Data connection, when peer_conn_type==PJ_TURN_TP_TCP (RFC 6062) */ 
     90    unsigned             data_conn_cnt; 
     91    tcp_data_conn_t      data_conn[PJ_TURN_MAX_TCP_CONN_CNT]; 
    6292}; 
    6393 
     
    83113                          pj_turn_state_t old_state, 
    84114                          pj_turn_state_t new_state); 
     115static void turn_on_connection_attempt(pj_turn_session *sess, 
     116                                       pj_uint32_t conn_id, 
     117                                       const pj_sockaddr_t *peer_addr, 
     118                                       unsigned addr_len); 
     119static void turn_on_connection_bind_status(pj_turn_session *sess, 
     120                                           pj_status_t status, 
     121                                           pj_uint32_t conn_id, 
     122                                           const pj_sockaddr_t *peer_addr, 
     123                                           unsigned addr_len); 
    85124 
    86125static pj_bool_t on_data_read(pj_activesock_t *asock, 
     
    92131                                     pj_status_t status); 
    93132 
    94  
     133static pj_bool_t dataconn_on_data_read(pj_activesock_t *asock, 
     134                                       void *data, 
     135                                       pj_size_t size, 
     136                                       pj_status_t status, 
     137                                       pj_size_t *remainder); 
     138static pj_bool_t dataconn_on_connect_complete(pj_activesock_t *asock, 
     139                                              pj_status_t status); 
     140static void dataconn_cleanup(tcp_data_conn_t *conn); 
    95141 
    96142static void turn_sock_on_destroy(void *comp); 
     
    194240    sess_cb.on_rx_data = &turn_on_rx_data; 
    195241    sess_cb.on_state = &turn_on_state; 
     242    sess_cb.on_connection_attempt = &turn_on_connection_attempt; 
     243    sess_cb.on_connection_bind_status = &turn_on_connection_bind_status; 
    196244    status = pj_turn_session_create(cfg, pool->obj_name, af, conn_type, 
    197245                                    turn_sock->grp_lock, &sess_cb, 0, 
     
    225273static void destroy(pj_turn_sock *turn_sock) 
    226274{ 
     275    unsigned i; 
     276 
    227277    PJ_LOG(4,(turn_sock->obj_name, "TURN socket destroy request, ref_cnt=%d", 
    228278              pj_grp_lock_get_ref(turn_sock->grp_lock))); 
     
    239289    if (turn_sock->active_sock) 
    240290        pj_activesock_close(turn_sock->active_sock); 
     291 
     292    for (i=0; i < PJ_TURN_MAX_TCP_CONN_CNT; ++i) { 
     293        dataconn_cleanup(&turn_sock->data_conn[i]); 
     294    } 
     295    turn_sock->data_conn_cnt = 0; 
     296 
    241297    pj_grp_lock_dec_ref(turn_sock->grp_lock); 
    242298    pj_grp_lock_release(turn_sock->grp_lock); 
     
    668724                              pj_turn_session_get_user_data(sess); 
    669725    pj_ssize_t len = pkt_len; 
    670     pj_status_t status; 
     726    pj_status_t status = PJ_SUCCESS; 
    671727 
    672728    if (turn_sock == NULL || turn_sock->is_destroying) { 
     
    681737                                      &turn_sock->send_key, pkt, &len, 0, 
    682738                                      dst_addr, dst_addr_len); 
     739    } else if (turn_sock->alloc_param.peer_conn_type == PJ_TURN_TP_TCP) { 
     740        pj_turn_session_info info; 
     741        pj_turn_session_get_info(turn_sock->sess, &info); 
     742        if (pj_sockaddr_cmp(&info.server, dst_addr) == 0) { 
     743            /* Destination address is TURN server */ 
     744            status = pj_activesock_send(turn_sock->active_sock, 
     745                                        &turn_sock->send_key, pkt, &len, 0); 
     746        } else { 
     747            /* Destination address is peer, lookup data connection */ 
     748            unsigned i; 
     749 
     750            status = PJ_ENOTFOUND; 
     751            for (i=0; i < PJ_TURN_MAX_TCP_CONN_CNT; ++i) { 
     752                tcp_data_conn_t *conn = &turn_sock->data_conn[i]; 
     753                if (conn->state < DATACONN_STATE_CONN_BINDING) 
     754                    continue; 
     755                if (pj_sockaddr_cmp(&conn->peer_addr, dst_addr) == 0) { 
     756                    status = pj_activesock_send(conn->asock, 
     757                                                &conn->send_key, 
     758                                                pkt, &len, 0); 
     759                    break; 
     760                } 
     761            } 
     762        } 
    683763    } else { 
    684764        status = pj_activesock_send(turn_sock->active_sock, 
    685765                                    &turn_sock->send_key, pkt, &len, 0); 
    686766    } 
     767 
    687768    if (status != PJ_SUCCESS && status != PJ_EPENDING) { 
    688769        show_err(turn_sock, "socket send()", status); 
     
    721802    if (turn_sock == NULL || turn_sock->is_destroying) { 
    722803        /* We've been destroyed */ 
     804        return; 
     805    } 
     806 
     807    if (turn_sock->alloc_param.peer_conn_type != PJ_TURN_TP_UDP) { 
     808        /* Data traffic for RFC 6062 is not via TURN session */ 
    723809        return; 
    724810    } 
     
    9371023 
    9381024 
     1025static void dataconn_cleanup(tcp_data_conn_t *conn) 
     1026{ 
     1027    if (conn->asock) 
     1028        pj_activesock_close(conn->asock); 
     1029 
     1030    pj_pool_safe_release(&conn->pool); 
     1031 
     1032    pj_bzero(conn, sizeof(conn)); 
     1033} 
     1034 
     1035static pj_bool_t dataconn_on_data_read(pj_activesock_t *asock, 
     1036                                       void *data, 
     1037                                       pj_size_t size, 
     1038                                       pj_status_t status, 
     1039                                       pj_size_t *remainder) 
     1040{ 
     1041    tcp_data_conn_t *conn = (tcp_data_conn_t*) 
     1042                            pj_activesock_get_user_data(asock); 
     1043    pj_turn_sock *turn_sock = conn->turn_sock; 
     1044 
     1045    pj_grp_lock_acquire(turn_sock->grp_lock); 
     1046 
     1047    if (size == 0 && status != PJ_SUCCESS) { 
     1048        /* Connection gone, release data connection */ 
     1049        dataconn_cleanup(conn); 
     1050        --turn_sock->data_conn_cnt; 
     1051        pj_grp_lock_release(turn_sock->grp_lock); 
     1052        return PJ_FALSE; 
     1053    } 
     1054 
     1055    if (conn->state == DATACONN_STATE_READY) { 
     1056        /* Application data */ 
     1057        if (turn_sock->cb.on_rx_data) { 
     1058            (*turn_sock->cb.on_rx_data)(turn_sock, data, size, 
     1059                                        &conn->peer_addr, 
     1060                                        conn->peer_addr_len); 
     1061        } 
     1062    } else if (conn->state == DATACONN_STATE_CONN_BINDING) { 
     1063        /* Waiting for ConnectionBind response */ 
     1064        pj_bool_t is_stun; 
     1065        pj_turn_session_on_rx_pkt_param prm; 
     1066         
     1067        /* Ignore if this is not a STUN message */ 
     1068        is_stun = ((((pj_uint8_t*)data)[0] & 0xC0) == 0); 
     1069        if (!is_stun) 
     1070            goto on_return; 
     1071 
     1072        pj_bzero(&prm, sizeof(prm)); 
     1073        prm.pkt = data; 
     1074        prm.pkt_len = size; 
     1075        prm.src_addr = &conn->peer_addr; 
     1076        prm.src_addr_len = conn->peer_addr_len; 
     1077        pj_turn_session_on_rx_pkt2(conn->turn_sock->sess, &prm); 
     1078        /* Got remainder? */ 
     1079        if (prm.parsed_len < size) { 
     1080            *remainder = size - prm.parsed_len; 
     1081            if (prm.parsed_len) { 
     1082                pj_memmove(data, (pj_uint8_t*)data+prm.parsed_len, 
     1083                           *remainder); 
     1084            } 
     1085        } 
     1086    } 
     1087 
     1088on_return: 
     1089    pj_grp_lock_release(turn_sock->grp_lock); 
     1090    return PJ_TRUE; 
     1091} 
     1092 
     1093static pj_bool_t dataconn_on_connect_complete(pj_activesock_t *asock, 
     1094                                              pj_status_t status) 
     1095{ 
     1096    tcp_data_conn_t *conn = (tcp_data_conn_t*) 
     1097                            pj_activesock_get_user_data(asock); 
     1098    pj_turn_sock *turn_sock = conn->turn_sock; 
     1099 
     1100    pj_grp_lock_acquire(turn_sock->grp_lock); 
     1101 
     1102    if (status == PJ_SUCCESS) { 
     1103        status = pj_activesock_start_read(asock, turn_sock->pool, 
     1104                                          turn_sock->setting.max_pkt_size, 0); 
     1105    } 
     1106    if (status == PJ_SUCCESS) { 
     1107        conn->state = DATACONN_STATE_CONN_BINDING; 
     1108        status = pj_turn_session_connection_bind(turn_sock->sess, 
     1109                                                 conn->pool, 
     1110                                                 conn->id, 
     1111                                                 &conn->peer_addr, 
     1112                                                 conn->peer_addr_len); 
     1113    } 
     1114    if (status != PJ_SUCCESS) { 
     1115        dataconn_cleanup(conn); 
     1116        --turn_sock->data_conn_cnt; 
     1117        pj_grp_lock_release(turn_sock->grp_lock); 
     1118        return PJ_FALSE; 
     1119    } 
     1120 
     1121    pj_grp_lock_release(turn_sock->grp_lock); 
     1122    return PJ_TRUE; 
     1123} 
     1124 
     1125 
     1126static void turn_on_connection_attempt(pj_turn_session *sess, 
     1127                                       pj_uint32_t conn_id, 
     1128                                       const pj_sockaddr_t *peer_addr, 
     1129                                       unsigned addr_len) 
     1130{ 
     1131    pj_turn_sock *turn_sock = (pj_turn_sock*)  
     1132                              pj_turn_session_get_user_data(sess); 
     1133    pj_pool_t *pool; 
     1134    tcp_data_conn_t *new_conn; 
     1135    pj_turn_session_info info; 
     1136    pj_sock_t sock = PJ_INVALID_SOCKET; 
     1137    pj_activesock_cfg asock_cfg; 
     1138    pj_activesock_cb asock_cb; 
     1139    pj_sockaddr bound_addr, *cfg_bind_addr; 
     1140    pj_uint16_t max_bind_retry; 
     1141    char addrtxt[PJ_INET6_ADDRSTRLEN+8]; 
     1142    pj_status_t status; 
     1143    unsigned i; 
     1144 
     1145    PJ_ASSERT_ON_FAIL(turn_sock->conn_type == PJ_TURN_TP_TCP && 
     1146                      turn_sock->alloc_param.peer_conn_type == PJ_TURN_TP_TCP, 
     1147                      return); 
     1148 
     1149    PJ_LOG(5,(turn_sock->pool->obj_name, "Connection attempt from peer %s", 
     1150              pj_sockaddr_print(&peer_addr, addrtxt, sizeof(addrtxt), 3))); 
     1151 
     1152    if (turn_sock == NULL) { 
     1153        /* We've been destroyed */ 
     1154        return; 
     1155    } 
     1156 
     1157    pj_grp_lock_acquire(turn_sock->grp_lock); 
     1158 
     1159    if (turn_sock->data_conn_cnt == PJ_TURN_MAX_TCP_CONN_CNT) { 
     1160        /* Data connection has reached limit */ 
     1161        pj_grp_lock_release(turn_sock->grp_lock); 
     1162        return; 
     1163    } 
     1164 
     1165    /* Check if app wants to accept this connection */ 
     1166    status = PJ_SUCCESS; 
     1167    if (turn_sock->cb.on_connection_attempt) { 
     1168        status = (*turn_sock->cb.on_connection_attempt)(turn_sock, conn_id, 
     1169                                                        peer_addr, addr_len); 
     1170    } 
     1171    /* App rejects it */ 
     1172    if (status != PJ_SUCCESS) { 
     1173        pj_perror(4, turn_sock->pool->obj_name, status, 
     1174                  "Rejected connection attempt from peer %s", 
     1175                  pj_sockaddr_print(peer_addr, addrtxt, sizeof(addrtxt), 3)); 
     1176        pj_grp_lock_release(turn_sock->grp_lock); 
     1177        return; 
     1178    } 
     1179 
     1180    /* Find free data connection slot */ 
     1181    for (i=0; i < PJ_TURN_MAX_TCP_CONN_CNT; ++i) { 
     1182        if (turn_sock->data_conn[i].state == DATACONN_STATE_NULL) 
     1183            break; 
     1184    } 
     1185    pj_assert(i < turn_sock->data_conn_cnt); 
     1186    ++turn_sock->data_conn_cnt; 
     1187 
     1188    /* Init new data connection */ 
     1189    new_conn = &turn_sock->data_conn[i]; 
     1190    pj_bzero(new_conn, sizeof(*new_conn)); 
     1191    pool = pj_pool_create(turn_sock->cfg.pf, "dataconn", 128, 128, NULL); 
     1192    new_conn->pool = pool; 
     1193    new_conn->id = conn_id; 
     1194    new_conn->turn_sock = turn_sock; 
     1195    pj_sockaddr_cp(&new_conn->peer_addr, peer_addr); 
     1196    new_conn->peer_addr_len = addr_len; 
     1197    pj_ioqueue_op_key_init(&new_conn->send_key, sizeof(new_conn->send_key)); 
     1198    new_conn->state = DATACONN_STATE_INITSOCK; 
     1199 
     1200    /* Init socket */ 
     1201    status = pj_sock_socket(turn_sock->af, pj_SOCK_STREAM(), 0, &sock); 
     1202    if (status != PJ_SUCCESS) 
     1203        goto on_return; 
     1204 
     1205    /* Bind socket */ 
     1206    cfg_bind_addr = &turn_sock->setting.bound_addr; 
     1207    max_bind_retry = MAX_BIND_RETRY; 
     1208    if (turn_sock->setting.port_range && 
     1209        turn_sock->setting.port_range < max_bind_retry) 
     1210    { 
     1211        max_bind_retry = turn_sock->setting.port_range; 
     1212    } 
     1213    pj_sockaddr_init(turn_sock->af, &bound_addr, NULL, 0); 
     1214    if (cfg_bind_addr->addr.sa_family == pj_AF_INET() || 
     1215        cfg_bind_addr->addr.sa_family == pj_AF_INET6()) 
     1216    { 
     1217        pj_sockaddr_cp(&bound_addr, cfg_bind_addr); 
     1218    } 
     1219    status = pj_sock_bind_random(sock, &bound_addr, 
     1220                                 turn_sock->setting.port_range, 
     1221                                 max_bind_retry); 
     1222    if (status != PJ_SUCCESS) 
     1223        goto on_return; 
     1224 
     1225    /* Apply socket buffer size */ 
     1226    if (turn_sock->setting.so_rcvbuf_size > 0) { 
     1227        unsigned sobuf_size = turn_sock->setting.so_rcvbuf_size; 
     1228        status = pj_sock_setsockopt_sobuf(sock, pj_SO_RCVBUF(), PJ_TRUE, 
     1229                                          &sobuf_size); 
     1230        if (status != PJ_SUCCESS) { 
     1231            pj_perror(3, turn_sock->obj_name, status, 
     1232                      "Failed setting SO_RCVBUF"); 
     1233        } else { 
     1234            if (sobuf_size < turn_sock->setting.so_rcvbuf_size) { 
     1235                PJ_LOG(4, (turn_sock->obj_name, 
     1236                           "Warning! Cannot set SO_RCVBUF as configured," 
     1237                           " now=%d, configured=%d", sobuf_size, 
     1238                           turn_sock->setting.so_rcvbuf_size)); 
     1239            } else { 
     1240                PJ_LOG(5, (turn_sock->obj_name, "SO_RCVBUF set to %d", 
     1241                           sobuf_size)); 
     1242            } 
     1243        } 
     1244    } 
     1245    if (turn_sock->setting.so_sndbuf_size > 0) { 
     1246        unsigned sobuf_size = turn_sock->setting.so_sndbuf_size; 
     1247        status = pj_sock_setsockopt_sobuf(sock, pj_SO_SNDBUF(), PJ_TRUE, 
     1248                                          &sobuf_size); 
     1249        if (status != PJ_SUCCESS) { 
     1250            pj_perror(3, turn_sock->obj_name, status, 
     1251                      "Failed setting SO_SNDBUF"); 
     1252        } else { 
     1253            if (sobuf_size < turn_sock->setting.so_sndbuf_size) { 
     1254                PJ_LOG(4, (turn_sock->obj_name, 
     1255                           "Warning! Cannot set SO_SNDBUF as configured," 
     1256                           " now=%d, configured=%d", sobuf_size, 
     1257                           turn_sock->setting.so_sndbuf_size)); 
     1258            } else { 
     1259                PJ_LOG(5, (turn_sock->obj_name, "SO_SNDBUF set to %d", 
     1260                           sobuf_size)); 
     1261            } 
     1262        } 
     1263    } 
     1264 
     1265    /* Create active socket */ 
     1266    pj_activesock_cfg_default(&asock_cfg); 
     1267    asock_cfg.grp_lock = turn_sock->grp_lock; 
     1268 
     1269    pj_bzero(&asock_cb, sizeof(asock_cb)); 
     1270    asock_cb.on_data_read = &dataconn_on_data_read; 
     1271    asock_cb.on_connect_complete = &dataconn_on_connect_complete; 
     1272    status = pj_activesock_create(pool, sock, 
     1273                                  pj_SOCK_STREAM(), &asock_cfg, 
     1274                                  turn_sock->cfg.ioqueue, &asock_cb, 
     1275                                  new_conn, &new_conn->asock); 
     1276    if (status != PJ_SUCCESS) 
     1277        goto on_return; 
     1278 
     1279    /* Connect to TURN server for data connection */ 
     1280    pj_turn_session_get_info(turn_sock->sess, &info); 
     1281    status = pj_activesock_start_connect(new_conn->asock, 
     1282                                         pool, 
     1283                                         &info.server, 
     1284                                         pj_sockaddr_get_len(&info.server)); 
     1285    if (status == PJ_SUCCESS) { 
     1286        dataconn_on_connect_complete(new_conn->asock, PJ_SUCCESS); 
     1287        pj_grp_lock_release(turn_sock->grp_lock); 
     1288        return; 
     1289    } 
     1290 
     1291on_return: 
     1292    if (status == PJ_EPENDING) { 
     1293        PJ_LOG(5,(pool->obj_name, 
     1294                  "Accepting connection from peer %s", 
     1295                  pj_sockaddr_print(peer_addr, addrtxt, sizeof(addrtxt), 3))); 
     1296    } else { 
     1297        /* not PJ_SUCCESS */ 
     1298        pj_perror(4, pool->obj_name, status, 
     1299                  "Failed in accepting connection from peer %s", 
     1300                  pj_sockaddr_print(peer_addr, addrtxt, sizeof(addrtxt), 3)); 
     1301 
     1302        if (!new_conn->asock && sock != PJ_INVALID_SOCKET) 
     1303            pj_sock_close(sock);     
     1304 
     1305        dataconn_cleanup(new_conn); 
     1306        --turn_sock->data_conn_cnt; 
     1307 
     1308        /* Notify app for failure */ 
     1309        if (turn_sock->cb.on_connection_status) { 
     1310            (*turn_sock->cb.on_connection_status)(turn_sock, status, conn_id, 
     1311                                                  peer_addr, addr_len); 
     1312        } 
     1313    } 
     1314    pj_grp_lock_release(turn_sock->grp_lock); 
     1315} 
     1316 
     1317static void turn_on_connection_bind_status(pj_turn_session *sess, 
     1318                                           pj_status_t status, 
     1319                                           pj_uint32_t conn_id, 
     1320                                           const pj_sockaddr_t *peer_addr, 
     1321                                           unsigned addr_len) 
     1322{ 
     1323    pj_turn_sock *turn_sock = (pj_turn_sock*)  
     1324                              pj_turn_session_get_user_data(sess); 
     1325    tcp_data_conn_t *conn = NULL; 
     1326    unsigned i; 
     1327 
     1328    pj_grp_lock_acquire(turn_sock->grp_lock); 
     1329 
     1330    for (i=0; i < PJ_TURN_MAX_TCP_CONN_CNT; ++i) { 
     1331        tcp_data_conn_t *c = &turn_sock->data_conn[i]; 
     1332        if (c->id == conn_id && 
     1333            pj_sockaddr_cmp(peer_addr, &c->peer_addr) == 0) 
     1334        { 
     1335            conn = c; 
     1336            break; 
     1337        } 
     1338    } 
     1339    if (!conn) { 
     1340        PJ_LOG(5,(turn_sock->pool->obj_name, 
     1341                  "Warning: stray connection bind event")); 
     1342        pj_grp_lock_release(turn_sock->grp_lock); 
     1343        return; 
     1344    } 
     1345 
     1346    if (status == PJ_SUCCESS) { 
     1347        conn->state = DATACONN_STATE_READY; 
     1348    } else { 
     1349        dataconn_cleanup(conn); 
     1350        --turn_sock->data_conn_cnt; 
     1351    } 
     1352 
     1353    pj_grp_lock_release(turn_sock->grp_lock); 
     1354 
     1355    if (turn_sock->cb.on_connection_status) { 
     1356        (*turn_sock->cb.on_connection_status)(turn_sock, status, conn_id, 
     1357                                              peer_addr, addr_len); 
     1358    } 
     1359} 
Note: See TracChangeset for help on using the changeset viewer.