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; } }
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; }
/* 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; }
/* * 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)); }
/* * 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; } }
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; }
/* * 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; }
/* * 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; } }
/* 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; }
/* * 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; }
/* 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 */ }
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; }
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, ¶m) ); /* 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, ¶m) ); /* 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); } }
/* 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); } }
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; }