#include <string.h>
#include <stdlib.h>
#include "event2/buffer.h"
#include "udp_punch.h"
#include "udt_c.h"
#include "utils.h"
#include "session.h"
#ifdef HAVE_VS
#include "../include/video_share.h"
#endif
#define PUNCH_SYN_SENT 1
#define PUNCH_SYN_RCVD 2
#define PUNCH_SYNACK_RCVD 3
#define PUNCH_UDT_CONNECTED 4
#define PUNCH_OPT_ACK 0x00000001
#ifndef PUNCH_PREPARING_TTL
#define PUNCH_PREPARING_TTL 5
#endif
/* private IP range -- RFC1918
* 10.0.0.0/8
* 172.16.0.0/12
* 192.168.0.0/16
*/
#define PRIVATE_NIPV4(__nip) (((__nip)&0x000000FF) == 0x0000000A || \
((__nip)&0x0000F0FF) == 0x000010AC || \
((__nip)&0x0000FFFF) == 0x0000A8C0)
#define UDT_SNDBUF_SIZE (256*1024)
#define UDT_RCVBUF_SIZE (16*1024)
#define UDT_MAX_SEG_SIZE 1420
#ifndef UDT_SNDLOWAT
#define UDT_SNDLOWAT 1
#endif
typedef struct punch_inner_data_s
{
struct event* evstun; /* event for UDP */
struct sockaddr_in sin_peer; /* peer address */
int status;
int punch_prepared;
int punch_try_cnt;
int fd_udt;
unsigned int udt_sndlowat;
struct evbuffer* evbuf_out;
} punch_inner_data_t;
static const char* _sockaddr_ntoa(const struct sockaddr_in* sin)
{
static char s[32];
snprintf(s, sizeof(s), "%s:%d", inet_ntoa(sin->sin_addr), ntohs(sin->sin_port));
return s;
}
static int stun_udtsyn(void* data, int size)
{
/* TODO: udt v4 */
static const char* udt4_syn =
"\x80\x00\x00\x00"
"\x00\x00\x00\x00"
"\x00\x00\x00\x00"
"\x00\x00\x00\x00"
"\x00\x00\x00\x04";
if ( size >= 20 && !memcmp(data, udt4_syn, 20) )
{
return 1;
}
return 0;
}
static int udt_get_state(int fd_udt)
{
int state = BROKEN, len;
len = sizeof(state);
if( udt_getsockopt(fd_udt, 0, UDT_STATE, &state, &len) )
{
return BROKEN;
}
return state;
}
static int udt_get_canrecv(int fd_udt)
{
int canrecv = -1, len;
len = sizeof(canrecv);
if( udt_getsockopt(fd_udt, 0, UDT_RCVDATA, &canrecv, &len) )
{
return -1;
}
return canrecv;
}
static int udt_get_cansend(int fd_udt)
{
int sendbufused, len;
len = sizeof(sendbufused);
if( udt_getsockopt(fd_udt, 0, UDT_SNDDATA, &sendbufused, &len) )
{
return -1;
}
return UDT_SNDBUF_SIZE - sendbufused*UDT_MAX_SEG_SIZE;
}
static int build_udt_socket(int fd_udp, int rendezvous, int without_linger)
{
UDTSOCKET fd_udt;
int opt, ret = 0;
fd_udt = udt_socket(PF_INET, SOCK_STREAM, 0);
if ( -1 == fd_udt )
{
DBG_LOG(DBG_ERR, "udt_socket failed, errcode %d\n", udt_getlasterror_code());
return -1;
}
/* set socket options: RENDEZVOUS, MSS, NONBLOCK, SND/RCV BUFFER size. */
opt = 1;
if ( rendezvous )
{
ret = udt_setsockopt(fd_udt, 0, UDT_RENDEZVOUS, &opt, sizeof(opt));
}
if ( without_linger )
{
struct linger lg;
lg.l_onoff = 0;
lg.l_linger = 0;
DBG_LOG(DBG_ERR, "udt socket without linger\n");
ret = ret ? ret : udt_setsockopt(fd_udt, 0, UDT_LINGER, &lg, sizeof(lg));
}
opt = UDT_MAX_SEG_SIZE; /* strips headers (PPPOE, PPP, ...) off */
ret = ret ? ret : udt_setsockopt(fd_udt, 0, UDT_MSS, &opt, sizeof(opt));
opt = 0;
ret = ret ? ret : udt_setsockopt(fd_udt, 0, UDT_RCVSYN, &opt, sizeof(opt));
if ( ret != 0 )
{
DBG_LOG(DBG_ERR, "udt_setsockopt failed, errcode %d\n", udt_getlasterror_code());
udt_close(fd_udt);
return -1;
}
/* set socket buffer size */
opt = UDT_SNDBUF_SIZE;
udt_setsockopt(fd_udt, 0, UDT_SNDBUF, &opt, sizeof(opt));
opt >>= 1;
udt_setsockopt(fd_udt, 0, UDP_SNDBUF, &opt, sizeof(opt));
opt = UDT_RCVBUF_SIZE;
udt_setsockopt(fd_udt, 0, UDT_RCVBUF, &opt, sizeof(opt));
opt >>= 1;
udt_setsockopt(fd_udt, 0, UDP_RCVBUF, &opt, sizeof(opt));
/* bind to local udp address */
if ( fd_udp != -1 )
{
/* FIXME: non-block fd(UDP) makes udt_bind2 crazy!!! */
evutil_make_socket_blocking(fd_udp);
if ( udt_bind2(fd_udt, fd_udp) != 0 )
{
DBG_LOG(DBG_ERR, "udt_bind2 failed, errcode %d\n", udt_getlasterror_code());
udt_close(fd_udt);
return -1;
}
}
return fd_udt;
}
static int udt_do_send_stream(punch_inner_data_t* inner_ctx)
{
int ret, sent = 0;
int size;
char buf[4096] = {0};
do
{
memset(buf, 0, sizeof(buf));
size = evbuffer_copyout(inner_ctx->evbuf_out, buf, sizeof(buf));
if ( size <= 0 )
{
break;
}
ret = udt_send(inner_ctx->fd_udt, buf, size, 0);
if ( -1 == ret )
{
if (udt_getlasterror_code() == EASYNCSND )
{
return 0;
}
else
{
DBG_LOG(DBG_ERR, "udt_send failed, errcode %d\n", udt_getlasterror_code());
return -1;
}
}
evbuffer_drain(inner_ctx->evbuf_out, ret);
sent += ret;
/* send not complete, or buffer empty */
if ( ret < size || size < sizeof(buf) )
{
break;
}
}
while (1);
return sent;
}
#ifdef HAVE_VS
#define HTTP_RESPONSE_503_6 \
"HTTP/1.0 503.6 Service Unavailable\r\n"\
"Server: p2pd\r\n"\
"Content-Length: 0\r\n"\
"Content-Type: text/html\r\n"\
"Connection: close\r\n"\
"\r\n"
#endif
static int read_local_data(punch_ctx_t *ctx)
{
punch_inner_data_t* inner_ctx = ctx->inner_data;
char buf[8192];
int buf_size = sizeof(buf);
int ret = 0;
int avail_buf_size = 0;
int iter_size = 0;
int recv_size_from_local = 0;
int send_size = 0;
if (!ctx->test_local_server(ctx->env))
{
return 0;
}
while (1)
{
avail_buf_size = udt_get_cansend(inner_ctx->fd_udt);
if (avail_buf_size <= 0)
{
break;
}
iter_size = (avail_buf_size > buf_size)?buf_size:avail_buf_size;
recv_size_from_local = ctx->read_from_local_server(ctx->env, (void *)buf, iter_size);
if (recv_size_from_local < 0)
{
DBG_LOG(DBG_ERR, "recv data from local server failed.\n");
return -1;
}
if (recv_size_from_local == 0)
{
break;
}
ret = udt_send(inner_ctx->fd_udt, buf, recv_size_from_local, 0);
if (ret < 0)
{
return -1;
}
}
return 0;
}
#ifdef ADD_REMOTE_ADDR
static int add_header_to_evbuf(session_t *sess, char *buf, int len)
{
if (sess->evbuf_in == NULL)
{
sess->evbuf_in = evbuffer_new();
if (NULL == sess->evbuf_in)
{
return -1;
}
}
if (HTTP_HEADER_MAX_LEN <= evbuffer_get_length(sess->evbuf_in))
{
DBG_LOG(DBG_ERR, "http header is too long.\n");
return -1;
}
evbuffer_add(sess->evbuf_in, buf, len);
return 0;
}
static int evbuf_contain_complete_header(struct evbuffer *evbuf)
{
char *header_end = NULL;
int buf_len = 0;
if (NULL == evbuf)
{
/* false */
return FALSE;
}
header_end = evbuffer_find(evbuf, "\r\n\r\n", 4);
if (NULL == header_end)
{
DBG_LOG(DBG_ERR, "http header is too long\n");
return FALSE;
}
return TRUE;
}
static int send_new_header(session_t *sess)
{
int ret = 0;
char *header_end = NULL;
int header_len = 0;
char buf[HTTP_HEADER_MAX_LEN + 1] = {0};
punch_ctx_t *ctx = &sess->punch_ctx;
ret = evbuffer_copyout(sess->evbuf_in, buf, sizeof(buf) - 1);
if (ret <= 0)
{
return -1;
}
header_end = strstr(buf, "\r\n\r\n");
if (NULL == header_end)
{
return -1;
}
/* add \r\n */
header_end += 2;
header_len = header_end - buf;
int left_len = sizeof(buf) - header_len;
/* �ⲿIP����ȷ���Զ˵ĵ�����ַ */
snprintf(header_end, left_len, "X-Client-Addr: %s\r\n\r\n", sess->prepare_req.eip);
if ( ctx->write_to_local_server(ctx->env, buf, strlen(buf)) < 0 )
{
return -1;
}
/* there is a "\r\n" left */
header_len += 2;
evbuffer_drain(sess->evbuf_in, header_len);
while (0 != evbuffer_get_length(sess->evbuf_in))
{
ret = evbuffer_remove(sess->evbuf_in, buf, sizeof(buf) - 1);
if (ret <= 0)
{
return -1;
}
if (ctx->write_to_local_server(ctx->env, buf, ret) < 0)
{
return -1;
}
}
evbuffer_free(sess->evbuf_in);
sess->evbuf_in = NULL;
sess->header_recv = 1;
return 0;
}
#endif
static int read_peer_data(punch_ctx_t *ctx)
{
int ret = 0;
punch_inner_data_t* inner_ctx = ctx->inner_data;
char buf[2048];
int buf_size = sizeof(buf);
session_t* sess = ctx->env;
#ifdef HAVE_VS
char sndbuf[256 + 1] = {0};
int size = 0;
time_t t;
#endif
#ifdef ADD_REMOTE_ADDR
char *new_header = NULL;
#endif
if(udt_get_canrecv(inner_ctx->fd_udt) <= 0 )
{
return 0;
}
ret = udt_recv(inner_ctx->fd_udt, buf, sizeof(buf), 0);
if ( -1 == ret && udt_getlasterror_code() == EASYNCRCV )
{
return 0;
}
if ( ret <= 0 )
{
return -1;
}
#ifdef HAVE_VS
/* need to check permission when it is video share */
if(sess->prepare_req.is_video_share)
{
DBG_LOG(DBG_DBG, "it is video share, need to check permission\n");
t = time(NULL);
if(strstr(buf, "/stream") != NULL)
{
DBG_LOG(DBG_DBG, "this request is POST /stream, now check permission\n");
if(0 == vs_share_rules_time_valid(&t, &sess->share_info->rules))
{
DBG_LOG(DBG_ERR, "permission denied, send 503.6 to peer\n");
size = snprintf(sndbuf, 200, HTTP_RESPONSE_503_6);
udt_send(inner_ctx->fd_udt, sndbuf, size, 0);
return 0;
}
}
else
{
DBG_LOG(DBG_DBG, "request type is not GET\n");
}
DBG_LOG(DBG_DBG, "permission check passed\n");
}
#endif
#ifdef ADD_REMOTE_ADDR
//printf("buf: %s\n", buf);
if (!sess->header_recv)
{
if (0 > add_header_to_evbuf(sess, buf, ret))
{
DBG_LOG(DBG_ERR, "failed to add header to evbuf\n");
return -1;
}
if (!evbuf_contain_complete_header(sess->evbuf_in))
{
return 0;
}
if (0 > send_new_header(sess))
{
DBG_LOG(DBG_ERR, "failed to send new header\n");
return -1;
}
return 0;
}
#endif
if ( ctx->write_to_local_server(ctx->env, buf, ret) < 0 )
{
return -1;
}
return 0;
}
static void udt_event_cb(evutil_socket_t fd, short events, void* arg)
{
int ret;
punch_ctx_t* ctx = arg;
punch_inner_data_t* inner_ctx = ctx->inner_data;
int udt_stat = NONEXIST;
udt_stat = udt_get_state(inner_ctx->fd_udt);
if (udt_stat != CONNECTED)
{
if ( !(inner_ctx->status & PUNCH_UDT_CONNECTED) )
{
return ;
}
else
{
DBG_LOG(DBG_ERR, "udt io-error, errcode %d\n", udt_getlasterror_code());
goto FAILED;
}
}
else
{
if (!(inner_ctx->status & PUNCH_UDT_CONNECTED))
{
DBG_LOG(DBG_DBG, "udt_connect OK, peer address: %s\n", _sockaddr_ntoa(&inner_ctx->sin_peer));
inner_ctx->status = PUNCH_UDT_CONNECTED;
if ( ctx->succeed_cb(ctx->env, &inner_ctx->sin_peer) < 0 )
{
DBG_LOG(DBG_ERR, "udt succeed_cb failed.\n");
goto FAILED;
}
}
}
ret = read_peer_data(ctx);
if (ret < 0)
{
DBG_LOG(DBG_ERR, "read_peer_data failed.\n");
goto FAILED;
}
ret = read_local_data(ctx);
if (ret < 0)
{
DBG_LOG(DBG_ERR, "read_local_data failed.\n");
goto FAILED;
}
return ;
FAILED:
ctx->error_cb(ctx->env);
}
static int udt_async_connect(punch_ctx_t* ctx, int rendezvous, int without_linger)
{
punch_inner_data_t* inner_ctx = ctx->inner_data;
struct timeval tv = {0, 100*1000};
event_del(inner_ctx->evstun);
event_assign(inner_ctx->evstun, ctx->base, -1, EV_TIMEOUT|EV_PERSIST, udt_event_cb, ctx);
if ( event_add(inner_ctx->evstun, &tv) < 0 )
{
return -1;
}
/* rendezvous */
inner_ctx->fd_udt = build_udt_socket(ctx->fd, rendezvous, without_linger);
if ( inner_ctx->fd_udt < 0 )
{
return -1;
}
DBG_LOG(DBG_DBG, "udt connect to %s...\n", _sockaddr_ntoa(&inner_ctx->sin_peer));
return udt_connect(inner_ctx->fd_udt, (void*)&inner_ctx->sin_peer, sizeof(inner_ctx->sin_peer));
}
static int stun_send_syn(punch_ctx_t* ctx, int opt)
{
punch_inner_data_t* inner_ctx = ctx->inner_data;
struct sockaddr_in sin, sin2;
char buf[512];
int ret = 0, len, i;
snprintf(buf, sizeof(buf), "%s", ctx->stun_sid);
if ( (opt & PUNCH_OPT_ACK) )
{
buf[0]++;
}
len = (int)strlen(buf) + 1;
sin = (opt & PUNCH_OPT_ACK) ? inner_ctx->sin_peer : ctx->sin_peer_wan;
DBG_LOG(DBG_DBG, "punch buffer is %s\n", buf);
// port = ntohs(sin.sin_port); //TODO: port prediction
//PRINTF("sending %s...\n", (opt & PUNCH_OPT_ACK) ? "SYN/ACK" : "SYN" );
if ( !(opt & PUNCH_OPT_ACK) && ctx->sin_peer_lan.sin_addr.s_addr != ctx->sin_peer_wan.sin_addr.s_addr )
{
/* LAN punching... */
ret = sendto(ctx->fd, buf, len, 0, (struct sockaddr*)&ctx->sin_peer_lan, sizeof(struct sockaddr_in));
}
/* WAN punching... */
if(!inner_ctx->punch_prepared)
{
/* the first SYN has low TTL,
* this low-TTL SYN is used to punch holes on the NATs of the PC's/IPC's own side.
*
* next we will send normal(whose TTL has not been set) SYNs,
* and these normal SYNs will be sent every 1 second.
* why 1 second, see the function udp_punch.
*/
int original_ttl, new_ttl, ttl_len;
new_ttl = PUNCH_PREPARING_TTL;
ttl_len = sizeof(original_ttl);
if(getsockopt(ctx->fd, IPPROTO_IP, IP_TTL, (char *) &original_ttl, &ttl_len)
|| setsockopt(ctx->fd, IPPROTO_IP, IP_TTL, (char *) &new_ttl, sizeof(new_ttl)) )
{
inner_ctx->punch_prepared = 1;
return 0;
}
/* send multiple times, avoid packet loss */
for(i = 0; i < 3; ++i)
{
sendto(ctx->fd, buf, len, 0, (struct sockaddr *) &sin, sizeof(struct sockaddr_in));
}
setsockopt(ctx->fd, IPPROTO_IP, IP_TTL, (char *) &original_ttl, ttl_len);
inner_ctx->punch_prepared = 1;
return 0;
}
ret = sendto(ctx->fd, buf, len, 0, (struct sockaddr*)&sin, sizeof(struct sockaddr_in));
if ( ret < 0 )
{
if ( !sock_func_will_block(ret) )
{
DBG_LOG(DBG_ERR, "sendto failed, fd:%d, sock_errno:%d\n", ctx->fd, sock_errno);
goto FAILED;
}
return 0;
}
inner_ctx->punch_try_cnt ++;
if(!(opt & PUNCH_OPT_ACK) && inner_ctx->punch_try_cnt > 3)
{
/* if we have sent the low-TTL SYN, as well as 3 normal SYNs(this takes 4 seconds approximately),
* but hole punching still not done, we start to do port prediction.
*
* NOTE: port prediction strategy will be changed if we have better one.
*/
sin2 = sin;
for(i = 1; i < 5; i++)
{
sin2.sin_port = htons(ntohs(sin.sin_port) + i);
sendto(ctx->fd, buf, len, 0, (struct sockaddr *)&sin2, sizeof(struct sockaddr_in));
}
}
return 0;
FAILED:
return -1;
}
static void punch_event_cb(evutil_socket_t fd, short events, void* arg)
{
punch_ctx_t* ctx = arg;
punch_inner_data_t* inner_ctx = ctx->inner_data;
int opt_ack = 0;
if (events & EV_TIMEOUT)
{
opt_ack = (PUNCH_SYN_SENT != inner_ctx->status) ? PUNCH_OPT_ACK : 0;
stun_send_syn(ctx, opt_ack);
return ;
}
if (events & EV_READ)
{
char buf[512] = {0};
struct sockaddr_in sin;
socklen_t socklen = sizeof(sin);
int size;
size = recvfrom(fd, buf, sizeof(buf)-1, 0, (struct sockaddr*)&sin, &socklen);
if ( size < 0 )
{
/* for udp socket, WSAENETRESET means TTL expired.
* this is an ICMP error, called asynchronous error,
* and only winSocket will tell udp socket this error,
* others like bsd/linux will not. */
DBG_LOG(DBG_ERR, "recvfrom failed, fd:%d, sock_errno:%d\n", fd, sock_errno);
if ( !sock_func_will_block(size) )
{
goto FAILED;
}
return;
}
/* SYN received */
int syn_recv = !strcmp(buf, ctx->stun_sid);
if (syn_recv)
{
if ( PRIVATE_NIPV4(sin.sin_addr.s_addr) || /* LAN */
PUNCH_SYN_SENT == inner_ctx->status )
{
/* ����ΪLAN�� */
inner_ctx->sin_peer = sin;
inner_ctx->status = PUNCH_SYN_RCVD;
}
stun_send_syn(ctx, PUNCH_OPT_ACK);
DBG_LOG(DBG_DBG, "recvd SYN from %s\n", _sockaddr_ntoa(&sin));
return;
}
else
{
buf[0]--;
int ack_recv = !strcmp(buf, ctx->stun_sid);
if (ack_recv)
{
DBG_LOG(DBG_DBG, "recvd SYN, ACK from %s\n", _sockaddr_ntoa(&sin));
}
else
{
DBG_LOG(DBG_DBG, "recvd trash data(%d bytes) from %s\n", size, _sockaddr_ntoa(&sin));
return;
}
}
if ( PUNCH_SYN_SENT == inner_ctx->status )
{
/* û���յ��Է���SYN����ֱ���յ�SYN+ACK */
/* ����Գ���+IP���� */
inner_ctx->sin_peer = sin;
stun_send_syn(ctx, PUNCH_OPT_ACK);
}
else if (PUNCH_SYN_RCVD == inner_ctx->status)
{
/* prefer local to remote/harpin connections */
int is_local_connection = PRIVATE_NIPV4(inner_ctx->sin_peer.sin_addr.s_addr);
int synack_from_remote_ip = sin.sin_addr.s_addr != inner_ctx->sin_peer.sin_addr.s_addr;
if (is_local_connection && synack_from_remote_ip)
{
return;
}
/* received SYN, SYN/ACK from different remote address */
int different_addr = (inner_ctx->sin_peer.sin_addr.s_addr != sin.sin_addr.s_addr) ||
(inner_ctx->sin_peer.sin_port != sin.sin_port);
if (different_addr)
{
inner_ctx->sin_peer = sin;
}
}
else
{
DBG_LOG(DBG_ERR, "punching status error: %d\n", inner_ctx->status);
return;
}
/* punching succeed */
DBG_LOG(DBG_DBG, "UDP hole punching succeed, peer address: %s\n", _sockaddr_ntoa(&inner_ctx->sin_peer));
stun_send_syn(ctx, PUNCH_OPT_ACK);
inner_ctx->status = PUNCH_SYNACK_RCVD;
int ret = udt_async_connect(ctx, 1, 0);
if ( ret < 0 )
{
goto FAILED;
}
return;
}
return;
FAILED:
DBG_LOG(DBG_ERR, "punching failed\n");
ctx->error_cb(ctx->env);
}
int udp_punch_send_stream(punch_ctx_t* ctx, const void* data, int size)
{
punch_inner_data_t* inner_ctx;
int sent = 0;
if (!ctx || !ctx->inner_data || !data || size < 0)
{
return -1;
}
inner_ctx = ctx->inner_data;
if ( !(inner_ctx->status & PUNCH_UDT_CONNECTED) )
{
return 0;
}
if ( !size )
{
return (int)evbuffer_get_length(inner_ctx->evbuf_out);
}
/* data enqueue in evbuffer, and sent when timer expires */
if ( evbuffer_add(inner_ctx->evbuf_out, (char*)data+sent, size-sent) < 0 )
{
return -1;
}
return (int)evbuffer_get_length(inner_ctx->evbuf_out);
}
int udp_punch(punch_ctx_t* ctx)
{
punch_inner_data_t* inner_ctx;
struct timeval tv = {1,1};
if (!ctx || !ctx->inner_data )
{
return -1;
}
inner_ctx = ctx->inner_data;
inner_ctx->evstun = event_new(ctx->base, ctx->fd,
EV_READ|EV_TIMEOUT|EV_PERSIST, punch_event_cb, ctx);
if (!inner_ctx->evstun)
{
DBG_LOG(DBG_ERR, "create evstun event, error\n");
return -1;
}
event_add(inner_ctx->evstun, &tv);
int opt_ack = 0;
stun_send_syn(ctx, opt_ack);
stun_send_syn(ctx, opt_ack);
inner_ctx->status = PUNCH_SYN_SENT;
return 0;
}
int udp_punch_clear_internal(punch_ctx_t* ctx)
{
punch_inner_data_t* inner_ctx;
if ( !ctx || !ctx->inner_data )
{
return -1;
}
inner_ctx = ctx->inner_data;
if ( inner_ctx->evstun )
{
event_free(inner_ctx->evstun);
}
if ( inner_ctx->fd_udt != -1 )
{
/* since UDP fd has been binded to UDT fd, we must avoid double closeing UDP fd */
udt_close(inner_ctx->fd_udt);
}
else if (ctx->fd != -1)
{
evutil_closesocket(ctx->fd);
}
if ( inner_ctx->evbuf_out )
{
evbuffer_free(inner_ctx->evbuf_out);
}
free(ctx->inner_data);
ctx->inner_data = NULL;
return 0;
}
int udp_punch_init_internal(punch_ctx_t* ctx)
{
punch_inner_data_t* inner_ctx;
if (!ctx || !ctx->base || ctx->fd < 0 )
{
return -1;
}
udp_punch_clear_internal(ctx);
ctx->inner_data = calloc(1, sizeof(punch_inner_data_t));
if (!ctx->inner_data)
{
return -1;
}
inner_ctx = ctx->inner_data;
inner_ctx->fd_udt = -1;
inner_ctx->udt_sndlowat = UDT_SNDLOWAT;
inner_ctx->evstun = NULL;
inner_ctx->evbuf_out = evbuffer_new();
if ( !inner_ctx->evbuf_out )
{
DBG_LOG(DBG_ERR, "create evbuf_out, error\n");
udp_punch_clear_internal(ctx);
return -1;
}
return 0;
}
int udp_punch_set_attr(punch_ctx_t* ctx, int attr, void* value)
{
punch_inner_data_t* inner_ctx;
if( !ctx || !ctx->inner_data || !value )
{
return -1;
}
inner_ctx = ctx->inner_data;
switch(attr)
{
case PUNCHING_ATTR_UDT_SNDLOWAT:
{
unsigned int udt_sndlowat;
udt_sndlowat = *(unsigned int*)value;
if( udt_sndlowat <= 0 )
{
return -1;
}
DBG_LOG(DBG_DBG, "udt_sndlowat changed: %u -> %u\n", inner_ctx->udt_sndlowat, udt_sndlowat);
inner_ctx->udt_sndlowat = udt_sndlowat;
return 0;
}/* case of PUNCHING_ATTR_UDT_SNDLOWAT */
default:
return -1;
}
return -1;
}
最新发布