/* * Set the IP address of an IP socket address from string address, * with resolving the host if necessary. The string address may be in a * standard numbers and dots notation or may be a hostname. If hostname * is specified, then the function will resolve the host into the IP * address. */ PJ_DEF(pj_status_t) pj_sockaddr_in_set_str_addr( pj_sockaddr_in *addr, const pj_str_t *str_addr) { PJ_CHECK_STACK(); pj_assert(str_addr && str_addr->slen < PJ_MAX_HOSTNAME); addr->sin_family = AF_INET; if (str_addr && str_addr->slen) { addr->sin_addr = pj_inet_addr(str_addr); if (addr->sin_addr.s_addr == PJ_INADDR_NONE) { pj_hostent he; if (pj_gethostbyname(str_addr, &he) == 0) { addr->sin_addr.s_addr = *(pj_uint32_t*)he.h_addr; } else { addr->sin_addr.s_addr = PJ_INADDR_NONE; return -1; } } } else { addr->sin_addr.s_addr = 0; } return PJ_SUCCESS; }
/* * Set the IP address of an IP socket address from string address, * with resolving the host if necessary. The string address may be in a * standard numbers and dots notation or may be a hostname. If hostname * is specified, then the function will resolve the host into the IP * address. */ PJ_DEF(pj_status_t) pj_sockaddr_in_set_str_addr( pj_sockaddr_in *addr, const pj_str_t *str_addr) { PJ_CHECK_STACK(); PJ_ASSERT_RETURN(!str_addr || str_addr->slen < PJ_MAX_HOSTNAME, (addr->sin_addr.s_addr=PJ_INADDR_NONE, PJ_EINVAL)); PJ_SOCKADDR_RESET_LEN(addr); addr->sin_family = PJ_AF_INET; pj_bzero(addr->sin_zero, sizeof(addr->sin_zero)); if (str_addr && str_addr->slen) { addr->sin_addr = pj_inet_addr(str_addr); if (addr->sin_addr.s_addr == PJ_INADDR_NONE) { pj_hostent he; pj_status_t rc; rc = pj_gethostbyname(str_addr, &he); if (rc == 0) { addr->sin_addr.s_addr = *(pj_uint32_t*)he.h_addr; } else { addr->sin_addr.s_addr = PJ_INADDR_NONE; return rc; } } } else { addr->sin_addr.s_addr = 0; } return PJ_SUCCESS; }
static void open_udp_socket($UPROTO$_server_t *userver, char *addr, int port) { pj_sockaddr_in saddr; pj_str_t s; int optval = 1; SHOW_LOG(3, __FILE__":open_udp_socket: %s:%d\n", addr, port); // create udp socket here CHECK(__FILE__, pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &userver->fd)); #ifdef __ICS_INTEL__ // Allow socket reuse //CHECK(__FILE__, pj_sock_setsockopt(userver->fd, PJ_SOL_SOCKET, 512, &optval, sizeof(optval))); CHECK(__FILE__, pj_sock_setsockopt(userver->fd, PJ_SOL_SOCKET, PJ_SO_REUSEADDR, &optval, sizeof(optval))); #endif // bind the socket pj_bzero(&saddr, sizeof(saddr)); saddr.sin_family = PJ_AF_INET; saddr.sin_port = pj_htons(port); saddr.sin_addr = pj_inet_addr(pj_cstr(&s,addr)); /*pj_status_t status = pj_sock_bind(userver->fd, &saddr, sizeof(saddr)); if( status != 0 ) { SHOW_LOG(1, __FILE__":open_udp_socket error(%d)\n", status); exit(-1); }*/ CHECK(__FILE__, pj_sock_bind(userver->fd, &saddr, sizeof(saddr))); }
void $UPROTO$_server_join($UPROTO$_server_t *userver, char *multicast_ip) { pj_ip_mreq mreq; pj_str_t s, s1; pj_status_t ret; pj_bzero(&mreq, sizeof(pj_ip_mreq)); mreq.imr_multiaddr = pj_inet_addr(pj_cstr(&s, multicast_ip)); mreq.imr_interface = pj_inet_addr(pj_cstr(&s1, "0.0.0.0")); //mreq.imr_multiaddr.s_addr = inet_addr(multicast_ip); //mreq.imr_interface.s_addr = htonl(INADDR_ANY); pj_mutex_lock(userver->mutex); ret = pj_sock_setsockopt(userver->fd, PJ_SOL_IP, PJ_IP_ADD_MEMBERSHIP, &mreq,sizeof(mreq)); PERROR_IF_TRUE(ret != 0, "Error in joining mcast group"); pj_mutex_unlock(userver->mutex); }
void $UPROTO$_server_leave($UPROTO$_server_t *userver, char *multicast_ip) { pj_ip_mreq mreq; pj_str_t s; pj_status_t ret; mreq.imr_multiaddr = pj_inet_addr(pj_cstr(&s, multicast_ip)); mreq.imr_interface.s_addr = pj_htonl(PJ_INADDR_ANY); pj_mutex_lock(userver->mutex); ret = pj_sock_setsockopt(userver->fd, PJ_SOL_IP, PJ_IP_DROP_MEMBERSHIP, &mreq,sizeof(mreq)); PERROR_IF_TRUE(ret != 0, "Error in leaving mcast group"); pj_mutex_unlock(userver->mutex); }
static void open_udp_socket(response_client_t *uclient, char *server, int port) { pj_sockaddr_in *saddr; pj_str_t s; // open socket CHECK(__FILE__, pj_sock_socket(PJ_AF_INET, PJ_SOCK_STREAM, 0, &uclient->fd)); uclient->connect_data = malloc(sizeof(pj_sockaddr_in)); saddr = (pj_sockaddr_in *)uclient->connect_data; pj_bzero((void *)saddr, sizeof(pj_sockaddr_in)); saddr->sin_family = PJ_AF_INET; saddr->sin_port = pj_htons(port); saddr->sin_addr = pj_inet_addr(pj_cstr(&s, server)); CHECK(__FILE__,pj_sock_connect(uclient->fd, (const pj_sockaddr_t *)saddr, sizeof(pj_sockaddr_in))); }
static void add_ref(pjsip_server_addresses *r, pjsip_transport_type_e type, char *addr, int port) { pj_sockaddr_in *a; pj_str_t tmp; r->entry[r->count].type = type; r->entry[r->count].priority = 0; r->entry[r->count].weight = 0; r->entry[r->count].addr_len = sizeof(pj_sockaddr_in); a = (pj_sockaddr_in *)&r->entry[r->count].addr; a->sin_family = pj_AF_INET(); tmp = pj_str(addr); a->sin_addr = pj_inet_addr(&tmp); a->sin_port = pj_htons((pj_uint16_t)port); r->count++; }
/* * Convert address string with numbers and dots to binary IP address. */ PJ_DEF(pj_in_addr) pj_inet_addr2(const char *cp) { pj_str_t str = pj_str((char*)cp); return pj_inet_addr(&str); }
/* * Repeated connect/accept on the same listener socket. */ static int compliance_test_2(pj_bool_t allow_concur) { #if defined(PJ_SYMBIAN) && PJ_SYMBIAN!=0 enum { MAX_PAIR = 1, TEST_LOOP = 2 }; #else enum { MAX_PAIR = 4, TEST_LOOP = 2 }; #endif struct listener { pj_sock_t sock; pj_ioqueue_key_t *key; pj_sockaddr_in addr; int addr_len; } listener; struct server { pj_sock_t sock; pj_ioqueue_key_t *key; pj_sockaddr_in local_addr; pj_sockaddr_in rem_addr; int rem_addr_len; pj_ioqueue_op_key_t accept_op; } server[MAX_PAIR]; struct client { pj_sock_t sock; pj_ioqueue_key_t *key; } client[MAX_PAIR]; pj_pool_t *pool = NULL; char *send_buf, *recv_buf; pj_ioqueue_t *ioque = NULL; int i, bufsize = BUF_MIN_SIZE; pj_ssize_t status; int test_loop, pending_op = 0; pj_timestamp t_elapsed; pj_str_t s; pj_status_t rc; listener.sock = PJ_INVALID_SOCKET; listener.key = NULL; for (i=0; i<MAX_PAIR; ++i) { server[i].sock = PJ_INVALID_SOCKET; server[i].key = NULL; } for (i=0; i<MAX_PAIR; ++i) { client[i].sock = PJ_INVALID_SOCKET; client[i].key = NULL; } // Create pool. pool = pj_pool_create(mem, NULL, POOL_SIZE, 4000, NULL); // Create I/O Queue. rc = pj_ioqueue_create(pool, PJ_IOQUEUE_MAX_HANDLES, &ioque); if (rc != PJ_SUCCESS) { app_perror("...ERROR in pj_ioqueue_create()", rc); return -10; } // Concurrency rc = pj_ioqueue_set_default_concurrency(ioque, allow_concur); if (rc != PJ_SUCCESS) { app_perror("...ERROR in pj_ioqueue_set_default_concurrency()", rc); return -11; } // Allocate buffers for send and receive. send_buf = (char*)pj_pool_alloc(pool, bufsize); recv_buf = (char*)pj_pool_alloc(pool, bufsize); // Create listener socket rc = pj_sock_socket(pj_AF_INET(), pj_SOCK_STREAM(), 0, &listener.sock); if (rc != PJ_SUCCESS) { app_perror("...error creating socket", rc); status=-20; goto on_error; } // Bind listener socket. pj_sockaddr_in_init(&listener.addr, 0, 0); if ((rc=pj_sock_bind(listener.sock, &listener.addr, sizeof(listener.addr))) != 0 ) { app_perror("...bind error", rc); status=-30; goto on_error; } // Get listener address. listener.addr_len = sizeof(listener.addr); rc = pj_sock_getsockname(listener.sock, &listener.addr, &listener.addr_len); if (rc != PJ_SUCCESS) { app_perror("...ERROR in pj_sock_getsockname()", rc); status=-40; goto on_error; } listener.addr.sin_addr = pj_inet_addr(pj_cstr(&s, "127.0.0.1")); // Register listener socket. rc = pj_ioqueue_register_sock(pool, ioque, listener.sock, NULL, &test_cb, &listener.key); if (rc != PJ_SUCCESS) { app_perror("...ERROR", rc); status=-50; goto on_error; } // Listener socket listen(). if (pj_sock_listen(listener.sock, 5)) { app_perror("...ERROR in pj_sock_listen()", rc); status=-60; goto on_error; } for (test_loop=0; test_loop < TEST_LOOP; ++test_loop) { // Client connect and server accept. for (i=0; i<MAX_PAIR; ++i) { rc = pj_sock_socket(pj_AF_INET(), pj_SOCK_STREAM(), 0, &client[i].sock); if (rc != PJ_SUCCESS) { app_perror("...error creating socket", rc); status=-70; goto on_error; } rc = pj_ioqueue_register_sock(pool, ioque, client[i].sock, NULL, &test_cb, &client[i].key); if (rc != PJ_SUCCESS) { app_perror("...error ", rc); status=-80; goto on_error; } // Server socket accept() pj_ioqueue_op_key_init(&server[i].accept_op, sizeof(server[i].accept_op)); server[i].rem_addr_len = sizeof(pj_sockaddr_in); status = pj_ioqueue_accept(listener.key, &server[i].accept_op, &server[i].sock, &server[i].local_addr, &server[i].rem_addr, &server[i].rem_addr_len); if (status!=PJ_SUCCESS && status != PJ_EPENDING) { app_perror("...ERROR in pj_ioqueue_accept()", rc); status=-90; goto on_error; } if (status==PJ_EPENDING) { ++pending_op; } // Client socket connect() status = pj_ioqueue_connect(client[i].key, &listener.addr, sizeof(listener.addr)); if (status!=PJ_SUCCESS && status != PJ_EPENDING) { app_perror("...ERROR in pj_ioqueue_connect()", rc); status=-100; goto on_error; } if (status==PJ_EPENDING) { ++pending_op; } } // Poll until all connected while (pending_op) { pj_time_val timeout = {1, 0}; #ifdef PJ_SYMBIAN status = pj_symbianos_poll(-1, 1000); #else status = pj_ioqueue_poll(ioque, &timeout); #endif if (status > 0) { if (status > pending_op) { PJ_LOG(3,(THIS_FILE, "...error: pj_ioqueue_poll() returned %d " "(only expecting %d)", status, pending_op)); return -110; } pending_op -= status; if (pending_op == 0) { status = 0; } } } // There's no pending operation. // When we poll the ioqueue, there must not be events. if (pending_op == 0) { pj_time_val timeout = {1, 0}; #ifdef PJ_SYMBIAN status = pj_symbianos_poll(-1, 1000); #else status = pj_ioqueue_poll(ioque, &timeout); #endif if (status != 0) { status=-120; goto on_error; } } for (i=0; i<MAX_PAIR; ++i) { // Check server socket. if (server[i].sock == PJ_INVALID_SOCKET) { status = -130; app_perror("...accept() error", pj_get_os_error()); goto on_error; } // Check addresses if (server[i].local_addr.sin_family != pj_AF_INET() || server[i].local_addr.sin_addr.s_addr == 0 || server[i].local_addr.sin_port == 0) { app_perror("...ERROR address not set", rc); status = -140; goto on_error; } if (server[i].rem_addr.sin_family != pj_AF_INET() || server[i].rem_addr.sin_addr.s_addr == 0 || server[i].rem_addr.sin_port == 0) { app_perror("...ERROR address not set", rc); status = -150; goto on_error; } // Register newly accepted socket. rc = pj_ioqueue_register_sock(pool, ioque, server[i].sock, NULL, &test_cb, &server[i].key); if (rc != PJ_SUCCESS) { app_perror("...ERROR in pj_ioqueue_register_sock", rc); status = -160; goto on_error; } // Test send and receive. t_elapsed.u32.lo = 0; status = send_recv_test(ioque, server[i].key, client[i].key, send_buf, recv_buf, bufsize, &t_elapsed); if (status != 0) { goto on_error; } } // Success status = 0; for (i=0; i<MAX_PAIR; ++i) { if (server[i].key != NULL) { pj_ioqueue_unregister(server[i].key); server[i].key = NULL; server[i].sock = PJ_INVALID_SOCKET; } else if (server[i].sock != PJ_INVALID_SOCKET) { pj_sock_close(server[i].sock); server[i].sock = PJ_INVALID_SOCKET; } if (client[i].key != NULL) { pj_ioqueue_unregister(client[i].key); client[i].key = NULL; client[i].sock = PJ_INVALID_SOCKET; } else if (client[i].sock != PJ_INVALID_SOCKET) { pj_sock_close(client[i].sock); client[i].sock = PJ_INVALID_SOCKET; } } } status = 0; on_error: for (i=0; i<MAX_PAIR; ++i) { if (server[i].key != NULL) { pj_ioqueue_unregister(server[i].key); server[i].key = NULL; server[i].sock = PJ_INVALID_SOCKET; } else if (server[i].sock != PJ_INVALID_SOCKET) { pj_sock_close(server[i].sock); server[i].sock = PJ_INVALID_SOCKET; } if (client[i].key != NULL) { pj_ioqueue_unregister(client[i].key); client[i].key = NULL; server[i].sock = PJ_INVALID_SOCKET; } else if (client[i].sock != PJ_INVALID_SOCKET) { pj_sock_close(client[i].sock); client[i].sock = PJ_INVALID_SOCKET; } } if (listener.key) { pj_ioqueue_unregister(listener.key); listener.key = NULL; } else if (listener.sock != PJ_INVALID_SOCKET) { pj_sock_close(listener.sock); listener.sock = PJ_INVALID_SOCKET; } if (ioque != NULL) pj_ioqueue_destroy(ioque); pj_pool_release(pool); return status; }
/* * Compliance test for success scenario. */ static int compliance_test_0(pj_bool_t allow_concur) { pj_sock_t ssock=-1, csock0=-1, csock1=-1; pj_sockaddr_in addr, client_addr, rmt_addr; int client_addr_len; pj_pool_t *pool = NULL; char *send_buf, *recv_buf; pj_ioqueue_t *ioque = NULL; pj_ioqueue_key_t *skey=NULL, *ckey0=NULL, *ckey1=NULL; pj_ioqueue_op_key_t accept_op; int bufsize = BUF_MIN_SIZE; pj_ssize_t status = -1; int pending_op = 0; pj_timestamp t_elapsed; pj_str_t s; pj_status_t rc; // Create pool. pool = pj_pool_create(mem, NULL, POOL_SIZE, 4000, NULL); // Allocate buffers for send and receive. send_buf = (char*)pj_pool_alloc(pool, bufsize); recv_buf = (char*)pj_pool_alloc(pool, bufsize); // Create server socket and client socket for connecting rc = pj_sock_socket(pj_AF_INET(), pj_SOCK_STREAM(), 0, &ssock); if (rc != PJ_SUCCESS) { app_perror("...error creating socket", rc); status=-1; goto on_error; } rc = pj_sock_socket(pj_AF_INET(), pj_SOCK_STREAM(), 0, &csock1); if (rc != PJ_SUCCESS) { app_perror("...error creating socket", rc); status=-1; goto on_error; } // Bind server socket. pj_sockaddr_in_init(&addr, 0, 0); if ((rc=pj_sock_bind(ssock, &addr, sizeof(addr))) != 0 ) { app_perror("...bind error", rc); status=-10; goto on_error; } // Get server address. client_addr_len = sizeof(addr); rc = pj_sock_getsockname(ssock, &addr, &client_addr_len); if (rc != PJ_SUCCESS) { app_perror("...ERROR in pj_sock_getsockname()", rc); status=-15; goto on_error; } addr.sin_addr = pj_inet_addr(pj_cstr(&s, "127.0.0.1")); // Create I/O Queue. rc = pj_ioqueue_create(pool, PJ_IOQUEUE_MAX_HANDLES, &ioque); if (rc != PJ_SUCCESS) { app_perror("...ERROR in pj_ioqueue_create()", rc); status=-20; goto on_error; } // Concurrency rc = pj_ioqueue_set_default_concurrency(ioque, allow_concur); if (rc != PJ_SUCCESS) { app_perror("...ERROR in pj_ioqueue_set_default_concurrency()", rc); status=-21; goto on_error; } // Register server socket and client socket. rc = pj_ioqueue_register_sock(pool, ioque, ssock, NULL, &test_cb, &skey); if (rc == PJ_SUCCESS) rc = pj_ioqueue_register_sock(pool, ioque, csock1, NULL, &test_cb, &ckey1); else ckey1 = NULL; if (rc != PJ_SUCCESS) { app_perror("...ERROR in pj_ioqueue_register_sock()", rc); status=-23; goto on_error; } // Server socket listen(). if (pj_sock_listen(ssock, 5)) { app_perror("...ERROR in pj_sock_listen()", rc); status=-25; goto on_error; } // Server socket accept() client_addr_len = sizeof(pj_sockaddr_in); status = pj_ioqueue_accept(skey, &accept_op, &csock0, &client_addr, &rmt_addr, &client_addr_len); if (status != PJ_EPENDING) { app_perror("...ERROR in pj_ioqueue_accept()", rc); status=-30; goto on_error; } if (status==PJ_EPENDING) { ++pending_op; } // Client socket connect() status = pj_ioqueue_connect(ckey1, &addr, sizeof(addr)); if (status!=PJ_SUCCESS && status != PJ_EPENDING) { app_perror("...ERROR in pj_ioqueue_connect()", rc); status=-40; goto on_error; } if (status==PJ_EPENDING) { ++pending_op; } // Poll until connected callback_read_size = callback_write_size = 0; callback_accept_status = callback_connect_status = -2; callback_call_count = 0; callback_read_key = callback_write_key = callback_accept_key = callback_connect_key = NULL; callback_accept_op = callback_read_op = callback_write_op = NULL; while (pending_op) { pj_time_val timeout = {1, 0}; #ifdef PJ_SYMBIAN callback_call_count = 0; pj_symbianos_poll(-1, 1000); status = callback_call_count; #else status = pj_ioqueue_poll(ioque, &timeout); #endif if (status > 0) { if (callback_accept_status != -2) { if (callback_accept_status != 0) { status=-41; goto on_error; } if (callback_accept_key != skey) { status=-42; goto on_error; } if (callback_accept_op != &accept_op) { status=-43; goto on_error; } callback_accept_status = -2; } if (callback_connect_status != -2) { if (callback_connect_status != 0) { status=-50; goto on_error; } if (callback_connect_key != ckey1) { status=-51; goto on_error; } callback_connect_status = -2; } if (status > pending_op) { PJ_LOG(3,(THIS_FILE, "...error: pj_ioqueue_poll() returned %d " "(only expecting %d)", status, pending_op)); return -52; } pending_op -= status; if (pending_op == 0) { status = 0; } } } // There's no pending operation. // When we poll the ioqueue, there must not be events. if (pending_op == 0) { pj_time_val timeout = {1, 0}; #ifdef PJ_SYMBIAN status = pj_symbianos_poll(-1, 1000); #else status = pj_ioqueue_poll(ioque, &timeout); #endif if (status != 0) { status=-60; goto on_error; } } // Check accepted socket. if (csock0 == PJ_INVALID_SOCKET) { status = -69; app_perror("...accept() error", pj_get_os_error()); goto on_error; } // Register newly accepted socket. rc = pj_ioqueue_register_sock(pool, ioque, csock0, NULL, &test_cb, &ckey0); if (rc != PJ_SUCCESS) { app_perror("...ERROR in pj_ioqueue_register_sock", rc); status = -70; goto on_error; } // Test send and receive. t_elapsed.u32.lo = 0; status = send_recv_test(ioque, ckey0, ckey1, send_buf, recv_buf, bufsize, &t_elapsed); if (status != 0) { goto on_error; } // Success status = 0; on_error: if (skey != NULL) pj_ioqueue_unregister(skey); else if (ssock != PJ_INVALID_SOCKET) pj_sock_close(ssock); if (ckey1 != NULL) pj_ioqueue_unregister(ckey1); else if (csock1 != PJ_INVALID_SOCKET) pj_sock_close(csock1); if (ckey0 != NULL) pj_ioqueue_unregister(ckey0); else if (csock0 != PJ_INVALID_SOCKET) pj_sock_close(csock0); if (ioque != NULL) pj_ioqueue_destroy(ioque); pj_pool_release(pool); return status; }
pj_status_t app_socketpair(int family, int type, int protocol, pj_sock_t *serverfd, pj_sock_t *clientfd) { int i; static unsigned short port = 11000; pj_sockaddr_in addr; pj_str_t s; pj_status_t rc = 0; pj_sock_t sock[2]; /* Create both sockets. */ for (i=0; i<2; ++i) { rc = pj_sock_socket(family, type, protocol, &sock[i]); if (rc != PJ_SUCCESS) { if (i==1) pj_sock_close(sock[0]); return rc; } } /* Retry bind */ pj_bzero(&addr, sizeof(addr)); addr.sin_family = pj_AF_INET(); for (i=0; i<5; ++i) { addr.sin_port = pj_htons(port++); rc = pj_sock_bind(sock[SERVER], &addr, sizeof(addr)); if (rc == PJ_SUCCESS) break; } if (rc != PJ_SUCCESS) goto on_error; /* For TCP, listen the socket. */ #if PJ_HAS_TCP if (type == pj_SOCK_STREAM()) { rc = pj_sock_listen(sock[SERVER], PJ_SOMAXCONN); if (rc != PJ_SUCCESS) goto on_error; } #endif /* Connect client socket. */ addr.sin_addr = pj_inet_addr(pj_cstr(&s, "127.0.0.1")); rc = pj_sock_connect(sock[CLIENT], &addr, sizeof(addr)); if (rc != PJ_SUCCESS) goto on_error; /* For TCP, must accept(), and get the new socket. */ #if PJ_HAS_TCP if (type == pj_SOCK_STREAM()) { pj_sock_t newserver; rc = pj_sock_accept(sock[SERVER], &newserver, NULL, NULL); if (rc != PJ_SUCCESS) goto on_error; /* Replace server socket with new socket. */ pj_sock_close(sock[SERVER]); sock[SERVER] = newserver; } #endif *serverfd = sock[SERVER]; *clientfd = sock[CLIENT]; return rc; on_error: for (i=0; i<2; ++i) pj_sock_close(sock[i]); return rc; }
int main(int argc, char *argv[]) { pj_str_t input, output, srtp_crypto, srtp_key, codec; pjmedia_aud_dev_index dev_id = PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV; pj_pcap_filter filter; pj_status_t status; enum { OPT_SRC_IP = 1, OPT_DST_IP, OPT_SRC_PORT, OPT_DST_PORT, OPT_CODEC, OPT_PLAY_DEV_ID }; struct pj_getopt_option long_options[] = { { "srtp-crypto", 1, 0, 'c' }, { "srtp-key", 1, 0, 'k' }, { "src-ip", 1, 0, OPT_SRC_IP }, { "dst-ip", 1, 0, OPT_DST_IP }, { "src-port", 1, 0, OPT_SRC_PORT }, { "dst-port", 1, 0, OPT_DST_PORT }, { "codec", 1, 0, OPT_CODEC }, { "play-dev-id", 1, 0, OPT_PLAY_DEV_ID }, { NULL, 0, 0, 0} }; int c; int option_index; char key_bin[32]; srtp_crypto.slen = srtp_key.slen = 0; codec.slen = 0; pj_pcap_filter_default(&filter); filter.link = PJ_PCAP_LINK_TYPE_ETH; filter.proto = PJ_PCAP_PROTO_TYPE_UDP; /* Parse arguments */ pj_optind = 0; while((c=pj_getopt_long(argc,argv, "c:k:", long_options, &option_index))!=-1) { switch (c) { case 'c': srtp_crypto = pj_str(pj_optarg); break; case 'k': { int key_len = sizeof(key_bin); srtp_key = pj_str(pj_optarg); if (pj_base64_decode(&srtp_key, (pj_uint8_t*)key_bin, &key_len)) { puts("Error: invalid key"); return 1; } srtp_key.ptr = key_bin; srtp_key.slen = key_len; } break; case OPT_SRC_IP: { pj_str_t t = pj_str(pj_optarg); pj_in_addr a = pj_inet_addr(&t); filter.ip_src = a.s_addr; } break; case OPT_DST_IP: { pj_str_t t = pj_str(pj_optarg); pj_in_addr a = pj_inet_addr(&t); filter.ip_dst = a.s_addr; } break; case OPT_SRC_PORT: filter.src_port = pj_htons((pj_uint16_t)atoi(pj_optarg)); break; case OPT_DST_PORT: filter.dst_port = pj_htons((pj_uint16_t)atoi(pj_optarg)); break; case OPT_CODEC: codec = pj_str(pj_optarg); break; case OPT_PLAY_DEV_ID: dev_id = atoi(pj_optarg); break; default: puts("Error: invalid option"); return 1; } } if (pj_optind != argc - 2) { puts(USAGE); return 1; } if (!(srtp_crypto.slen) != !(srtp_key.slen)) { puts("Error: both SRTP crypto and key must be specified"); puts(USAGE); return 1; } input = pj_str(argv[pj_optind]); output = pj_str(argv[pj_optind+1]); T( pj_init() ); pj_caching_pool_init(&app.cp, NULL, 0); app.pool = pj_pool_create(&app.cp.factory, "pcaputil", 1000, 1000, NULL); T( pjlib_util_init() ); T( pjmedia_endpt_create(&app.cp.factory, NULL, 0, &app.mept) ); T( pj_pcap_open(app.pool, input.ptr, &app.pcap) ); T( pj_pcap_set_filter(app.pcap, &filter) ); pcap2wav(&codec, &output, dev_id, &srtp_crypto, &srtp_key); cleanup(); return 0; }
static int udp_test(void) { pj_sock_t cs = PJ_INVALID_SOCKET, ss = PJ_INVALID_SOCKET; pj_sockaddr_in dstaddr, srcaddr; pj_str_t s; pj_status_t rc = 0, retval; PJ_LOG(3,("test", "...udp_test()")); rc = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &ss); if (rc != 0) { app_perror("...error: unable to create socket", rc); return -100; } rc = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &cs); if (rc != 0) return -110; /* Bind server socket. */ pj_bzero(&dstaddr, sizeof(dstaddr)); dstaddr.sin_family = pj_AF_INET(); dstaddr.sin_port = pj_htons(UDP_PORT); dstaddr.sin_addr = pj_inet_addr(pj_cstr(&s, ADDRESS)); if ((rc=pj_sock_bind(ss, &dstaddr, sizeof(dstaddr))) != 0) { app_perror("...bind error udp:"ADDRESS, rc); rc = -120; goto on_error; } /* Bind client socket. */ pj_bzero(&srcaddr, sizeof(srcaddr)); srcaddr.sin_family = pj_AF_INET(); srcaddr.sin_port = pj_htons(UDP_PORT-1); srcaddr.sin_addr = pj_inet_addr(pj_cstr(&s, ADDRESS)); if ((rc=pj_sock_bind(cs, &srcaddr, sizeof(srcaddr))) != 0) { app_perror("...bind error", rc); rc = -121; goto on_error; } /* Test send/recv, with sendto */ rc = send_recv_test(pj_SOCK_DGRAM(), ss, cs, &dstaddr, NULL, sizeof(dstaddr)); if (rc != 0) goto on_error; /* Test send/recv, with sendto and recvfrom */ rc = send_recv_test(pj_SOCK_DGRAM(), ss, cs, &dstaddr, &srcaddr, sizeof(dstaddr)); if (rc != 0) goto on_error; /* Disable this test on Symbian since UDP connect()/send() failed * with S60 3rd edition (including MR2). * See http://www.pjsip.org/trac/ticket/264 */ #if !defined(PJ_SYMBIAN) || PJ_SYMBIAN==0 /* connect() the sockets. */ rc = pj_sock_connect(cs, &dstaddr, sizeof(dstaddr)); if (rc != 0) { app_perror("...connect() error", rc); rc = -122; goto on_error; } /* Test send/recv with send() */ rc = send_recv_test(pj_SOCK_DGRAM(), ss, cs, NULL, NULL, 0); if (rc != 0) goto on_error; /* Test send/recv with send() and recvfrom */ rc = send_recv_test(pj_SOCK_DGRAM(), ss, cs, NULL, &srcaddr, sizeof(srcaddr)); if (rc != 0) goto on_error; #endif on_error: retval = rc; if (cs != PJ_INVALID_SOCKET) { rc = pj_sock_close(cs); if (rc != PJ_SUCCESS) { app_perror("...error in closing socket", rc); return -1000; } } if (ss != PJ_INVALID_SOCKET) { rc = pj_sock_close(ss); if (rc != PJ_SUCCESS) { app_perror("...error in closing socket", rc); return -1010; } } return retval; }
static int udp_test(void) { pj_sock_t cs = PJ_INVALID_SOCKET, ss = PJ_INVALID_SOCKET; pj_sockaddr_in dstaddr, srcaddr; pj_str_t s; pj_status_t rc = 0, retval; PJ_LOG(3,("test", "...udp_test()")); rc = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &ss); if (rc != 0) { app_perror("...error: unable to create socket", rc); return -100; } rc = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &cs); if (rc != 0) return -110; /* Bind server socket. */ pj_bzero(&dstaddr, sizeof(dstaddr)); dstaddr.sin_family = PJ_AF_INET; dstaddr.sin_port = pj_htons(UDP_PORT); dstaddr.sin_addr = pj_inet_addr(pj_cstr(&s, ADDRESS)); if ((rc=pj_sock_bind(ss, &dstaddr, sizeof(dstaddr))) != 0) { app_perror("...bind error udp:"ADDRESS, rc); rc = -120; goto on_error; } /* Bind client socket. */ pj_bzero(&srcaddr, sizeof(srcaddr)); srcaddr.sin_family = PJ_AF_INET; srcaddr.sin_port = pj_htons(UDP_PORT-1); srcaddr.sin_addr = pj_inet_addr(pj_cstr(&s, ADDRESS)); if ((rc=pj_sock_bind(cs, &srcaddr, sizeof(srcaddr))) != 0) { app_perror("...bind error", rc); rc = -121; goto on_error; } /* Test send/recv, with sendto */ rc = send_recv_test(PJ_SOCK_DGRAM, ss, cs, &dstaddr, NULL, sizeof(dstaddr)); if (rc != 0) goto on_error; /* Test send/recv, with sendto and recvfrom */ rc = send_recv_test(PJ_SOCK_DGRAM, ss, cs, &dstaddr, &srcaddr, sizeof(dstaddr)); if (rc != 0) goto on_error; /* connect() the sockets. */ rc = pj_sock_connect(cs, &dstaddr, sizeof(dstaddr)); if (rc != 0) { app_perror("...connect() error", rc); rc = -122; goto on_error; } /* Test send/recv with send() */ rc = send_recv_test(PJ_SOCK_DGRAM, ss, cs, NULL, NULL, 0); if (rc != 0) goto on_error; /* Test send/recv with send() and recvfrom */ rc = send_recv_test(PJ_SOCK_DGRAM, ss, cs, NULL, &srcaddr, sizeof(srcaddr)); if (rc != 0) goto on_error; on_error: retval = rc; if (cs != PJ_INVALID_SOCKET) { rc = pj_sock_close(cs); if (rc != PJ_SUCCESS) { app_perror("...error in closing socket", rc); return -1000; } } if (ss != PJ_INVALID_SOCKET) { rc = pj_sock_close(ss); if (rc != PJ_SUCCESS) { app_perror("...error in closing socket", rc); return -1010; } } return retval; }
static void add_dns_entries(pj_dns_resolver *resv) { /* Inject DNS SRV entry */ pj_dns_parsed_packet pkt; pj_dns_parsed_query q; pj_dns_parsed_rr ans[4]; pj_dns_parsed_rr ar[5]; pj_str_t tmp; unsigned i; /* * This is answer to SRV query to "example.com" domain, and * the answer contains full reference to the A records of * the server. The full DNS records is : _sip._udp.example.com 3600 IN SRV 0 0 5060 sip01.example.com. _sip._udp.example.com 3600 IN SRV 0 20 5060 sip02.example.com. _sip._udp.example.com 3600 IN SRV 0 10 5060 sip03.example.com. _sip._udp.example.com 3600 IN SRV 1 0 5060 sip04.example.com. sip01.example.com. 3600 IN A 1.1.1.1 sip02.example.com. 3600 IN A 2.2.2.2 sip03.example.com. 3600 IN A 3.3.3.3 sip04.example.com. 3600 IN A 4.4.4.4 ; Additionally, add A record for "example.com" example.com. 3600 IN A 5.5.5.5 */ pj_bzero(&pkt, sizeof(pkt)); pj_bzero(ans, sizeof(ans)); pj_bzero(ar, sizeof(ar)); pkt.hdr.flags = PJ_DNS_SET_QR(1); pkt.hdr.anscount = PJ_ARRAY_SIZE(ans); pkt.hdr.arcount = 0; pkt.ans = ans; pkt.arr = ar; ans[0].name = pj_str("_sip._udp.example.com"); ans[0].type = PJ_DNS_TYPE_SRV; ans[0].dnsclass = PJ_DNS_CLASS_IN; ans[0].ttl = 3600; ans[0].rdata.srv.prio = 0; ans[0].rdata.srv.weight = 0; ans[0].rdata.srv.port = 5060; ans[0].rdata.srv.target = pj_str("sip01.example.com"); ans[1].name = pj_str("_sip._udp.example.com"); ans[1].type = PJ_DNS_TYPE_SRV; ans[1].dnsclass = PJ_DNS_CLASS_IN; ans[1].ttl = 3600; ans[1].rdata.srv.prio = 0; ans[1].rdata.srv.weight = 20; ans[1].rdata.srv.port = 5060; ans[1].rdata.srv.target = pj_str("sip02.example.com"); ans[2].name = pj_str("_sip._udp.example.com"); ans[2].type = PJ_DNS_TYPE_SRV; ans[2].dnsclass = PJ_DNS_CLASS_IN; ans[2].ttl = 3600; ans[2].rdata.srv.prio = 0; ans[2].rdata.srv.weight = 10; ans[2].rdata.srv.port = 5060; ans[2].rdata.srv.target = pj_str("sip03.example.com"); ans[3].name = pj_str("_sip._udp.example.com"); ans[3].type = PJ_DNS_TYPE_SRV; ans[3].dnsclass = PJ_DNS_CLASS_IN; ans[3].ttl = 3600; ans[3].rdata.srv.prio = 1; ans[3].rdata.srv.weight = 0; ans[3].rdata.srv.port = 5060; ans[3].rdata.srv.target = pj_str("sip04.example.com"); pj_dns_resolver_add_entry( resv, &pkt, PJ_FALSE); ar[0].name = pj_str("sip01.example.com"); ar[0].type = PJ_DNS_TYPE_A; ar[0].dnsclass = PJ_DNS_CLASS_IN; ar[0].ttl = 3600; ar[0].rdata.a.ip_addr = pj_inet_addr(pj_cstr(&tmp, "1.1.1.1")); ar[1].name = pj_str("sip02.example.com"); ar[1].type = PJ_DNS_TYPE_A; ar[1].dnsclass = PJ_DNS_CLASS_IN; ar[1].ttl = 3600; ar[1].rdata.a.ip_addr = pj_inet_addr(pj_cstr(&tmp, "2.2.2.2")); ar[2].name = pj_str("sip03.example.com"); ar[2].type = PJ_DNS_TYPE_A; ar[2].dnsclass = PJ_DNS_CLASS_IN; ar[2].ttl = 3600; ar[2].rdata.a.ip_addr = pj_inet_addr(pj_cstr(&tmp, "3.3.3.3")); ar[3].name = pj_str("sip04.example.com"); ar[3].type = PJ_DNS_TYPE_A; ar[3].dnsclass = PJ_DNS_CLASS_IN; ar[3].ttl = 3600; ar[3].rdata.a.ip_addr = pj_inet_addr(pj_cstr(&tmp, "4.4.4.4")); ar[4].name = pj_str("example.com"); ar[4].type = PJ_DNS_TYPE_A; ar[4].dnsclass = PJ_DNS_CLASS_IN; ar[4].ttl = 3600; ar[4].rdata.a.ip_addr = pj_inet_addr(pj_cstr(&tmp, "5.5.5.5")); /* * Create individual A records for all hosts in "example.com" domain. */ for (i=0; i<PJ_ARRAY_SIZE(ar); ++i) { pj_bzero(&pkt, sizeof(pkt)); pkt.hdr.flags = PJ_DNS_SET_QR(1); pkt.hdr.qdcount = 1; pkt.q = &q; q.name = ar[i].name; q.type = ar[i].type; q.dnsclass = PJ_DNS_CLASS_IN; pkt.hdr.anscount = 1; pkt.ans = &ar[i]; pj_dns_resolver_add_entry( resv, &pkt, PJ_FALSE); } /* * Simulate DNS error response by creating these answers. * Sample of invalid SRV records: _sip._udp.sip01.example.com. */ for (i=0; i<PJ_ARRAY_SIZE(ans); ++i) { pj_dns_parsed_query q; char buf[128]; char *services[] = { "_sip._udp.", "_sip._tcp.", "_sips._tcp."}; unsigned j; for (j=0; j<PJ_ARRAY_SIZE(services); ++j) { q.dnsclass = PJ_DNS_CLASS_IN; q.type = PJ_DNS_TYPE_SRV; q.name.ptr = buf; pj_bzero(buf, sizeof(buf)); pj_strcpy2(&q.name, services[j]); pj_strcat(&q.name, &ans[i].rdata.srv.target); pj_bzero(&pkt, sizeof(pkt)); pkt.hdr.qdcount = 1; pkt.hdr.flags = PJ_DNS_SET_QR(1) | PJ_DNS_SET_RCODE(PJ_DNS_RCODE_NXDOMAIN); pkt.q = &q; pj_dns_resolver_add_entry( resv, &pkt, PJ_FALSE); } } /* * ANOTHER DOMAIN. * * This time we let SRV and A get answered in different DNS * query. */ /* The "domain.com" DNS records (note the different the port): _sip._tcp.domain.com 3600 IN SRV 1 0 50060 sip06.domain.com. _sip._tcp.domain.com 3600 IN SRV 2 0 50060 sip07.domain.com. sip06.domain.com. 3600 IN A 6.6.6.6 sip07.domain.com. 3600 IN A 7.7.7.7 */ pj_bzero(&pkt, sizeof(pkt)); pj_bzero(&ans, sizeof(ans)); pkt.hdr.flags = PJ_DNS_SET_QR(1); pkt.hdr.anscount = 2; pkt.ans = ans; /* Add the SRV records, with reverse priority (to test that sorting * works. */ ans[0].name = pj_str("_sip._tcp.domain.com"); ans[0].type = PJ_DNS_TYPE_SRV; ans[0].dnsclass = PJ_DNS_CLASS_IN; ans[0].ttl = 3600; ans[0].rdata.srv.prio = 2; ans[0].rdata.srv.weight = 0; ans[0].rdata.srv.port = 50060; ans[0].rdata.srv.target = pj_str("SIP07.DOMAIN.COM"); ans[1].name = pj_str("_sip._tcp.domain.com"); ans[1].type = PJ_DNS_TYPE_SRV; ans[1].dnsclass = PJ_DNS_CLASS_IN; ans[1].ttl = 3600; ans[1].rdata.srv.prio = 1; ans[1].rdata.srv.weight = 0; ans[1].rdata.srv.port = 50060; ans[1].rdata.srv.target = pj_str("SIP06.DOMAIN.COM"); pj_dns_resolver_add_entry( resv, &pkt, PJ_FALSE); /* From herein there is only one answer */ pkt.hdr.anscount = 1; /* Add a single SRV for UDP */ ans[0].name = pj_str("_sip._udp.domain.com"); ans[0].type = PJ_DNS_TYPE_SRV; ans[0].dnsclass = PJ_DNS_CLASS_IN; ans[0].ttl = 3600; ans[0].rdata.srv.prio = 0; ans[0].rdata.srv.weight = 0; ans[0].rdata.srv.port = 50060; ans[0].rdata.srv.target = pj_str("SIP06.DOMAIN.COM"); pj_dns_resolver_add_entry( resv, &pkt, PJ_FALSE); /* Add the A record for sip06.domain.com */ ans[0].name = pj_str("sip06.domain.com"); ans[0].type = PJ_DNS_TYPE_A; ans[0].dnsclass = PJ_DNS_CLASS_IN; ans[0].ttl = 3600; ans[0].rdata.a.ip_addr = pj_inet_addr(pj_cstr(&tmp, "6.6.6.6")); pkt.hdr.qdcount = 1; pkt.q = &q; q.name = ans[0].name; q.type = ans[0].type; q.dnsclass = ans[0].dnsclass; pj_dns_resolver_add_entry( resv, &pkt, PJ_FALSE); /* Add the A record for sip07.domain.com */ ans[0].name = pj_str("sip07.domain.com"); ans[0].type = PJ_DNS_TYPE_A; ans[0].dnsclass = PJ_DNS_CLASS_IN; ans[0].ttl = 3600; ans[0].rdata.a.ip_addr = pj_inet_addr(pj_cstr(&tmp, "7.7.7.7")); pkt.hdr.qdcount = 1; pkt.q = &q; q.name = ans[0].name; q.type = ans[0].type; q.dnsclass = ans[0].dnsclass; pj_dns_resolver_add_entry( resv, &pkt, PJ_FALSE); pkt.hdr.qdcount = 0; }
/* * Perform round-robin/load balance test. */ static int round_robin_test(pj_pool_t *pool) { enum { COUNT = 400, PCT_ALLOWANCE = 5 }; unsigned i; struct server_hit { char *ip_addr; unsigned percent; unsigned hits; } server_hit[] = { { "1.1.1.1", 3, 0 }, { "2.2.2.2", 65, 0 }, { "3.3.3.3", 32, 0 }, { "4.4.4.4", 0, 0 } }; PJ_LOG(3,(THIS_FILE, " Performing round-robin/load-balance test..")); /* Do multiple resolve request to "example.com". * The resolver should select the server based on the weight proportion * the the servers in the SRV entry. */ for (i=0; i<COUNT; ++i) { pjsip_host_info dest; struct result result; unsigned j; dest.type = PJSIP_TRANSPORT_UDP; dest.flag = pjsip_transport_get_flag_from_type(PJSIP_TRANSPORT_UDP); dest.addr.host = pj_str("example.com"); dest.addr.port = 0; result.status = 0x12345678; pjsip_endpt_resolve(endpt, pool, &dest, &result, &cb); while (result.status == 0x12345678) { int i = 0; pj_time_val timeout = { 1, 0 }; pjsip_endpt_handle_events(endpt, &timeout); if (i == 1) pj_dns_resolver_dump(pjsip_endpt_get_resolver(endpt), PJ_TRUE); } /* Find which server was "hit" */ for (j=0; j<PJ_ARRAY_SIZE(server_hit); ++j) { pj_str_t tmp; pj_in_addr a1; pj_sockaddr_in *a2; tmp = pj_str(server_hit[j].ip_addr); a1 = pj_inet_addr(&tmp); a2 = (pj_sockaddr_in*) &result.servers.entry[0].addr; if (a1.s_addr == a2->sin_addr.s_addr) { server_hit[j].hits++; break; } } if (j == PJ_ARRAY_SIZE(server_hit)) { PJ_LOG(1,(THIS_FILE, "..round_robin_test() error 10: returned address mismatch")); return 10; } } /* Print the actual hit rate */ for (i=0; i<PJ_ARRAY_SIZE(server_hit); ++i) { PJ_LOG(3,(THIS_FILE, " ..Server %s: weight=%d%%, hit %d%% times", server_hit[i].ip_addr, server_hit[i].percent, (server_hit[i].hits * 100) / COUNT)); } /* Compare the actual hit with the weight proportion */ for (i=0; i<PJ_ARRAY_SIZE(server_hit); ++i) { int actual_pct = (server_hit[i].hits * 100) / COUNT; if (actual_pct + PCT_ALLOWANCE < (int)server_hit[i].percent || actual_pct - PCT_ALLOWANCE > (int)server_hit[i].percent) { PJ_LOG(1,(THIS_FILE, "..round_robin_test() error 20: " "hit rate difference for server %s (%d%%) is more than " "tolerable allowance (%d%%)", server_hit[i].ip_addr, actual_pct - server_hit[i].percent, PCT_ALLOWANCE)); return 20; } } PJ_LOG(3,(THIS_FILE, " Load balance test success, hit-rate is " "within %d%% allowance", PCT_ALLOWANCE)); return PJ_SUCCESS; }
/* * select_test() * * Test main entry. */ int select_test() { pj_sock_t udp1=PJ_INVALID_SOCKET, udp2=PJ_INVALID_SOCKET; pj_sockaddr_in udp_addr; int status; int setcount[3]; pj_str_t s; const char data[] = "hello"; const int datalen = 5; pj_ssize_t sent, received; char buf[10]; pj_status_t rc; PJ_LOG(3, (THIS_FILE, "...Testing simple UDP select()")); // Create two UDP sockets. rc = pj_sock_socket( pj_AF_INET(), pj_SOCK_DGRAM(), 0, &udp1); if (rc != PJ_SUCCESS) { app_perror("...error: unable to create socket", rc); status=-10; goto on_return; } rc = pj_sock_socket( pj_AF_INET(), pj_SOCK_DGRAM(), 0, &udp2); if (udp2 == PJ_INVALID_SOCKET) { app_perror("...error: unable to create socket", rc); status=-20; goto on_return; } // Bind one of the UDP socket. pj_bzero(&udp_addr, sizeof(udp_addr)); udp_addr.sin_family = pj_AF_INET(); udp_addr.sin_port = UDP_PORT; udp_addr.sin_addr = pj_inet_addr(pj_cstr(&s, "127.0.0.1")); if (pj_sock_bind(udp2, &udp_addr, sizeof(udp_addr))) { status=-30; goto on_return; } // Send data. sent = datalen; rc = pj_sock_sendto(udp1, data, &sent, 0, &udp_addr, sizeof(udp_addr)); if (rc != PJ_SUCCESS || sent != datalen) { app_perror("...error: sendto() error", rc); status=-40; goto on_return; } // Sleep a bit. See http://trac.pjsip.org/repos/ticket/890 pj_thread_sleep(10); // Check that socket is marked as reable. // Note that select() may also report that sockets are writable. status = do_select(udp1, udp2, setcount); if (status < 0) { char errbuf[128]; pj_strerror(pj_get_netos_error(), errbuf, sizeof(errbuf)); PJ_LOG(1,(THIS_FILE, "...error: %s", errbuf)); status=-50; goto on_return; } if (status == 0) { status=-60; goto on_return; } if (setcount[READ_FDS] != 1) { status=-70; goto on_return; } if (setcount[WRITE_FDS] != 0) { if (setcount[WRITE_FDS] == 2) { PJ_LOG(3,(THIS_FILE, "...info: system reports writable sockets")); } else { status=-80; goto on_return; } } else { PJ_LOG(3,(THIS_FILE, "...info: system doesn't report writable sockets")); } if (setcount[EXCEPT_FDS] != 0) { status=-90; goto on_return; } // Read the socket to clear readable sockets. received = sizeof(buf); rc = pj_sock_recv(udp2, buf, &received, 0); if (rc != PJ_SUCCESS || received != 5) { status=-100; goto on_return; } status = 0; // Test timeout on the read part. // This won't necessarily return zero, as select() may report that // sockets are writable. setcount[0] = setcount[1] = setcount[2] = 0; status = do_select(udp1, udp2, setcount); if (status != 0 && status != setcount[WRITE_FDS]) { PJ_LOG(3,(THIS_FILE, "...error: expecting timeout but got %d sks set", status)); PJ_LOG(3,(THIS_FILE, " rdset: %d, wrset: %d, exset: %d", setcount[0], setcount[1], setcount[2])); status = -110; goto on_return; } if (setcount[READ_FDS] != 0) { PJ_LOG(3,(THIS_FILE, "...error: readable socket not expected")); status = -120; goto on_return; } status = 0; on_return: if (udp1 != PJ_INVALID_SOCKET) pj_sock_close(udp1); if (udp2 != PJ_INVALID_SOCKET) pj_sock_close(udp2); return status; }
/* * Create stream info from SDP media line. */ PJ_DEF(pj_status_t) pjmedia_stream_info_from_sdp( pjmedia_stream_info *si, pj_pool_t *pool, pjmedia_endpt *endpt, const pjmedia_sdp_session *local, const pjmedia_sdp_session *remote, unsigned stream_idx) { pjmedia_codec_mgr *mgr; const pjmedia_sdp_attr *attr; const pjmedia_sdp_media *local_m; const pjmedia_sdp_media *rem_m; const pjmedia_sdp_conn *local_conn; const pjmedia_sdp_conn *rem_conn; pjmedia_sdp_rtpmap *rtpmap; int local_fmtp_mode = 0, rem_fmtp_mode = 0; unsigned i, pt, fmti; pj_status_t status; /* Validate arguments: */ PJ_ASSERT_RETURN(pool && si && local && remote, PJ_EINVAL); PJ_ASSERT_RETURN(stream_idx < local->media_count, PJ_EINVAL); PJ_ASSERT_RETURN(stream_idx < remote->media_count, PJ_EINVAL); /* Get codec manager. */ mgr = pjmedia_endpt_get_codec_mgr(endpt); /* Keep SDP shortcuts */ local_m = local->media[stream_idx]; rem_m = remote->media[stream_idx]; local_conn = local_m->conn ? local_m->conn : local->conn; if (local_conn == NULL) return PJMEDIA_SDP_EMISSINGCONN; rem_conn = rem_m->conn ? rem_m->conn : remote->conn; if (rem_conn == NULL) return PJMEDIA_SDP_EMISSINGCONN; /* Reset: */ pj_bzero(si, sizeof(*si)); /* Media type: */ if (pj_stricmp(&local_m->desc.media, &ID_AUDIO) == 0) { si->type = PJMEDIA_TYPE_AUDIO; } else if (pj_stricmp(&local_m->desc.media, &ID_VIDEO) == 0) { si->type = PJMEDIA_TYPE_VIDEO; } else { si->type = PJMEDIA_TYPE_UNKNOWN; return PJMEDIA_EINVALIMEDIATYPE; } /* Transport type must be equal */ if (pj_stricmp(&rem_m->desc.transport, &local_m->desc.transport) != 0) { si->type = PJMEDIA_TYPE_UNKNOWN; return PJMEDIA_SDPNEG_EINVANSTP; } /* Media direction: */ if (local_m->desc.port == 0 || pj_inet_addr(&local_conn->addr).s_addr==0 || pj_inet_addr(&rem_conn->addr).s_addr==0 || pjmedia_sdp_media_find_attr(local_m, &STR_INACTIVE, NULL)!=NULL) { /* Inactive stream. */ si->dir = PJMEDIA_DIR_NONE; } else if (pjmedia_sdp_media_find_attr(local_m, &STR_SENDONLY, NULL)!=NULL) { /* Send only stream. */ si->dir = PJMEDIA_DIR_ENCODING; } else if (pjmedia_sdp_media_find_attr(local_m, &STR_RECVONLY, NULL)!=NULL) { /* Recv only stream. */ si->dir = PJMEDIA_DIR_DECODING; } else { /* Send and receive stream. */ si->dir = PJMEDIA_DIR_ENCODING_DECODING; } /* Set remote address: */ status = pj_sockaddr_in_init(&si->rem_addr, &rem_conn->addr, rem_m->desc.port); if (status != PJ_SUCCESS) { /* Invalid IP address. */ return PJMEDIA_EINVALIDIP; } /* If "rtcp" attribute is present in the SDP, set the RTCP address * from that attribute. Otherwise, calculate from RTP address. */ attr = pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr, "rtcp", NULL); if (attr) { pjmedia_sdp_rtcp_attr rtcp; status = pjmedia_sdp_attr_get_rtcp(attr, &rtcp); if (status == PJ_SUCCESS) { if (rtcp.addr.slen) { status = pj_sockaddr_in_init(&si->rem_rtcp, &rtcp.addr, (pj_uint16_t)rtcp.port); } else { pj_sockaddr_in_init(&si->rem_rtcp, NULL, (pj_uint16_t)rtcp.port); si->rem_rtcp.sin_addr.s_addr = si->rem_addr.sin_addr.s_addr; } } } if (si->rem_rtcp.sin_addr.s_addr == 0) { int rtcp_port; pj_memcpy(&si->rem_rtcp, &si->rem_addr, sizeof(pj_sockaddr_in)); rtcp_port = pj_ntohs(si->rem_addr.sin_port) + 1; si->rem_rtcp.sin_port = pj_htons((pj_uint16_t)rtcp_port); } /* Get the payload number for receive channel. */ /* Previously we used to rely on fmt[0] being the selected codec, but some UA sends telephone-event as fmt[0] and this would cause assert failure below. Thanks Chris Hamilton <chamilton .at. cs.dal.ca> for this patch. // And codec must be numeric! if (!pj_isdigit(*local_m->desc.fmt[0].ptr) || !pj_isdigit(*rem_m->desc.fmt[0].ptr)) { return PJMEDIA_EINVALIDPT; } pt = pj_strtoul(&local_m->desc.fmt[0]); pj_assert(PJMEDIA_RTP_PT_TELEPHONE_EVENTS==0 || pt != PJMEDIA_RTP_PT_TELEPHONE_EVENTS); */ /* This is to suppress MSVC warning about uninitialized var */ pt = 0; /* Find the first codec which is not telephone-event */ for ( fmti = 0; fmti < local_m->desc.fmt_count; ++fmti ) { if ( !pj_isdigit(*local_m->desc.fmt[fmti].ptr) ) return PJMEDIA_EINVALIDPT; pt = pj_strtoul(&local_m->desc.fmt[fmti]); if ( PJMEDIA_RTP_PT_TELEPHONE_EVENTS == 0 || pt != PJMEDIA_RTP_PT_TELEPHONE_EVENTS ) break; } if ( fmti >= local_m->desc.fmt_count ) return PJMEDIA_EINVALIDPT; /* Get codec info. * For static payload types, get the info from codec manager. * For dynamic payload types, MUST get the rtpmap. */ if (pt < 96) { pj_bool_t has_rtpmap; rtpmap = NULL; has_rtpmap = PJ_TRUE; attr = pjmedia_sdp_media_find_attr(local_m, &ID_RTPMAP, &local_m->desc.fmt[fmti]); if (attr == NULL) { has_rtpmap = PJ_FALSE; } if (attr != NULL) { status = pjmedia_sdp_attr_to_rtpmap(pool, attr, &rtpmap); if (status != PJ_SUCCESS) has_rtpmap = PJ_FALSE; } /* Build codec format info: */ if (has_rtpmap) { si->fmt.type = si->type; si->fmt.pt = pj_strtoul(&local_m->desc.fmt[fmti]); pj_strdup(pool, &si->fmt.encoding_name, &rtpmap->enc_name); si->fmt.clock_rate = rtpmap->clock_rate; /* For audio codecs, rtpmap parameters denotes the number of * channels. */ if (si->type == PJMEDIA_TYPE_AUDIO && rtpmap->param.slen) { if (rtpmap->param.slen == 2) { si->fmt.channel_cnt = rtpmap->param.ptr[1] - '0'; } else { pj_str_t cnt; cnt.ptr = rtpmap->param.ptr + 1; cnt.slen = rtpmap->param.slen - 1; si->fmt.channel_cnt = (unsigned) pj_strtoul(&cnt); } } else { si->fmt.channel_cnt = 1; } } else { const pjmedia_codec_info *p_info; status = pjmedia_codec_mgr_get_codec_info( mgr, pt, &p_info); if (status != PJ_SUCCESS) return status; pj_memcpy(&si->fmt, p_info, sizeof(pjmedia_codec_info)); } /* For static payload type, pt's are symetric */ si->tx_pt = pt; } else { attr = pjmedia_sdp_media_find_attr(local_m, &ID_RTPMAP, &local_m->desc.fmt[fmti]); if (attr == NULL) return PJMEDIA_EMISSINGRTPMAP; status = pjmedia_sdp_attr_to_rtpmap(pool, attr, &rtpmap); if (status != PJ_SUCCESS) return status; /* Build codec format info: */ si->fmt.type = si->type; si->fmt.pt = pj_strtoul(&local_m->desc.fmt[fmti]); pj_strdup(pool, &si->fmt.encoding_name, &rtpmap->enc_name); si->fmt.clock_rate = rtpmap->clock_rate; /* For audio codecs, rtpmap parameters denotes the number of * channels. */ if (si->type == PJMEDIA_TYPE_AUDIO && rtpmap->param.slen) { if (rtpmap->param.slen == 2) { si->fmt.channel_cnt = rtpmap->param.ptr[1] - '0'; } else { pj_str_t cnt; cnt.ptr = rtpmap->param.ptr + 1; cnt.slen = rtpmap->param.slen - 1; si->fmt.channel_cnt = (unsigned) pj_strtoul(&cnt); } } else { si->fmt.channel_cnt = 1; } /* Get fmtp mode= param in local SDP, if any */ get_fmtp_mode(local_m, &local_m->desc.fmt[fmti], &local_fmtp_mode); /* Determine payload type for outgoing channel, by finding * dynamic payload type in remote SDP that matches the answer. */ si->tx_pt = 0xFFFF; for (i=0; i<rem_m->desc.fmt_count; ++i) { unsigned rpt; pjmedia_sdp_attr *r_attr; pjmedia_sdp_rtpmap r_rtpmap; rpt = pj_strtoul(&rem_m->desc.fmt[i]); if (rpt < 96) continue; r_attr = pjmedia_sdp_media_find_attr(rem_m, &ID_RTPMAP, &rem_m->desc.fmt[i]); if (!r_attr) continue; if (pjmedia_sdp_attr_get_rtpmap(r_attr, &r_rtpmap) != PJ_SUCCESS) continue; if (!pj_stricmp(&rtpmap->enc_name, &r_rtpmap.enc_name) && rtpmap->clock_rate == r_rtpmap.clock_rate) { /* Found matched codec. */ si->tx_pt = rpt; /* Get fmtp mode param in remote SDP, if any */ get_fmtp_mode(rem_m, &rtpmap->pt, &rem_fmtp_mode); break; } } if (si->tx_pt == 0xFFFF) return PJMEDIA_EMISSINGRTPMAP; } /* Now that we have codec info, get the codec param. */ si->param = pj_pool_alloc(pool, sizeof(*si->param)); status = pjmedia_codec_mgr_get_default_param(mgr, &si->fmt, si->param); if (status != PJ_SUCCESS) return status; /* Set fmtp mode for both local and remote */ if (local_fmtp_mode != 0) si->param->setting.dec_fmtp_mode = (pj_int8_t)local_fmtp_mode; if (rem_fmtp_mode != 0) si->param->setting.enc_fmtp_mode = (pj_int8_t)rem_fmtp_mode; /* Get incomming payload type for telephone-events */ si->rx_event_pt = -1; for (i=0; i<local_m->attr_count; ++i) { pjmedia_sdp_rtpmap r; attr = local_m->attr[i]; if (pj_strcmp(&attr->name, &ID_RTPMAP) != 0) continue; if (pjmedia_sdp_attr_get_rtpmap(attr, &r) != PJ_SUCCESS) continue; if (pj_strcmp(&r.enc_name, &ID_TELEPHONE_EVENT) == 0) { si->rx_event_pt = pj_strtoul(&r.pt); break; } } /* Get outgoing payload type for telephone-events */ si->tx_event_pt = -1; for (i=0; i<rem_m->attr_count; ++i) { pjmedia_sdp_rtpmap r; attr = rem_m->attr[i]; if (pj_strcmp(&attr->name, &ID_RTPMAP) != 0) continue; if (pjmedia_sdp_attr_get_rtpmap(attr, &r) != PJ_SUCCESS) continue; if (pj_strcmp(&r.enc_name, &ID_TELEPHONE_EVENT) == 0) { si->tx_event_pt = pj_strtoul(&r.pt); break; } } /* Leave SSRC to random. */ si->ssrc = pj_rand(); /* Set default jitter buffer parameter. */ si->jb_init = si->jb_max = si->jb_min_pre = si->jb_max_pre = -1; return PJ_SUCCESS; }