Пример #1
0
PJ_DEF(void) pjmedia_rtp_session_update2( pjmedia_rtp_session *ses, 
					  const pjmedia_rtp_hdr *hdr,
					  pjmedia_rtp_status *p_seq_st,
					  pj_bool_t check_pt)
{
    pjmedia_rtp_status seq_st;

    /* for now check_pt MUST be either PJ_TRUE or PJ_FALSE.
     * In the future we might change check_pt from boolean to 
     * unsigned integer to accommodate more flags.
     */
    pj_assert(check_pt==PJ_TRUE || check_pt==PJ_FALSE);

    /* Init status */
    seq_st.status.value = 0;
    seq_st.diff = 0;

    /* Check SSRC. */
    if (!ses->has_peer_ssrc && ses->peer_ssrc == 0)
        ses->peer_ssrc = pj_ntohl(hdr->ssrc);

    if (pj_ntohl(hdr->ssrc) != ses->peer_ssrc) {
	seq_st.status.flag.badssrc = 1;
	if (!ses->has_peer_ssrc)
	    ses->peer_ssrc = pj_ntohl(hdr->ssrc);
    }

    /* Check payload type. */
    if (check_pt && hdr->pt != ses->out_pt) {
	if (p_seq_st) {
	    p_seq_st->status.value = seq_st.status.value;
	    p_seq_st->status.flag.bad = 1;
	    p_seq_st->status.flag.badpt = 1;
	}
	return;
    }

    /* Initialize sequence number on first packet received. */
    if (ses->received == 0)
	pjmedia_rtp_seq_init( &ses->seq_ctrl, pj_ntohs(hdr->seq) );

    /* Check sequence number to see if remote session has been restarted. */
    pjmedia_rtp_seq_update( &ses->seq_ctrl, pj_ntohs(hdr->seq), &seq_st);
    if (seq_st.status.flag.restart) {
	++ses->received;

    } else if (!seq_st.status.flag.bad) {
	++ses->received;
    }

    if (p_seq_st) {
	p_seq_st->status.value = seq_st.status.value;
	p_seq_st->diff = seq_st.diff;
    }
}
Пример #2
0
PJ_DEF(pj_status_t) pjmedia_rtp_encode_rtp( pjmedia_rtp_session *ses, 
					    int pt, int m,
					    int payload_len, int ts_len,
					    const void **rtphdr, int *hdrlen )
{
    /* Update timestamp */
    ses->out_hdr.ts = pj_htonl(pj_ntohl(ses->out_hdr.ts)+ts_len);

    /* If payload_len is zero, bail out.
     * This is a clock frame; we're not really transmitting anything.
     */
    if (payload_len == 0)
	return PJ_SUCCESS;

    /* Update session. */
    ses->out_extseq++;

    /* Create outgoing header. */
    ses->out_hdr.pt = (pj_uint8_t) ((pt == -1) ? ses->out_pt : pt);
    ses->out_hdr.m = (pj_uint16_t) m;
    ses->out_hdr.seq = pj_htons( (pj_uint16_t) ses->out_extseq);

    /* Return values */
    *rtphdr = &ses->out_hdr;
    *hdrlen = sizeof(pjmedia_rtp_hdr);

    return PJ_SUCCESS;
}
Пример #3
0
/* Open pcap file */
PJ_DEF(pj_status_t) pj_pcap_open(pj_pool_t *pool,
				 const char *path,
				 pj_pcap_file **p_file)
{
    pj_pcap_file *file;
    pj_ssize_t sz;
    pj_status_t status;

    PJ_ASSERT_RETURN(pool && path && p_file, PJ_EINVAL);

    /* More sanity checks */
    TRACE_(("pcap", "sizeof(pj_pcap_eth_hdr)=%d",
	    sizeof(pj_pcap_eth_hdr)));
    PJ_ASSERT_RETURN(sizeof(pj_pcap_eth_hdr)==14, PJ_EBUG);
    TRACE_(("pcap", "sizeof(pj_pcap_ip_hdr)=%d",
	    sizeof(pj_pcap_ip_hdr)));
    PJ_ASSERT_RETURN(sizeof(pj_pcap_ip_hdr)==20, PJ_EBUG);
    TRACE_(("pcap", "sizeof(pj_pcap_udp_hdr)=%d",
	    sizeof(pj_pcap_udp_hdr)));
    PJ_ASSERT_RETURN(sizeof(pj_pcap_udp_hdr)==8, PJ_EBUG);
    
    file = PJ_POOL_ZALLOC_T(pool, pj_pcap_file);

    pj_ansi_strcpy(file->obj_name, "pcap");

    status = pj_file_open(pool, path, PJ_O_RDONLY, &file->fd);
    if (status != PJ_SUCCESS)
	return status;

    /* Read file pcap header */
    sz = sizeof(file->hdr);
    status = pj_file_read(file->fd, &file->hdr, &sz);
    if (status != PJ_SUCCESS) {
	pj_file_close(file->fd);
	return status;
    }

    /* Check magic number */
    if (file->hdr.magic_number == 0xa1b2c3d4) {
	file->swap = PJ_FALSE;
    } else if (file->hdr.magic_number == 0xd4c3b2a1) {
	file->swap = PJ_TRUE;
	file->hdr.network = pj_ntohl(file->hdr.network);
    } else {
	/* Not PCAP file */
	pj_file_close(file->fd);
	return PJ_EINVALIDOP;
    }

    TRACE_((file->obj_name, "PCAP file %s opened", path));
    
    *p_file = file;
    return PJ_SUCCESS;
}
Пример #4
0
/*
 * Convert an Internet host address given in network byte order
 * to string in standard numbers and dots notation.
 */
PJ_DEF(char*) pj_inet_ntoa(pj_in_addr inaddr)
{
	static char str8[PJ_INET_ADDRSTRLEN];
    TBuf<PJ_INET_ADDRSTRLEN> str16(0);

    /* (Symbian IP address is in host byte order) */
    TInetAddr temp_addr((TUint32)pj_ntohl(inaddr.s_addr), (TUint)0);
    temp_addr.Output(str16);
 
    return pj_unicode_to_ansi((const wchar_t*)str16.PtrZ(), str16.Length(),
			      str8, sizeof(str8));
}
Пример #5
0
/*
 * Convert IPv4/IPv6 address to text.
 */
PJ_DEF(pj_status_t) pj_inet_ntop(int af, const void *src,
				 char *dst, int size)

