Skip to content

Instantly share code, notes, and snippets.

@shadeslayer
Created February 11, 2012 19:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save shadeslayer/1803805 to your computer and use it in GitHub Desktop.
Save shadeslayer/1803805 to your computer and use it in GitHub Desktop.
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#ifdef __sun
#define _XPG4_2 1
#endif
#ifndef _WIN32
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <unistd.h>
#include <errno.h>
#include <limits.h>
#include <glib.h>
#include "stunagent.h"
#include "agent-priv.h"
#include "agent.h"
#ifndef SOL_IP
# define SOL_IP IPPROTO_IP
#endif
#ifndef SOL_IPV6
# define SOL_IPV6 IPPROTO_IPV6
#endif
#ifndef IPV6_RECVPKTINFO
# define IPV6_RECVPKTINFO IPV6_PKTINFO
#endif
/** Default port for STUN binding discovery */
#define IPPORT_STUN 3456
static const uint16_t known_attributes[] = {
0
};
NiceCandidate remote_candidate[3];
static int m_index = 1;
static GMainLoop *loop = NULL;
GMutex candidate_mutex, remote_list_mutex;
GCond candidate_gathering_done, add_new_remote;
bool all_remote_addr_added = FALSE;
/*
* Creates a listening socket
*/
static int listen_socket (unsigned int port)
{
int yes = 1;
int fd = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (fd == -1)
{
perror ("Error opening IP port");
return -1;
}
if (fd < 3)
goto error;
struct sockaddr_in addr;
memset (&addr, 0, sizeof (addr));
addr.sin_family = AF_INET;
inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr);
addr.sin_port = htons(port);
setsockopt (fd, SOL_IP, IP_RECVERR, &yes, sizeof (yes));
if (bind (fd, (struct sockaddr_in *)&addr, sizeof (struct sockaddr_in)))
{
perror ("Error opening IP port");
goto error;
}
return fd;
error:
close (fd);
return -1;
}
static int dgram_process (int sock, StunAgent *oldagent, StunAgent *newagent)
{
struct sockaddr_storage addr;
socklen_t addr_len;
uint8_t buf[STUN_MAX_MESSAGE_SIZE];
size_t buf_len = 0;
size_t len = 0;
StunMessage request;
StunMessage response;
StunValidationStatus validation;
StunAgent *agent = NULL;
addr_len = sizeof (struct sockaddr_in);
g_debug("listening for STUN requests");
len = recvfrom (sock, buf, sizeof(buf), 0,
(struct sockaddr *)&addr, &addr_len);
if (buf_len == (size_t)-1)
return -1;
validation = stun_agent_validate (newagent, &request, buf, len, NULL, 0);
if (validation == STUN_VALIDATION_SUCCESS) {
agent = newagent;
}
else {
validation = stun_agent_validate (oldagent, &request, buf, len, NULL, 0);
agent = oldagent;
}
/* Unknown attributes */
if (validation == STUN_VALIDATION_UNKNOWN_REQUEST_ATTRIBUTE)
{
buf_len = stun_agent_build_unknown_attributes_error (agent, &response, buf,
sizeof (buf), &request);
goto send_buf;
}
/* Mal-formatted packets */
if (validation != STUN_VALIDATION_SUCCESS ||
stun_message_get_class (&request) != STUN_REQUEST) {
return -1;
}
switch (stun_message_get_method (&request))
{
case STUN_BINDING:
stun_agent_init_response (agent, &response, buf, sizeof (buf), &request);
if (stun_message_has_cookie (&request))
stun_message_append_xor_addr (&response,
STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS,
(struct sockaddr *)&addr, addr_len);
else
stun_message_append_addr (&response, STUN_ATTRIBUTE_MAPPED_ADDRESS,
(struct sockaddr *)&addr, addr_len);
break;
default:
if (!stun_agent_init_error (agent, &response, buf, sizeof (buf),
&request, STUN_ERROR_BAD_REQUEST))
return -1;
}
buf_len = stun_agent_finish_message (agent, &response, NULL, 0);
send_buf:
while(!all_remote_addr_added) {
g_debug("Sending STUN response");
g_mutex_lock(&candidate_mutex);
g_cond_wait(&candidate_gathering_done, &candidate_mutex);
len = sendto (sock, buf, buf_len, 0,
(struct sockaddr *)&addr, addr_len);
g_mutex_unlock(&candidate_mutex);
return (len < buf_len) ? -1 : 0;
}
return -1;
}
static gpointer stun_thread_func (const gpointer user_data)
{
StunAgent oldagent;
StunAgent newagent;
int sock = listen_socket (IPPORT_STUN);
if (sock == -1)
return GINT_TO_POINTER(-1);
stun_agent_init (&oldagent, known_attributes,
STUN_COMPATIBILITY_RFC3489, 0);
stun_agent_init (&newagent, known_attributes,
STUN_COMPATIBILITY_RFC5389, STUN_AGENT_USAGE_USE_FINGERPRINT);
for (;;)
dgram_process (sock, &oldagent, &newagent);
return NULL;
}
static NiceCandidate* nice_candidate_from_nice_address(NiceAddress* addr, guint stream_id, NiceAgent* agent)
{
NiceCandidate *cand = nice_candidate_new(NICE_CANDIDATE_TYPE_HOST);
cand->addr = *addr;
cand->stream_id = stream_id;
gchar *ufrag = NULL, *password = NULL;
nice_agent_get_local_credentials(agent, stream_id, &ufrag, &password);
cand->username = ufrag;
cand->password = password;
g_free(ufrag);
g_free(password);
return cand;
}
static void cb_set_new_remote_address(NiceAgent *agent, guint stream_id,guint component_id, guint state, gpointer user_data)
{
g_debug("test-new-dribble-mode: %s", G_STRFUNC);
if(( state == NICE_COMPONENT_STATE_READY || state == NICE_COMPONENT_STATE_FAILED || state == NICE_COMPONENT_STATE_CONNECTED ) && m_index <= 2) {
GSList* remote_list;
g_debug("All previous remote candidates passed/failed connchecks, adding another remote candidate");
remote_list = nice_agent_get_remote_candidates(agent, stream_id, component_id);
remote_list = g_slist_prepend(remote_list, nice_candidate_from_nice_address(user_data, stream_id, agent));
g_assert(nice_agent_set_remote_candidates(agent, stream_id, component_id, remote_list));
m_index++;
g_debug("Index is : %d", m_index);
}
if(m_index >= 3) {
g_mutex_lock(&candidate_mutex);
all_remote_addr_added = TRUE;
g_cond_signal(&candidate_gathering_done);
g_main_loop_quit(loop);
g_mutex_unlock(&candidate_mutex);
}
}
gint
main (void)
{
#if !GLIB_CHECK_VERSION(2,31,0)
g_thread_init(NULL);
#endif
NiceAgent *agent;
NiceAddress addr_local;
guint stream_id;
GThread *stun_thread;
GSList* remote = NULL;
NiceAddress addr_remote[3];
int i = 0;
g_debug("dribble mode test initiated");
#if !GLIB_CHECK_VERSION(2,31,8)
g_thread_init(NULL);
#endif
nice_address_init (&addr_local);
for(i = 0; i < 3; i++) {
nice_address_init (&addr_remote[i]);
}
g_type_init ();
loop = g_main_loop_new(NULL, FALSE);
g_mutex_init(&candidate_mutex);
g_cond_init(&candidate_gathering_done);
agent = nice_agent_new (g_main_loop_get_context (loop), NICE_COMPATIBILITY_RFC5245);
g_assert (nice_address_set_from_string (&addr_local, "127.0.0.1"));
g_assert (agent->local_addresses == NULL);
/* add one local address */
nice_agent_add_local_address (agent, &addr_local);
g_assert (agent->local_addresses != NULL);
g_assert (g_slist_length (agent->local_addresses) == 1);
g_assert (nice_address_equal (agent->local_addresses->data, &addr_local));
/* add a stream */
stream_id = nice_agent_add_stream (agent, 1);
g_debug("Listening for signal");
g_signal_connect (G_OBJECT (agent), "component-state-changed",
G_CALLBACK (cb_set_new_remote_address), &addr_remote[m_index]);
// Set stun server and port
g_object_set (G_OBJECT (agent), "stun-server", "127.0.0.1", NULL);
g_object_set (G_OBJECT (agent), "stun-server-port", IPPORT_STUN, NULL);
g_assert (nice_address_set_from_string (&addr_remote[0], "127.0.0.1"));
g_assert (nice_address_set_from_string (&addr_remote[1], "172.1.0.1"));
g_assert (nice_address_set_from_string (&addr_remote[2], "8.8.8.8"));
for( i = 0; i < 3; i++) {
nice_address_set_port (&addr_remote[i], 2345);
g_assert (nice_address_is_valid(&addr_remote[i]));
}
// Start STUN Daemon in another thread
stun_thread = g_thread_new("listen for STUN requests", stun_thread_func, NULL);
nice_agent_gather_candidates (agent, stream_id);
remote = g_slist_prepend(remote, nice_candidate_from_nice_address(&addr_remote[0], stream_id, agent));
nice_agent_set_remote_candidates(agent, stream_id, NICE_COMPONENT_TYPE_RTP, remote);
g_main_loop_run(loop);
// Clean up
g_slist_free(remote);
g_thread_unref(stun_thread);
g_object_unref(agent);
return 0;
}
#else
int main (int argc, char **argv) {
return 0;
}
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment