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.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • 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 
Note: See TracChangeset for help on using the changeset viewer.