{
    PJ_ASSERT_RETURN(src && dst && size, PJ_EINVAL);

    *dst = '\0';

    if (af==PJ_AF_INET) {

	TBuf<PJ_INET_ADDRSTRLEN> str16;
	pj_in_addr inaddr;

	if (size < PJ_INET_ADDRSTRLEN)
	    return PJ_ETOOSMALL;

	pj_memcpy(&inaddr, src, 4);

	/* Symbian IP address is in host byte order */
	TInetAddr temp_addr((TUint32)pj_ntohl(inaddr.s_addr), (TUint)0);
	temp_addr.Output(str16);
 
	pj_unicode_to_ansi((const wchar_t*)str16.PtrZ(), str16.Length(),
			   dst, size);
	return PJ_SUCCESS;

    } else if (af==PJ_AF_INET6) {
	TBuf<PJ_INET6_ADDRSTRLEN> str16;

	if (size < PJ_INET6_ADDRSTRLEN)
	    return PJ_ETOOSMALL;

	TIp6Addr ip6;
	pj_memcpy(ip6.u.iAddr8, src, 16);

	TInetAddr temp_addr(ip6, (TUint)0);
	temp_addr.Output(str16);
 
	pj_unicode_to_ansi((const wchar_t*)str16.PtrZ(), str16.Length(),
			   dst, size);
	return PJ_SUCCESS;

    } else {
	pj_assert(!"Unsupport address family");
	return PJ_EINVAL;
    }

}
Пример #6
0
static void guid_to_str( GUID *guid, pj_str_t *str )
{
    unsigned i;
    const unsigned char *src = (const unsigned char*)guid;
    char *dst = str->ptr;

    guid->Data1 = pj_ntohl(guid->Data1);
    guid->Data2 = pj_ntohs(guid->Data2);
    guid->Data3 = pj_ntohs(guid->Data3);

    for (i=0; i<16; ++i) {
	hex2digit( *src, dst );
	dst += 2;
	++src;
    }
    str->slen = 32;
}
Пример #7
0
/*
 * Transmit DTMF
 */
static void create_dtmf_payload(pjmedia_stream *stream, 
			        struct pjmedia_frame *frame_out,
				int *first, int *last)
{
    pjmedia_rtp_dtmf_event *event;
    struct dtmf *digit = &stream->tx_dtmf_buf[0];
    pj_uint32_t cur_ts;

    pj_assert(sizeof(pjmedia_rtp_dtmf_event) == 4);

    *first = *last = 0;

    event = (pjmedia_rtp_dtmf_event*) frame_out->buf;
    cur_ts = pj_ntohl(stream->enc->rtp.out_hdr.ts);

    if (digit->duration == 0) {
	PJ_LOG(5,(stream->port.info.name.ptr, "Sending DTMF digit id %c", 
		  digitmap[digit->event]));
	*first = 1;
    }

    digit->duration += stream->port.info.samples_per_frame;

    event->event = (pj_uint8_t)digit->event;
    event->e_vol = 10;
    event->duration = pj_htons((pj_uint16_t)digit->duration);


    if (digit->duration >= PJMEDIA_DTMF_DURATION) {

	event->e_vol |= 0x80;
	*last = 1;

	/* Prepare next digit. */
	pj_mutex_lock(stream->jb_mutex);

	pj_array_erase(stream->tx_dtmf_buf, sizeof(stream->tx_dtmf_buf[0]),
		       stream->tx_dtmf_count, 0);
	--stream->tx_dtmf_count;

	pj_mutex_unlock(stream->jb_mutex);
    }

    frame_out->size = 4;
}
Пример #8
0
/*
 * send_rtp() is called to send RTP packet. The "pkt" and "size" argument
 * contain both the RTP header and the payload.
 */
static pj_status_t transport_send_rtp(pjmedia_transport *tp,
                                      const void *pkt,
                                      pj_size_t size)
{
    struct tp_zrtp *zrtp = (struct tp_zrtp*)tp;
    pj_uint32_t* pui = (pj_uint32_t*)pkt;
    int32_t newLen = 0;
    pj_status_t rc = PJ_SUCCESS;

    PJ_ASSERT_RETURN(tp && pkt, PJ_EINVAL);


    if (!zrtp->started && zrtp->enableZrtp)
    {
        if (zrtp->localSSRC == 0)
            zrtp->localSSRC = pj_ntohl(pui[2]);   /* Learn own SSRC before starting ZRTP */

        pjmedia_transport_zrtp_startZrtp((pjmedia_transport *)zrtp);
    }

    if (zrtp->srtpSend == NULL)
    {
        return pjmedia_transport_send_rtp(zrtp->slave_tp, pkt, size);
    }
    else
    {
        if (size+80 > MAX_RTP_BUFFER_LEN)
            return PJ_ETOOBIG;

        pj_memcpy(zrtp->sendBuffer, pkt, size);
        rc = zsrtp_protect(zrtp->srtpSend, zrtp->sendBuffer, size, &newLen);
        zrtp->protect++;

        if (rc == 1)
            return pjmedia_transport_send_rtp(zrtp->slave_tp, zrtp->sendBuffer, newLen);
        else
            return PJ_EIGNORED;
    }
}
Пример #9
0
/* Resolve the IP address of local machine */
PJ_DEF(pj_status_t) pj_gethostip(int af, pj_sockaddr *addr)
{
    unsigned i, count, cand_cnt;
    enum {
	CAND_CNT = 8,

	/* Weighting to be applied to found addresses */
	WEIGHT_HOSTNAME	= 1,	/* hostname IP is not always valid! */
	WEIGHT_DEF_ROUTE = 2,
	WEIGHT_INTERFACE = 1,
	WEIGHT_LOOPBACK = -5,
	WEIGHT_LINK_LOCAL = -4,
	WEIGHT_DISABLED = -50,

	MIN_WEIGHT = WEIGHT_DISABLED+1	/* minimum weight to use */
    };
    /* candidates: */
    pj_sockaddr cand_addr[CAND_CNT];
    int		cand_weight[CAND_CNT];
    int	        selected_cand;
    char	strip[PJ_INET6_ADDRSTRLEN+10];
    /* Special IPv4 addresses. */
    struct spec_ipv4_t
    {
	pj_uint32_t addr;
	pj_uint32_t mask;
	int	    weight;
    } spec_ipv4[] =
    {
	/* 127.0.0.0/8, loopback addr will be used if there is no other
	 * addresses.
	 */
	{ 0x7f000000, 0xFF000000, WEIGHT_LOOPBACK },

	/* 0.0.0.0/8, special IP that doesn't seem to be practically useful */
	{ 0x00000000, 0xFF000000, WEIGHT_DISABLED },

	/* 169.254.0.0/16, a zeroconf/link-local address, which has higher
	 * priority than loopback and will be used if there is no other
	 * valid addresses.
	 */
	{ 0xa9fe0000, 0xFFFF0000, WEIGHT_LINK_LOCAL }
    };
    /* Special IPv6 addresses */
    struct spec_ipv6_t
    {
	pj_uint8_t addr[16];
	pj_uint8_t mask[16];
	int	   weight;
    } spec_ipv6[] =
    {
	/* Loopback address, ::1/128 */
	{ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
	  {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff},
	  WEIGHT_LOOPBACK
	},

	/* Link local, fe80::/10 */
	{ {0xfe,0x80,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
	  {0xff,0xc0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
	  WEIGHT_LINK_LOCAL
	},

	/* Disabled, ::/128 */
	{ {0x0,0x0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
	{ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
	  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff},
	  WEIGHT_DISABLED
	}
    };
    pj_addrinfo ai;
    pj_status_t status;

    /* May not be used if TRACE_ is disabled */
    PJ_UNUSED_ARG(strip);

#ifdef _MSC_VER
    /* Get rid of "uninitialized he variable" with MS compilers */
    pj_bzero(&ai, sizeof(ai));
#endif

    cand_cnt = 0;
    pj_bzero(cand_addr, sizeof(cand_addr));
    pj_bzero(cand_weight, sizeof(cand_weight));
    for (i=0; i<PJ_ARRAY_SIZE(cand_addr); ++i) {
	cand_addr[i].addr.sa_family = (pj_uint16_t)af;
	PJ_SOCKADDR_RESET_LEN(&cand_addr[i]);
    }

    addr->addr.sa_family = (pj_uint16_t)af;
    PJ_SOCKADDR_RESET_LEN(addr);

#if !defined(PJ_GETHOSTIP_DISABLE_LOCAL_RESOLUTION) || \
	PJ_GETHOSTIP_DISABLE_LOCAL_RESOLUTION == 0
	TRACE_((THIS_FILE, "pj_gethostip() pj_getaddrinfo1"));
    /* Get hostname's IP address */
    count = 1;
    status = pj_getaddrinfo(af, pj_gethostname(), &count, &ai);
    if (status == PJ_SUCCESS) {
    	pj_assert(ai.ai_addr.addr.sa_family == (pj_uint16_t)af);
    	pj_sockaddr_copy_addr(&cand_addr[cand_cnt], &ai.ai_addr);
	pj_sockaddr_set_port(&cand_addr[cand_cnt], 0);
	cand_weight[cand_cnt] += WEIGHT_HOSTNAME;
	++cand_cnt;

	TRACE_((THIS_FILE, "hostname IP is %s",
		pj_sockaddr_print(&ai.ai_addr, strip, sizeof(strip), 0)));
	}
	TRACE_((THIS_FILE, "pj_gethostip() pj_getaddrinfo2"));
#else
    PJ_UNUSED_ARG(ai);
    PJ_UNUSED_ARG(count);
#endif

    /* Get default interface (interface for default route) */
    if (cand_cnt < PJ_ARRAY_SIZE(cand_addr)) {
	status = pj_getdefaultipinterface(af, addr);
	if (status == PJ_SUCCESS) {
	    TRACE_((THIS_FILE, "default IP is %s",
		    pj_sockaddr_print(addr, strip, sizeof(strip), 0)));

	    pj_sockaddr_set_port(addr, 0);
	    for (i=0; i<cand_cnt; ++i) {
		if (pj_sockaddr_cmp(&cand_addr[i], addr)==0)
		    break;
	    }

	    cand_weight[i] += WEIGHT_DEF_ROUTE;
	    if (i >= cand_cnt) {
		pj_sockaddr_copy_addr(&cand_addr[i], addr);
		++cand_cnt;
	    }
	}
    }


    /* Enumerate IP interfaces */
    if (cand_cnt < PJ_ARRAY_SIZE(cand_addr)) {
	unsigned start_if = cand_cnt;
	unsigned count = PJ_ARRAY_SIZE(cand_addr) - start_if;

	status = pj_enum_ip_interface(af, &count, &cand_addr[start_if]);
	if (status == PJ_SUCCESS && count) {
	    /* Clear the port number */
	    for (i=0; i<count; ++i)            
		pj_sockaddr_set_port(&cand_addr[start_if+i], 0);

	    /* For each candidate that we found so far (that is the hostname
	     * address and default interface address, check if they're found
	     * in the interface list. If found, add the weight, and if not,
	     * decrease the weight.
	     */
	    for (i=0; i<cand_cnt; ++i) {
		unsigned j;
		for (j=0; j<count; ++j) {
		    if (pj_sockaddr_cmp(&cand_addr[i], 
					&cand_addr[start_if+j])==0)
			break;
		}

		if (j == count) {
		    /* Not found */
		    cand_weight[i] -= WEIGHT_INTERFACE;
		} else {
		    cand_weight[i] += WEIGHT_INTERFACE;
		}
	    }

	    /* Add remaining interface to candidate list. */
	    for (i=0; i<count; ++i) {
		unsigned j;
		for (j=0; j<cand_cnt; ++j) {
		    if (pj_sockaddr_cmp(&cand_addr[start_if+i], 
					&cand_addr[j])==0)
			break;
		}

		if (j == cand_cnt) {
		    pj_sockaddr_copy_addr(&cand_addr[cand_cnt], 
					  &cand_addr[start_if+i]);
		    cand_weight[cand_cnt] += WEIGHT_INTERFACE;
		    ++cand_cnt;
		}
	    }
	}
    }

    /* Apply weight adjustment for special IPv4/IPv6 addresses
     * See http://trac.pjsip.org/repos/ticket/1046
     */
    if (af == PJ_AF_INET) {
	for (i=0; i<cand_cnt; ++i) {
	    unsigned j;
	    for (j=0; j<PJ_ARRAY_SIZE(spec_ipv4); ++j) {
		    pj_uint32_t a = pj_ntohl(cand_addr[i].ipv4.sin_addr.s_addr);
		    pj_uint32_t pa = spec_ipv4[j].addr;
		    pj_uint32_t pm = spec_ipv4[j].mask;

		    if ((a & pm) == pa) {
			cand_weight[i] += spec_ipv4[j].weight;
			break;
		    }
	    }
	}
    } else if (af == PJ_AF_INET6) {
	for (i=0; i<PJ_ARRAY_SIZE(spec_ipv6); ++i) {
		unsigned j;
		for (j=0; j<cand_cnt; ++j) {
		    pj_uint8_t *a = cand_addr[j].ipv6.sin6_addr.s6_addr;
		    pj_uint8_t am[16];
		    pj_uint8_t *pa = spec_ipv6[i].addr;
		    pj_uint8_t *pm = spec_ipv6[i].mask;
		    unsigned k;

		    for (k=0; k<16; ++k) {
			am[k] = (pj_uint8_t)((a[k] & pm[k]) & 0xFF);
		    }

		    if (pj_memcmp(am, pa, 16)==0) {
			cand_weight[j] += spec_ipv6[i].weight;
		    }
		}
	}
    } else {
	return PJ_EAFNOTSUP;
    }

    /* Enumerate candidates to get the best IP address to choose */
    selected_cand = -1;
    for (i=0; i<cand_cnt; ++i) {
	TRACE_((THIS_FILE, "Checking candidate IP %s, weight=%d",
		pj_sockaddr_print(&cand_addr[i], strip, sizeof(strip), 0),
		cand_weight[i]));

	if (cand_weight[i] < MIN_WEIGHT) {
	    continue;
	}

	if (selected_cand == -1)
	    selected_cand = i;
	else if (cand_weight[i] > cand_weight[selected_cand])
	    selected_cand = i;
    }

    /* If else fails, returns loopback interface as the last resort */
    if (selected_cand == -1) {
	if (af==PJ_AF_INET) {
	    addr->ipv4.sin_addr.s_addr = pj_htonl (0x7f000001);
	} else {
	    pj_in6_addr *s6_addr;

	    s6_addr = (pj_in6_addr*) pj_sockaddr_get_addr(addr);
	    pj_bzero(s6_addr, sizeof(pj_in6_addr));
	    s6_addr->s6_addr[15] = 1;
	}
	TRACE_((THIS_FILE, "Loopback IP %s returned",
		pj_sockaddr_print(addr, strip, sizeof(strip), 0)));
    } else {
	pj_sockaddr_copy_addr(addr, &cand_addr[selected_cand]);
	TRACE_((THIS_FILE, "Candidate %s selected",
		pj_sockaddr_print(addr, strip, sizeof(strip), 0)));
    }

    return PJ_SUCCESS;
}
Пример #10
0
/*
 * Get IPv4 address
 */
PJ_DEF(pj_in_addr) pj_sockaddr_in_get_addr(const pj_sockaddr_in *addr)
{
    pj_in_addr in_addr;
    in_addr.s_addr = pj_ntohl(addr->sin_addr.s_addr);
    return in_addr;
}
Пример #11
0
/* Read UDP packet */
PJ_DEF(pj_status_t) pj_pcap_read_udp(pj_pcap_file *file,
				     pj_pcap_udp_hdr *udp_hdr,
				     pj_uint8_t *udp_payload,
				     pj_size_t *udp_payload_size)
{
    PJ_ASSERT_RETURN(file && udp_payload && udp_payload_size, PJ_EINVAL);
    PJ_ASSERT_RETURN(*udp_payload_size, PJ_EINVAL);

    /* Check data link type in PCAP file header */
    if ((file->filter.link && 
	    file->hdr.network != (pj_uint32_t)file->filter.link) ||
	file->hdr.network != PJ_PCAP_LINK_TYPE_ETH)
    {
	/* Link header other than Ethernet is not supported for now */
	return PJ_ENOTSUP;
    }

    /* Loop until we have the packet */
    for (;;) {
	union {
	    pj_pcap_rec_hdr rec;
	    pj_pcap_eth_hdr eth;
	    pj_pcap_ip_hdr ip;
	    pj_pcap_udp_hdr udp;
	} tmp;
	unsigned rec_incl;
	pj_ssize_t sz;
	unsigned sz_read = 0;
	pj_status_t status;

	TRACE_((file->obj_name, "Reading packet.."));

	/* Read PCAP packet header */
	sz = sizeof(tmp.rec);
	status = read_file(file, &tmp.rec, &sz);
	if (status != PJ_SUCCESS) {
	    TRACE_((file->obj_name, "read_file() error: %d", status));
	    return status;
	}

	rec_incl = tmp.rec.incl_len;

	/* Swap byte ordering */
	if (file->swap) {
	    tmp.rec.incl_len = pj_ntohl(tmp.rec.incl_len);
	    tmp.rec.orig_len = pj_ntohl(tmp.rec.orig_len);
	    tmp.rec.ts_sec = pj_ntohl(tmp.rec.ts_sec);
	    tmp.rec.ts_usec = pj_ntohl(tmp.rec.ts_usec);
	}

	/* Read link layer header */
	switch (file->hdr.network) {
	case PJ_PCAP_LINK_TYPE_ETH:
	    sz = sizeof(tmp.eth);
	    status = read_file(file, &tmp.eth, &sz);
	    break;
	default:
	    TRACE_((file->obj_name, "Error: link layer not Ethernet"));
	    return PJ_ENOTSUP;
	}

	if (status != PJ_SUCCESS) {
	    TRACE_((file->obj_name, "Error reading Eth header: %d", status));
	    return status;
	}

	sz_read += sz;
	    
	/* Read IP header */
	sz = sizeof(tmp.ip);
	status = read_file(file, &tmp.ip, &sz);
	if (status != PJ_SUCCESS) {
	    TRACE_((file->obj_name, "Error reading IP header: %d", status));
	    return status;
	}

	sz_read += sz;

	/* Skip if IP source mismatch */
	if (file->filter.ip_src && tmp.ip.ip_src != file->filter.ip_src) {
	    TRACE_((file->obj_name, "IP source %s mismatch, skipping", 
		    pj_inet_ntoa(*(pj_in_addr*)&tmp.ip.ip_src)));
	    SKIP_PKT();
	    continue;
	}

	/* Skip if IP destination mismatch */
	if (file->filter.ip_dst && tmp.ip.ip_dst != file->filter.ip_dst) {
	    TRACE_((file->obj_name, "IP detination %s mismatch, skipping", 
		    pj_inet_ntoa(*(pj_in_addr*)&tmp.ip.ip_dst)));
	    SKIP_PKT();
	    continue;
	}

	/* Skip if proto mismatch */
	if (file->filter.proto && tmp.ip.proto != file->filter.proto) {
	    TRACE_((file->obj_name, "IP proto %d mismatch, skipping", 
		    tmp.ip.proto));
	    SKIP_PKT();
	    continue;
	}

	/* Read transport layer header */
	switch (tmp.ip.proto) {
	case PJ_PCAP_PROTO_TYPE_UDP:
	    sz = sizeof(tmp.udp);
	    status = read_file(file, &tmp.udp, &sz);
	    if (status != PJ_SUCCESS) {
		TRACE_((file->obj_name, "Error reading UDP header: %d",status));
		return status;
	    }

	    sz_read += sz;

	    /* Skip if source port mismatch */
	    if (file->filter.src_port && 
	        tmp.udp.src_port != file->filter.src_port) 
	    {
		TRACE_((file->obj_name, "UDP src port %d mismatch, skipping", 
			pj_ntohs(tmp.udp.src_port)));
		SKIP_PKT();
		continue;
	    }

	    /* Skip if destination port mismatch */
	    if (file->filter.dst_port && 
		tmp.udp.dst_port != file->filter.dst_port) 
	    {
		TRACE_((file->obj_name, "UDP dst port %d mismatch, skipping", 
			pj_ntohs(tmp.udp.dst_port)));
		SKIP_PKT();
		continue;
	    }

	    /* Copy UDP header if caller wants it */
	    if (udp_hdr) {
		pj_memcpy(udp_hdr, &tmp.udp, sizeof(*udp_hdr));
	    }

	    /* Calculate payload size */
	    sz = pj_ntohs(tmp.udp.len) - sizeof(tmp.udp);
	    break;
	default:
	    TRACE_((file->obj_name, "Not UDP, skipping"));
	    SKIP_PKT();
	    continue;
	}

	/* Check if payload fits the buffer */
	if (sz > (pj_ssize_t)*udp_payload_size) {
	    TRACE_((file->obj_name, 
		    "Error: packet too large (%d bytes required)", sz));
	    SKIP_PKT();
	    return PJ_ETOOSMALL;
	}

	/* Read the payload */
	status = read_file(file, udp_payload, &sz);
	if (status != PJ_SUCCESS) {
	    TRACE_((file->obj_name, "Error reading payload: %d", status));
	    return status;
	}

	sz_read += sz;

	*udp_payload_size = sz;

	// Some layers may have trailer, e.g: link eth2.
	/* Check that we've read all the packets */
	//PJ_ASSERT_RETURN(sz_read == rec_incl, PJ_EBUG);

	/* Skip trailer */
	while (sz_read < rec_incl) {
	    sz = rec_incl - sz_read;
	    status = read_file(file, &tmp.eth, &sz);
	    if (status != PJ_SUCCESS) {
		TRACE_((file->obj_name, "Error reading trailer: %d", status));
		return status;
	    }
	    sz_read += sz;
	}

	return PJ_SUCCESS;
    }

    /* Does not reach here */
}
Пример #12
0
PJ_DEF(pj_status_t) pjstun_get_mapped_addr( pj_pool_factory *pf,
					    int sock_cnt, pj_sock_t sock[],
					    const pj_str_t *srv1, int port1,
					    const pj_str_t *srv2, int port2,
					    pj_sockaddr_in mapped_addr[])
{
    unsigned srv_cnt;
    pj_sockaddr_in srv_addr[2];
    int i, j, send_cnt = 0, nfds;
    pj_pool_t *pool;
    struct query_rec {
	struct {
	    pj_uint32_t	mapped_addr;
	    pj_uint32_t	mapped_port;
	} srv[2];
    } *rec;
    void       *out_msg;
    pj_size_t	out_msg_len;
    int wait_resp = 0;
    pj_status_t status;

    PJ_CHECK_STACK();

    TRACE_((THIS_FILE, "Entering pjstun_get_mapped_addr()"));

    /* Create pool. */
    pool = pj_pool_create(pf, "stun%p", 400, 400, NULL);
    if (!pool)
	return PJ_ENOMEM;


    /* Allocate client records */
    rec = (struct query_rec*) pj_pool_calloc(pool, sock_cnt, sizeof(*rec));
    if (!rec) {
	status = PJ_ENOMEM;
	goto on_error;
    }

    TRACE_((THIS_FILE, "  Memory allocated."));

    /* Create the outgoing BIND REQUEST message template */
    status = pjstun_create_bind_req( pool, &out_msg, &out_msg_len, 
				      pj_rand(), pj_rand());
    if (status != PJ_SUCCESS)
	goto on_error;

    TRACE_((THIS_FILE, "  Binding request created."));

    /* Resolve servers. */
    status = pj_sockaddr_in_init(&srv_addr[0], srv1, (pj_uint16_t)port1);
    if (status != PJ_SUCCESS)
		goto on_error;

    srv_cnt = 1;

    if (srv2 && port2) {
	status = pj_sockaddr_in_init(&srv_addr[1], srv2, (pj_uint16_t)port2);
	if (status != PJ_SUCCESS)
		goto on_error;

	if (srv_addr[1].sin_addr.s_addr != srv_addr[0].sin_addr.s_addr &&
	    srv_addr[1].sin_port != srv_addr[0].sin_port)
	{
	    srv_cnt++;
	}
    }

    TRACE_((THIS_FILE, "  Server initialized, using %d server(s)", srv_cnt));

    /* Init mapped addresses to zero */
    pj_memset(mapped_addr, 0, sock_cnt * sizeof(pj_sockaddr_in));

    /* We need these many responses */
    wait_resp = sock_cnt * srv_cnt;

    TRACE_((THIS_FILE, "  Done initialization."));

#if defined(PJ_SELECT_NEEDS_NFDS) && PJ_SELECT_NEEDS_NFDS!=0
    nfds = -1;
    for (i=0; i<sock_cnt; ++i) {
	if (sock[i] > nfds) {
	    nfds = sock[i];
	}
    }
#else
    nfds = FD_SETSIZE-1;
#endif

    /* Main retransmission loop. */
    for (send_cnt=0; send_cnt<MAX_REQUEST; ++send_cnt) {
	pj_time_val next_tx, now;
	pj_fd_set_t r;
	int select_rc;

	PJ_FD_ZERO(&r);

	/* Send messages to servers that has not given us response. */
	for (i=0; i<sock_cnt && status==PJ_SUCCESS; ++i) {
	    for (j=0; j<srv_cnt && status==PJ_SUCCESS; ++j) {
		pjstun_msg_hdr *msg_hdr = (pjstun_msg_hdr*) out_msg;
                pj_ssize_t sent_len;

		if (rec[i].srv[j].mapped_port != 0)
		    continue;

		/* Modify message so that we can distinguish response. */
		msg_hdr->tsx[2] = pj_htonl(i);
		msg_hdr->tsx[3] = pj_htonl(j);

		/* Send! */
                sent_len = out_msg_len;
		status = pj_sock_sendto(sock[i], out_msg, &sent_len, 0,
					(pj_sockaddr_t*)&srv_addr[j],
					sizeof(pj_sockaddr_in));
	    }
	}

	/* All requests sent.
	 * The loop below will wait for responses until all responses have
	 * been received (i.e. wait_resp==0) or timeout occurs, which then
	 * we'll go to the next retransmission iteration.
	 */
	TRACE_((THIS_FILE, "  Request(s) sent, counter=%d", send_cnt));

	/* Calculate time of next retransmission. */
	pj_gettimeofday(&next_tx);
	next_tx.sec += (stun_timer[send_cnt]/1000);
	next_tx.msec += (stun_timer[send_cnt]%1000);
	pj_time_val_normalize(&next_tx);
	
	for (pj_gettimeofday(&now), select_rc=1; 
	     status==PJ_SUCCESS && select_rc>=1 && wait_resp>0 
	       && PJ_TIME_VAL_LT(now, next_tx); 
	     pj_gettimeofday(&now)) 
	{
	    pj_time_val timeout;

	    timeout = next_tx;
	    PJ_TIME_VAL_SUB(timeout, now);

	    for (i=0; i<sock_cnt; ++i) {
		PJ_FD_SET(sock[i], &r);
	    }

	    select_rc = pj_sock_select(nfds+1, &r, NULL, NULL, &timeout);
	    TRACE_((THIS_FILE, "  select() rc=%d", select_rc));
	    if (select_rc < 1)
		continue;

	    for (i=0; i<sock_cnt; ++i) {
		int sock_idx, srv_idx;
                pj_ssize_t len;
		pjstun_msg msg;
		pj_sockaddr_in addr;
		int addrlen = sizeof(addr);
		pjstun_mapped_addr_attr *attr;
		char recv_buf[128];

		if (!PJ_FD_ISSET(sock[i], &r))
		    continue;

                len = sizeof(recv_buf);
		status = pj_sock_recvfrom( sock[i], recv_buf, 
				           &len, 0,
				           (pj_sockaddr_t*)&addr,
						   &addrlen);

		if (status != PJ_SUCCESS) {
		    char errmsg[PJ_ERR_MSG_SIZE];

		    PJ_LOG(4,(THIS_FILE, "recvfrom() error ignored: %s",
			      pj_strerror(status, errmsg,sizeof(errmsg)).ptr));

		    /* Ignore non-PJ_SUCCESS status.
		     * It possible that other SIP entity is currently 
		     * sending SIP request to us, and because SIP message
		     * is larger than STUN, we could get EMSGSIZE when
		     * we call recvfrom().
		     */
		    status = PJ_SUCCESS;
		    continue;
		}

		status = pjstun_parse_msg(recv_buf, len, &msg);
		if (status != PJ_SUCCESS) {
		    char errmsg[PJ_ERR_MSG_SIZE];

		    PJ_LOG(4,(THIS_FILE, "STUN parsing error ignored: %s",
			      pj_strerror(status, errmsg,sizeof(errmsg)).ptr));

		    /* Also ignore non-successful parsing. This may not
		     * be STUN response at all. See the comment above.
		     */
		    status = PJ_SUCCESS;
		    continue;
		}

		sock_idx = pj_ntohl(msg.hdr->tsx[2]);
		srv_idx = pj_ntohl(msg.hdr->tsx[3]);

		if (sock_idx<0 || sock_idx>=sock_cnt || sock_idx!=i ||
			srv_idx<0 || srv_idx>=2)
		{
		    status = PJLIB_UTIL_ESTUNININDEX;
		    continue;
		}

		if (pj_ntohs(msg.hdr->type) != PJSTUN_BINDING_RESPONSE) {
		    status = PJLIB_UTIL_ESTUNNOBINDRES;
		    continue;
		}

		if (rec[sock_idx].srv[srv_idx].mapped_port != 0) {
		    /* Already got response */
		    continue;
		}

		/* From this part, we consider the packet as a valid STUN
		 * response for our request.
		 */
		--wait_resp;

		if (pjstun_msg_find_attr(&msg, PJSTUN_ATTR_ERROR_CODE) != NULL) {
		    status = PJLIB_UTIL_ESTUNRECVERRATTR;
		    continue;
		}

		attr = (pjstun_mapped_addr_attr*) 
		       pjstun_msg_find_attr(&msg, PJSTUN_ATTR_MAPPED_ADDR);
		if (!attr) {
		    attr = (pjstun_mapped_addr_attr*) 
			   pjstun_msg_find_attr(&msg, PJSTUN_ATTR_XOR_MAPPED_ADDR);
		    if (!attr || attr->family != 1) {
			status = PJLIB_UTIL_ESTUNNOMAP;
			continue;
		    }
		}

		rec[sock_idx].srv[srv_idx].mapped_addr = attr->addr;
		rec[sock_idx].srv[srv_idx].mapped_port = attr->port;
		if (pj_ntohs(attr->hdr.type) == PJSTUN_ATTR_XOR_MAPPED_ADDR) {
		    rec[sock_idx].srv[srv_idx].mapped_addr ^= pj_htonl(STUN_MAGIC);
		    rec[sock_idx].srv[srv_idx].mapped_port ^= pj_htons(STUN_MAGIC >> 16);
		}
	    }
	}

	/* The best scenario is if all requests have been replied.
	 * Then we don't need to go to the next retransmission iteration.
	 */
	if (wait_resp <= 0)
	    break;
    }
Пример #13
0
static void pcap2wav(const pj_str_t *codec,
		     const pj_str_t *wav_filename,
		     pjmedia_aud_dev_index dev_id,
		     const pj_str_t *srtp_crypto,
		     const pj_str_t *srtp_key)
{
    const pj_str_t WAV = {".wav", 4};
    struct pkt
    {
	pj_uint8_t	 buffer[320];
	pjmedia_rtp_hdr	*rtp;
	pj_uint8_t	*payload;
	unsigned	 payload_len;
    } pkt0;
    pjmedia_codec_mgr *cmgr;
    const pjmedia_codec_info *ci;
    pjmedia_codec_param param;
    unsigned samples_per_frame;
    pj_status_t status;

    /* Initialize all codecs */
    T( pjmedia_codec_register_audio_codecs(app.mept, NULL) );

    /* Create SRTP transport is needed */
#if PJMEDIA_HAS_SRTP
    if (srtp_crypto->slen) {
	pjmedia_srtp_crypto crypto;

	pj_bzero(&crypto, sizeof(crypto));
	crypto.key = *srtp_key;
	crypto.name = *srtp_crypto;
	T( pjmedia_transport_srtp_create(app.mept, NULL, NULL, &app.srtp) );
	T( pjmedia_transport_srtp_start(app.srtp, &crypto, &crypto) );
    }
#else
    PJ_UNUSED_ARG(srtp_crypto);
    PJ_UNUSED_ARG(srtp_key);
#endif

    /* Read first packet */
    read_rtp(pkt0.buffer, sizeof(pkt0.buffer), &pkt0.rtp, 
	     &pkt0.payload, &pkt0.payload_len, PJ_FALSE);

    cmgr = pjmedia_endpt_get_codec_mgr(app.mept);

    /* Get codec info and param for the specified payload type */
    app.pt = pkt0.rtp->pt;
    if (app.pt >=0 && app.pt < 96) {
	T( pjmedia_codec_mgr_get_codec_info(cmgr, pkt0.rtp->pt, &ci) );
    } else {
	unsigned cnt = 2;
	const pjmedia_codec_info *info[2];
	T( pjmedia_codec_mgr_find_codecs_by_id(cmgr, codec, &cnt, 
					       info, NULL) );
	if (cnt != 1)
	    err_exit("Codec ID must be specified and unique!", 0);

	ci = info[0];
    }
    T( pjmedia_codec_mgr_get_default_param(cmgr, ci, &param) );

    /* Alloc and init codec */
    T( pjmedia_codec_mgr_alloc_codec(cmgr, ci, &app.codec) );
    T( pjmedia_codec_init(app.codec, app.pool) );
    T( pjmedia_codec_open(app.codec, &param) );

    /* Init audio device or WAV file */
    samples_per_frame = ci->clock_rate * param.info.frm_ptime / 1000;
    if (pj_strcmp2(wav_filename, "-") == 0) {
	pjmedia_aud_param aud_param;

	/* Open audio device */
	T( pjmedia_aud_dev_default_param(dev_id, &aud_param) );
	aud_param.dir = PJMEDIA_DIR_PLAYBACK;
	aud_param.channel_count = ci->channel_cnt;
	aud_param.clock_rate = ci->clock_rate;
	aud_param.samples_per_frame = samples_per_frame;
	T( pjmedia_aud_stream_create(&aud_param, NULL, &play_cb, 
				     NULL, &app.aud_strm) );
	T( pjmedia_aud_stream_start(app.aud_strm) );
    } else if (pj_stristr(wav_filename, &WAV)) {
	/* Open WAV file */
	T( pjmedia_wav_writer_port_create(app.pool, wav_filename->ptr,
					  ci->clock_rate, ci->channel_cnt,
					  samples_per_frame,
					  param.info.pcm_bits_per_sample, 0, 0,
					  &app.wav) );
    } else {
	err_exit("invalid output file", PJ_EINVAL);
    }

    /* Loop reading PCAP and writing WAV file */
    for (;;) {
	struct pkt pkt1;
	pj_timestamp ts;
	pjmedia_frame frames[16], pcm_frame;
	short pcm[320];
	unsigned i, frame_cnt;
	long samples_cnt, ts_gap;

	pj_assert(sizeof(pcm) >= samples_per_frame);

	/* Parse first packet */
	ts.u64 = 0;
	frame_cnt = PJ_ARRAY_SIZE(frames);
	T( pjmedia_codec_parse(app.codec, pkt0.payload, pkt0.payload_len, 
				&ts, &frame_cnt, frames) );

	/* Decode and write to WAV file */
	samples_cnt = 0;
	for (i=0; i<frame_cnt; ++i) {
	    pjmedia_frame pcm_frame;

	    pcm_frame.buf = pcm;
	    pcm_frame.size = samples_per_frame * 2;

	    T( pjmedia_codec_decode(app.codec, &frames[i], 
				    (unsigned)pcm_frame.size, &pcm_frame) );
	    if (app.wav) {
		T( pjmedia_port_put_frame(app.wav, &pcm_frame) );
	    }
	    if (app.aud_strm) {
		T( wait_play(&pcm_frame) );
	    }
	    samples_cnt += samples_per_frame;
	}

	/* Read next packet */
	read_rtp(pkt1.buffer, sizeof(pkt1.buffer), &pkt1.rtp,
		 &pkt1.payload, &pkt1.payload_len, PJ_TRUE);

	/* Fill in the gap (if any) between pkt0 and pkt1 */
	ts_gap = pj_ntohl(pkt1.rtp->ts) - pj_ntohl(pkt0.rtp->ts) -
		 samples_cnt;
	while (ts_gap >= (long)samples_per_frame) {

	    pcm_frame.buf = pcm;
	    pcm_frame.size = samples_per_frame * 2;

	    if (app.codec->op->recover) {
		T( pjmedia_codec_recover(app.codec, (unsigned)pcm_frame.size, 
					 &pcm_frame) );
	    } else {
		pj_bzero(pcm_frame.buf, pcm_frame.size);
	    }

	    if (app.wav) {
		T( pjmedia_port_put_frame(app.wav, &pcm_frame) );
	    }
	    if (app.aud_strm) {
		T( wait_play(&pcm_frame) );
	    }
	    ts_gap -= samples_per_frame;
	}
	
	/* Next */
	pkt0 = pkt1;
	pkt0.rtp = (pjmedia_rtp_hdr*)pkt0.buffer;
	pkt0.payload = pkt0.buffer + (pkt1.payload - pkt1.buffer);
    }
}
Пример #14
0
/* This is our RTP callback, that is called by the slave transport when it
 * receives RTP packet.
 */
static void transport_rtp_cb(void *user_data, void *pkt, pj_ssize_t size)
{
    struct tp_zrtp *zrtp = (struct tp_zrtp*)user_data;

    pj_uint8_t* buffer = (pj_uint8_t*)pkt;
    int32_t newLen = 0;
    pj_status_t rc = PJ_SUCCESS;

    pj_assert(zrtp && zrtp->stream_rtcp_cb && pkt);

    // check if this could be a real RTP/SRTP packet.
    if ((*buffer & 0xf0) != 0x10)
    {
        //  Could be real RTP, check if we are in secure mode
        if (zrtp->srtpReceive == NULL || size < 0)
        {
            zrtp->stream_rtp_cb(zrtp->stream_user_data, pkt, size);
        }
        else
        {
            rc = zsrtp_unprotect(zrtp->srtpReceive, pkt, size, &newLen);
            if (rc == 1)
            {
                zrtp->unprotect++;
                zrtp->stream_rtp_cb(zrtp->stream_user_data, pkt,
                                    newLen);
                zrtp->unprotect_err = 0;
            }
            else
            {
                if (zrtp->userCallback != NULL)
                {
                    if (rc == -1) {
                        zrtp->userCallback->zrtp_showMessage(zrtp->userCallback->userData,
                                                            zrtp_Warning, 
                                                            zrtp_WarningSRTPauthError);
                    }
                    else {
                        zrtp->userCallback->zrtp_showMessage(zrtp->userCallback->userData,
                                                            zrtp_Warning,
                                                            zrtp_WarningSRTPreplayError);
                    }
                }
                zrtp->unprotect_err = rc;
            }
        }
        if (!zrtp->started && zrtp->enableZrtp)
            pjmedia_transport_zrtp_startZrtp((pjmedia_transport *)zrtp);

        return;
    }

    // We assume all other packets are ZRTP packets here. Process
    // if ZRTP processing is enabled. Because valid RTP packets are
    // already handled we delete any packets here after processing.
    if (zrtp->enableZrtp && zrtp->zrtpCtx != NULL)
    {
        // Get CRC value into crc (see above how to compute the offset)
        pj_uint16_t temp = size - CRC_SIZE;
        pj_uint32_t crc = *(uint32_t*)(buffer + temp);
        crc = pj_ntohl(crc);

        if (!zrtp_CheckCksum(buffer, temp, crc))
        {
            if (zrtp->userCallback != NULL)
                zrtp->userCallback->zrtp_showMessage(zrtp->userCallback->userData, zrtp_Warning, zrtp_WarningCRCmismatch);
            return;
        }

        pj_uint32_t magic = *(pj_uint32_t*)(buffer + 4);
        magic = pj_ntohl(magic);

        // Check if it is really a ZRTP packet, return, no further processing
        if (magic != ZRTP_MAGIC || zrtp->zrtpCtx == NULL)
        {
            return;
        }
        // cover the case if the other party sends _only_ ZRTP packets at the
        // beginning of a session. Start ZRTP in this case as well.
        if (!zrtp->started)
        {
            pjmedia_transport_zrtp_startZrtp((pjmedia_transport *)zrtp);
        }
        // this now points beyond the undefined and length field.
        // We need them, thus adjust
        unsigned char* zrtpMsg = (buffer + 12);

        // store peer's SSRC in host order, used when creating the CryptoContext
        zrtp->peerSSRC = *(pj_uint32_t*)(buffer + 8);
        zrtp->peerSSRC = pj_ntohl(zrtp->peerSSRC);
        zrtp_processZrtpMessage(zrtp->zrtpCtx, zrtpMsg, zrtp->peerSSRC, size);
    }
}
Пример #15
0
PJ_DECL(pj_status_t) pjstun_get_mapped_addr( pj_pool_factory *pf,
					      int sock_cnt, pj_sock_t sock[],
					      const pj_str_t *srv1, int port1,
					      const pj_str_t *srv2, int port2,
					      pj_sockaddr_in mapped_addr[])
{
    pj_sockaddr_in srv_addr[2];
    int i, j, send_cnt = 0;
    pj_pool_t *pool;
    struct {
	struct {
	    pj_uint32_t	mapped_addr;
	    pj_uint32_t	mapped_port;
	} srv[2];
    } *rec;
    void       *out_msg;
    pj_size_t	out_msg_len;
    int wait_resp = 0;
    pj_status_t status;

    PJ_CHECK_STACK();

    /* Create pool. */
    pool = pj_pool_create(pf, "stun%p", 1024, 1024, NULL);
    if (!pool)
	return PJ_ENOMEM;


    /* Allocate client records */
    rec = pj_pool_calloc(pool, sock_cnt, sizeof(*rec));
    if (!rec) {
	status = PJ_ENOMEM;
	goto on_error;
    }


    /* Create the outgoing BIND REQUEST message template */
    status = pjstun_create_bind_req( pool, &out_msg, &out_msg_len, 
				      pj_rand(), pj_rand());
    if (status != PJ_SUCCESS)
	goto on_error;

    /* Resolve servers. */
    status = pj_sockaddr_in_init(&srv_addr[0], srv1, (pj_uint16_t)port1);
    if (status != PJ_SUCCESS)
	goto on_error;

    status = pj_sockaddr_in_init(&srv_addr[1], srv2, (pj_uint16_t)port2);
    if (status != PJ_SUCCESS)
	goto on_error;

    /* Init mapped addresses to zero */
    pj_memset(mapped_addr, 0, sock_cnt * sizeof(pj_sockaddr_in));

    /* Main retransmission loop. */
    for (send_cnt=0; send_cnt<MAX_REQUEST; ++send_cnt) {
	pj_time_val next_tx, now;
	pj_fd_set_t r;
	int select_rc;

	PJ_FD_ZERO(&r);

	/* Send messages to servers that has not given us response. */
	for (i=0; i<sock_cnt && status==PJ_SUCCESS; ++i) {
	    for (j=0; j<2 && status==PJ_SUCCESS; ++j) {
		pjstun_msg_hdr *msg_hdr = out_msg;
                pj_ssize_t sent_len;

		if (rec[i].srv[j].mapped_port != 0)
		    continue;

		/* Modify message so that we can distinguish response. */
		msg_hdr->tsx[2] = pj_htonl(i);
		msg_hdr->tsx[3] = pj_htonl(j);

		/* Send! */
                sent_len = out_msg_len;
		status = pj_sock_sendto(sock[i], out_msg, &sent_len, 0,
					(pj_sockaddr_t*)&srv_addr[j], 
					sizeof(pj_sockaddr_in));
		if (status == PJ_SUCCESS)
		    ++wait_resp;
	    }
	}

	/* All requests sent.
	 * The loop below will wait for responses until all responses have
	 * been received (i.e. wait_resp==0) or timeout occurs, which then
	 * we'll go to the next retransmission iteration.
	 */

	/* Calculate time of next retransmission. */
	pj_gettimeofday(&next_tx);
	next_tx.sec += (stun_timer[send_cnt]/1000);
	next_tx.msec += (stun_timer[send_cnt]%1000);
	pj_time_val_normalize(&next_tx);

	for (pj_gettimeofday(&now), select_rc=1; 
	     status==PJ_SUCCESS && select_rc==1 && wait_resp>0 
	       && PJ_TIME_VAL_LT(now, next_tx); 
	     pj_gettimeofday(&now)) 
	{
	    pj_time_val timeout;

	    timeout = next_tx;
	    PJ_TIME_VAL_SUB(timeout, now);

	    for (i=0; i<sock_cnt; ++i) {
		PJ_FD_SET(sock[i], &r);
	    }

	    select_rc = pj_sock_select(FD_SETSIZE, &r, NULL, NULL, &timeout);
	    if (select_rc < 1)
		continue;

	    for (i=0; i<sock_cnt; ++i) {
		int sock_idx, srv_idx;
                pj_ssize_t len;
		pjstun_msg msg;
		pj_sockaddr_in addr;
		int addrlen = sizeof(addr);
		pjstun_mapped_addr_attr *attr;
		char recv_buf[128];

		if (!PJ_FD_ISSET(sock[i], &r))
		    continue;

                len = sizeof(recv_buf);
		status = pj_sock_recvfrom( sock[i], recv_buf, 
				           &len, 0,
				           (pj_sockaddr_t*)&addr,
					   &addrlen);

		--wait_resp;

		if (status != PJ_SUCCESS)
		    continue;

		status = pjstun_parse_msg(recv_buf, len, &msg);
		if (status != PJ_SUCCESS) {
		    continue;
		}


		sock_idx = pj_ntohl(msg.hdr->tsx[2]);
		srv_idx = pj_ntohl(msg.hdr->tsx[3]);

		if (sock_idx<0 || sock_idx>=sock_cnt || srv_idx<0 || srv_idx>=2) {
		    status = PJLIB_UTIL_ESTUNININDEX;
		    continue;
		}

		if (pj_ntohs(msg.hdr->type) != PJSTUN_BINDING_RESPONSE) {
		    status = PJLIB_UTIL_ESTUNNOBINDRES;
		    continue;
		}

		if (pjstun_msg_find_attr(&msg, PJSTUN_ATTR_ERROR_CODE) != NULL) {
		    status = PJLIB_UTIL_ESTUNRECVERRATTR;
		    continue;
		}

		attr = (void*)pjstun_msg_find_attr(&msg, PJSTUN_ATTR_MAPPED_ADDR);
		if (!attr) {
		    status = PJLIB_UTIL_ESTUNNOMAP;
		    continue;
		}

		rec[sock_idx].srv[srv_idx].mapped_addr = attr->addr;
		rec[sock_idx].srv[srv_idx].mapped_port = attr->port;
	    }
	}

	/* The best scenario is if all requests have been replied.
	 * Then we don't need to go to the next retransmission iteration.
	 */
	if (wait_resp <= 0)
	    break;
    }

    for (i=0; i<sock_cnt && status==PJ_SUCCESS; ++i) {
	if (rec[i].srv[0].mapped_addr == rec[i].srv[1].mapped_addr &&
	    rec[i].srv[0].mapped_port == rec[i].srv[1].mapped_port)
	{
	    mapped_addr[i].sin_family = PJ_AF_INET;
	    mapped_addr[i].sin_addr.s_addr = rec[i].srv[0].mapped_addr;
	    mapped_addr[i].sin_port = (pj_uint16_t)rec[i].srv[0].mapped_port;

	    if (rec[i].srv[0].mapped_addr == 0 || rec[i].srv[0].mapped_port == 0) {
		status = PJLIB_UTIL_ESTUNNOTRESPOND;
		break;
	    }
	} else {
	    status = PJLIB_UTIL_ESTUNSYMMETRIC;
	    break;
	}
    }

    pj_pool_release(pool);

    return status;

on_error:
    if (pool) pj_pool_release(pool);
    return status;
}