├── README.md ├── .gitignore ├── Makefile ├── sendmsg_v1.c ├── sendmsgTCP.c ├── sendmsg.c ├── sendmsg_to_peer.c ├── sendmsg_to_server.c ├── LICENSE ├── stun_sock.c └── icedemo.c /README.md: -------------------------------------------------------------------------------- 1 | simplep2p 2 | ========= 3 | 4 | Test NAT Traversal after using pjnath to see if we can send data to peer 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | *.ko 4 | 5 | # Libraries 6 | *.lib 7 | *.a 8 | 9 | # Shared objects (inc. Windows DLLs) 10 | *.dll 11 | *.so 12 | *.so.* 13 | *.dylib 14 | 15 | # Executables 16 | *.exe 17 | *.out 18 | *.app 19 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | #f your application is in a file named myapp.cpp or myapp.c 2 | # this is the line you will need to build the binary. 3 | all: sendmsg_to_server sendmsg_to_peer 4 | 5 | sendmsg_to_server: sendmsg_to_server.c 6 | $(CC) -o $@ $< -g -DDEBUG -lpthread 7 | sendmsg_to_peer: sendmsg_to_peer.c 8 | $(CC) -o $@ $< -g -DDEBUG -lpthread 9 | clean: 10 | rm -f sendmsg_to_peer sendmsg_to_peer.o sendmsg_to_server sendmsg_to_server.o 11 | -------------------------------------------------------------------------------- /sendmsg_v1.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | 11 | #define PJ_INET6_ADDRSTRLEN 46 12 | static int UDPCreate(); 13 | static void UDPCreateThread(); 14 | static void* UDPRecvThread(void* arg); 15 | static void* UDPSendThread(void* arg); 16 | 17 | 18 | static struct app_t 19 | { 20 | int rport; 21 | char ripaddr[PJ_INET6_ADDRSTRLEN]; 22 | int lport; 23 | char lipaddr[PJ_INET6_ADDRSTRLEN]; 24 | int localfd; 25 | } icedemo; 26 | 27 | 28 | static int UDPCreate(){ 29 | int sfd; 30 | struct sockaddr_in addr_server,addr_local; 31 | int len=sizeof(struct sockaddr); 32 | int sent,on=1; 33 | 34 | 35 | //icedemo_stop_session(); 36 | sfd=socket(AF_INET,SOCK_DGRAM,0); 37 | if(sfd==-1){ 38 | perror("SOCKET"); 39 | return ; 40 | } 41 | setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR | SO_BROADCAST,&on,sizeof(on)); 42 | memset(&addr_server,0,len); 43 | addr_server.sin_family=AF_INET; 44 | addr_server.sin_addr.s_addr=inet_addr(icedemo.ripaddr); 45 | addr_server.sin_port=htons(icedemo.rport); 46 | 47 | memset(&addr_local,0,len); 48 | addr_local.sin_family=AF_INET; 49 | addr_local.sin_addr.s_addr=inet_addr(icedemo.lipaddr); 50 | addr_local.sin_port=htons(icedemo.lport); 51 | 52 | if(bind(sfd,(struct sockaddr*)&addr_local,len)==-1){ 53 | perror("BIND"); 54 | return ; 55 | } 56 | if(connect(sfd,(struct sockaddr*)&addr_server,len)==-1){ 57 | perror("CONNECT"); 58 | return ; 59 | } 60 | printf("OUR OWN UDP SOCKET CREATED:%d\n",sfd); 61 | return sfd; 62 | } 63 | static void UDPCreateThread(){ 64 | int rt; 65 | pthread_t idsend,idrecv; 66 | rt=pthread_create(&idsend,NULL,UDPSendThread,NULL); 67 | if(rt!=0){ 68 | perror("PTHREAD_CREATE UDPSendThread"); 69 | } 70 | rt=pthread_create(&idrecv,NULL,UDPRecvThread,NULL); 71 | if(rt!=0){ 72 | perror("PTHREAD_CREATE UDPRecvThread"); 73 | } 74 | 75 | pthread_join(idsend,NULL); 76 | pthread_join(idrecv,NULL); 77 | } 78 | 79 | static void* UDPRecvThread(void* arg){ 80 | printf("UDPRecvThread CREATED\n"); 81 | char buffer[1024]; 82 | int n; 83 | for(;;){ 84 | n=read(icedemo.localfd,buffer,sizeof(buffer)); 85 | printf("read %d bytes,data:%s\n",n,buffer); 86 | 87 | } 88 | } 89 | 90 | static void* UDPSendThread(void* arg){ 91 | int n; 92 | char buffer[1024]; 93 | printf("UDPSendThread CREATED\n"); 94 | printf("sleep 10\n"); 95 | sleep(10); 96 | for(;;){ 97 | gets(&buffer); 98 | n=strlen(buffer); 99 | n=write(icedemo.localfd,buffer,n); 100 | printf("%d byte to send,%d bytes sent\n",strlen(buffer),n); 101 | } 102 | 103 | 104 | } 105 | 106 | /** 107 | *arg1=local addr 108 | *arg2=local port 109 | *arg3=remote addr 110 | *argv4=remote port 111 | **/ 112 | int main(int argc,char* argv[]){ 113 | sprintf(icedemo.lipaddr,"%s",argv[1]); 114 | icedemo.lport=atoi(argv[2]); 115 | sprintf(icedemo.ripaddr,"%s",argv[3]); 116 | icedemo.rport=atoi(argv[4]); 117 | 118 | icedemo.localfd=UDPCreate(); 119 | /* a thread to read and a thread to write*/ 120 | UDPCreateThread(); 121 | return 0; 122 | } 123 | -------------------------------------------------------------------------------- /sendmsgTCP.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | 11 | #define PJ_INET6_ADDRSTRLEN 46 12 | static void TCPClient(); 13 | static void TCPServer(); 14 | 15 | static struct app_t 16 | { 17 | int rport; 18 | char ripaddr[PJ_INET6_ADDRSTRLEN]; 19 | int lport; 20 | char lipaddr[PJ_INET6_ADDRSTRLEN]; 21 | int localfd; 22 | } icedemo; 23 | 24 | 25 | static void TCPClient(){ 26 | int sfd; 27 | struct sockaddr_in addr_server,addr_local; 28 | int len=sizeof(struct sockaddr); 29 | char buffer[1024]; 30 | int n,sent,on=1; 31 | 32 | 33 | sfd=socket(AF_INET,SOCK_STREAM,0); 34 | if(sfd==-1){ 35 | perror("SOCKET"); 36 | return ; 37 | } 38 | setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR ,&on,sizeof(on)); 39 | memset(&addr_server,0,len); 40 | addr_server.sin_family=AF_INET; 41 | addr_server.sin_addr.s_addr=inet_addr(icedemo.ripaddr); 42 | addr_server.sin_port=htons(icedemo.rport); 43 | 44 | memset(&addr_local,0,len); 45 | addr_local.sin_family=AF_INET; 46 | addr_local.sin_addr.s_addr=inet_addr(icedemo.lipaddr); 47 | addr_local.sin_port=htons(icedemo.lport); 48 | //use this addr to connect to server 49 | if(bind(sfd,(struct sockaddr*)&addr_local,len)==-1){ 50 | perror("BIND"); 51 | return ; 52 | } 53 | if(connect(sfd,(struct sockaddr*)&addr_server,len)==-1){ 54 | perror("CONNECT"); 55 | return ; 56 | } 57 | printf("OUR OWN UDP SOCKET CREATED:%d\n",sfd); 58 | 59 | for(;;){ 60 | gets(&buffer); 61 | n=strlen(buffer); 62 | n=write(sfd,buffer,n); 63 | printf("%d byte to send,%d bytes sent\n",strlen(buffer),n); 64 | } 65 | 66 | 67 | } 68 | 69 | 70 | static void TCPServer(){ 71 | int sfd,connfd; 72 | struct sockaddr_in addr_local,addr_peer; // local addr to bind listen 73 | int len=sizeof(struct sockaddr); 74 | char peeraddr[1024]; 75 | char data[1024]; 76 | int n,sent,on=1; 77 | 78 | 79 | sfd=socket(AF_INET,SOCK_STREAM,0); 80 | if(sfd==-1){ 81 | perror("SOCKET"); 82 | return ; 83 | } 84 | setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR ,&on,sizeof(on)); 85 | 86 | memset(&addr_local,0,len); 87 | addr_local.sin_family=AF_INET; 88 | addr_local.sin_addr.s_addr=inet_addr(icedemo.lipaddr); 89 | addr_local.sin_port=htons(icedemo.lport); 90 | 91 | if(bind(sfd,(struct sockaddr*)&addr_local,len)==-1){ 92 | perror("BIND"); 93 | return ; 94 | } 95 | if(listen(sfd,10)==-1){ 96 | perror("listen"); 97 | return ; 98 | } 99 | for(;;){ 100 | printf("Listening for connect:\n"); 101 | len=sizeof(struct sockaddr_in); 102 | connfd=accept(sfd,(struct sockaddr*)&addr_peer,&len); 103 | if(connfd==-1){ 104 | perror("ACCEPT"); 105 | continue; 106 | } 107 | printf("remote peer:%s\n",inet_ntoa(addr_peer.sin_addr)); 108 | while(1){ 109 | n=read(connfd,data,sizeof(data)); 110 | data[n]='\0'; 111 | printf("read %d bytes,content:%s\n",n,data); 112 | } 113 | 114 | } 115 | } 116 | 117 | 118 | 119 | int main(int argc,char* argv[]){ 120 | int servering=0; 121 | if( argc<6){ 122 | printf("USERAGE: local_addr local_port remote_addr remote_port servering(0|1)\n"); 123 | return 1; 124 | } 125 | sprintf(icedemo.lipaddr,"%s",argv[1]); 126 | icedemo.lport=atoi(argv[2]); 127 | sprintf(icedemo.ripaddr,"%s",argv[3]); 128 | icedemo.rport=atoi(argv[4]); 129 | servering=atoi(argv[5]); 130 | if (servering){ 131 | TCPServer(); 132 | } 133 | else{ 134 | TCPClient(); 135 | } 136 | return 0; 137 | } 138 | -------------------------------------------------------------------------------- /sendmsg.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | 11 | #define PJ_INET6_ADDRSTRLEN 46 12 | static int UDPCreate(); 13 | static void UDPCreateThread(); 14 | static void* UDPRecvThread(void* arg); 15 | static void* UDPSendThread(void* arg); 16 | 17 | 18 | static struct app_t 19 | { 20 | int rport; 21 | char ripaddr[PJ_INET6_ADDRSTRLEN]; 22 | int lport; 23 | char lipaddr[PJ_INET6_ADDRSTRLEN]; 24 | int localfd; 25 | 26 | struct sockaddr_in addr_server,addr_local; 27 | int len; 28 | } icedemo; 29 | 30 | 31 | static int UDPCreate(){ 32 | int sfd; 33 | int sent,on=1; 34 | 35 | icedemo.len=sizeof(struct sockaddr); 36 | //icedemo_stop_session(); 37 | sfd=socket(AF_INET,SOCK_DGRAM,0); 38 | if(sfd==-1){ 39 | perror("SOCKET"); 40 | return ; 41 | } 42 | setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR | SO_BROADCAST,&on,sizeof(on)); 43 | memset(&icedemo.addr_server,0,icedemo.len); 44 | icedemo.addr_server.sin_family=AF_INET; 45 | icedemo.addr_server.sin_addr.s_addr=inet_addr(icedemo.ripaddr); 46 | icedemo.addr_server.sin_port=htons(icedemo.rport); 47 | 48 | memset(&icedemo.addr_local,0,icedemo.len); 49 | icedemo.addr_local.sin_family=AF_INET; 50 | icedemo.addr_local.sin_addr.s_addr=inet_addr(icedemo.lipaddr); 51 | icedemo.addr_local.sin_port=htons(icedemo.lport); 52 | 53 | if(bind(sfd,(struct sockaddr*)&icedemo.addr_local,icedemo.len)==-1){ 54 | perror("BIND"); 55 | return ; 56 | } 57 | /* 58 | if(connect(sfd,(struct sockaddr*)&addr_server,len)==-1){ 59 | perror("CONNECT"); 60 | return ; 61 | } 62 | */ 63 | printf("OUR OWN UDP SOCKET CREATED:%d\n",sfd); 64 | return sfd; 65 | } 66 | static void UDPCreateThread(){ 67 | int rt; 68 | pthread_t idsend,idrecv; 69 | rt=pthread_create(&idsend,NULL,UDPSendThread,NULL); 70 | if(rt!=0){ 71 | perror("PTHREAD_CREATE UDPSendThread"); 72 | } 73 | rt=pthread_create(&idrecv,NULL,UDPRecvThread,NULL); 74 | if(rt!=0){ 75 | perror("PTHREAD_CREATE UDPRecvThread"); 76 | } 77 | 78 | pthread_join(idsend,NULL); 79 | pthread_join(idrecv,NULL); 80 | } 81 | 82 | static void* UDPRecvThread(void* arg){ 83 | printf("UDPRecvThread CREATED\n"); 84 | char buffer[1024]; 85 | int n; 86 | struct sockaddr_in addr; 87 | int addr_len=sizeof(struct sockaddr_in); 88 | for(;;){ 89 | //n=read(icedemo.localfd,buffer,sizeof(buffer)); 90 | n=recvfrom(icedemo.localfd,buffer,sizeof(buffer), 0 , (struct sockaddr *)&icedemo.addr_server ,&addr_len); 91 | printf("receive from %s:%d\n" , inet_ntoa( icedemo.addr_server.sin_addr),ntohs(icedemo.addr_server.sin_port)); 92 | buffer[n]='\0'; 93 | printf("read %d bytes,data:%s\n",n,buffer); 94 | 95 | } 96 | } 97 | 98 | static void* UDPSendThread(void* arg){ 99 | int n; 100 | char buffer[1024]; 101 | printf("UDPSendThread CREATED\n"); 102 | printf("sleep 10\n"); 103 | sleep(10); 104 | for(;;){ 105 | gets(&buffer); 106 | n=strlen(buffer); 107 | //n=write(icedemo.localfd,buffer,n); 108 | n=sendto(icedemo.localfd,buffer,n,0,(struct sockaddr*)&icedemo.addr_server,icedemo.len); 109 | printf("%d byte to send,%d bytes sent\n",strlen(buffer),n); 110 | } 111 | 112 | 113 | } 114 | 115 | 116 | int main(int argc,char* argv[]){ 117 | sprintf(icedemo.lipaddr,"%s",argv[1]); 118 | icedemo.lport=atoi(argv[2]); 119 | sprintf(icedemo.ripaddr,"%s",argv[3]); 120 | icedemo.rport=atoi(argv[4]); 121 | 122 | icedemo.localfd=UDPCreate(); 123 | /* a thread to read and a thread to write*/ 124 | UDPCreateThread(); 125 | return 0; 126 | } 127 | -------------------------------------------------------------------------------- /sendmsg_to_peer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | 11 | #define PJ_INET6_ADDRSTRLEN 46 12 | static int UDPCreate(); 13 | static void UDPCreateThread(); 14 | static void* UDPRecvThread(void* arg); 15 | static void* UDPSendThread(void* arg); 16 | 17 | 18 | static struct app_t 19 | { 20 | int rport; 21 | char ripaddr[PJ_INET6_ADDRSTRLEN]; 22 | int lport; 23 | char lipaddr[PJ_INET6_ADDRSTRLEN]; 24 | int localfd; 25 | 26 | struct sockaddr_in addr_server,addr_local; 27 | int len; 28 | } icedemo; 29 | 30 | 31 | static int UDPCreate(){ 32 | int sfd; 33 | int sent,on=1; 34 | 35 | icedemo.len=sizeof(struct sockaddr); 36 | //icedemo_stop_session(); 37 | sfd=socket(AF_INET,SOCK_DGRAM,0); 38 | if(sfd==-1){ 39 | perror("SOCKET"); 40 | return ; 41 | } 42 | setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR | SO_BROADCAST,&on,sizeof(on)); 43 | memset(&icedemo.addr_server,0,icedemo.len); 44 | icedemo.addr_server.sin_family=AF_INET; 45 | icedemo.addr_server.sin_addr.s_addr=inet_addr(icedemo.ripaddr); 46 | icedemo.addr_server.sin_port=htons(icedemo.rport); 47 | 48 | memset(&icedemo.addr_local,0,icedemo.len); 49 | icedemo.addr_local.sin_family=AF_INET; 50 | icedemo.addr_local.sin_addr.s_addr=inet_addr(icedemo.lipaddr); 51 | icedemo.addr_local.sin_port=htons(icedemo.lport); 52 | 53 | if(bind(sfd,(struct sockaddr*)&icedemo.addr_local,icedemo.len)==-1){ 54 | perror("BIND"); 55 | return ; 56 | } 57 | /* 58 | if(connect(sfd,(struct sockaddr*)&addr_server,len)==-1){ 59 | perror("CONNECT"); 60 | return ; 61 | } 62 | */ 63 | printf("OUR OWN UDP SOCKET CREATED:%d\n",sfd); 64 | return sfd; 65 | } 66 | static void UDPCreateThread(){ 67 | int rt; 68 | pthread_t idsend,idrecv; 69 | rt=pthread_create(&idsend,NULL,UDPSendThread,NULL); 70 | if(rt!=0){ 71 | perror("PTHREAD_CREATE UDPSendThread"); 72 | } 73 | rt=pthread_create(&idrecv,NULL,UDPRecvThread,NULL); 74 | if(rt!=0){ 75 | perror("PTHREAD_CREATE UDPRecvThread"); 76 | } 77 | 78 | pthread_join(idsend,NULL); 79 | pthread_join(idrecv,NULL); 80 | } 81 | 82 | static void* UDPRecvThread(void* arg){ 83 | printf("UDPRecvThread CREATED\n"); 84 | char buffer[1024]; 85 | int n; 86 | struct sockaddr_in addr; 87 | int addr_len=sizeof(struct sockaddr_in); 88 | for(;;){ 89 | //n=read(icedemo.localfd,buffer,sizeof(buffer)); 90 | n=recvfrom(icedemo.localfd,buffer,sizeof(buffer), 0 , (struct sockaddr *)&icedemo.addr_server ,&addr_len); 91 | printf("receive from %s:%d\n" , inet_ntoa( icedemo.addr_server.sin_addr),ntohs(icedemo.addr_server.sin_port)); 92 | buffer[n]='\0'; 93 | printf("read %d bytes,data:%s\n",n,buffer); 94 | 95 | } 96 | } 97 | 98 | static void* UDPSendThread(void* arg){ 99 | int n; 100 | char buffer[1024]="client-----2"; 101 | printf("UDPSendThread CREATED\n"); 102 | printf("sleep 10\n"); 103 | for(;;){ 104 | //gets(&buffer); 105 | sleep(3); 106 | n=strlen(buffer); 107 | //n=write(icedemo.localfd,buffer,n); 108 | n=sendto(icedemo.localfd,buffer,n,0,(struct sockaddr*)&icedemo.addr_server,icedemo.len); 109 | printf("%d byte to send,%d bytes sent\n",strlen(buffer),n); 110 | } 111 | 112 | 113 | } 114 | 115 | 116 | int main(int argc,char* argv[]){ 117 | sprintf(icedemo.lipaddr,"%s",argv[1]); 118 | icedemo.lport=atoi(argv[2]); 119 | sprintf(icedemo.ripaddr,"%s",argv[3]); 120 | icedemo.rport=atoi(argv[4]); 121 | 122 | icedemo.localfd=UDPCreate(); 123 | /* a thread to read and a thread to write*/ 124 | UDPCreateThread(); 125 | return 0; 126 | } 127 | -------------------------------------------------------------------------------- /sendmsg_to_server.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | 11 | #define PJ_INET6_ADDRSTRLEN 46 12 | static int UDPCreate(); 13 | static void UDPCreateThread(); 14 | static void* UDPRecvThread(void* arg); 15 | static void* UDPSendThread(void* arg); 16 | 17 | 18 | static struct app_t 19 | { 20 | int rport; 21 | char ripaddr[PJ_INET6_ADDRSTRLEN]; 22 | int lport; 23 | char lipaddr[PJ_INET6_ADDRSTRLEN]; 24 | int localfd; 25 | 26 | int server; 27 | 28 | struct sockaddr_in addr_server,addr_local; 29 | int len; 30 | } icedemo; 31 | 32 | 33 | static int UDPCreate(){ 34 | int sfd; 35 | int sent,on=1; 36 | 37 | icedemo.len=sizeof(struct sockaddr); 38 | //icedemo_stop_session(); 39 | sfd=socket(AF_INET,SOCK_DGRAM,0); 40 | if(sfd==-1){ 41 | perror("SOCKET"); 42 | return ; 43 | } 44 | setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR | SO_BROADCAST,&on,sizeof(on)); 45 | memset(&icedemo.addr_server,0,icedemo.len); 46 | icedemo.addr_server.sin_family=AF_INET; 47 | icedemo.addr_server.sin_addr.s_addr=inet_addr(icedemo.ripaddr); 48 | icedemo.addr_server.sin_port=htons(icedemo.rport); 49 | 50 | memset(&icedemo.addr_local,0,icedemo.len); 51 | icedemo.addr_local.sin_family=AF_INET; 52 | icedemo.addr_local.sin_addr.s_addr=inet_addr(icedemo.lipaddr); 53 | icedemo.addr_local.sin_port=htons(icedemo.lport); 54 | 55 | if(bind(sfd,(struct sockaddr*)&icedemo.addr_local,icedemo.len)==-1){ 56 | perror("BIND"); 57 | return ; 58 | } 59 | /* 60 | if(connect(sfd,(struct sockaddr*)&addr_server,len)==-1){ 61 | perror("CONNECT"); 62 | return ; 63 | } 64 | */ 65 | printf("OUR OWN UDP SOCKET CREATED:%d\n",sfd); 66 | return sfd; 67 | } 68 | static void UDPCreateThread(){ 69 | int rt; 70 | pthread_t idsend,idrecv; 71 | if(!icedemo.server){ 72 | rt=pthread_create(&idsend,NULL,UDPSendThread,NULL); 73 | if(rt!=0){ 74 | perror("PTHREAD_CREATE UDPSendThread"); 75 | } 76 | } 77 | else{ 78 | rt=pthread_create(&idrecv,NULL,UDPRecvThread,NULL); 79 | if(rt!=0){ 80 | perror("PTHREAD_CREATE UDPRecvThread"); 81 | } 82 | } 83 | if(!icedemo.server){ 84 | pthread_join(idsend,NULL); 85 | } 86 | else{ 87 | pthread_join(idrecv,NULL); 88 | } 89 | } 90 | 91 | static void* UDPRecvThread(void* arg){ 92 | printf("UDPRecvThread CREATED\n"); 93 | char buffer[1024]; 94 | int n; 95 | struct sockaddr_in addr_server; 96 | int addr_len=sizeof(struct sockaddr_in); 97 | for(;;){ 98 | //n=read(icedemo.localfd,buffer,sizeof(buffer)); 99 | n=recvfrom(icedemo.localfd,buffer,sizeof(buffer), 0 , (struct sockaddr *)&addr_server ,&addr_len); 100 | printf("receive from %s:%d\n" , inet_ntoa(addr_server.sin_addr),ntohs(addr_server.sin_port)); 101 | buffer[n]='\0'; 102 | printf("read %d bytes,data:%s\n",n,buffer); 103 | 104 | sleep(4); 105 | 106 | 107 | 108 | } 109 | } 110 | 111 | static void* UDPSendThread(void* arg){ 112 | int n; 113 | char buffer[1024]="hello"; 114 | printf("UDPSendThread CREATED\n"); 115 | for(;;){ 116 | sleep(3); 117 | //gets(&buffer); 118 | n=strlen(buffer); 119 | //n=write(icedemo.localfd,buffer,n); 120 | n=sendto(icedemo.localfd,buffer,n,0,(struct sockaddr*)&icedemo.addr_server,icedemo.len); 121 | printf("%d byte to send,%d bytes sent\n",strlen(buffer),n); 122 | } 123 | 124 | 125 | } 126 | 127 | 128 | int main(int argc,char* argv[]){ 129 | sprintf(icedemo.lipaddr,"%s",argv[1]); 130 | icedemo.lport=atoi(argv[2]); 131 | sprintf(icedemo.ripaddr,"%s",argv[3]); 132 | icedemo.rport=atoi(argv[4]); 133 | 134 | icedemo.server=atoi(argv[5]); 135 | 136 | icedemo.localfd=UDPCreate(); 137 | /* a thread to read and a thread to write*/ 138 | UDPCreateThread(); 139 | return 0; 140 | } 141 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /stun_sock.c: -------------------------------------------------------------------------------- 1 | /* $Id: stun_sock.c 4360 2013-02-21 11:26:35Z bennylp $ */ 2 | /* 3 | * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) 4 | * Copyright (C) 2003-2008 Benny Prijono 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 | */ 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | #if 1 41 | # define TRACE_(x) PJ_LOG(5,x) 42 | #else 43 | # define TRACE_(x) 44 | #endif 45 | 46 | enum { MAX_BIND_RETRY = 100 }; 47 | 48 | struct pj_stun_sock 49 | { 50 | char *obj_name; /* Log identification */ 51 | pj_pool_t *pool; /* Pool */ 52 | void *user_data; /* Application user data */ 53 | pj_bool_t is_destroying; /* Destroy already called */ 54 | int af; /* Address family */ 55 | pj_stun_config stun_cfg; /* STUN config (ioqueue etc)*/ 56 | pj_stun_sock_cb cb; /* Application callbacks */ 57 | 58 | int ka_interval; /* Keep alive interval */ 59 | pj_timer_entry ka_timer; /* Keep alive timer. */ 60 | 61 | pj_sockaddr srv_addr; /* Resolved server addr */ 62 | pj_sockaddr mapped_addr; /* Our public address */ 63 | 64 | pj_dns_srv_async_query *q; /* Pending DNS query */ 65 | pj_sock_t sock_fd; /* Socket descriptor */ 66 | pj_activesock_t *active_sock; /* Active socket object */ 67 | pj_ioqueue_op_key_t send_key; /* Default send key for app */ 68 | pj_ioqueue_op_key_t int_send_key; /* Send key for internal */ 69 | 70 | pj_uint16_t tsx_id[6]; /* .. to match STUN msg */ 71 | pj_stun_session *stun_sess; /* STUN session */ 72 | pj_grp_lock_t *grp_lock; /* Session group lock */ 73 | }; 74 | 75 | /* 76 | * Prototypes for static functions 77 | */ 78 | 79 | /* Destructor for group lock */ 80 | static void stun_sock_destructor(void *obj); 81 | 82 | /* This callback is called by the STUN session to send packet */ 83 | static pj_status_t sess_on_send_msg(pj_stun_session *sess, 84 | void *token, 85 | const void *pkt, 86 | pj_size_t pkt_size, 87 | const pj_sockaddr_t *dst_addr, 88 | unsigned addr_len); 89 | 90 | /* This callback is called by the STUN session when outgoing transaction 91 | * is complete 92 | */ 93 | static void sess_on_request_complete(pj_stun_session *sess, 94 | pj_status_t status, 95 | void *token, 96 | pj_stun_tx_data *tdata, 97 | const pj_stun_msg *response, 98 | const pj_sockaddr_t *src_addr, 99 | unsigned src_addr_len); 100 | /* DNS resolver callback */ 101 | static void dns_srv_resolver_cb(void *user_data, 102 | pj_status_t status, 103 | const pj_dns_srv_record *rec); 104 | 105 | /* Start sending STUN Binding request */ 106 | static pj_status_t get_mapped_addr(pj_stun_sock *stun_sock); 107 | 108 | /* Callback from active socket when incoming packet is received */ 109 | static pj_bool_t on_data_recvfrom(pj_activesock_t *asock, 110 | void *data, 111 | pj_size_t size, 112 | const pj_sockaddr_t *src_addr, 113 | int addr_len, 114 | pj_status_t status); 115 | 116 | /* Callback from active socket about send status */ 117 | static pj_bool_t on_data_sent(pj_activesock_t *asock, 118 | pj_ioqueue_op_key_t *send_key, 119 | pj_ssize_t sent); 120 | 121 | /* Schedule keep-alive timer */ 122 | static void start_ka_timer(pj_stun_sock *stun_sock); 123 | 124 | /* Keep-alive timer callback */ 125 | static void ka_timer_cb(pj_timer_heap_t *th, pj_timer_entry *te); 126 | 127 | #define INTERNAL_MSG_TOKEN (void*)1 128 | 129 | 130 | /* 131 | * Retrieve the name representing the specified operation. 132 | */ 133 | PJ_DEF(const char*) pj_stun_sock_op_name(pj_stun_sock_op op) 134 | { 135 | const char *names[] = { 136 | "?", 137 | "DNS resolution", 138 | "STUN Binding request", 139 | "Keep-alive", 140 | "Mapped addr. changed" 141 | }; 142 | 143 | return op < PJ_ARRAY_SIZE(names) ? names[op] : "???"; 144 | }; 145 | 146 | 147 | /* 148 | * Initialize the STUN transport setting with its default values. 149 | */ 150 | PJ_DEF(void) pj_stun_sock_cfg_default(pj_stun_sock_cfg *cfg) 151 | { 152 | pj_bzero(cfg, sizeof(*cfg)); 153 | cfg->max_pkt_size = PJ_STUN_SOCK_PKT_LEN; 154 | cfg->async_cnt = 1; 155 | cfg->ka_interval = PJ_STUN_KEEP_ALIVE_SEC; 156 | cfg->qos_type = PJ_QOS_TYPE_BEST_EFFORT; 157 | cfg->qos_ignore_error = PJ_TRUE; 158 | } 159 | 160 | 161 | /* Check that configuration setting is valid */ 162 | static pj_bool_t pj_stun_sock_cfg_is_valid(const pj_stun_sock_cfg *cfg) 163 | { 164 | return cfg->max_pkt_size > 1 && cfg->async_cnt >= 1; 165 | } 166 | 167 | /* 168 | * Create the STUN transport using the specified configuration. 169 | */ 170 | PJ_DEF(pj_status_t) pj_stun_sock_create( pj_stun_config *stun_cfg, 171 | const char *name, 172 | int af, 173 | const pj_stun_sock_cb *cb, 174 | const pj_stun_sock_cfg *cfg, 175 | void *user_data, 176 | pj_stun_sock **p_stun_sock) 177 | { 178 | pj_pool_t *pool; 179 | pj_stun_sock *stun_sock; 180 | pj_stun_sock_cfg default_cfg; 181 | pj_sockaddr bound_addr; 182 | unsigned i; 183 | pj_uint16_t max_bind_retry; 184 | pj_status_t status; 185 | int on=1; //Iadd 186 | 187 | PJ_ASSERT_RETURN(stun_cfg && cb && p_stun_sock, PJ_EINVAL); 188 | PJ_ASSERT_RETURN(af==pj_AF_INET()||af==pj_AF_INET6(), PJ_EAFNOTSUP); 189 | PJ_ASSERT_RETURN(!cfg || pj_stun_sock_cfg_is_valid(cfg), PJ_EINVAL); 190 | PJ_ASSERT_RETURN(cb->on_status, PJ_EINVAL); 191 | 192 | status = pj_stun_config_check_valid(stun_cfg); 193 | if (status != PJ_SUCCESS) 194 | return status; 195 | 196 | if (name == NULL) 197 | name = "stuntp%p"; 198 | 199 | if (cfg == NULL) { 200 | pj_stun_sock_cfg_default(&default_cfg); 201 | cfg = &default_cfg; 202 | } 203 | 204 | 205 | /* Create structure */ 206 | pool = pj_pool_create(stun_cfg->pf, name, 256, 512, NULL); 207 | stun_sock = PJ_POOL_ZALLOC_T(pool, pj_stun_sock); 208 | stun_sock->pool = pool; 209 | stun_sock->obj_name = pool->obj_name; 210 | stun_sock->user_data = user_data; 211 | stun_sock->af = af; 212 | stun_sock->sock_fd = PJ_INVALID_SOCKET; 213 | pj_memcpy(&stun_sock->stun_cfg, stun_cfg, sizeof(*stun_cfg)); 214 | pj_memcpy(&stun_sock->cb, cb, sizeof(*cb)); 215 | 216 | stun_sock->ka_interval = cfg->ka_interval; 217 | if (stun_sock->ka_interval == 0) 218 | stun_sock->ka_interval = PJ_STUN_KEEP_ALIVE_SEC; 219 | 220 | if (cfg && cfg->grp_lock) { 221 | stun_sock->grp_lock = cfg->grp_lock; 222 | } else { 223 | status = pj_grp_lock_create(pool, NULL, &stun_sock->grp_lock); 224 | if (status != PJ_SUCCESS) { 225 | pj_pool_release(pool); 226 | return status; 227 | } 228 | } 229 | 230 | pj_grp_lock_add_ref(stun_sock->grp_lock); 231 | pj_grp_lock_add_handler(stun_sock->grp_lock, pool, stun_sock, 232 | &stun_sock_destructor); 233 | 234 | /* Create socket and bind socket */ 235 | status = pj_sock_socket(af, pj_SOCK_DGRAM(), 0, &stun_sock->sock_fd); 236 | /*!!i add, set reuseable here */ 237 | status=pj_sock_setsockopt(stun_sock->sock_fd,SOL_SOCKET,SO_REUSEADDR | SO_BROADCAST,&on,sizeof(int)); 238 | if (status != PJ_SUCCESS) 239 | goto on_error; 240 | 241 | /* Apply QoS, if specified */ 242 | status = pj_sock_apply_qos2(stun_sock->sock_fd, cfg->qos_type, 243 | &cfg->qos_params, 2, stun_sock->obj_name, 244 | NULL); 245 | if (status != PJ_SUCCESS && !cfg->qos_ignore_error) 246 | goto on_error; 247 | 248 | /* Bind socket */ 249 | max_bind_retry = MAX_BIND_RETRY; 250 | if (cfg->port_range && cfg->port_range < max_bind_retry) 251 | max_bind_retry = cfg->port_range; 252 | pj_sockaddr_init(af, &bound_addr, NULL, 0); 253 | if (cfg->bound_addr.addr.sa_family == pj_AF_INET() || 254 | cfg->bound_addr.addr.sa_family == pj_AF_INET6()) 255 | { 256 | pj_sockaddr_cp(&bound_addr, &cfg->bound_addr); 257 | } 258 | status = pj_sock_bind_random(stun_sock->sock_fd, &bound_addr, 259 | cfg->port_range, max_bind_retry); 260 | if (status != PJ_SUCCESS) 261 | goto on_error; 262 | 263 | /* Create more useful information string about this transport */ 264 | #if 0 265 | { 266 | pj_sockaddr bound_addr; 267 | int addr_len = sizeof(bound_addr); 268 | 269 | status = pj_sock_getsockname(stun_sock->sock_fd, &bound_addr, 270 | &addr_len); 271 | if (status != PJ_SUCCESS) 272 | goto on_error; 273 | 274 | stun_sock->info = pj_pool_alloc(pool, PJ_INET6_ADDRSTRLEN+10); 275 | pj_sockaddr_print(&bound_addr, stun_sock->info, 276 | PJ_INET6_ADDRSTRLEN, 3); 277 | } 278 | #endif 279 | 280 | /* Init active socket configuration */ 281 | { 282 | pj_activesock_cfg activesock_cfg; 283 | pj_activesock_cb activesock_cb; 284 | 285 | pj_activesock_cfg_default(&activesock_cfg); 286 | activesock_cfg.grp_lock = stun_sock->grp_lock; 287 | activesock_cfg.async_cnt = cfg->async_cnt; 288 | activesock_cfg.concurrency = 0; 289 | 290 | /* Create the active socket */ 291 | pj_bzero(&activesock_cb, sizeof(activesock_cb)); 292 | activesock_cb.on_data_recvfrom = &on_data_recvfrom; 293 | activesock_cb.on_data_sent = &on_data_sent; 294 | status = pj_activesock_create(pool, stun_sock->sock_fd, 295 | pj_SOCK_DGRAM(), 296 | &activesock_cfg, stun_cfg->ioqueue, 297 | &activesock_cb, stun_sock, 298 | &stun_sock->active_sock); 299 | if (status != PJ_SUCCESS) 300 | goto on_error; 301 | 302 | /* Start asynchronous read operations */ 303 | status = pj_activesock_start_recvfrom(stun_sock->active_sock, pool, 304 | cfg->max_pkt_size, 0); 305 | if (status != PJ_SUCCESS) 306 | goto on_error; 307 | 308 | /* Init send keys */ 309 | pj_ioqueue_op_key_init(&stun_sock->send_key, 310 | sizeof(stun_sock->send_key)); 311 | pj_ioqueue_op_key_init(&stun_sock->int_send_key, 312 | sizeof(stun_sock->int_send_key)); 313 | } 314 | 315 | /* Create STUN session */ 316 | { 317 | pj_stun_session_cb sess_cb; 318 | 319 | pj_bzero(&sess_cb, sizeof(sess_cb)); 320 | sess_cb.on_request_complete = &sess_on_request_complete; 321 | sess_cb.on_send_msg = &sess_on_send_msg; 322 | status = pj_stun_session_create(&stun_sock->stun_cfg, 323 | stun_sock->obj_name, 324 | &sess_cb, PJ_FALSE, 325 | stun_sock->grp_lock, 326 | &stun_sock->stun_sess); 327 | if (status != PJ_SUCCESS) 328 | goto on_error; 329 | } 330 | 331 | /* Associate us with the STUN session */ 332 | pj_stun_session_set_user_data(stun_sock->stun_sess, stun_sock); 333 | 334 | /* Initialize random numbers to be used as STUN transaction ID for 335 | * outgoing Binding request. We use the 80bit number to distinguish 336 | * STUN messages we sent with STUN messages that the application sends. 337 | * The last 16bit value in the array is a counter. 338 | */ 339 | for (i=0; itsx_id); ++i) { 340 | stun_sock->tsx_id[i] = (pj_uint16_t) pj_rand(); 341 | } 342 | stun_sock->tsx_id[5] = 0; 343 | 344 | 345 | /* Init timer entry */ 346 | stun_sock->ka_timer.cb = &ka_timer_cb; 347 | stun_sock->ka_timer.user_data = stun_sock; 348 | 349 | /* Done */ 350 | *p_stun_sock = stun_sock; 351 | return PJ_SUCCESS; 352 | 353 | on_error: 354 | pj_stun_sock_destroy(stun_sock); 355 | return status; 356 | } 357 | 358 | /* Start socket. */ 359 | PJ_DEF(pj_status_t) pj_stun_sock_start( pj_stun_sock *stun_sock, 360 | const pj_str_t *domain, 361 | pj_uint16_t default_port, 362 | pj_dns_resolver *resolver) 363 | { 364 | pj_status_t status; 365 | 366 | PJ_ASSERT_RETURN(stun_sock && domain && default_port, PJ_EINVAL); 367 | 368 | pj_grp_lock_acquire(stun_sock->grp_lock); 369 | 370 | /* Check whether the domain contains IP address */ 371 | stun_sock->srv_addr.addr.sa_family = (pj_uint16_t)stun_sock->af; 372 | status = pj_inet_pton(stun_sock->af, domain, 373 | pj_sockaddr_get_addr(&stun_sock->srv_addr)); 374 | if (status != PJ_SUCCESS) { 375 | stun_sock->srv_addr.addr.sa_family = (pj_uint16_t)0; 376 | } 377 | 378 | /* If resolver is set, try to resolve with DNS SRV first. It 379 | * will fallback to DNS A/AAAA when no SRV record is found. 380 | */ 381 | if (status != PJ_SUCCESS && resolver) { 382 | const pj_str_t res_name = pj_str("_stun._udp."); 383 | unsigned opt; 384 | 385 | pj_assert(stun_sock->q == NULL); 386 | 387 | opt = PJ_DNS_SRV_FALLBACK_A; 388 | if (stun_sock->af == pj_AF_INET6()) { 389 | opt |= (PJ_DNS_SRV_RESOLVE_AAAA | PJ_DNS_SRV_FALLBACK_AAAA); 390 | } 391 | 392 | status = pj_dns_srv_resolve(domain, &res_name, default_port, 393 | stun_sock->pool, resolver, opt, 394 | stun_sock, &dns_srv_resolver_cb, 395 | &stun_sock->q); 396 | 397 | /* Processing will resume when the DNS SRV callback is called */ 398 | 399 | } else { 400 | 401 | if (status != PJ_SUCCESS) { 402 | pj_addrinfo ai; 403 | unsigned cnt = 1; 404 | 405 | status = pj_getaddrinfo(stun_sock->af, domain, &cnt, &ai); 406 | if (status != PJ_SUCCESS) 407 | return status; 408 | 409 | pj_sockaddr_cp(&stun_sock->srv_addr, &ai.ai_addr); 410 | } 411 | 412 | pj_sockaddr_set_port(&stun_sock->srv_addr, (pj_uint16_t)default_port); 413 | 414 | /* Start sending Binding request */ 415 | status = get_mapped_addr(stun_sock); 416 | } 417 | 418 | pj_grp_lock_release(stun_sock->grp_lock); 419 | return status; 420 | } 421 | 422 | /* Destructor */ 423 | static void stun_sock_destructor(void *obj) 424 | { 425 | pj_stun_sock *stun_sock = (pj_stun_sock*)obj; 426 | 427 | if (stun_sock->q) { 428 | pj_dns_srv_cancel_query(stun_sock->q, PJ_FALSE); 429 | stun_sock->q = NULL; 430 | } 431 | 432 | /* 433 | if (stun_sock->stun_sess) { 434 | pj_stun_session_destroy(stun_sock->stun_sess); 435 | stun_sock->stun_sess = NULL; 436 | } 437 | */ 438 | 439 | if (stun_sock->pool) { 440 | pj_pool_t *pool = stun_sock->pool; 441 | stun_sock->pool = NULL; 442 | pj_pool_release(pool); 443 | } 444 | 445 | TRACE_(("", "STUN sock %p destroyed", stun_sock)); 446 | 447 | } 448 | 449 | /* Destroy */ 450 | PJ_DEF(pj_status_t) pj_stun_sock_destroy(pj_stun_sock *stun_sock) 451 | { 452 | TRACE_((stun_sock->obj_name, "STUN sock %p request, ref_cnt=%d", 453 | stun_sock, pj_grp_lock_get_ref(stun_sock->grp_lock))); 454 | 455 | pj_grp_lock_acquire(stun_sock->grp_lock); 456 | if (stun_sock->is_destroying) { 457 | /* Destroy already called */ 458 | pj_grp_lock_release(stun_sock->grp_lock); 459 | return PJ_EINVALIDOP; 460 | } 461 | 462 | stun_sock->is_destroying = PJ_TRUE; 463 | pj_timer_heap_cancel_if_active(stun_sock->stun_cfg.timer_heap, 464 | &stun_sock->ka_timer, 0); 465 | 466 | if (stun_sock->active_sock != NULL) { 467 | stun_sock->sock_fd = PJ_INVALID_SOCKET; 468 | pj_activesock_close(stun_sock->active_sock); 469 | } else if (stun_sock->sock_fd != PJ_INVALID_SOCKET) { 470 | pj_sock_close(stun_sock->sock_fd); 471 | stun_sock->sock_fd = PJ_INVALID_SOCKET; 472 | } 473 | 474 | if (stun_sock->stun_sess) { 475 | pj_stun_session_destroy(stun_sock->stun_sess); 476 | } 477 | pj_grp_lock_dec_ref(stun_sock->grp_lock); 478 | pj_grp_lock_release(stun_sock->grp_lock); 479 | return PJ_SUCCESS; 480 | } 481 | 482 | /* Associate user data */ 483 | PJ_DEF(pj_status_t) pj_stun_sock_set_user_data( pj_stun_sock *stun_sock, 484 | void *user_data) 485 | { 486 | PJ_ASSERT_RETURN(stun_sock, PJ_EINVAL); 487 | stun_sock->user_data = user_data; 488 | return PJ_SUCCESS; 489 | } 490 | 491 | 492 | /* Get user data */ 493 | PJ_DEF(void*) pj_stun_sock_get_user_data(pj_stun_sock *stun_sock) 494 | { 495 | PJ_ASSERT_RETURN(stun_sock, NULL); 496 | return stun_sock->user_data; 497 | } 498 | 499 | /* Notify application that session has failed */ 500 | static pj_bool_t sess_fail(pj_stun_sock *stun_sock, 501 | pj_stun_sock_op op, 502 | pj_status_t status) 503 | { 504 | pj_bool_t ret; 505 | 506 | PJ_PERROR(4,(stun_sock->obj_name, status, 507 | "Session failed because %s failed", 508 | pj_stun_sock_op_name(op))); 509 | 510 | ret = (*stun_sock->cb.on_status)(stun_sock, op, status); 511 | 512 | return ret; 513 | } 514 | 515 | /* DNS resolver callback */ 516 | static void dns_srv_resolver_cb(void *user_data, 517 | pj_status_t status, 518 | const pj_dns_srv_record *rec) 519 | { 520 | pj_stun_sock *stun_sock = (pj_stun_sock*) user_data; 521 | 522 | pj_grp_lock_acquire(stun_sock->grp_lock); 523 | 524 | /* Clear query */ 525 | stun_sock->q = NULL; 526 | 527 | /* Handle error */ 528 | if (status != PJ_SUCCESS) { 529 | sess_fail(stun_sock, PJ_STUN_SOCK_DNS_OP, status); 530 | pj_grp_lock_release(stun_sock->grp_lock); 531 | return; 532 | } 533 | 534 | pj_assert(rec->count); 535 | pj_assert(rec->entry[0].server.addr_count); 536 | 537 | PJ_TODO(SUPPORT_IPV6_IN_RESOLVER); 538 | pj_assert(stun_sock->af == pj_AF_INET()); 539 | 540 | /* Set the address */ 541 | pj_sockaddr_in_init(&stun_sock->srv_addr.ipv4, NULL, 542 | rec->entry[0].port); 543 | stun_sock->srv_addr.ipv4.sin_addr = rec->entry[0].server.addr[0]; 544 | 545 | /* Start sending Binding request */ 546 | get_mapped_addr(stun_sock); 547 | 548 | pj_grp_lock_release(stun_sock->grp_lock); 549 | } 550 | 551 | 552 | /* Start sending STUN Binding request */ 553 | static pj_status_t get_mapped_addr(pj_stun_sock *stun_sock) 554 | { 555 | pj_stun_tx_data *tdata; 556 | pj_status_t status; 557 | 558 | /* Increment request counter and create STUN Binding request */ 559 | ++stun_sock->tsx_id[5]; 560 | status = pj_stun_session_create_req(stun_sock->stun_sess, 561 | PJ_STUN_BINDING_REQUEST, 562 | PJ_STUN_MAGIC, 563 | (const pj_uint8_t*)stun_sock->tsx_id, 564 | &tdata); 565 | if (status != PJ_SUCCESS) 566 | goto on_error; 567 | 568 | /* Send request */ 569 | status=pj_stun_session_send_msg(stun_sock->stun_sess, INTERNAL_MSG_TOKEN, 570 | PJ_FALSE, PJ_TRUE, &stun_sock->srv_addr, 571 | pj_sockaddr_get_len(&stun_sock->srv_addr), 572 | tdata); 573 | if (status != PJ_SUCCESS && status != PJ_EPENDING) 574 | goto on_error; 575 | 576 | return PJ_SUCCESS; 577 | 578 | on_error: 579 | sess_fail(stun_sock, PJ_STUN_SOCK_BINDING_OP, status); 580 | return status; 581 | } 582 | 583 | /* Get info */ 584 | PJ_DEF(pj_status_t) pj_stun_sock_get_info( pj_stun_sock *stun_sock, 585 | pj_stun_sock_info *info) 586 | { 587 | int addr_len; 588 | pj_status_t status; 589 | 590 | PJ_ASSERT_RETURN(stun_sock && info, PJ_EINVAL); 591 | 592 | pj_grp_lock_acquire(stun_sock->grp_lock); 593 | 594 | /* Copy STUN server address and mapped address */ 595 | pj_memcpy(&info->srv_addr, &stun_sock->srv_addr, 596 | sizeof(pj_sockaddr)); 597 | pj_memcpy(&info->mapped_addr, &stun_sock->mapped_addr, 598 | sizeof(pj_sockaddr)); 599 | 600 | /* Retrieve bound address */ 601 | addr_len = sizeof(info->bound_addr); 602 | status = pj_sock_getsockname(stun_sock->sock_fd, &info->bound_addr, 603 | &addr_len); 604 | if (status != PJ_SUCCESS) { 605 | pj_grp_lock_release(stun_sock->grp_lock); 606 | return status; 607 | } 608 | 609 | /* If socket is bound to a specific interface, then only put that 610 | * interface in the alias list. Otherwise query all the interfaces 611 | * in the host. 612 | */ 613 | if (pj_sockaddr_has_addr(&info->bound_addr)) { 614 | info->alias_cnt = 1; 615 | pj_sockaddr_cp(&info->aliases[0], &info->bound_addr); 616 | } else { 617 | pj_sockaddr def_addr; 618 | pj_uint16_t port = pj_sockaddr_get_port(&info->bound_addr); 619 | unsigned i; 620 | 621 | /* Get the default address */ 622 | status = pj_gethostip(stun_sock->af, &def_addr); 623 | if (status != PJ_SUCCESS) { 624 | pj_grp_lock_release(stun_sock->grp_lock); 625 | return status; 626 | } 627 | 628 | pj_sockaddr_set_port(&def_addr, port); 629 | 630 | /* Enum all IP interfaces in the host */ 631 | info->alias_cnt = PJ_ARRAY_SIZE(info->aliases); 632 | status = pj_enum_ip_interface(stun_sock->af, &info->alias_cnt, 633 | info->aliases); 634 | if (status != PJ_SUCCESS) { 635 | pj_grp_lock_release(stun_sock->grp_lock); 636 | return status; 637 | } 638 | 639 | /* Set the port number for each address. 640 | */ 641 | for (i=0; ialias_cnt; ++i) { 642 | pj_sockaddr_set_port(&info->aliases[i], port); 643 | } 644 | 645 | /* Put the default IP in the first slot */ 646 | for (i=0; ialias_cnt; ++i) { 647 | if (pj_sockaddr_cmp(&info->aliases[i], &def_addr)==0) { 648 | if (i!=0) { 649 | pj_sockaddr_cp(&info->aliases[i], &info->aliases[0]); 650 | pj_sockaddr_cp(&info->aliases[0], &def_addr); 651 | } 652 | break; 653 | } 654 | } 655 | } 656 | 657 | pj_grp_lock_release(stun_sock->grp_lock); 658 | return PJ_SUCCESS; 659 | } 660 | 661 | /* Send application data */ 662 | PJ_DEF(pj_status_t) pj_stun_sock_sendto( pj_stun_sock *stun_sock, 663 | pj_ioqueue_op_key_t *send_key, 664 | const void *pkt, 665 | unsigned pkt_len, 666 | unsigned flag, 667 | const pj_sockaddr_t *dst_addr, 668 | unsigned addr_len) 669 | { 670 | pj_ssize_t size; 671 | pj_status_t status; 672 | 673 | PJ_ASSERT_RETURN(stun_sock && pkt && dst_addr && addr_len, PJ_EINVAL); 674 | 675 | pj_grp_lock_acquire(stun_sock->grp_lock); 676 | 677 | if (!stun_sock->active_sock) { 678 | /* We have been shutdown, but this callback may still get called 679 | * by retransmit timer. 680 | */ 681 | pj_grp_lock_release(stun_sock->grp_lock); 682 | return PJ_EINVALIDOP; 683 | } 684 | 685 | if (send_key==NULL) 686 | send_key = &stun_sock->send_key; 687 | 688 | size = pkt_len; 689 | status = pj_activesock_sendto(stun_sock->active_sock, send_key, 690 | pkt, &size, flag, dst_addr, addr_len); 691 | 692 | pj_grp_lock_release(stun_sock->grp_lock); 693 | return status; 694 | } 695 | 696 | /* This callback is called by the STUN session to send packet */ 697 | static pj_status_t sess_on_send_msg(pj_stun_session *sess, 698 | void *token, 699 | const void *pkt, 700 | pj_size_t pkt_size, 701 | const pj_sockaddr_t *dst_addr, 702 | unsigned addr_len) 703 | { 704 | pj_stun_sock *stun_sock; 705 | pj_ssize_t size; 706 | 707 | stun_sock = (pj_stun_sock *) pj_stun_session_get_user_data(sess); 708 | if (!stun_sock || !stun_sock->active_sock) { 709 | /* We have been shutdown, but this callback may still get called 710 | * by retransmit timer. 711 | */ 712 | return PJ_EINVALIDOP; 713 | } 714 | 715 | pj_assert(token==INTERNAL_MSG_TOKEN); 716 | PJ_UNUSED_ARG(token); 717 | 718 | size = pkt_size; 719 | return pj_activesock_sendto(stun_sock->active_sock, 720 | &stun_sock->int_send_key, 721 | pkt, &size, 0, dst_addr, addr_len); 722 | } 723 | 724 | /* This callback is called by the STUN session when outgoing transaction 725 | * is complete 726 | */ 727 | static void sess_on_request_complete(pj_stun_session *sess, 728 | pj_status_t status, 729 | void *token, 730 | pj_stun_tx_data *tdata, 731 | const pj_stun_msg *response, 732 | const pj_sockaddr_t *src_addr, 733 | unsigned src_addr_len) 734 | { 735 | pj_stun_sock *stun_sock; 736 | const pj_stun_sockaddr_attr *mapped_attr; 737 | pj_stun_sock_op op; 738 | pj_bool_t mapped_changed; 739 | pj_bool_t resched = PJ_TRUE; 740 | 741 | stun_sock = (pj_stun_sock *) pj_stun_session_get_user_data(sess); 742 | if (!stun_sock) 743 | return; 744 | 745 | PJ_UNUSED_ARG(tdata); 746 | PJ_UNUSED_ARG(token); 747 | PJ_UNUSED_ARG(src_addr); 748 | PJ_UNUSED_ARG(src_addr_len); 749 | 750 | /* Check if this is a keep-alive or the first Binding request */ 751 | if (pj_sockaddr_has_addr(&stun_sock->mapped_addr)) 752 | op = PJ_STUN_SOCK_KEEP_ALIVE_OP; 753 | else 754 | op = PJ_STUN_SOCK_BINDING_OP; 755 | 756 | /* Handle failure */ 757 | if (status != PJ_SUCCESS) { 758 | resched = sess_fail(stun_sock, op, status); 759 | goto on_return; 760 | } 761 | 762 | /* Get XOR-MAPPED-ADDRESS, or MAPPED-ADDRESS when XOR-MAPPED-ADDRESS 763 | * doesn't exist. 764 | */ 765 | mapped_attr = (const pj_stun_sockaddr_attr*) 766 | pj_stun_msg_find_attr(response, PJ_STUN_ATTR_XOR_MAPPED_ADDR, 767 | 0); 768 | if (mapped_attr==NULL) { 769 | mapped_attr = (const pj_stun_sockaddr_attr*) 770 | pj_stun_msg_find_attr(response, PJ_STUN_ATTR_MAPPED_ADDR, 771 | 0); 772 | } 773 | 774 | if (mapped_attr == NULL) { 775 | resched = sess_fail(stun_sock, op, PJNATH_ESTUNNOMAPPEDADDR); 776 | goto on_return; 777 | } 778 | 779 | /* Determine if mapped address has changed, and save the new mapped 780 | * address and call callback if so 781 | */ 782 | mapped_changed = !pj_sockaddr_has_addr(&stun_sock->mapped_addr) || 783 | pj_sockaddr_cmp(&stun_sock->mapped_addr, 784 | &mapped_attr->sockaddr) != 0; 785 | if (mapped_changed) { 786 | /* Print mapped adress */ 787 | { 788 | char addrinfo[PJ_INET6_ADDRSTRLEN+10]; 789 | PJ_LOG(4,(stun_sock->obj_name, 790 | "STUN mapped address found/changed: %s", 791 | pj_sockaddr_print(&mapped_attr->sockaddr, 792 | addrinfo, sizeof(addrinfo), 3))); 793 | } 794 | 795 | pj_sockaddr_cp(&stun_sock->mapped_addr, &mapped_attr->sockaddr); 796 | 797 | if (op==PJ_STUN_SOCK_KEEP_ALIVE_OP) 798 | op = PJ_STUN_SOCK_MAPPED_ADDR_CHANGE; 799 | } 800 | 801 | /* Notify user */ 802 | resched = (*stun_sock->cb.on_status)(stun_sock, op, PJ_SUCCESS); 803 | 804 | on_return: 805 | /* Start/restart keep-alive timer */ 806 | if (resched) 807 | start_ka_timer(stun_sock); 808 | } 809 | 810 | /* Schedule keep-alive timer */ 811 | static void start_ka_timer(pj_stun_sock *stun_sock) 812 | { 813 | pj_timer_heap_cancel_if_active(stun_sock->stun_cfg.timer_heap, 814 | &stun_sock->ka_timer, 0); 815 | 816 | pj_assert(stun_sock->ka_interval != 0); 817 | if (stun_sock->ka_interval > 0 && !stun_sock->is_destroying) { 818 | pj_time_val delay; 819 | 820 | delay.sec = stun_sock->ka_interval; 821 | delay.msec = 0; 822 | 823 | pj_timer_heap_schedule_w_grp_lock(stun_sock->stun_cfg.timer_heap, 824 | &stun_sock->ka_timer, 825 | &delay, PJ_TRUE, 826 | stun_sock->grp_lock); 827 | } 828 | } 829 | 830 | /* Keep-alive timer callback */ 831 | static void ka_timer_cb(pj_timer_heap_t *th, pj_timer_entry *te) 832 | { 833 | pj_stun_sock *stun_sock; 834 | 835 | stun_sock = (pj_stun_sock *) te->user_data; 836 | 837 | PJ_UNUSED_ARG(th); 838 | pj_grp_lock_acquire(stun_sock->grp_lock); 839 | 840 | /* Time to send STUN Binding request */ 841 | if (get_mapped_addr(stun_sock) != PJ_SUCCESS) { 842 | pj_grp_lock_release(stun_sock->grp_lock); 843 | return; 844 | } 845 | 846 | /* Next keep-alive timer will be scheduled once the request 847 | * is complete. 848 | */ 849 | pj_grp_lock_release(stun_sock->grp_lock); 850 | } 851 | 852 | /* Callback from active socket when incoming packet is received */ 853 | static pj_bool_t on_data_recvfrom(pj_activesock_t *asock, 854 | void *data, 855 | pj_size_t size, 856 | const pj_sockaddr_t *src_addr, 857 | int addr_len, 858 | pj_status_t status) 859 | { 860 | pj_stun_sock *stun_sock; 861 | pj_stun_msg_hdr *hdr; 862 | pj_uint16_t type; 863 | 864 | stun_sock = (pj_stun_sock*) pj_activesock_get_user_data(asock); 865 | if (!stun_sock) 866 | return PJ_FALSE; 867 | 868 | /* Log socket error */ 869 | if (status != PJ_SUCCESS) { 870 | PJ_PERROR(2,(stun_sock->obj_name, status, "recvfrom() error")); 871 | return PJ_TRUE; 872 | } 873 | 874 | pj_grp_lock_acquire(stun_sock->grp_lock); 875 | 876 | /* Check that this is STUN message */ 877 | status = pj_stun_msg_check((const pj_uint8_t*)data, size, 878 | PJ_STUN_IS_DATAGRAM | PJ_STUN_CHECK_PACKET); 879 | if (status != PJ_SUCCESS) { 880 | /* Not STUN -- give it to application */ 881 | goto process_app_data; 882 | } 883 | 884 | /* Treat packet as STUN header and copy the STUN message type. 885 | * We don't want to access the type directly from the header 886 | * since it may not be properly aligned. 887 | */ 888 | hdr = (pj_stun_msg_hdr*) data; 889 | pj_memcpy(&type, &hdr->type, 2); 890 | type = pj_ntohs(type); 891 | 892 | /* If the packet is a STUN Binding response and part of the 893 | * transaction ID matches our internal ID, then this is 894 | * our internal STUN message (Binding request or keep alive). 895 | * Give it to our STUN session. 896 | */ 897 | if (!PJ_STUN_IS_RESPONSE(type) || 898 | PJ_STUN_GET_METHOD(type) != PJ_STUN_BINDING_METHOD || 899 | pj_memcmp(hdr->tsx_id, stun_sock->tsx_id, 10) != 0) 900 | { 901 | /* Not STUN Binding response, or STUN transaction ID mismatch. 902 | * This is not our message too -- give it to application. 903 | */ 904 | goto process_app_data; 905 | } 906 | 907 | /* This is our STUN Binding response. Give it to the STUN session */ 908 | status = pj_stun_session_on_rx_pkt(stun_sock->stun_sess, data, size, 909 | PJ_STUN_IS_DATAGRAM, NULL, NULL, 910 | src_addr, addr_len); 911 | 912 | status = pj_grp_lock_release(stun_sock->grp_lock); 913 | 914 | return status!=PJ_EGONE ? PJ_TRUE : PJ_FALSE; 915 | 916 | process_app_data: 917 | if (stun_sock->cb.on_rx_data) { 918 | pj_bool_t ret; 919 | 920 | ret = (*stun_sock->cb.on_rx_data)(stun_sock, data, size, 921 | src_addr, addr_len); 922 | status = pj_grp_lock_release(stun_sock->grp_lock); 923 | return status!=PJ_EGONE ? PJ_TRUE : PJ_FALSE; 924 | } 925 | 926 | status = pj_grp_lock_release(stun_sock->grp_lock); 927 | return status!=PJ_EGONE ? PJ_TRUE : PJ_FALSE; 928 | } 929 | 930 | /* Callback from active socket about send status */ 931 | static pj_bool_t on_data_sent(pj_activesock_t *asock, 932 | pj_ioqueue_op_key_t *send_key, 933 | pj_ssize_t sent) 934 | { 935 | pj_stun_sock *stun_sock; 936 | 937 | stun_sock = (pj_stun_sock*) pj_activesock_get_user_data(asock); 938 | if (!stun_sock) 939 | return PJ_FALSE; 940 | 941 | /* Don't report to callback if this is internal message */ 942 | if (send_key == &stun_sock->int_send_key) { 943 | return PJ_TRUE; 944 | } 945 | 946 | /* Report to callback */ 947 | if (stun_sock->cb.on_data_sent) { 948 | pj_bool_t ret; 949 | 950 | pj_grp_lock_acquire(stun_sock->grp_lock); 951 | 952 | /* If app gives NULL send_key in sendto() function, then give 953 | * NULL in the callback too 954 | */ 955 | if (send_key == &stun_sock->send_key) 956 | send_key = NULL; 957 | 958 | /* Call callback */ 959 | ret = (*stun_sock->cb.on_data_sent)(stun_sock, send_key, sent); 960 | 961 | pj_grp_lock_release(stun_sock->grp_lock); 962 | return ret; 963 | } 964 | 965 | return PJ_TRUE; 966 | } 967 | 968 | -------------------------------------------------------------------------------- /icedemo.c: -------------------------------------------------------------------------------- 1 | /* $Id: icedemo.c 4217 2012-07-27 17:24:12Z nanang $ */ 2 | /* 3 | * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | */ 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | 33 | #define THIS_FILE "icedemo.c" 34 | 35 | /* For this demo app, configure longer STUN keep-alive time 36 | * so that it does't clutter the screen output. 37 | */ 38 | #define KA_INTERVAL 300 39 | 40 | #define PRINT(fmt, arg0, arg1, arg2, arg3, arg4, arg5) \ 41 | printed = pj_ansi_snprintf(p, maxlen - (p-buffer), \ 42 | fmt, arg0, arg1, arg2, arg3, arg4, arg5); \ 43 | if (printed <= 0) return -PJ_ETOOSMALL; \ 44 | p += printed 45 | typedef struct pj_ice_strans_comp 46 | { 47 | pj_ice_strans *ice_st; /**< ICE stream transport. */ 48 | unsigned comp_id; /**< Component ID. */ 49 | 50 | pj_stun_sock *stun_sock; /**< STUN transport. */ 51 | pj_turn_sock *turn_sock; /**< TURN relay transport. */ 52 | pj_bool_t turn_log_off; /**< TURN loggin off? */ 53 | unsigned turn_err_cnt; /**< TURN disconnected count. */ 54 | 55 | unsigned cand_cnt; /**< # of candidates/aliaes. */ 56 | pj_ice_sess_cand cand_list[PJ_ICE_ST_MAX_CAND]; /**< Cand array */ 57 | 58 | unsigned default_cand; /**< Default candidate. */ 59 | 60 | } pj_ice_strans_comp; 61 | 62 | 63 | static void icedemo_show_ice(void); 64 | static void icedemo_create_instance(); 65 | static void icedemo_init_session(unsigned rolechar); 66 | static void icedemo_stop_session(void); 67 | static int UDPCreate(); 68 | static void UDPsend(void*data, int data_len); 69 | static void* UDPRecvThread(void* arg); 70 | static void UDPCreateThread(); 71 | 72 | /* This is our global variables */ 73 | static struct app_t 74 | { 75 | /* Command line options are stored here */ 76 | struct options 77 | { 78 | unsigned comp_cnt; 79 | pj_str_t ns; 80 | int max_host; 81 | pj_bool_t regular; 82 | pj_str_t stun_srv; 83 | pj_str_t turn_srv; 84 | pj_bool_t turn_tcp; 85 | pj_str_t turn_username; 86 | pj_str_t turn_password; 87 | pj_bool_t turn_fingerprint; 88 | const char *log_file; 89 | } opt; 90 | 91 | /* Our global variables */ 92 | pj_caching_pool cp; 93 | pj_pool_t *pool; 94 | pj_thread_t *thread; 95 | pj_bool_t thread_quit_flag; 96 | pj_ice_strans_cfg ice_cfg; 97 | pj_ice_strans *icest; 98 | FILE *log_fhnd; 99 | int rport; 100 | char ripaddr[PJ_INET6_ADDRSTRLEN]; 101 | int lport; 102 | char lipaddr[PJ_INET6_ADDRSTRLEN]; 103 | 104 | int localfd; 105 | 106 | /* Variables to store parsed remote ICE info */ 107 | struct rem_info 108 | { 109 | char ufrag[80]; 110 | char pwd[80]; 111 | unsigned comp_cnt; 112 | pj_sockaddr def_addr[PJ_ICE_MAX_COMP]; 113 | unsigned cand_cnt; 114 | pj_ice_sess_cand cand[PJ_ICE_ST_MAX_CAND]; 115 | } rem; 116 | 117 | } icedemo; 118 | static void UDPCreateThread(){ 119 | int rt; 120 | pthread_t id; 121 | rt=pthread_create(&id,NULL,UDPRecvThread,NULL); 122 | if(rt!=0){ 123 | perror("PTHREAD_CREATE"); 124 | } 125 | } 126 | 127 | static void* UDPRecvThread(void* arg){ 128 | printf("UDPRecvThread CREATED\n"); 129 | char buffer[1024]; 130 | int n; 131 | for(;;){ 132 | n=read(icedemo.localfd,&buffer,sizeof(buffer)); 133 | printf("read %d bytes,data:%s\n",n,buffer); 134 | 135 | } 136 | 137 | } 138 | static int UDPCreate(){ 139 | int sfd; 140 | struct sockaddr_in addr_server,addr_local; 141 | int len=sizeof(struct sockaddr); 142 | int sent,on=1; 143 | 144 | 145 | //icedemo_stop_session(); 146 | sfd=socket(AF_INET,SOCK_DGRAM,0); 147 | if(sfd==-1){ 148 | perror("SOCKET"); 149 | return ; 150 | } 151 | setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR | SO_BROADCAST,&on,sizeof(on)); 152 | memset(&addr_server,0,len); 153 | addr_server.sin_family=AF_INET; 154 | addr_server.sin_addr.s_addr=inet_addr(icedemo.ripaddr); 155 | addr_server.sin_port=htons(icedemo.rport); 156 | 157 | memset(&addr_local,0,len); 158 | addr_local.sin_family=AF_INET; 159 | addr_local.sin_addr.s_addr=inet_addr(icedemo.lipaddr); 160 | addr_local.sin_port=htons(icedemo.lport); 161 | 162 | if(bind(sfd,(struct sockaddr*)&addr_local,len)==-1){ 163 | perror("BIND"); 164 | return ; 165 | } 166 | if(connect(sfd,(struct sockaddr*)&addr_server,len)==-1){ 167 | perror("CONNECT"); 168 | return ; 169 | } 170 | printf("OUR OWN UDP SOCKET CREATED:%d\n",sfd); 171 | return sfd; 172 | } 173 | 174 | 175 | static void UDPsend(void*data, int data_len){ 176 | int sent; 177 | sent=write(icedemo.localfd,data,data_len); 178 | 179 | if(sent==-1){ 180 | perror("WRITE"); 181 | } 182 | printf("data to WRITE:%d,data WRITE:%d\n",data_len,sent); 183 | } 184 | 185 | /* Utility to display error messages */ 186 | static void icedemo_perror(const char *title, pj_status_t status) 187 | { 188 | char errmsg[PJ_ERR_MSG_SIZE]; 189 | 190 | pj_strerror(status, errmsg, sizeof(errmsg)); 191 | PJ_LOG(1,(THIS_FILE, "%s: %s", title, errmsg)); 192 | } 193 | 194 | /* Utility: display error message and exit application (usually 195 | * because of fatal error. 196 | */ 197 | static void err_exit(const char *title, pj_status_t status) 198 | { 199 | if (status != PJ_SUCCESS) { 200 | icedemo_perror(title, status); 201 | } 202 | PJ_LOG(3,(THIS_FILE, "Shutting down..")); 203 | 204 | if (icedemo.icest) 205 | pj_ice_strans_destroy(icedemo.icest); 206 | 207 | pj_thread_sleep(500); 208 | 209 | icedemo.thread_quit_flag = PJ_TRUE; 210 | if (icedemo.thread) { 211 | pj_thread_join(icedemo.thread); 212 | pj_thread_destroy(icedemo.thread); 213 | } 214 | 215 | if (icedemo.ice_cfg.stun_cfg.ioqueue) 216 | pj_ioqueue_destroy(icedemo.ice_cfg.stun_cfg.ioqueue); 217 | 218 | if (icedemo.ice_cfg.stun_cfg.timer_heap) 219 | pj_timer_heap_destroy(icedemo.ice_cfg.stun_cfg.timer_heap); 220 | 221 | pj_caching_pool_destroy(&icedemo.cp); 222 | 223 | pj_shutdown(); 224 | 225 | if (icedemo.log_fhnd) { 226 | fclose(icedemo.log_fhnd); 227 | icedemo.log_fhnd = NULL; 228 | } 229 | 230 | exit(status != PJ_SUCCESS); 231 | } 232 | 233 | #define CHECK(expr) status=expr; \ 234 | if (status!=PJ_SUCCESS) { \ 235 | err_exit(#expr, status); \ 236 | } 237 | 238 | /* 239 | * This function checks for events from both timer and ioqueue (for 240 | * network events). It is invoked by the worker thread. 241 | */ 242 | static pj_status_t handle_events(unsigned max_msec, unsigned *p_count) 243 | { 244 | enum { MAX_NET_EVENTS = 1 }; 245 | pj_time_val max_timeout = {0, 0}; 246 | pj_time_val timeout = { 0, 0}; 247 | unsigned count = 0, net_event_count = 0; 248 | int c; 249 | 250 | max_timeout.msec = max_msec; 251 | 252 | /* Poll the timer to run it and also to retrieve the earliest entry. */ 253 | timeout.sec = timeout.msec = 0; 254 | c = pj_timer_heap_poll( icedemo.ice_cfg.stun_cfg.timer_heap, &timeout ); 255 | if (c > 0) 256 | count += c; 257 | 258 | /* timer_heap_poll should never ever returns negative value, or otherwise 259 | * ioqueue_poll() will block forever! 260 | */ 261 | pj_assert(timeout.sec >= 0 && timeout.msec >= 0); 262 | if (timeout.msec >= 1000) timeout.msec = 999; 263 | 264 | /* compare the value with the timeout to wait from timer, and use the 265 | * minimum value. 266 | */ 267 | if (PJ_TIME_VAL_GT(timeout, max_timeout)) 268 | timeout = max_timeout; 269 | 270 | /* Poll ioqueue. 271 | * Repeat polling the ioqueue while we have immediate events, because 272 | * timer heap may process more than one events, so if we only process 273 | * one network events at a time (such as when IOCP backend is used), 274 | * the ioqueue may have trouble keeping up with the request rate. 275 | * 276 | * For example, for each send() request, one network event will be 277 | * reported by ioqueue for the send() completion. If we don't poll 278 | * the ioqueue often enough, the send() completion will not be 279 | * reported in timely manner. 280 | */ 281 | do { 282 | c = pj_ioqueue_poll( icedemo.ice_cfg.stun_cfg.ioqueue, &timeout); 283 | if (c < 0) { 284 | pj_status_t err = pj_get_netos_error(); 285 | pj_thread_sleep(PJ_TIME_VAL_MSEC(timeout)); 286 | if (p_count) 287 | *p_count = count; 288 | return err; 289 | } else if (c == 0) { 290 | break; 291 | } else { 292 | net_event_count += c; 293 | timeout.sec = timeout.msec = 0; 294 | } 295 | } while (c > 0 && net_event_count < MAX_NET_EVENTS); 296 | 297 | count += net_event_count; 298 | if (p_count) 299 | *p_count = count; 300 | 301 | return PJ_SUCCESS; 302 | 303 | } 304 | 305 | /* 306 | * This is the worker thread that polls event in the background. 307 | */ 308 | static int icedemo_worker_thread(void *unused) 309 | { 310 | PJ_UNUSED_ARG(unused); 311 | 312 | while (!icedemo.thread_quit_flag) { 313 | handle_events(500, NULL); 314 | } 315 | 316 | return 0; 317 | } 318 | 319 | /* 320 | * This is the callback that is registered to the ICE stream transport to 321 | * receive notification about incoming data. By "data" it means application 322 | * data such as RTP/RTCP, and not packets that belong to ICE signaling (such 323 | * as STUN connectivity checks or TURN signaling). 324 | */ 325 | static void cb_on_rx_data(pj_ice_strans *ice_st, 326 | unsigned comp_id, 327 | void *pkt, pj_size_t size, 328 | const pj_sockaddr_t *src_addr, 329 | unsigned src_addr_len) 330 | { 331 | char ipstr[PJ_INET6_ADDRSTRLEN+10]; 332 | 333 | PJ_UNUSED_ARG(ice_st); 334 | PJ_UNUSED_ARG(src_addr_len); 335 | PJ_UNUSED_ARG(pkt); 336 | 337 | // Don't do this! It will ruin the packet buffer in case TCP is used! 338 | //((char*)pkt)[size] = '\0'; 339 | 340 | PJ_LOG(3,(THIS_FILE, "Component %d: received %d bytes data from %s: \"%.*s\"", 341 | comp_id, size, 342 | pj_sockaddr_print(src_addr, ipstr, sizeof(ipstr), 3), 343 | (unsigned)size, 344 | (char*)pkt)); 345 | } 346 | 347 | /* 348 | * This is the callback that is registered to the ICE stream transport to 349 | * receive notification about ICE state progression. 350 | */ 351 | static void cb_on_ice_complete(pj_ice_strans *ice_st, 352 | pj_ice_strans_op op, 353 | pj_status_t status) 354 | { 355 | pj_ice_sess_check* sess_check; 356 | pj_ice_sess_cand* lcand; 357 | pj_ice_sess_cand* rcand; 358 | int printed; 359 | 360 | const char *opname = 361 | (op==PJ_ICE_STRANS_OP_INIT? "initialization" : 362 | (op==PJ_ICE_STRANS_OP_NEGOTIATION ? "negotiation" : "unknown_op")); 363 | 364 | /* when the initialization complete,we can get the local candidates */ 365 | if(status==PJ_SUCCESS && op==PJ_ICE_STRANS_OP_INIT ){ 366 | PJ_LOG(4,(THIS_FILE,"!!<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>> NEGOTIATION COMPLETE! THEN GET PAIR")); 375 | sess_check=pj_ice_strans_get_valid_pair(ice_st,1); 376 | lcand=sess_check->lcand; 377 | rcand=sess_check->rcand; 378 | pj_sockaddr_print(&rcand->addr,icedemo.ripaddr,sizeof(icedemo.ripaddr),0); 379 | icedemo.rport=(int)pj_sockaddr_get_port(&rcand->addr); 380 | printf("REMOTE--> port:%d,IP:%s\n",icedemo.rport,icedemo.ripaddr); 381 | 382 | 383 | pj_sockaddr_print(&lcand->addr,icedemo.lipaddr,sizeof(icedemo.lipaddr),0); 384 | icedemo.lport=(int)pj_sockaddr_get_port(&lcand->addr); 385 | printf("LOCAL--> port:%d,IP:%s\n",icedemo.lport,icedemo.lipaddr); 386 | 387 | } 388 | 389 | if (status == PJ_SUCCESS) { 390 | PJ_LOG(3,(THIS_FILE, "ICE %s successful", opname)); 391 | } else { 392 | char errmsg[PJ_ERR_MSG_SIZE]; 393 | 394 | pj_strerror(status, errmsg, sizeof(errmsg)); 395 | PJ_LOG(1,(THIS_FILE, "ICE %s failed: %s", opname, errmsg)); 396 | pj_ice_strans_destroy(ice_st); 397 | icedemo.icest = NULL; 398 | } 399 | } 400 | 401 | /* log callback to write to file */ 402 | static void log_func(int level, const char *data, int len) 403 | { 404 | pj_log_write(level, data, len); 405 | if (icedemo.log_fhnd) { 406 | if (fwrite(data, len, 1, icedemo.log_fhnd) != 1) 407 | return; 408 | } 409 | } 410 | 411 | /* 412 | * This is the main application initialization function. It is called 413 | * once (and only once) during application initialization sequence by 414 | * main(). 415 | */ 416 | static pj_status_t icedemo_init(void) 417 | { 418 | pj_status_t status; 419 | 420 | if (icedemo.opt.log_file) { 421 | icedemo.log_fhnd = fopen(icedemo.opt.log_file, "a"); 422 | pj_log_set_log_func(&log_func); 423 | } 424 | 425 | /* Initialize the libraries before anything else */ 426 | CHECK( pj_init() ); 427 | CHECK( pjlib_util_init() ); 428 | CHECK( pjnath_init() ); 429 | 430 | /* Must create pool factory, where memory allocations come from */ 431 | pj_caching_pool_init(&icedemo.cp, NULL, 0); 432 | 433 | /* Init our ICE settings with null values */ 434 | pj_ice_strans_cfg_default(&icedemo.ice_cfg); 435 | 436 | icedemo.ice_cfg.stun_cfg.pf = &icedemo.cp.factory; 437 | 438 | /* Create application memory pool */ 439 | icedemo.pool = pj_pool_create(&icedemo.cp.factory, "icedemo", 440 | 512, 512, NULL); 441 | 442 | /* Create timer heap for timer stuff */ 443 | CHECK( pj_timer_heap_create(icedemo.pool, 100, 444 | &icedemo.ice_cfg.stun_cfg.timer_heap) ); 445 | 446 | /* and create ioqueue for network I/O stuff */ 447 | CHECK( pj_ioqueue_create(icedemo.pool, 16, 448 | &icedemo.ice_cfg.stun_cfg.ioqueue) ); 449 | 450 | /* something must poll the timer heap and ioqueue, 451 | * unless we're on Symbian where the timer heap and ioqueue run 452 | * on themselves. 453 | */ 454 | CHECK( pj_thread_create(icedemo.pool, "icedemo", &icedemo_worker_thread, 455 | NULL, 0, 0, &icedemo.thread) ); 456 | 457 | icedemo.ice_cfg.af = pj_AF_INET(); 458 | 459 | /* Create DNS resolver if nameserver is set */ 460 | if (icedemo.opt.ns.slen) { 461 | CHECK( pj_dns_resolver_create(&icedemo.cp.factory, 462 | "resolver", 463 | 0, 464 | icedemo.ice_cfg.stun_cfg.timer_heap, 465 | icedemo.ice_cfg.stun_cfg.ioqueue, 466 | &icedemo.ice_cfg.resolver) ); 467 | 468 | CHECK( pj_dns_resolver_set_ns(icedemo.ice_cfg.resolver, 1, 469 | &icedemo.opt.ns, NULL) ); 470 | } 471 | 472 | /* -= Start initializing ICE stream transport config =- */ 473 | 474 | /* Maximum number of host candidates */ 475 | if (icedemo.opt.max_host != -1) 476 | icedemo.ice_cfg.stun.max_host_cands = icedemo.opt.max_host; 477 | 478 | /* Nomination strategy */ 479 | if (icedemo.opt.regular) 480 | icedemo.ice_cfg.opt.aggressive = PJ_FALSE; 481 | else 482 | icedemo.ice_cfg.opt.aggressive = PJ_TRUE; 483 | 484 | /* Configure STUN/srflx candidate resolution */ 485 | if (icedemo.opt.stun_srv.slen) { 486 | char *pos; 487 | 488 | /* Command line option may contain port number */ 489 | if ((pos=pj_strchr(&icedemo.opt.stun_srv, ':')) != NULL) { 490 | icedemo.ice_cfg.stun.server.ptr = icedemo.opt.stun_srv.ptr; 491 | icedemo.ice_cfg.stun.server.slen = (pos - icedemo.opt.stun_srv.ptr); 492 | 493 | icedemo.ice_cfg.stun.port = (pj_uint16_t)atoi(pos+1); 494 | } else { 495 | icedemo.ice_cfg.stun.server = icedemo.opt.stun_srv; 496 | icedemo.ice_cfg.stun.port = PJ_STUN_PORT; 497 | } 498 | 499 | /* For this demo app, configure longer STUN keep-alive time 500 | * so that it does't clutter the screen output. 501 | */ 502 | icedemo.ice_cfg.stun.cfg.ka_interval = KA_INTERVAL; 503 | } 504 | 505 | /* Configure TURN candidate */ 506 | if (icedemo.opt.turn_srv.slen) { 507 | char *pos; 508 | 509 | /* Command line option may contain port number */ 510 | if ((pos=pj_strchr(&icedemo.opt.turn_srv, ':')) != NULL) { 511 | icedemo.ice_cfg.turn.server.ptr = icedemo.opt.turn_srv.ptr; 512 | icedemo.ice_cfg.turn.server.slen = (pos - icedemo.opt.turn_srv.ptr); 513 | 514 | icedemo.ice_cfg.turn.port = (pj_uint16_t)atoi(pos+1); 515 | } else { 516 | icedemo.ice_cfg.turn.server = icedemo.opt.turn_srv; 517 | icedemo.ice_cfg.turn.port = PJ_STUN_PORT; 518 | } 519 | 520 | /* TURN credential */ 521 | icedemo.ice_cfg.turn.auth_cred.type = PJ_STUN_AUTH_CRED_STATIC; 522 | icedemo.ice_cfg.turn.auth_cred.data.static_cred.username = icedemo.opt.turn_username; 523 | icedemo.ice_cfg.turn.auth_cred.data.static_cred.data_type = PJ_STUN_PASSWD_PLAIN; 524 | icedemo.ice_cfg.turn.auth_cred.data.static_cred.data = icedemo.opt.turn_password; 525 | 526 | /* Connection type to TURN server */ 527 | if (icedemo.opt.turn_tcp) 528 | icedemo.ice_cfg.turn.conn_type = PJ_TURN_TP_TCP; 529 | else 530 | icedemo.ice_cfg.turn.conn_type = PJ_TURN_TP_UDP; 531 | 532 | /* For this demo app, configure longer keep-alive time 533 | * so that it does't clutter the screen output. 534 | */ 535 | icedemo.ice_cfg.turn.alloc_param.ka_interval = KA_INTERVAL; 536 | } 537 | 538 | /* -= That's it for now, initialization is complete =- */ 539 | return PJ_SUCCESS; 540 | } 541 | 542 | 543 | /* 544 | * Create ICE stream transport instance, invoked from the menu. 545 | */ 546 | static void icedemo_create_instance(void) 547 | { 548 | pj_ice_strans_cb icecb; 549 | pj_status_t status; 550 | 551 | if (icedemo.icest != NULL) { 552 | puts("ICE instance already created, destroy it first"); 553 | return; 554 | } 555 | 556 | /* init the callback */ 557 | pj_bzero(&icecb, sizeof(icecb)); 558 | icecb.on_rx_data = cb_on_rx_data; 559 | icecb.on_ice_complete = cb_on_ice_complete; 560 | 561 | /* create the instance */ 562 | status = pj_ice_strans_create("icedemo", /* object name */ 563 | &icedemo.ice_cfg, /* settings */ 564 | icedemo.opt.comp_cnt, /* comp_cnt */ 565 | NULL, /* user data */ 566 | &icecb, /* callback */ 567 | &icedemo.icest) /* instance ptr */ 568 | ; 569 | if (status != PJ_SUCCESS) 570 | icedemo_perror("error creating ice", status); 571 | else 572 | PJ_LOG(3,(THIS_FILE, "ICE instance successfully created")); 573 | } 574 | 575 | /* Utility to nullify parsed remote info */ 576 | static void reset_rem_info(void) 577 | { 578 | pj_bzero(&icedemo.rem, sizeof(icedemo.rem)); 579 | } 580 | 581 | 582 | /* 583 | * Destroy ICE stream transport instance, invoked from the menu. 584 | */ 585 | static void icedemo_destroy_instance(void) 586 | { 587 | if (icedemo.icest == NULL) { 588 | PJ_LOG(1,(THIS_FILE, "Error: No ICE instance, create it first")); 589 | return; 590 | } 591 | 592 | pj_ice_strans_destroy(icedemo.icest); 593 | icedemo.icest = NULL; 594 | 595 | reset_rem_info(); 596 | 597 | 598 | PJ_LOG(3,(THIS_FILE, "ICE instance destroyed")); 599 | 600 | } 601 | 602 | 603 | /* 604 | * Create ICE session, invoked from the menu. 605 | */ 606 | static void icedemo_init_session(unsigned rolechar) 607 | { 608 | pj_ice_sess_role role = (pj_tolower((pj_uint8_t)rolechar)=='o' ? 609 | PJ_ICE_SESS_ROLE_CONTROLLING : 610 | PJ_ICE_SESS_ROLE_CONTROLLED); 611 | pj_status_t status; 612 | 613 | if (icedemo.icest == NULL) { 614 | PJ_LOG(1,(THIS_FILE, "Error: No ICE instance, create it first")); 615 | return; 616 | } 617 | 618 | if (pj_ice_strans_has_sess(icedemo.icest)) { 619 | PJ_LOG(1,(THIS_FILE, "Error: Session already created")); 620 | return; 621 | } 622 | 623 | status = pj_ice_strans_init_ice(icedemo.icest, role, NULL, NULL); 624 | if (status != PJ_SUCCESS) 625 | icedemo_perror("error creating session", status); 626 | else 627 | PJ_LOG(3,(THIS_FILE, "ICE session created")); 628 | 629 | reset_rem_info(); 630 | } 631 | 632 | 633 | /* 634 | * Stop/destroy ICE session, invoked from the menu. 635 | */ 636 | static void icedemo_stop_session(void) 637 | { 638 | pj_status_t status; 639 | 640 | if (icedemo.icest == NULL) { 641 | PJ_LOG(1,(THIS_FILE, "Error: No ICE instance, create it first")); 642 | return; 643 | } 644 | 645 | if (!pj_ice_strans_has_sess(icedemo.icest)) { 646 | PJ_LOG(1,(THIS_FILE, "Error: No ICE session, initialize first")); 647 | return; 648 | } 649 | 650 | status = pj_ice_strans_stop_ice(icedemo.icest); 651 | if (status != PJ_SUCCESS) 652 | icedemo_perror("error stopping session", status); 653 | else 654 | PJ_LOG(3,(THIS_FILE, "ICE session stopped")); 655 | 656 | reset_rem_info(); 657 | 658 | } 659 | 660 | 661 | 662 | /* Utility to create a=candidate SDP attribute */ 663 | static int print_cand(char buffer[], unsigned maxlen, 664 | const pj_ice_sess_cand *cand) 665 | { 666 | char ipaddr[PJ_INET6_ADDRSTRLEN]; 667 | char *p = buffer; 668 | int printed; 669 | 670 | PRINT("a=candidate:%.*s %u UDP %u %s %u typ ", 671 | (int)cand->foundation.slen, 672 | cand->foundation.ptr, 673 | (unsigned)cand->comp_id, 674 | cand->prio, 675 | pj_sockaddr_print(&cand->addr, ipaddr, 676 | sizeof(ipaddr), 0), 677 | (unsigned)pj_sockaddr_get_port(&cand->addr)); 678 | 679 | PRINT("%s\n", 680 | pj_ice_get_cand_type_name(cand->type), 681 | 0, 0, 0, 0, 0); 682 | 683 | if (p == buffer+maxlen) 684 | return -PJ_ETOOSMALL; 685 | 686 | *p = '\0'; 687 | 688 | return p-buffer; 689 | } 690 | 691 | /* 692 | * Encode ICE information in SDP. 693 | */ 694 | static int encode_session(char buffer[], unsigned maxlen) 695 | { 696 | char *p = buffer; 697 | unsigned comp; 698 | int printed; 699 | pj_str_t local_ufrag, local_pwd; 700 | pj_status_t status; 701 | 702 | /* Write "dummy" SDP v=, o=, s=, and t= lines */ 703 | PRINT("v=0\no=- 3414953978 3414953978 IN IP4 localhost\ns=ice\nt=0 0\n", 704 | 0, 0, 0, 0, 0, 0); 705 | 706 | /* Get ufrag and pwd from current session */ 707 | pj_ice_strans_get_ufrag_pwd(icedemo.icest, &local_ufrag, &local_pwd, 708 | NULL, NULL); 709 | 710 | /* Write the a=ice-ufrag and a=ice-pwd attributes */ 711 | PRINT("a=ice-ufrag:%.*s\na=ice-pwd:%.*s\n", 712 | (int)local_ufrag.slen, 713 | local_ufrag.ptr, 714 | (int)local_pwd.slen, 715 | local_pwd.ptr, 716 | 0, 0); 717 | 718 | /* Write each component */ 719 | for (comp=0; comp"); 872 | if (stdout) fflush(stdout); 873 | 874 | if (fgets(linebuf, sizeof(linebuf), stdin)==NULL) 875 | break; 876 | 877 | len = strlen(linebuf); 878 | while (len && (linebuf[len-1] == '\r' || linebuf[len-1] == '\n')) 879 | linebuf[--len] = '\0'; 880 | 881 | line = linebuf; 882 | while (len && pj_isspace(*line)) 883 | ++line, --len; 884 | 885 | if (len==0) 886 | break; 887 | 888 | /* Ignore subsequent media descriptors */ 889 | if (media_cnt > 1) 890 | continue; 891 | 892 | switch (line[0]) { 893 | case 'm': 894 | { 895 | int cnt; 896 | char media[32], portstr[32]; 897 | 898 | ++media_cnt; 899 | if (media_cnt > 1) { 900 | puts("Media line ignored"); 901 | break; 902 | } 903 | 904 | cnt = sscanf(line+2, "%s %s RTP/", media, portstr); 905 | if (cnt != 2) { 906 | PJ_LOG(1,(THIS_FILE, "Error parsing media line")); 907 | goto on_error; 908 | } 909 | 910 | comp0_port = atoi(portstr); 911 | 912 | } 913 | break; 914 | case 'c': 915 | { 916 | int cnt; 917 | char c[32], net[32], ip[80]; 918 | 919 | cnt = sscanf(line+2, "%s %s %s", c, net, ip); 920 | if (cnt != 3) { 921 | PJ_LOG(1,(THIS_FILE, "Error parsing connection line")); 922 | goto on_error; 923 | } 924 | 925 | strcpy(comp0_addr, ip); 926 | } 927 | break; 928 | case 'a': 929 | { 930 | char *attr = strtok(line+2, ": \t\r\n"); 931 | if (strcmp(attr, "ice-ufrag")==0) { 932 | strcpy(icedemo.rem.ufrag, attr+strlen(attr)+1); 933 | } else if (strcmp(attr, "ice-pwd")==0) { 934 | strcpy(icedemo.rem.pwd, attr+strlen(attr)+1); 935 | } else if (strcmp(attr, "rtcp")==0) { 936 | char *val = attr+strlen(attr)+1; 937 | int af, cnt; 938 | int port; 939 | char net[32], ip[64]; 940 | pj_str_t tmp_addr; 941 | pj_status_t status; 942 | 943 | cnt = sscanf(val, "%d IN %s %s", &port, net, ip); 944 | if (cnt != 3) { 945 | PJ_LOG(1,(THIS_FILE, "Error parsing rtcp attribute")); 946 | goto on_error; 947 | } 948 | 949 | if (strchr(ip, ':')) 950 | af = pj_AF_INET6(); 951 | else 952 | af = pj_AF_INET(); 953 | 954 | pj_sockaddr_init(af, &icedemo.rem.def_addr[1], NULL, 0); 955 | tmp_addr = pj_str(ip); 956 | status = pj_sockaddr_set_str_addr(af, &icedemo.rem.def_addr[1], 957 | &tmp_addr); 958 | if (status != PJ_SUCCESS) { 959 | PJ_LOG(1,(THIS_FILE, "Invalid IP address")); 960 | goto on_error; 961 | } 962 | pj_sockaddr_set_port(&icedemo.rem.def_addr[1], (pj_uint16_t)port); 963 | 964 | } else if (strcmp(attr, "candidate")==0) { 965 | char *sdpcand = attr+strlen(attr)+1; 966 | int af, cnt; 967 | char foundation[32], transport[12], ipaddr[80], type[32]; 968 | pj_str_t tmpaddr; 969 | int comp_id, prio, port; 970 | pj_ice_sess_cand *cand; 971 | pj_status_t status; 972 | 973 | cnt = sscanf(sdpcand, "%s %d %s %d %s %d typ %s", 974 | foundation, 975 | &comp_id, 976 | transport, 977 | &prio, 978 | ipaddr, 979 | &port, 980 | type); 981 | if (cnt != 7) { 982 | PJ_LOG(1, (THIS_FILE, "error: Invalid ICE candidate line")); 983 | goto on_error; 984 | } 985 | 986 | cand = &icedemo.rem.cand[icedemo.rem.cand_cnt]; 987 | pj_bzero(cand, sizeof(*cand)); 988 | 989 | if (strcmp(type, "host")==0) 990 | cand->type = PJ_ICE_CAND_TYPE_HOST; 991 | else if (strcmp(type, "srflx")==0) 992 | cand->type = PJ_ICE_CAND_TYPE_SRFLX; 993 | else if (strcmp(type, "relay")==0) 994 | cand->type = PJ_ICE_CAND_TYPE_RELAYED; 995 | else { 996 | PJ_LOG(1, (THIS_FILE, "Error: invalid candidate type '%s'", 997 | type)); 998 | goto on_error; 999 | } 1000 | 1001 | cand->comp_id = (pj_uint8_t)comp_id; 1002 | pj_strdup2(icedemo.pool, &cand->foundation, foundation); 1003 | cand->prio = prio; 1004 | 1005 | if (strchr(ipaddr, ':')) 1006 | af = pj_AF_INET6(); 1007 | else 1008 | af = pj_AF_INET(); 1009 | 1010 | tmpaddr = pj_str(ipaddr); 1011 | pj_sockaddr_init(af, &cand->addr, NULL, 0); 1012 | status = pj_sockaddr_set_str_addr(af, &cand->addr, &tmpaddr); 1013 | if (status != PJ_SUCCESS) { 1014 | PJ_LOG(1,(THIS_FILE, "Error: invalid IP address '%s'", 1015 | ipaddr)); 1016 | goto on_error; 1017 | } 1018 | 1019 | pj_sockaddr_set_port(&cand->addr, (pj_uint16_t)port); 1020 | 1021 | ++icedemo.rem.cand_cnt; 1022 | 1023 | if (cand->comp_id > icedemo.rem.comp_cnt) 1024 | icedemo.rem.comp_cnt = cand->comp_id; 1025 | } 1026 | } 1027 | break; 1028 | } 1029 | } 1030 | 1031 | if (icedemo.rem.cand_cnt==0 || 1032 | icedemo.rem.ufrag[0]==0 || 1033 | icedemo.rem.pwd[0]==0 || 1034 | icedemo.rem.comp_cnt == 0) 1035 | { 1036 | PJ_LOG(1, (THIS_FILE, "Error: not enough info")); 1037 | goto on_error; 1038 | } 1039 | 1040 | if (comp0_port==0 || comp0_addr[0]=='\0') { 1041 | PJ_LOG(1, (THIS_FILE, "Error: default address for component 0 not found")); 1042 | goto on_error; 1043 | } else { 1044 | int af; 1045 | pj_str_t tmp_addr; 1046 | pj_status_t status; 1047 | 1048 | if (strchr(comp0_addr, ':')) 1049 | af = pj_AF_INET6(); 1050 | else 1051 | af = pj_AF_INET(); 1052 | 1053 | pj_sockaddr_init(af, &icedemo.rem.def_addr[0], NULL, 0); 1054 | tmp_addr = pj_str(comp0_addr); 1055 | status = pj_sockaddr_set_str_addr(af, &icedemo.rem.def_addr[0], 1056 | &tmp_addr); 1057 | if (status != PJ_SUCCESS) { 1058 | PJ_LOG(1,(THIS_FILE, "Invalid IP address in c= line")); 1059 | goto on_error; 1060 | } 1061 | pj_sockaddr_set_port(&icedemo.rem.def_addr[0], (pj_uint16_t)comp0_port); 1062 | } 1063 | 1064 | PJ_LOG(3, (THIS_FILE, "Done, %d remote candidate(s) added", 1065 | icedemo.rem.cand_cnt)); 1066 | return; 1067 | 1068 | on_error: 1069 | reset_rem_info(); 1070 | } 1071 | 1072 | 1073 | /* 1074 | * Start ICE negotiation! This function is invoked from the menu. 1075 | */ 1076 | static void icedemo_start_nego(void) 1077 | { 1078 | pj_str_t rufrag, rpwd; 1079 | pj_status_t status; 1080 | 1081 | if (icedemo.icest == NULL) { 1082 | PJ_LOG(1,(THIS_FILE, "Error: No ICE instance, create it first")); 1083 | return; 1084 | } 1085 | 1086 | if (!pj_ice_strans_has_sess(icedemo.icest)) { 1087 | PJ_LOG(1,(THIS_FILE, "Error: No ICE session, initialize first")); 1088 | return; 1089 | } 1090 | 1091 | if (icedemo.rem.cand_cnt == 0) { 1092 | PJ_LOG(1,(THIS_FILE, "Error: No remote info, input remote info first")); 1093 | return; 1094 | } 1095 | 1096 | PJ_LOG(3,(THIS_FILE, "Starting ICE negotiation..")); 1097 | 1098 | status = pj_ice_strans_start_ice(icedemo.icest, 1099 | pj_cstr(&rufrag, icedemo.rem.ufrag), 1100 | pj_cstr(&rpwd, icedemo.rem.pwd), 1101 | icedemo.rem.cand_cnt, 1102 | icedemo.rem.cand); 1103 | if (status != PJ_SUCCESS) 1104 | icedemo_perror("Error starting ICE", status); 1105 | else 1106 | PJ_LOG(3,(THIS_FILE, "ICE negotiation started")); 1107 | } 1108 | 1109 | 1110 | /* 1111 | * Send application data to remote agent. 1112 | */ 1113 | static void icedemo_send_data(unsigned comp_id, const char *data) 1114 | { 1115 | pj_status_t status; 1116 | int sock; 1117 | pj_ice_sess* ice; 1118 | pj_ice_strans *ice_st; 1119 | pj_ice_strans_comp *comp; 1120 | pj_stun_sock* stun_sock; 1121 | pj_activesock_t* active_sock; 1122 | pj_ioqueue_key_t * key; 1123 | 1124 | if (icedemo.icest == NULL) { 1125 | PJ_LOG(1,(THIS_FILE, "Error: No ICE instance, create it first")); 1126 | return; 1127 | } 1128 | 1129 | if (!pj_ice_strans_has_sess(icedemo.icest)) { 1130 | PJ_LOG(1,(THIS_FILE, "Error: No ICE session, initialize first")); 1131 | return; 1132 | } 1133 | 1134 | if (!pj_ice_strans_sess_is_complete(icedemo.icest)) { 1135 | PJ_LOG(1,(THIS_FILE, "Error: ICE negotiation has not been started or is in progress")); 1136 | return; 1137 | } 1138 | 1139 | if (comp_id<1||comp_id>pj_ice_strans_get_running_comp_cnt(icedemo.icest)) { 1140 | PJ_LOG(1,(THIS_FILE, "Error: invalid component ID")); 1141 | return; 1142 | } 1143 | 1144 | /* 1145 | PJ_LOG(3,(THIS_FILE,">>>>>>>>>>>>>>being UDPsend!!")); 1146 | UDPsend((void*)data,strlen(data)); 1147 | sock=(icedemo.icest->ice->comp[0])->stun_sock->active_sock->key->fd; 1148 | ice=icedemo.icest->ice; 1149 | ice_st = (pj_ice_strans*)ice->user_data; 1150 | comp=(icedemo.icest)->comp[0]; 1151 | stun_sock=comp->stun_sock; 1152 | active_sock=stun_sock->active_sock; 1153 | key=active_sock->key; 1154 | sock=key->fd; 1155 | 1156 | //printf("ice fd:%d\n",sock); 1157 | //getsockname 1158 | 1159 | */ 1160 | status = pj_ice_strans_sendto(icedemo.icest, comp_id, data, strlen(data), 1161 | &icedemo.rem.def_addr[comp_id-1], 1162 | pj_sockaddr_get_len(&icedemo.rem.def_addr[comp_id-1])); 1163 | if (status != PJ_SUCCESS) 1164 | icedemo_perror("Error sending data", status); 1165 | else 1166 | PJ_LOG(3,(THIS_FILE, "Data sent")); 1167 | } 1168 | 1169 | 1170 | /* 1171 | * Display help for the menu. 1172 | */ 1173 | static void icedemo_help_menu(void) 1174 | { 1175 | puts(""); 1176 | puts("-= Help on using ICE and this icedemo program =-"); 1177 | puts(""); 1178 | puts("This application demonstrates how to use ICE in pjnath without having\n" 1179 | "to use the SIP protocol. To use this application, you will need to run\n" 1180 | "two instances of this application, to simulate two ICE agents.\n"); 1181 | 1182 | puts("Basic ICE flow:\n" 1183 | " create instance [menu \"c\"]\n" 1184 | " repeat these steps as wanted:\n" 1185 | " - init session as offerer or answerer [menu \"i\"]\n" 1186 | " - display our SDP [menu \"s\"]\n" 1187 | " - \"send\" our SDP from the \"show\" output above to remote, by\n" 1188 | " copy-pasting the SDP to the other icedemo application\n" 1189 | " - parse remote SDP, by pasting SDP generated by the other icedemo\n" 1190 | " instance [menu \"r\"]\n" 1191 | " - begin ICE negotiation in our end [menu \"b\"], and \n" 1192 | " - immediately begin ICE negotiation in the other icedemo instance\n" 1193 | " - ICE negotiation will run, and result will be printed to screen\n" 1194 | " - send application data to remote [menu \"x\"]\n" 1195 | " - end/stop ICE session [menu \"e\"]\n" 1196 | " destroy instance [menu \"d\"]\n" 1197 | ""); 1198 | 1199 | puts(""); 1200 | puts("This concludes the help screen."); 1201 | puts(""); 1202 | } 1203 | 1204 | 1205 | /* 1206 | * Display console menu 1207 | */ 1208 | static void icedemo_print_menu(void) 1209 | { 1210 | puts(""); 1211 | puts("+----------------------------------------------------------------------+"); 1212 | puts("| M E N U |"); 1213 | puts("+---+------------------------------------------------------------------+"); 1214 | puts("| c | create Create the instance |"); 1215 | puts("| d | destroy Destroy the instance |"); 1216 | puts("| i | init o|a Initialize ICE session as offerer or answerer |"); 1217 | puts("| e | stop End/stop ICE session |"); 1218 | puts("| s | show Display local ICE info |"); 1219 | puts("| r | remote Input remote ICE info |"); 1220 | puts("| b | start Begin ICE negotiation |"); 1221 | puts("| x | send .. Send data to remote |"); 1222 | puts("+---+------------------------------------------------------------------+"); 1223 | puts("| h | help * Help! * |"); 1224 | puts("| q | quit Quit |"); 1225 | puts("+----------------------------------------------------------------------+"); 1226 | } 1227 | 1228 | 1229 | /* 1230 | * Main console loop. 1231 | */ 1232 | static void icedemo_console(void) 1233 | { 1234 | pj_bool_t app_quit = PJ_FALSE; 1235 | 1236 | while (!app_quit) { 1237 | char input[80], *cmd; 1238 | const char *SEP = " \t\r\n"; 1239 | int len; 1240 | 1241 | icedemo_print_menu(); 1242 | 1243 | printf("Input: "); 1244 | if (stdout) fflush(stdout); 1245 | 1246 | pj_bzero(input, sizeof(input)); 1247 | if (fgets(input, sizeof(input), stdin) == NULL) 1248 | break; 1249 | 1250 | len = strlen(input); 1251 | while (len && (input[len-1]=='\r' || input[len-1]=='\n')) 1252 | input[--len] = '\0'; 1253 | 1254 | cmd = strtok(input, SEP); 1255 | if (!cmd) 1256 | continue; 1257 | 1258 | if (strcmp(cmd, "create")==0 || strcmp(cmd, "c")==0) { 1259 | 1260 | icedemo_create_instance(); 1261 | 1262 | } else if (strcmp(cmd, "destroy")==0 || strcmp(cmd, "d")==0) { 1263 | 1264 | icedemo_destroy_instance(); 1265 | 1266 | } else if (strcmp(cmd, "init")==0 || strcmp(cmd, "i")==0) { 1267 | 1268 | char *role = strtok(NULL, SEP); 1269 | if (role) 1270 | icedemo_init_session(*role); 1271 | else 1272 | puts("error: Role required"); 1273 | 1274 | } else if (strcmp(cmd, "stop")==0 || strcmp(cmd, "e")==0) { 1275 | 1276 | icedemo_stop_session(); 1277 | 1278 | } else if (strcmp(cmd, "show")==0 || strcmp(cmd, "s")==0) { 1279 | 1280 | icedemo_show_ice(); 1281 | 1282 | } else if (strcmp(cmd, "remote")==0 || strcmp(cmd, "r")==0) { 1283 | 1284 | icedemo_input_remote(); 1285 | 1286 | } else if (strcmp(cmd, "start")==0 || strcmp(cmd, "b")==0) { 1287 | 1288 | icedemo_start_nego(); 1289 | 1290 | } else if (strcmp(cmd, "send")==0 || strcmp(cmd, "x")==0) { 1291 | 1292 | char *comp = strtok(NULL, SEP); 1293 | 1294 | if (!comp) { 1295 | PJ_LOG(1,(THIS_FILE, "Error: component ID required")); 1296 | } else { 1297 | char *data = comp + strlen(comp) + 1; 1298 | if (!data) 1299 | data = ""; 1300 | icedemo_send_data(atoi(comp), data); 1301 | } 1302 | 1303 | } else if (strcmp(cmd, "help")==0 || strcmp(cmd, "h")==0) { 1304 | 1305 | icedemo_help_menu(); 1306 | 1307 | } else if (strcmp(cmd, "quit")==0 || strcmp(cmd, "q")==0) { 1308 | 1309 | app_quit = PJ_TRUE; 1310 | 1311 | } else { 1312 | 1313 | printf("Invalid command '%s'\n", cmd); 1314 | 1315 | } 1316 | } 1317 | } 1318 | 1319 | 1320 | /* 1321 | * Display program usage. 1322 | */ 1323 | static void icedemo_usage() 1324 | { 1325 | puts("Usage: icedemo [optons]"); 1326 | printf("icedemo v%s by pjsip.org\n", pj_get_version()); 1327 | puts(""); 1328 | puts("General options:"); 1329 | puts(" --comp-cnt, -c N Component count (default=1)"); 1330 | puts(" --nameserver, -n IP Configure nameserver to activate DNS SRV"); 1331 | puts(" resolution"); 1332 | puts(" --max-host, -H N Set max number of host candidates to N"); 1333 | puts(" --regular, -R Use regular nomination (default aggressive)"); 1334 | puts(" --log-file, -L FILE Save output to log FILE"); 1335 | puts(" --help, -h Display this screen."); 1336 | puts(""); 1337 | puts("STUN related options:"); 1338 | puts(" --stun-srv, -s HOSTDOM Enable srflx candidate by resolving to STUN server."); 1339 | puts(" HOSTDOM may be a \"host_or_ip[:port]\" or a domain"); 1340 | puts(" name if DNS SRV resolution is used."); 1341 | puts(""); 1342 | puts("TURN related options:"); 1343 | puts(" --turn-srv, -t HOSTDOM Enable relayed candidate by using this TURN server."); 1344 | puts(" HOSTDOM may be a \"host_or_ip[:port]\" or a domain"); 1345 | puts(" name if DNS SRV resolution is used."); 1346 | puts(" --turn-tcp, -T Use TCP to connect to TURN server"); 1347 | puts(" --turn-username, -u UID Set TURN username of the credential to UID"); 1348 | puts(" --turn-password, -p PWD Set password of the credential to WPWD"); 1349 | puts(" --turn-fingerprint, -F Use fingerprint for outgoing TURN requests"); 1350 | puts(""); 1351 | } 1352 | 1353 | 1354 | /* 1355 | * And here's the main() 1356 | */ 1357 | int main(int argc, char *argv[]) 1358 | { 1359 | struct pj_getopt_option long_options[] = { 1360 | { "comp-cnt", 1, 0, 'c'}, 1361 | { "nameserver", 1, 0, 'n'}, 1362 | { "max-host", 1, 0, 'H'}, 1363 | { "help", 0, 0, 'h'}, 1364 | { "stun-srv", 1, 0, 's'}, 1365 | { "turn-srv", 1, 0, 't'}, 1366 | { "turn-tcp", 0, 0, 'T'}, 1367 | { "turn-username", 1, 0, 'u'}, 1368 | { "turn-password", 1, 0, 'p'}, 1369 | { "turn-fingerprint", 0, 0, 'F'}, 1370 | { "regular", 0, 0, 'R'}, 1371 | { "log-file", 1, 0, 'L'}, 1372 | }; 1373 | int c, opt_id; 1374 | pj_status_t status; 1375 | 1376 | icedemo.opt.comp_cnt = 1; 1377 | icedemo.opt.max_host = -1; 1378 | 1379 | while((c=pj_getopt_long(argc,argv, "c:n:s:t:u:p:H:L:hTFR", long_options, &opt_id))!=-1) { 1380 | switch (c) { 1381 | case 'c': 1382 | icedemo.opt.comp_cnt = atoi(pj_optarg); 1383 | if (icedemo.opt.comp_cnt < 1 || icedemo.opt.comp_cnt >= PJ_ICE_MAX_COMP) { 1384 | puts("Invalid component count value"); 1385 | return 1; 1386 | } 1387 | break; 1388 | case 'n': 1389 | icedemo.opt.ns = pj_str(pj_optarg); 1390 | break; 1391 | case 'H': 1392 | icedemo.opt.max_host = atoi(pj_optarg); 1393 | break; 1394 | case 'h': 1395 | icedemo_usage(); 1396 | return 0; 1397 | case 's': 1398 | icedemo.opt.stun_srv = pj_str(pj_optarg); 1399 | break; 1400 | case 't': 1401 | icedemo.opt.turn_srv = pj_str(pj_optarg); 1402 | break; 1403 | case 'T': 1404 | icedemo.opt.turn_tcp = PJ_TRUE; 1405 | break; 1406 | case 'u': 1407 | icedemo.opt.turn_username = pj_str(pj_optarg); 1408 | break; 1409 | case 'p': 1410 | icedemo.opt.turn_password = pj_str(pj_optarg); 1411 | break; 1412 | case 'F': 1413 | icedemo.opt.turn_fingerprint = PJ_TRUE; 1414 | break; 1415 | case 'R': 1416 | icedemo.opt.regular = PJ_TRUE; 1417 | break; 1418 | case 'L': 1419 | icedemo.opt.log_file = pj_optarg; 1420 | break; 1421 | default: 1422 | printf("Argument \"%s\" is not valid. Use -h to see help", 1423 | argv[pj_optind]); 1424 | return 1; 1425 | } 1426 | } 1427 | 1428 | status = icedemo_init(); 1429 | if (status != PJ_SUCCESS) 1430 | return 1; 1431 | 1432 | icedemo_console(); 1433 | 1434 | err_exit("Quitting..", PJ_SUCCESS); 1435 | return 0; 1436 | } 1437 | --------------------------------------------------------------------------------