/** Malformed responses test */ static void bad_responses (void) { struct sockaddr_storage addr; socklen_t addrlen = sizeof (addr); ssize_t val, len; uint8_t buf[STUN_MAX_MESSAGE_SIZE]; uint8_t req[STUN_MAX_MESSAGE_SIZE]; size_t req_len; StunAgent agent; StunMessage msg; StunMessage req_msg; int servfd, fd; uint16_t known_attributes[] = { STUN_ATTRIBUTE_MAPPED_ADDRESS, STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS, STUN_ATTRIBUTE_PRIORITY, STUN_ATTRIBUTE_USERNAME, STUN_ATTRIBUTE_MESSAGE_INTEGRITY, STUN_ATTRIBUTE_ERROR_CODE, 0}; stun_agent_init (&agent, known_attributes, STUN_COMPATIBILITY_RFC5389, 0); /* Allocate a local UDP port */ servfd = listen_dgram (); assert (servfd != -1); val = getsockname (servfd, (struct sockaddr *)&addr, &addrlen); assert (val == 0); fd = socket (addr.ss_family, SOCK_DGRAM, 0); assert (fd != -1); req_len = stun_usage_bind_create (&agent, &req_msg, req, sizeof(req)); assert (req_len > 0); val = sendto (fd, req, req_len, MSG_DONTWAIT | MSG_NOSIGNAL, (struct sockaddr *)&addr, addrlen); assert (val >= 0); /* Send to/receive from our client instance only */ val = getsockname (fd, (struct sockaddr *)&addr, &addrlen); assert (val == 0); /* Send request instead of response */ val = getsockname (servfd, (struct sockaddr *)&addr, &addrlen); assert (val == 0); len = recvfrom (servfd, buf, 1000, MSG_DONTWAIT, NULL, 0); assert (len >= 20); assert (stun_agent_validate (&agent, &msg, buf, len, NULL, NULL) == STUN_VALIDATION_SUCCESS); val = stun_usage_bind_process (&msg, (struct sockaddr *) &addr, &addrlen, (struct sockaddr *) &addr, &addrlen); assert (val == STUN_USAGE_BIND_RETURN_INVALID); /* Send response with wrong request type */ buf[0] |= 0x03; buf[0] ^= 0x02; /* Send error response without ERROR-CODE */ buf[1] |= 0x10; val = stun_usage_bind_process (&msg, (struct sockaddr *) &addr, &addrlen, (struct sockaddr *) &addr, &addrlen); assert (val == STUN_USAGE_BIND_RETURN_INVALID); close (fd); close (servfd); }
/** Various responses test */ static void responses (void) { struct sockaddr_storage addr; socklen_t addrlen = sizeof (addr); ssize_t val; size_t len; int servfd, fd; uint8_t buf[STUN_MAX_MESSAGE_SIZE]; uint8_t req[STUN_MAX_MESSAGE_SIZE]; size_t req_len; StunAgent agent; StunMessage msg; StunMessage req_msg; uint16_t known_attributes[] = { STUN_ATTRIBUTE_MAPPED_ADDRESS, STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS, STUN_ATTRIBUTE_PRIORITY, STUN_ATTRIBUTE_USERNAME, STUN_ATTRIBUTE_MESSAGE_INTEGRITY, STUN_ATTRIBUTE_ERROR_CODE, 0}; stun_agent_init (&agent, known_attributes, STUN_COMPATIBILITY_RFC5389, 0); /* Allocate a local UDP port for server */ servfd = listen_dgram (); assert (servfd != -1); val = getsockname (servfd, (struct sockaddr *)&addr, &addrlen); assert (val == 0); /* Allocate a client socket and connect to server */ fd = socket (addr.ss_family, SOCK_DGRAM, 0); assert (fd != -1); /* Send error response */ req_len = stun_usage_bind_create (&agent, &req_msg, req, sizeof(req)); assert (req_len > 0); val = getsockname (servfd, (struct sockaddr *)&addr, &addrlen); assert (val == 0); val = sendto (fd, req, req_len, MSG_DONTWAIT | MSG_NOSIGNAL, (struct sockaddr *)&addr, addrlen); assert (val >= 0); val = recvfrom (servfd, buf, 1000, MSG_DONTWAIT, NULL, 0); assert (val >= 0); assert (stun_agent_validate (&agent, &msg, buf, val, NULL, NULL) == STUN_VALIDATION_SUCCESS); stun_agent_init_error (&agent, &msg, buf, sizeof (buf), &msg, STUN_ERROR_SERVER_ERROR); len = stun_agent_finish_message (&agent, &msg, NULL, 0); assert (len > 0); val = getsockname (servfd, (struct sockaddr *)&addr, &addrlen); assert (val == 0); val = stun_usage_bind_process (&msg, (struct sockaddr *) &addr, &addrlen, (struct sockaddr *) &addr, &addrlen); assert (val == STUN_USAGE_BIND_RETURN_ERROR); /* Send response with a no mapped address at all */ req_len = stun_usage_bind_create (&agent, &req_msg, req, sizeof(req)); assert (req_len > 0); val = getsockname (servfd, (struct sockaddr *)&addr, &addrlen); assert (val == 0); val = sendto (fd, req, req_len, MSG_DONTWAIT | MSG_NOSIGNAL, (struct sockaddr *)&addr, addrlen); assert (val >= 0); val = recvfrom (servfd, buf, 1000, MSG_DONTWAIT, NULL, 0); assert (val >= 0); assert (stun_agent_validate (&agent, &msg, buf, val, NULL, NULL) == STUN_VALIDATION_SUCCESS); stun_agent_init_response (&agent, &msg, buf, sizeof (buf), &msg); len = stun_agent_finish_message (&agent, &msg, NULL, 0); assert (len > 0); assert (stun_agent_validate (&agent, &msg, buf, len, NULL, NULL) == STUN_VALIDATION_SUCCESS); val = getsockname (servfd, (struct sockaddr *)&addr, &addrlen); assert (val == 0); val = stun_usage_bind_process (&msg, (struct sockaddr *) &addr, &addrlen, (struct sockaddr *) &addr, &addrlen); assert (val == STUN_USAGE_BIND_RETURN_ERROR); /* Send old-style response */ req_len = stun_usage_bind_create (&agent, &req_msg, req, sizeof(req)); assert (req_len > 0); val = getsockname (servfd, (struct sockaddr *)&addr, &addrlen); assert (val == 0); val = sendto (fd, req, req_len, MSG_DONTWAIT | MSG_NOSIGNAL, (struct sockaddr *)&addr, addrlen); assert (val >= 0); val = recvfrom (servfd, buf, 1000, MSG_DONTWAIT, NULL, 0); assert (val >= 0); assert (stun_agent_validate (&agent, &msg, buf, val, NULL, NULL) == STUN_VALIDATION_SUCCESS); stun_agent_init_response (&agent, &msg, buf, sizeof (buf), &msg); assert (stun_message_append_addr (&msg, STUN_ATTRIBUTE_MAPPED_ADDRESS, (struct sockaddr *) &addr, addrlen) == STUN_MESSAGE_RETURN_SUCCESS); len = stun_agent_finish_message (&agent, &msg, NULL, 0); assert (len > 0); assert (stun_agent_validate (&agent, &msg, buf, len, NULL, NULL) == STUN_VALIDATION_SUCCESS); val = getsockname (servfd, (struct sockaddr *)&addr, &addrlen); assert (val == 0); val = stun_usage_bind_process (&msg, (struct sockaddr *) &addr, &addrlen, (struct sockaddr *) &addr, &addrlen); assert (val == STUN_USAGE_BIND_RETURN_SUCCESS); /* End */ close (servfd); val = close (fd); assert (val == 0); }
/* * Timer callback that handles scheduling new candidate discovery * processes (paced by the Ta timer), and handles running of the * existing discovery processes. * * This function is designed for the g_timeout_add() interface. * * @return will return FALSE when no more pending timers. */ static gboolean priv_discovery_tick_unlocked (gpointer pointer) { CandidateDiscovery *cand; NiceAgent *agent = pointer; GSList *i; int not_done = 0; /* note: track whether to continue timer */ size_t buffer_len = 0; { static int tick_counter = 0; if (tick_counter++ % 50 == 0) nice_debug ("Agent %p : discovery tick #%d with list %p (1)", agent, tick_counter, agent->discovery_list); } for (i = agent->discovery_list; i ; i = i->next) { cand = i->data; if (cand->pending != TRUE) { cand->pending = TRUE; if (agent->discovery_unsched_items) --agent->discovery_unsched_items; if (nice_debug_is_enabled ()) { gchar tmpbuf[INET6_ADDRSTRLEN]; nice_address_to_string (&cand->server, tmpbuf); nice_debug ("Agent %p : discovery - scheduling cand type %u addr %s.", agent, cand->type, tmpbuf); } if (nice_address_is_valid (&cand->server) && (cand->type == NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE || cand->type == NICE_CANDIDATE_TYPE_RELAYED)) { if (cand->component->state == NICE_COMPONENT_STATE_DISCONNECTED || cand->component->state == NICE_COMPONENT_STATE_FAILED) agent_signal_component_state_change (agent, cand->stream->id, cand->component->id, NICE_COMPONENT_STATE_GATHERING); if (cand->type == NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE) { buffer_len = stun_usage_bind_create (&cand->stun_agent, &cand->stun_message, cand->stun_buffer, sizeof(cand->stun_buffer)); } else if (cand->type == NICE_CANDIDATE_TYPE_RELAYED) { uint8_t *username = (uint8_t *)cand->turn->username; gsize username_len = strlen (cand->turn->username); uint8_t *password = (uint8_t *)cand->turn->password; gsize password_len = strlen (cand->turn->password); StunUsageTurnCompatibility turn_compat = agent_to_turn_compatibility (agent); if (turn_compat == STUN_USAGE_TURN_COMPATIBILITY_MSN || turn_compat == STUN_USAGE_TURN_COMPATIBILITY_OC2007) { username = g_base64_decode ((gchar *)username, &username_len); password = g_base64_decode ((gchar *)password, &password_len); } buffer_len = stun_usage_turn_create (&cand->stun_agent, &cand->stun_message, cand->stun_buffer, sizeof(cand->stun_buffer), cand->stun_resp_msg.buffer == NULL ? NULL : &cand->stun_resp_msg, STUN_USAGE_TURN_REQUEST_PORT_NORMAL, -1, -1, username, username_len, password, password_len, turn_compat); if (turn_compat == STUN_USAGE_TURN_COMPATIBILITY_MSN || turn_compat == STUN_USAGE_TURN_COMPATIBILITY_OC2007) { g_free (username); g_free (password); } } if (buffer_len > 0) { if (nice_socket_is_reliable (cand->nicesock)) { stun_timer_start_reliable (&cand->timer, agent->stun_reliable_timeout); } else { stun_timer_start (&cand->timer, agent->stun_initial_timeout, agent->stun_max_retransmissions); } /* send the conncheck */ agent_socket_send (cand->nicesock, &cand->server, buffer_len, (gchar *)cand->stun_buffer); /* case: success, start waiting for the result */ g_get_current_time (&cand->next_tick); } else { /* case: error in starting discovery, start the next discovery */ cand->done = TRUE; cand->stun_message.buffer = NULL; cand->stun_message.buffer_len = 0; continue; } } else /* allocate relayed candidates */ g_assert_not_reached (); ++not_done; /* note: new discovery scheduled */ } if (cand->done != TRUE) { GTimeVal now; g_get_current_time (&now); if (cand->stun_message.buffer == NULL) { nice_debug ("Agent %p : STUN discovery was cancelled, marking discovery done.", agent); cand->done = TRUE; } else if (priv_timer_expired (&cand->next_tick, &now)) { switch (stun_timer_refresh (&cand->timer)) { case STUN_USAGE_TIMER_RETURN_TIMEOUT: { /* Time out */ /* case: error, abort processing */ StunTransactionId id; stun_message_id (&cand->stun_message, id); stun_agent_forget_transaction (&cand->stun_agent, id); cand->done = TRUE; cand->stun_message.buffer = NULL; cand->stun_message.buffer_len = 0; nice_debug ("Agent %p : bind discovery timed out, aborting discovery item.", agent); break; } case STUN_USAGE_TIMER_RETURN_RETRANSMIT: { /* case: not ready complete, so schedule next timeout */ unsigned int timeout = stun_timer_remainder (&cand->timer); stun_debug ("STUN transaction retransmitted (timeout %dms).", timeout); /* retransmit */ agent_socket_send (cand->nicesock, &cand->server, stun_message_length (&cand->stun_message), (gchar *)cand->stun_buffer); /* note: convert from milli to microseconds for g_time_val_add() */ cand->next_tick = now; g_time_val_add (&cand->next_tick, timeout * 1000); ++not_done; /* note: retry later */ break; } case STUN_USAGE_TIMER_RETURN_SUCCESS: { unsigned int timeout = stun_timer_remainder (&cand->timer); cand->next_tick = now; g_time_val_add (&cand->next_tick, timeout * 1000); ++not_done; /* note: retry later */ break; } default: /* Nothing to do. */ break; } } else { ++not_done; /* note: discovery not expired yet */ } } } if (not_done == 0) { nice_debug ("Agent %p : Candidate gathering FINISHED, stopping discovery timer.", agent); discovery_free (agent); agent_gathering_done (agent); /* note: no pending timers, return FALSE to stop timer */ return FALSE; } return TRUE; }