Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
454a36d
Fix: compilation error when only IPv4 is enabled (IPv6 disabled).
sqqdfny Jul 17, 2026
a3f46c2
1.增加:LIBPEER_USE_SHARED_ENTROPY 宏,用于选择是否使用全局共享的熵源(MBEDTLS).
sqqdfny Jul 22, 2026
6755d1d
1.修复:局域网连接时,chorme 返回的 sdp 中的地址为 mDNS 主机名,此时 src/ice.c/ice_candidate_…
sqqdfny Jul 23, 2026
79a8501
1.修改: 日志打印宏 "#define LOG_LEVEL" -> "#define LIBPEER_LOG_LEVEL", 避免跟主项…
sqqdfny Jul 27, 2026
4a91f51
1.修复: 局域网中 PC 通过 WIFI 直连设备时,mDNS 协议会解析到PC的有线网口 IP 地址,导致连接建立失败的问题。
sqqdfny Jul 28, 2026
d1af1e8
1.修改: PEER_CONNECTION_CHECKING/PEER_CONNECTION_CONNECTED 两种状态增加超时处理。
sqqdfny Jul 29, 2026
ae06efd
1.新增: config.h 定义 SCTP_LOCAL_RWND(0x100000),sctp.c 的 SACK/INIT/INIT_A…
sqqdfny Jul 31, 2026
807af9a
1.修复编译警告
sqqdfny Aug 4, 2026
76159a6
feat: sctp 增加对 HEARTBEAT/ACK、SHUTDOWN/ACK/COMPLETE、ERROR、FORWARD_TSN …
sqqdfny Aug 4, 2026
886c7cd
refactor(utils.h):
sqqdfny Aug 4, 2026
3ddd746
fix(libpeer): ICE Agent 支持从 inbound STUN request 创建 peer-reflexive ca…
sqqdfny Aug 7, 2026
2714d8f
fix(webrtc): SCTP_DATA 接收路径 SACK 先于 onmessage 发送,消除 ping-pong 死循环
sqqdfny Aug 11, 2026
a715a04
fix(src/peer_connection.c): 创建 SDP 时,如果有多个 "m=" 行,浏览器不响应 STUN Binding…
sqqdfny Aug 11, 2026
ddf6c5f
fix(libpeer): DTLS-SRTP srtp_in 空指针崩溃防御 + 密钥派生失败感知
sqqdfny Aug 12, 2026
e2c281a
fix(include): 修复编译错误。
sqqdfny Aug 12, 2026
dba1473
fix(libpeer): H264 RTP 封包改为 per-access-unit 时间戳语义(RFC 6184)
sqqdfny Aug 14, 2026
783e51f
feat(libpeer): 新增 RTCP generic NACK 重传,密文级 ring 缓存(RFC 4585)
sqqdfny Aug 18, 2026
2ec3546
feat(libpeer): DTLS-SRTP 支持 AEAD_AES_128_GCM(RFC 7714),入站鉴权失败丢弃
sqqdfny Aug 21, 2026
a70f805
feat(libpeer): sendto 失败退避重试 + RTP 出包统计,FU-A 整帧放弃
sqqdfny Aug 27, 2026
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
2 changes: 1 addition & 1 deletion include/peer.h
2 changes: 1 addition & 1 deletion include/peer_connection.h
2 changes: 1 addition & 1 deletion include/peer_signaling.h
30 changes: 26 additions & 4 deletions src/address.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@

void addr_set_family(Address* addr, int family) {
switch (family) {
#if CONFIG_USE_IPV6
case AF_INET6:
addr->family = AF_INET6;
break;
#endif
case AF_INET:
default:
addr->family = AF_INET;
Expand All @@ -20,9 +22,11 @@ void addr_set_family(Address* addr, int family) {
void addr_set_port(Address* addr, uint16_t port) {
addr->port = port;
switch (addr->family) {
#if CONFIG_USE_IPV6
case AF_INET6:
addr->sin6.sin6_port = htons(port);
break;
#endif
case AF_INET:
default:
addr->sin.sin_port = htons(port);
Expand All @@ -34,18 +38,23 @@ int addr_from_string(const char* buf, Address* addr) {
if (inet_pton(AF_INET, buf, &(addr->sin.sin_addr)) == 1) {
addr_set_family(addr, AF_INET);
return 1;
} else if (inet_pton(AF_INET6, buf, &(addr->sin6.sin6_addr)) == 1) {
}
#if CONFIG_USE_IPV6
else if (inet_pton(AF_INET6, buf, &(addr->sin6.sin6_addr)) == 1) {
addr_set_family(addr, AF_INET6);
return 1;
}
#endif
return 0;
}

int addr_to_string(const Address* addr, char* buf, size_t len) {
memset(buf, 0, sizeof(len));
memset(buf, 0, len);
switch (addr->family) {
#if CONFIG_USE_IPV6
case AF_INET6:
return inet_ntop(AF_INET6, &addr->sin6.sin6_addr, buf, len) != NULL;
#endif
case AF_INET:
default:
return inet_ntop(AF_INET, &addr->sin.sin_addr, buf, len) != NULL;
Expand All @@ -54,6 +63,19 @@ int addr_to_string(const Address* addr, char* buf, size_t len) {
}

int addr_equal(const Address* a, const Address* b) {
// TODO
return 1;
if (!a || !b) return 0;
if (a->family != b->family) return 0;
if (a->port != b->port) return 0;

switch (a->family) {
case AF_INET:
return a->sin.sin_addr.s_addr == b->sin.sin_addr.s_addr;
#if CONFIG_USE_IPV6
case AF_INET6:
return memcmp(&a->sin6.sin6_addr, &b->sin6.sin6_addr,
sizeof(struct in6_addr)) == 0;
#endif
default:
return 0;
}
}
6 changes: 6 additions & 0 deletions src/address.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,18 @@
#endif
#include <stdint.h>

#if CONFIG_USE_IPV6
#define ADDRSTRLEN INET6_ADDRSTRLEN
#else
#define ADDRSTRLEN INET_ADDRSTRLEN
#endif

typedef struct Address {
uint8_t family;
struct sockaddr_in sin;
#if CONFIG_USE_IPV6
struct sockaddr_in6 sin6;
#endif
uint16_t port;
} Address;

Expand Down
147 changes: 118 additions & 29 deletions src/agent.c
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,70 @@ void agent_process_stun_request(Agent* agent, StunMessage* stun_msg, Address* ad
agent_create_binding_response(agent, &msg, addr);
agent_socket_send(agent, addr, msg.buf, msg.size);
agent->binding_request_time = ports_get_epoch_time();

/**
* When there are no candidate pairs (e.g., the browser's mDNS hostname cannot be resolved),
* create a candidate pair from the UDP source address of the STUN request.
* Mark it as FROZEN; later, the standard ICE procedure will select the pair,
* send USE‑CANDIDATE, and establish connectivity.
*/
if (agent->candidate_pairs_num == 0 && agent->local_candidates_count > 0) {
memcpy(&agent->remote_candidates[0].addr, addr, sizeof(Address));
agent->remote_candidates[0].type = ICE_CANDIDATE_TYPE_HOST;
agent->remote_candidates[0].addr.port = addr->port;
agent->remote_candidates_count = 1;
agent->candidate_pairs[0].local = &agent->local_candidates[0];
agent->candidate_pairs[0].remote = &agent->remote_candidates[0];
agent->candidate_pairs[0].state = ICE_CANDIDATE_STATE_FROZEN;
agent->candidate_pairs[0].priority = agent->local_candidates[0].priority;
agent->candidate_pairs[0].conncheck = 0;
agent->candidate_pairs_num = 1;
agent->nominated_pair = &agent->candidate_pairs[0];
} else {
int found = 0;

/* Phase A: match source address to existing remote candidates */
for (int i = 0; i < agent->candidate_pairs_num; i++) {
if (addr_equal(&agent->candidate_pairs[i].remote->addr, addr)) {
agent->candidate_pairs[i].state = ICE_CANDIDATE_STATE_SUCCEEDED;
agent->nominated_pair = &agent->candidate_pairs[i];
agent->selected_pair = &agent->candidate_pairs[i];
LOGD("ICE pair %d SUCCEEDED via inbound STUN request", i);
found = 1;
break;
}
}

/* Phase B: source differs from SDP — create peer-reflexive candidate */
if (!found
&& agent->remote_candidates_count < AGENT_MAX_CANDIDATES
&& agent->local_candidates_count > 0) {
IceCandidate *prflx =
&agent->remote_candidates[agent->remote_candidates_count];
ice_candidate_create(prflx, agent->remote_candidates_count,
ICE_CANDIDATE_TYPE_PRFLX, addr);
agent->remote_candidates_count++;

for (int j = 0; j < agent->local_candidates_count; j++) {
if (agent->local_candidates[j].addr.family == addr->family
&& agent->candidate_pairs_num < AGENT_MAX_CANDIDATE_PAIRS) {
int idx = agent->candidate_pairs_num;
agent->candidate_pairs[idx].local = &agent->local_candidates[j];
agent->candidate_pairs[idx].remote = prflx;
agent->candidate_pairs[idx].priority =
agent->local_candidates[j].priority + prflx->priority;
agent->candidate_pairs[idx].state = ICE_CANDIDATE_STATE_SUCCEEDED;
agent->candidate_pairs[idx].conncheck = 0;
agent->nominated_pair = &agent->candidate_pairs[idx];
agent->selected_pair = &agent->candidate_pairs[idx];
agent->candidate_pairs_num++;
LOGI("ICE: created PRFLX candidate pair from inbound STUN");
found = 1;
break;
}
}
}
}
}
break;
default:
Expand Down Expand Up @@ -395,36 +459,46 @@ int agent_recv(Agent* agent, uint8_t* buf, int len) {
}

void agent_set_remote_description(Agent* agent, char* description) {
/*
a=ice-ufrag:Iexb
a=ice-pwd:IexbSoY7JulyMbjKwISsG9
a=candidate:1 1 UDP 1 36.231.28.50 38143 typ srflx
*/
int i;

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

char* line_start = description;
char* line_end = NULL;

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;
}
}
if (i == agent->remote_candidates_count) {
agent->remote_candidates_count++;
/*
a=ice-ufrag:Iexb
a=ice-pwd:IexbSoY7JulyMbjKwISsG9
a=candidate:1 1 UDP 1 36.231.28.50 38143 typ srflx
*/
int i;

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:", sizeof("a=ice-ufrag:") - 1) == 0) {
line_start += sizeof("a=ice-ufrag:") - 1;
size_t len = line_end - line_start;
len = len >= sizeof(agent->remote_ufrag) ? (sizeof(agent->remote_ufrag) - 1) : len;
strncpy(agent->remote_ufrag, line_start, len);
agent->remote_ufrag[len] = '\0';
} else
if (strncmp(line_start, "a=ice-pwd:", sizeof("a=ice-pwd:") - 1) == 0) {
line_start += sizeof("a=ice-pwd:") - 1;
size_t len = line_end - line_start;
len = len >= sizeof(agent->remote_upwd) ? (sizeof(agent->remote_upwd) - 1) : len;
strncpy(agent->remote_upwd, line_start, len);
agent->remote_upwd[len] = '\0';
} else
if (strncmp(line_start, "a=candidate:", sizeof("a=candidate:") - 1) == 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;
}
}
if (i == agent->remote_candidates_count) {
agent->remote_candidates_count++;
}
}
}
}

line_start = line_end + 2;
Expand Down Expand Up @@ -456,6 +530,21 @@ int agent_connectivity_check(Agent* agent) {
uint8_t buf[1400];
StunMessage msg;

if (agent->nominated_pair == NULL) {
/**
* No candidate pairs yet (all mDNS attempts failed),
* only receive and process STUN requests actively sent by the browser.
*/
agent_recv(agent, buf, sizeof(buf));
return -1;
}

/* Handle pair already marked SUCCEEDED by agent_process_stun_request */
if (agent->nominated_pair->state == ICE_CANDIDATE_STATE_SUCCEEDED) {
agent->selected_pair = agent->nominated_pair;
return 0;
}

if (agent->nominated_pair->state != ICE_CANDIDATE_STATE_INPROGRESS) {
LOGI("nominated pair is not in progress");
return -1;
Expand Down
15 changes: 14 additions & 1 deletion src/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
#define SCTP_MTU (1200)
#define CONFIG_MTU (1300)

// Advertised receiver window. Incoming DATA is dispatched to the user callback
// immediately without buffering, so the window is always fully available.
#define SCTP_LOCAL_RWND (0x100000)

#ifndef CONFIG_USE_LWIP
#define CONFIG_USE_LWIP 0
#endif
Expand Down Expand Up @@ -49,6 +53,15 @@
#define CONFIG_TLS_READ_TIMEOUT 3000
#endif

#ifndef CONFIG_CHECKING_TIMEOUT
// 默认的 PEER_CONNECTION_CHECKING 状态超时为 15S
#define CONFIG_CHECKING_TIMEOUT 15000
#endif

#ifndef CONFIG_DTLS_HANDSHAKE_TIMEOUT
#define CONFIG_DTLS_HANDSHAKE_TIMEOUT 30000
#endif

#ifndef CONFIG_KEEPALIVE_TIMEOUT
#define CONFIG_KEEPALIVE_TIMEOUT 10000
#endif
Expand All @@ -65,7 +78,7 @@
// empty will use first active interface
#define CONFIG_IFACE_PREFIX ""

// #define LOG_LEVEL LEVEL_DEBUG
// #define LIBPEER_LOG_LEVEL LIBPEER_LOG_LEVEL_DEBUG
#ifndef LOG_REDIRECT
#define LOG_REDIRECT 0
#endif
Expand Down
Loading