Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ if (DEFINED ENV{IDF_PATH})
return()
endif()

project(peer)
project(peer C)

set(CMAKE_C_STANDARD 99)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS ON)

option(ENABLE_TESTS "Enable tests" OFF)
option(BUILD_SHARED_LIBS "Build shared libraries" ON)
Expand Down Expand Up @@ -65,6 +69,7 @@ add_subdirectory(src)
add_subdirectory(examples)

if(ENABLE_TESTS)
enable_testing()
add_subdirectory(tests)
endif()

Expand Down
2 changes: 1 addition & 1 deletion src/address.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ int addr_from_string(const char* buf, Address* addr) {
}

int addr_to_string(const Address* addr, char* buf, size_t len) {
memset(buf, 0, sizeof(len));
memset(buf, 0, len);
switch (addr->family) {
case AF_INET6:
return inet_ntop(AF_INET6, &addr->sin6.sin6_addr, buf, len) != NULL;
Expand Down
125 changes: 96 additions & 29 deletions src/agent.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,35 +23,57 @@ void agent_clear_candidates(Agent* agent) {
agent->candidate_pairs_num = 0;
}

int agent_create(Agent* agent) {
int ret;
if ((ret = udp_socket_open(&agent->udp_sockets[0], AF_INET, 0)) < 0) {
int agent_create(Agent* agent, uint16_t port_range_begin,
uint16_t port_range_end) {
uint32_t port;
int result = -1;

agent->udp_sockets[0].fd = -1;
agent->udp_sockets[1].fd = -1;
if (port_range_begin == 0 && port_range_end == 0) {
result = udp_socket_open(&agent->udp_sockets[0], AF_INET, 0);
} else if (port_range_begin > 0 && port_range_end >= port_range_begin) {
for (port = port_range_begin;
result < 0 && port <= port_range_end;
port++) {
result = udp_socket_open(&agent->udp_sockets[0], AF_INET, (int)port);
}
} else {
LOGE("Invalid UDP port range: %u-%u", port_range_begin, port_range_end);
}

if (result < 0) {
LOGE("Failed to create UDP socket.");
return ret;
} else {
LOGI("create IPv4 UDP socket: %d", agent->udp_sockets[0].fd);
}
LOGI("create IPv4 UDP socket: %d", agent->udp_sockets[0].fd);

#if CONFIG_IPV6
if ((ret = udp_socket_open(&agent->udp_sockets[1], AF_INET6, 0)) < 0) {
if (result == 0 &&
(result = udp_socket_open(&agent->udp_sockets[1], AF_INET6, 0)) < 0) {
LOGE("Failed to create IPv6 UDP socket.");
return ret;
udp_socket_close(&agent->udp_sockets[0]);
} else if (result == 0) {
LOGI("create IPv6 UDP socket: %d", agent->udp_sockets[1].fd);
}
LOGI("create IPv6 UDP socket: %d", agent->udp_sockets[1].fd);
#endif

agent_clear_candidates(agent);
memset(agent->remote_ufrag, 0, sizeof(agent->remote_ufrag));
memset(agent->remote_upwd, 0, sizeof(agent->remote_upwd));
return 0;
if (result == 0) {
agent_clear_candidates(agent);
memset(agent->remote_ufrag, 0, sizeof(agent->remote_ufrag));
memset(agent->remote_upwd, 0, sizeof(agent->remote_upwd));
}

return result;
}

void agent_destroy(Agent* agent) {
if (agent->udp_sockets[0].fd > 0) {
if (agent->udp_sockets[0].fd >= 0) {
udp_socket_close(&agent->udp_sockets[0]);
}

#if CONFIG_IPV6
if (agent->udp_sockets[1].fd > 0) {
if (agent->udp_sockets[1].fd >= 0) {
udp_socket_close(&agent->udp_sockets[1]);
}
#endif
Expand Down Expand Up @@ -365,13 +387,27 @@ int agent_send_binding_request(Agent* agent) {
void agent_process_stun_request(Agent* agent, StunMessage* stun_msg, Address* addr) {
StunMessage msg;
StunHeader* header;
char addr_string[ADDRSTRLEN];

switch (stun_msg->stunmethod) {
case STUN_METHOD_BINDING:
if (stun_msg_is_valid(stun_msg->buf, stun_msg->size, agent->local_upwd) == 0) {
header = (StunHeader*)stun_msg->buf;
memcpy(agent->transaction_id, header->transaction_id, sizeof(header->transaction_id));
agent_create_binding_response(agent, &msg, addr);
agent_socket_send(agent, addr, msg.buf, msg.size);
if (agent_socket_send(agent, addr, msg.buf, msg.size) >= 0) {
agent->binding_request_time = ports_get_epoch_time();
if (stun_msg->use_candidate && agent->nominated_pair != NULL &&
agent->nominated_pair->remote != NULL &&
agent->nominated_pair->state == ICE_CANDIDATE_STATE_INPROGRESS) {
memcpy(&agent->nominated_pair->remote->addr, addr, sizeof(Address));
agent->nominated_pair->remote->type = ICE_CANDIDATE_TYPE_PRFLX;
agent->nominated_pair->state = ICE_CANDIDATE_STATE_SUCCEEDED;
addr_to_string(addr, addr_string, sizeof(addr_string));
LOGI("Accepted peer-reflexive ICE source %s:%d", addr_string,
addr->port);
}
}
}
break;
default:
Expand Down Expand Up @@ -428,28 +464,59 @@ void agent_set_remote_description(Agent* agent, char* description) {
a=candidate:1 1 UDP 1 36.231.28.50 38143 typ srflx
*/
int i;
const char* ufrag_prefix = "a=ice-ufrag:";
const char* upwd_prefix = "a=ice-pwd:";
size_t prefix_length;
size_t value_length;

LOGD("Set remote description:\n%s", description);

char* line_start = description;
char* line_end = NULL;
agent->remote_ufrag[0] = '\0';
agent->remote_upwd[0] = '\0';

while ((line_end = strstr(line_start, "\r\n")) != NULL) {
if (strncmp(line_start, "a=ice-ufrag:", strlen("a=ice-ufrag:")) == 0) {
strncpy(agent->remote_ufrag, line_start + strlen("a=ice-ufrag:"), line_end - line_start - strlen("a=ice-ufrag:"));

} else if (strncmp(line_start, "a=ice-pwd:", strlen("a=ice-pwd:")) == 0) {
strncpy(agent->remote_upwd, line_start + strlen("a=ice-pwd:"), line_end - line_start - strlen("a=ice-pwd:"));

} else if (strncmp(line_start, "a=candidate:", strlen("a=candidate:")) == 0) {
if (ice_candidate_from_description(&agent->remote_candidates[agent->remote_candidates_count], line_start, line_end) == 0) {
for (i = 0; i < agent->remote_candidates_count; i++) {
if (strcmp(agent->remote_candidates[i].foundation, agent->remote_candidates[agent->remote_candidates_count].foundation) == 0) {
break;
}
prefix_length = strlen(ufrag_prefix);
if (strncmp(line_start, ufrag_prefix, prefix_length) == 0) {
value_length = (size_t)(line_end - line_start);
if (value_length >= prefix_length) {
value_length -= prefix_length;
} else {
value_length = 0;
}
if (value_length >= sizeof(agent->remote_ufrag)) {
value_length = sizeof(agent->remote_ufrag) - 1;
}
memcpy(agent->remote_ufrag, line_start + prefix_length, value_length);
agent->remote_ufrag[value_length] = '\0';

} else {
prefix_length = strlen(upwd_prefix);
if (strncmp(line_start, upwd_prefix, prefix_length) == 0) {
value_length = (size_t)(line_end - line_start);
if (value_length >= prefix_length) {
value_length -= prefix_length;
} else {
value_length = 0;
}
if (i == agent->remote_candidates_count) {
agent->remote_candidates_count++;
if (value_length >= sizeof(agent->remote_upwd)) {
value_length = sizeof(agent->remote_upwd) - 1;
}
memcpy(agent->remote_upwd, line_start + prefix_length, value_length);
agent->remote_upwd[value_length] = '\0';

} else if (strncmp(line_start, "a=candidate:", strlen("a=candidate:")) == 0 &&
agent->remote_candidates_count < AGENT_MAX_CANDIDATES) {
if (ice_candidate_from_description(&agent->remote_candidates[agent->remote_candidates_count], line_start, line_end) == 0) {
for (i = 0; i < agent->remote_candidates_count; i++) {
if (strcmp(agent->remote_candidates[i].foundation, agent->remote_candidates[agent->remote_candidates_count].foundation) == 0) {
break;
}
}
if (i == agent->remote_candidates_count) {
agent->remote_candidates_count++;
}
}
}
}
Expand Down
6 changes: 5 additions & 1 deletion src/agent.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ int agent_send(Agent* agent, const uint8_t* buf, int len);

int agent_recv(Agent* agent, uint8_t* buf, int len);

void agent_process_stun_request(Agent* agent, StunMessage* stun_msg,
Address* addr);

void agent_set_remote_description(Agent* agent, char* description);

int agent_select_candidate_pair(Agent* agent);
Expand All @@ -85,7 +88,8 @@ int agent_send_binding_request(Agent* agent);

void agent_clear_candidates(Agent* agent);

int agent_create(Agent* agent);
int agent_create(Agent* agent, uint16_t port_range_begin,
uint16_t port_range_end);

void agent_destroy(Agent* agent);

Expand Down
133 changes: 91 additions & 42 deletions src/ice.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/types.h>

#include "ice.h"
Expand Down Expand Up @@ -77,57 +78,105 @@ void ice_candidate_to_description(IceCandidate* candidate, char* description, in
typ_raddr);
}

int ice_candidate_from_description(IceCandidate* candidate, char* description, char* end) {
char* candidate_start = description;
uint32_t port;
char type[16];
char addrstring[ADDRSTRLEN];
static int ice_is_mdns_hostname(const char* hostname) {
size_t length;
int result = 0;

if (strncmp("a=", candidate_start, strlen("a=")) == 0) {
candidate_start += strlen("a=");
}
candidate_start += strlen("candidate:");

// a=candidate:448736988 1 udp 2122260223 172.17.0.1 49250 typ host generation 0 network-id 1 network-cost 50
// a=candidate:udpcandidate 1 udp 120 192.168.1.102 8000 typ host
if (sscanf(candidate_start, "%s %d %s %" PRIu32 " %s %" PRIu32 " typ %s",
candidate->foundation,
&candidate->component,
candidate->transport,
&candidate->priority,
addrstring,
&port,
type) != 7) {
LOGE("Failed to parse ICE candidate description");
return -1;
if (hostname != NULL) {
length = strlen(hostname);
if (length > 0 && hostname[length - 1] == '.') {
length--;
}
if (length > 6 && strncasecmp(hostname + length - 6, ".local", 6) == 0) {
result = 1;
}
}

if (strncmp(candidate->transport, "UDP", 3) != 0 && strncmp(candidate->transport, "udp", 3) != 0) {
LOGE("Only UDP transport is supported");
return -1;
return result;
}

int ice_candidate_from_description(IceCandidate* candidate, char* description,
char* end) {
char line[512];
char* candidate_start = line;
char type[16];
char addrstring[256];
uint32_t port = 0;
size_t line_length = 0;
int parsed = 0;
int result = -1;

if (candidate != NULL && description != NULL) {
if (end == NULL) {
end = description + strlen(description);
}
if (end >= description) {
line_length = (size_t)(end - description);
}
}

if (strncmp(type, "host", 4) == 0) {
candidate->type = ICE_CANDIDATE_TYPE_HOST;
} else if (strncmp(type, "srflx", 5) == 0) {
candidate->type = ICE_CANDIDATE_TYPE_SRFLX;
} else if (strncmp(type, "relay", 5) == 0) {
candidate->type = ICE_CANDIDATE_TYPE_RELAY;
} else {
LOGE("Unknown candidate type: %s", type);
return -1;
if (line_length > 0 && line_length < sizeof(line)) {
memcpy(line, description, line_length);
line[line_length] = '\0';
memset(candidate, 0, sizeof(*candidate));
memset(type, 0, sizeof(type));
memset(addrstring, 0, sizeof(addrstring));

if (strncmp(candidate_start, "a=", 2) == 0) {
candidate_start += 2;
}
if (strncmp(candidate_start, "candidate:", 10) == 0) {
candidate_start += 10;
parsed = sscanf(candidate_start,
"%32s %d %32s %" PRIu32 " %255s %" PRIu32
" typ %15s",
candidate->foundation,
&candidate->component,
candidate->transport,
&candidate->priority,
addrstring,
&port,
type);
}
}

addr_set_port(&candidate->addr, port);
if (parsed != 7) {
LOGE("Failed to parse ICE candidate description");
} else if (strcasecmp(candidate->transport, "UDP") != 0) {
LOGE("Only UDP transport is supported");
} else if (candidate->component < 1 || candidate->component > 256) {
LOGE("ICE candidate component is out of range");
} else if (port > UINT16_MAX) {
LOGE("ICE candidate port is out of range");
} else {
if (strcasecmp(type, "host") == 0) {
candidate->type = ICE_CANDIDATE_TYPE_HOST;
} else if (strcasecmp(type, "srflx") == 0) {
candidate->type = ICE_CANDIDATE_TYPE_SRFLX;
} else if (strcasecmp(type, "relay") == 0) {
candidate->type = ICE_CANDIDATE_TYPE_RELAY;
} else {
LOGE("Unknown candidate type: %s", type);
parsed = 0;
}

if (strstr(addrstring, "local") != NULL) {
if (mdns_resolve_addr(addrstring, &candidate->addr) == 0) {
LOGW("Failed to resolve mDNS address");
return -1;
if (parsed == 7) {
addr_set_family(&candidate->addr, AF_INET);
addr_set_port(&candidate->addr, (uint16_t)port);
if (candidate->type == ICE_CANDIDATE_TYPE_HOST &&
ice_is_mdns_hostname(addrstring)) {
if (mdns_resolve_addr(addrstring, &candidate->addr) == 0) {
LOGW("Failed to resolve mDNS address; retaining host candidate");
}
result = 0;
} else if (addr_from_string(addrstring, &candidate->addr) != 0) {
addr_set_port(&candidate->addr, (uint16_t)port);
result = 0;
} else {
LOGE("Failed to parse ICE candidate address");
}
}
} else if (addr_from_string(addrstring, &candidate->addr) == 0) {
return -1;
}

return 0;
return result;
}
Loading