Changeset 5737


Ignore:
Timestamp:
Feb 15, 2018 1:57:11 PM (7 years ago)
Author:
riza
Message:

Fix #2091: On iOS11, replace_udp_sock() might fail and lead to unusable UDP transport.

Location:
pjproject/trunk
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • pjproject/trunk/pjlib/src/pj/ioqueue_common_abs.c

    r5692 r5737  
    344344                                 "Send error for socket %d, retrying", 
    345345                                 h->fd)); 
    346                     replace_udp_sock(h); 
     346                    send_rc = replace_udp_sock(h); 
    347347                    continue; 
    348348                } 
     
    579579                h->fd_type==pj_SOCK_DGRAM()) 
    580580            { 
    581                 replace_udp_sock(h); 
     581                rc = replace_udp_sock(h); 
     582                if (rc != PJ_SUCCESS) { 
     583                    bytes_read = -rc; 
     584                } 
    582585            } 
    583586#endif 
  • pjproject/trunk/pjlib/src/pj/ioqueue_select.c

    r5680 r5737  
    728728    PJ_LOG(4,(THIS_FILE, "Attempting to replace UDP socket %d", old_sock)); 
    729729 
    730     /* Investigate the old socket */ 
    731     addr_len = sizeof(local_addr); 
    732     status = pj_sock_getsockname(old_sock, &local_addr, &addr_len); 
     730    for (msec=20; (msec<1000 && status != PJ_SUCCESS) ; 
     731         msec<1000? msec=msec*2 : 1000) 
     732    { 
     733        if (msec > 20) { 
     734            PJ_LOG(4,(THIS_FILE, "Retry to replace UDP socket %d", old_sock)); 
     735            pj_thread_sleep(msec); 
     736        } 
     737         
     738        /* Investigate the old socket */ 
     739        addr_len = sizeof(local_addr); 
     740        status = pj_sock_getsockname(old_sock, &local_addr, &addr_len); 
     741        if (status != PJ_SUCCESS) { 
     742            PJ_LOG(5,(THIS_FILE, "Error get socket name %d", status)); 
     743            continue; 
     744        } 
     745         
     746        addr_len = sizeof(rem_addr); 
     747        status = pj_sock_getpeername(old_sock, &rem_addr, &addr_len); 
     748        if (status != PJ_SUCCESS) { 
     749            PJ_LOG(5,(THIS_FILE, "Error get peer name %d", status)); 
     750        } else { 
     751            flags |= HAS_PEER_ADDR; 
     752        } 
     753 
     754        status = pj_sock_get_qos_params(old_sock, &qos_params); 
     755        if (status == PJ_STATUS_FROM_OS(EBADF) || 
     756            status == PJ_STATUS_FROM_OS(EINVAL)) 
     757        { 
     758            PJ_LOG(5,(THIS_FILE, "Error get qos param %d", status)); 
     759            continue; 
     760        } 
     761         
     762        if (status != PJ_SUCCESS) { 
     763            PJ_LOG(5,(THIS_FILE, "Error get qos param %d", status)); 
     764        } else { 
     765            flags |= HAS_QOS; 
     766        } 
     767 
     768        /* We're done with the old socket, close it otherwise we'll get 
     769         * error in bind() 
     770         */ 
     771        status = pj_sock_close(old_sock); 
     772        if (status != PJ_SUCCESS) { 
     773            PJ_LOG(5,(THIS_FILE, "Error closing socket %d", status)); 
     774        } 
     775 
     776        /* Prepare the new socket */ 
     777        status = pj_sock_socket(local_addr.addr.sa_family, PJ_SOCK_DGRAM, 0, 
     778                                &new_sock); 
     779        if (status != PJ_SUCCESS) { 
     780            PJ_LOG(5,(THIS_FILE, "Error create socket %d", status)); 
     781            continue; 
     782        } 
     783 
     784        /* Even after the socket is closed, we'll still get "Address in use" 
     785         * errors, so force it with SO_REUSEADDR 
     786         */ 
     787        val = 1; 
     788        status = pj_sock_setsockopt(new_sock, SOL_SOCKET, SO_REUSEADDR, 
     789                                    &val, sizeof(val)); 
     790        if (status == PJ_STATUS_FROM_OS(EBADF) || 
     791            status == PJ_STATUS_FROM_OS(EINVAL)) 
     792        { 
     793            PJ_LOG(5,(THIS_FILE, "Error set socket option %d", 
     794                      status)); 
     795            continue; 
     796        } 
     797 
     798        /* The loop is silly, but what else can we do? */ 
     799        addr_len = pj_sockaddr_get_len(&local_addr); 
     800        for (msec=20; msec<1000 ; msec<1000? msec=msec*2 : 1000) { 
     801            status = pj_sock_bind(new_sock, &local_addr, addr_len); 
     802            if (status != PJ_STATUS_FROM_OS(EADDRINUSE)) 
     803                break; 
     804            PJ_LOG(4,(THIS_FILE, "Address is still in use, retrying..")); 
     805            pj_thread_sleep(msec); 
     806        } 
     807 
     808        if (status != PJ_SUCCESS) 
     809            continue; 
     810 
     811        if (flags & HAS_QOS) { 
     812            status = pj_sock_set_qos_params(new_sock, &qos_params); 
     813            if (status == PJ_STATUS_FROM_OS(EINVAL)) { 
     814                PJ_LOG(5,(THIS_FILE, "Error set qos param %d", status)); 
     815                continue; 
     816            } 
     817        } 
     818 
     819        if (flags & HAS_PEER_ADDR) { 
     820            status = pj_sock_connect(new_sock, &rem_addr, addr_len); 
     821            if (status != PJ_SUCCESS) { 
     822                PJ_LOG(5,(THIS_FILE, "Error connect socket %d", status)); 
     823                continue; 
     824            } 
     825        } 
     826    } 
     827     
    733828    if (status != PJ_SUCCESS) 
    734         goto on_error; 
     829        goto on_error; 
    735830     
    736     addr_len = sizeof(rem_addr); 
    737     status = pj_sock_getpeername(old_sock, &rem_addr, &addr_len); 
    738     if (status == PJ_SUCCESS) 
    739         flags |= HAS_PEER_ADDR; 
    740  
    741     status = pj_sock_get_qos_params(old_sock, &qos_params); 
    742     if (status == PJ_SUCCESS) 
    743         flags |= HAS_QOS; 
    744  
    745     /* We're done with the old socket, close it otherwise we'll get 
    746      * error in bind() 
    747      */ 
    748     pj_sock_close(old_sock); 
    749  
    750     /* Prepare the new socket */ 
    751     status = pj_sock_socket(local_addr.addr.sa_family, PJ_SOCK_DGRAM, 0, 
    752                             &new_sock); 
    753     if (status != PJ_SUCCESS) 
    754         goto on_error; 
    755  
    756     /* Even after the socket is closed, we'll still get "Address in use" 
    757      * errors, so force it with SO_REUSEADDR 
    758      */ 
    759     val = 1; 
    760     status = pj_sock_setsockopt(new_sock, SOL_SOCKET, SO_REUSEADDR, 
    761                                 &val, sizeof(val)); 
    762     if (status != PJ_SUCCESS) 
    763         goto on_error; 
    764  
    765     /* The loop is silly, but what else can we do? */ 
    766     addr_len = pj_sockaddr_get_len(&local_addr); 
    767     for (msec=20; ; msec<1000? msec=msec*2 : 1000) { 
    768         status = pj_sock_bind(new_sock, &local_addr, addr_len); 
    769         if (status != PJ_STATUS_FROM_OS(EADDRINUSE)) 
    770             break; 
    771         PJ_LOG(4,(THIS_FILE, "Address is still in use, retrying..")); 
    772         pj_thread_sleep(msec); 
    773     } 
    774  
    775     if (status != PJ_SUCCESS) 
    776         goto on_error; 
    777  
    778     if (flags & HAS_QOS) { 
    779         status = pj_sock_set_qos_params(new_sock, &qos_params); 
    780         if (status != PJ_SUCCESS) 
    781             goto on_error; 
    782     } 
    783  
    784     if (flags & HAS_PEER_ADDR) { 
    785         status = pj_sock_connect(new_sock, &rem_addr, addr_len); 
    786         if (status != PJ_SUCCESS) 
    787             goto on_error; 
    788     } 
    789  
    790831    /* Set socket to nonblocking. */ 
    791832    val = 1; 
     
    830871    if (new_sock != PJ_INVALID_SOCKET) 
    831872        pj_sock_close(new_sock); 
    832     PJ_PERROR(1,(THIS_FILE, status, "Error replacing socket")); 
     873    PJ_PERROR(1,(THIS_FILE, status, "Error replacing socket [%d]", status)); 
    833874    pj_lock_release(h->ioqueue->lock); 
    834     return status; 
     875    return PJ_ESOCKETSTOP; 
    835876} 
    836877#endif 
  • pjproject/trunk/pjmedia/src/pjmedia/transport_udp.c

    r5654 r5737  
    7070    unsigned            tx_drop_pct;    /**< Percent of tx pkts to drop.    */ 
    7171    unsigned            rx_drop_pct;    /**< Percent of rx pkts to drop.    */ 
     72    pj_ioqueue_t        *ioqueue;       /**< Ioqueue instance.              */ 
    7273 
    7374    pj_sock_t           rtp_sock;       /**< RTP socket                     */ 
     
    152153static pj_status_t transport_destroy  (pjmedia_transport *tp); 
    153154 
     155static pj_status_t transport_restart  (pj_bool_t is_rtp,  
     156                                       struct transport_udp *udp); 
     157 
    154158 
    155159static pjmedia_transport_op transport_udp_op =  
     
    387391        goto on_error; 
    388392 
     393    tp->ioqueue = ioqueue; 
    389394 
    390395    /* Done */ 
     
    441446 
    442447/* Notification from ioqueue about incoming RTP packet */ 
    443 static void on_rx_rtp( pj_ioqueue_key_t *key,  
    444                        pj_ioqueue_op_key_t *op_key,  
    445                        pj_ssize_t bytes_read) 
     448static void on_rx_rtp(pj_ioqueue_key_t *key, 
     449                      pj_ioqueue_op_key_t *op_key, 
     450                      pj_ssize_t bytes_read) 
    446451{ 
    447452    struct transport_udp *udp; 
     
    451456 
    452457    udp = (struct transport_udp*) pj_ioqueue_get_user_data(key); 
     458 
     459#if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \ 
     460            PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0 
     461    if (-bytes_read == PJ_ESOCKETSTOP) { 
     462        /* Try to recover by restarting the transport. */ 
     463        PJ_LOG(4, (udp->base.name, "Restarting RTP transport")); 
     464        status = transport_restart(PJ_TRUE, udp); 
     465        if (status == PJ_SUCCESS) { 
     466            PJ_LOG(4, (udp->base.name, "Success restarting RTP transport")); 
     467        } else { 
     468            PJ_PERROR(1, (udp->base.name, status,  
     469                          "Error restarting RTP transport")); 
     470        } 
     471        return; 
     472    } 
     473#endif 
    453474 
    454475    do { 
     
    565586 
    566587    udp = (struct transport_udp*) pj_ioqueue_get_user_data(key); 
     588 
     589#if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \ 
     590            PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0 
     591    if (-bytes_read == PJ_ESOCKETSTOP) { 
     592        /* Try to recover by restarting the transport. */ 
     593        PJ_LOG(4, (udp->base.name, "Restarting RTCP transport")); 
     594        status = transport_restart(PJ_FALSE, udp); 
     595        if (status == PJ_SUCCESS) { 
     596            PJ_LOG(4, (udp->base.name, "Success restarting RTCP transport")); 
     597        } else { 
     598            PJ_PERROR(1, (udp->base.name, status,  
     599                          "Error restarting RTCP transport")); 
     600        } 
     601        return; 
     602    } 
     603#endif 
    567604 
    568605    do { 
     
    9961033} 
    9971034 
     1035static pj_status_t transport_restart(pj_bool_t is_rtp, 
     1036                                     struct transport_udp *udp) 
     1037{ 
     1038    pj_ioqueue_key_t *key = (is_rtp ? udp->rtp_key : udp->rtcp_key); 
     1039    pj_sock_t *sock = (is_rtp ? &udp->rtp_sock : &udp->rtcp_sock); 
     1040    pj_status_t status; 
     1041    int af; 
     1042    pj_sockaddr *addr; 
     1043    pj_ioqueue_callback cb; 
     1044    pj_ssize_t size; 
     1045 
     1046    /* Destroy existing socket, if any. */     
     1047    if (key) { 
     1048        /* This will block the execution if callback is still 
     1049         * being called. 
     1050         */ 
     1051        pj_ioqueue_unregister(key); 
     1052        if (is_rtp) { 
     1053            udp->rtp_key = NULL; 
     1054        } else { 
     1055            udp->rtcp_key = NULL; 
     1056        } 
     1057    } else if (*sock != PJ_INVALID_SOCKET) { 
     1058        pj_sock_close(*sock); 
     1059    } 
     1060    *sock = PJ_INVALID_SOCKET; 
     1061 
     1062    /* Create socket */ 
     1063    af = udp->rtp_addr_name.addr.sa_family; 
     1064    status = pj_sock_socket(af, pj_SOCK_DGRAM(), 0, sock); 
     1065 
     1066    if (status != PJ_SUCCESS) 
     1067        goto on_error; 
     1068 
     1069    addr = (is_rtp) ? &udp->rtp_addr_name : &udp->rtcp_addr_name; 
     1070    status = pj_sock_bind(*sock, addr, pj_sockaddr_get_len(addr)); 
     1071    if (status != PJ_SUCCESS) 
     1072        goto on_error; 
     1073 
     1074    /* Set buffer size for RTP socket */ 
     1075#if PJMEDIA_TRANSPORT_SO_RCVBUF_SIZE 
     1076    { 
     1077        unsigned sobuf_size = PJMEDIA_TRANSPORT_SO_RCVBUF_SIZE; 
     1078 
     1079        status = pj_sock_setsockopt_sobuf(udp->rtp_sock, pj_SO_RCVBUF(), 
     1080                                          PJ_TRUE, &sobuf_size); 
     1081        if (status != PJ_SUCCESS) { 
     1082            pj_perror(3, udp->base.name, status, "Failed setting SO_RCVBUF"); 
     1083        } else { 
     1084            if (sobuf_size < PJMEDIA_TRANSPORT_SO_RCVBUF_SIZE) { 
     1085                PJ_LOG(4, (udp->base.name, 
     1086                           "Warning! Cannot set SO_RCVBUF as configured, " 
     1087                           "now=%d, configured=%d", 
     1088                           sobuf_size, PJMEDIA_TRANSPORT_SO_RCVBUF_SIZE)); 
     1089            } else { 
     1090                PJ_LOG(5, (udp->base.name, "SO_RCVBUF set to %d", sobuf_size)); 
     1091            } 
     1092        } 
     1093    } 
     1094#endif 
     1095#if PJMEDIA_TRANSPORT_SO_SNDBUF_SIZE 
     1096    { 
     1097        unsigned sobuf_size = PJMEDIA_TRANSPORT_SO_SNDBUF_SIZE; 
     1098 
     1099        status = pj_sock_setsockopt_sobuf(udp->rtp_sock, pj_SO_SNDBUF(), 
     1100                                          PJ_TRUE, &sobuf_size); 
     1101        if (status != PJ_SUCCESS) { 
     1102            pj_perror(3, udp->base.name, status, "Failed setting SO_SNDBUF"); 
     1103        } else { 
     1104            if (sobuf_size < PJMEDIA_TRANSPORT_SO_SNDBUF_SIZE) { 
     1105                PJ_LOG(4, (udp->base.name, 
     1106                           "Warning! Cannot set SO_SNDBUF as configured, " 
     1107                           "now=%d, configured=%d", 
     1108                           sobuf_size, PJMEDIA_TRANSPORT_SO_SNDBUF_SIZE)); 
     1109            } else { 
     1110                PJ_LOG(5, (udp->base.name, "SO_SNDBUF set to %d", sobuf_size)); 
     1111            } 
     1112        } 
     1113    } 
     1114#endif 
     1115    pj_bzero(&cb, sizeof(cb)); 
     1116    if (is_rtp) 
     1117        cb.on_read_complete = &on_rx_rtp; 
     1118    else  
     1119        cb.on_read_complete = &on_rx_rtcp; 
     1120 
     1121    if (is_rtp) { 
     1122        status = pj_ioqueue_register_sock(udp->pool, udp->ioqueue, *sock, udp, 
     1123                                          &cb, &udp->rtp_key); 
     1124    } else { 
     1125        status = pj_ioqueue_register_sock(udp->pool, udp->ioqueue, *sock, udp, 
     1126                                          &cb, &udp->rtcp_key);     
     1127    } 
     1128 
     1129    if (status != PJ_SUCCESS) 
     1130        goto on_error; 
     1131 
     1132    if (is_rtp) { 
     1133        size = sizeof(udp->rtp_pkt); 
     1134        status = pj_ioqueue_recvfrom(udp->rtp_key, &udp->rtp_read_op, 
     1135                                     udp->rtp_pkt, &size,  
     1136                                     PJ_IOQUEUE_ALWAYS_ASYNC, 
     1137                                     &udp->rtp_src_addr, &udp->rtp_addrlen); 
     1138    } else { 
     1139        size = sizeof(udp->rtcp_pkt); 
     1140        status = pj_ioqueue_recvfrom(udp->rtcp_key, &udp->rtcp_read_op, 
     1141                                     udp->rtcp_pkt, &size,  
     1142                                     PJ_IOQUEUE_ALWAYS_ASYNC, 
     1143                                     &udp->rtcp_src_addr, &udp->rtcp_addr_len); 
     1144    } 
     1145    if (status != PJ_EPENDING) 
     1146        goto on_error; 
     1147 
     1148 
     1149    return PJ_SUCCESS; 
     1150on_error: 
     1151    if (*sock != PJ_INVALID_SOCKET) { 
     1152        pj_sock_close(*sock); 
     1153        *sock = PJ_INVALID_SOCKET; 
     1154    } 
     1155    return status; 
     1156} 
  • pjproject/trunk/pjsip/src/pjsip/sip_transport_udp.c

    r5649 r5737  
    143143        goto on_return; 
    144144 
     145#if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \ 
     146            PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0 
     147    if (-bytes_read == PJ_ESOCKETSTOP) { 
     148        --tp->read_loop_spin; 
     149        /* Try to recover by restarting the transport. */ 
     150        PJ_LOG(4,(tp->base.obj_name, "Restarting SIP UDP transport")); 
     151        status = pjsip_udp_transport_restart2( 
     152                            &tp->base, 
     153                            PJSIP_UDP_TRANSPORT_DESTROY_SOCKET, 
     154                            PJ_INVALID_SOCKET, 
     155                            &tp->base.local_addr, 
     156                            &tp->base.local_name); 
     157 
     158        if (status != PJ_SUCCESS) { 
     159            PJ_PERROR(1,(THIS_FILE, status, 
     160                         "Error restarting SIP UDP transport")); 
     161        } 
     162        return; 
     163    } 
     164#endif 
     165 
    145166    /* 
    146167     * The idea of the loop is to process immediate data received by 
     
    262283                    status != PJ_STATUS_FROM_OS(OSERR_ECONNRESET))  
    263284                { 
    264  
    265285                    PJSIP_ENDPT_LOG_ERROR((rdata->tp_info.transport->endpt, 
    266286                                           rdata->tp_info.transport->obj_name, 
     
    305325    tdata_op_key->tdata = NULL; 
    306326 
     327#if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \ 
     328            PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0 
     329    if (-bytes_sent == PJ_ESOCKETSTOP) { 
     330        pj_status_t status; 
     331        /* Try to recover by restarting the transport. */ 
     332        PJ_LOG(4,(tp->base.obj_name, "Restarting SIP UDP transport")); 
     333        status = pjsip_udp_transport_restart2( 
     334                            &tp->base, 
     335                            PJSIP_UDP_TRANSPORT_DESTROY_SOCKET, 
     336                            PJ_INVALID_SOCKET, 
     337                            &tp->base.local_addr, 
     338                            &tp->base.local_name); 
     339 
     340        if (status != PJ_SUCCESS) { 
     341            PJ_PERROR(1,(THIS_FILE, status, 
     342                         "Error restarting SIP UDP transport")); 
     343        } 
     344        return; 
     345    } 
     346#endif 
     347 
    307348    if (tdata_op_key->callback) { 
    308349        tdata_op_key->callback(&tp->base, tdata_op_key->token, bytes_sent); 
     
    562603 
    563604    pj_assert(a_name->host.slen != 0); 
     605     
     606    if (pj_strcmp(&tp->base.local_name.host, &a_name->host) == 0 && 
     607        tp->base.local_name.port == a_name->port) 
     608    { 
     609        return; 
     610    } 
     611     
    564612    pj_strdup_with_null(tp->base.pool, &tp->base.local_name.host,  
    565613                        &a_name->host); 
     
    11331181        /* Create the socket if it's not specified */ 
    11341182        if (sock == PJ_INVALID_SOCKET) { 
    1135             status = create_socket(local->addr.sa_family, local,  
    1136                                    pj_sockaddr_get_len(local), &sock); 
     1183            status = create_socket(local?local->addr.sa_family:pj_AF_UNSPEC(),  
     1184                                   local, local?pj_sockaddr_get_len(local):0,  
     1185                                   &sock); 
    11371186            if (status != PJ_SUCCESS) 
    11381187                return status; 
Note: See TracChangeset for help on using the changeset viewer.