本篇文章主要记录这段时间sip开发过程中遇到的一些问题和解决方法。
owt引用的sipua默认没有开启内网穿透,但是sipua已经具备了内网穿透的功能模块,下面记录如何开启内网穿透功能。
sipua实现内网穿透主要方式有以下几种:
owt的sipua编译脚本并没有将stun ice netpmp编译进工程,所以想要启用这些功能首先需要将这些模块编译进工程。
comedia比较特殊,它的实现思路不需要客户端参与公网地址发现,而是sip 客户端主动向 sip gateway 的udp端口发送rtp和rtcp包以达到sip gateway能够发现客户端公网地址端口的目的。
snprintf(account_str, sizeof(account_str),
"%.128s ;regint=%d;regq=0.5;answermode=auto;rtpkeep=zero;" ,
disp_name, user_name, password, sip_server, 7200 ) ;
修改此处代码,sipua会主动向服务器发送空rtp包。
要特别注意的是:若想使用comedia功能,需要服务端支持comedia,所以sip gateway要开启comedia,以asterisk为例修改配置文件/etc/asterisk/sip.conf
:
开启stun和ice的流程是一样的,首先修改Makefile将stun和ice模块编译进主工程
sdp中connection信息为网卡ip,即使使用ice和stun也只是修改了m=行的connection。而sip gateway使用的是全局的connection,所以需要将全局connection的ip换成ice和stun获得的ip。
修改后的stun.c
代码
/**
* @file stun.c STUN Module for Media NAT-traversal
*
* Copyright (C) 2010 Creytiv.com
*/
#include
#include
/**
* @defgroup stun stun
*
* Session Traversal Utilities for NAT (STUN) for media NAT traversal
*/
enum {LAYER = 0, INTERVAL = 30};
struct mnat_sess {
struct list medial;
struct sa srv;
struct stun_dns *dnsq;
mnat_estab_h *estabh;
void *arg;
int mediac;
struct sdp_session *ss;
};
struct mnat_media {
struct le le;
struct sa addr1;
struct sa addr2;
struct mnat_sess *sess;
struct sdp_media *sdpm;
struct stun_keepalive *ska1;
struct stun_keepalive *ska2;
void *sock1;
void *sock2;
int proto;
struct sdp_session *ss;
};
static struct mnat *mnat;
static void session_destructor(void *arg)
{
struct mnat_sess *sess = arg;
list_flush(&sess->medial);
mem_deref(sess->dnsq);
}
static void media_destructor(void *arg)
{
struct mnat_media *m = arg;
list_unlink(&m->le);
mem_deref(m->sdpm);
mem_deref(m->ska1);
mem_deref(m->ska2);
mem_deref(m->sock1);
mem_deref(m->sock2);
}
static void mapped_handler1(int err, const struct sa *map_addr, void *arg)
{
struct mnat_media *m = arg;
if (!err) {
sdp_session_set_laddr(m->ss, map_addr);
sdp_media_set_laddr(m->sdpm, map_addr);
m->addr1 = *map_addr;
if (m->ska2 && !sa_isset(&m->addr2, SA_ALL))
return;
if (--m->sess->mediac)
return;
}
m->sess->estabh(err, 0, NULL, m->sess->arg);
}
static void mapped_handler2(int err, const struct sa *map_addr, void *arg)
{
struct mnat_media *m = arg;
if (!err) {
sdp_media_set_laddr_rtcp(m->sdpm, map_addr);
m->addr2 = *map_addr;
if (m->ska1 && !sa_isset(&m->addr1, SA_ALL))
return;
if (--m->sess->mediac)
return;
}
m->sess->estabh(err, 0, NULL, m->sess->arg);
}
static int media_start(struct mnat_sess *sess, struct mnat_media *m)
{
int err = 0;
if (m->sock1) {
err |= stun_keepalive_alloc(&m->ska1, m->proto,
m->sock1, LAYER, &sess->srv, NULL,
mapped_handler1, m);
}
if (m->sock2) {
err |= stun_keepalive_alloc(&m->ska2, m->proto,
m->sock2, LAYER, &sess->srv, NULL,
mapped_handler2, m);
}
if (err)
return err;
stun_keepalive_enable(m->ska1, INTERVAL);
stun_keepalive_enable(m->ska2, INTERVAL);
return 0;
}
static void dns_handler(int err, const struct sa *srv, void *arg)
{
struct mnat_sess *sess = arg;
struct le *le;
if (err)
goto out;
sess->srv = *srv;
for (le=sess->medial.head; le; le=le->next) {
struct mnat_media *m = le->data;
m->ss = sess->ss;
err = media_start(sess, m);
if (err)
goto out;
}
return;
out:
sess->estabh(err, 0, NULL, sess->arg);
}
static int session_alloc(struct mnat_sess **sessp, struct dnsc *dnsc,
int af, const char *srv, uint16_t port,
const char *user, const char *pass,
struct sdp_session *ss, bool offerer,
mnat_estab_h *estabh, void *arg)
{
struct mnat_sess *sess;
int err;
(void)user;
(void)pass;
// (void)ss;
(void)offerer;
if (!sessp || !dnsc || !srv || !ss || !estabh)
return EINVAL;
sess = mem_zalloc(sizeof(*sess), session_destructor);
if (!sess)
return ENOMEM;
sess->estabh = estabh;
sess->arg = arg;
sess->ss = ss;
err = stun_server_discover(&sess->dnsq, dnsc,
stun_usage_binding, stun_proto_udp,
af, srv, port, dns_handler, sess);
if (err)
mem_deref(sess);
else
*sessp = sess;
return err;
}
static int media_alloc(struct mnat_media **mp, struct mnat_sess *sess,
int proto, void *sock1, void *sock2,
struct sdp_media *sdpm)
{
struct mnat_media *m;
int err = 0;
if (!mp || !sess || !sdpm)
return EINVAL;
m = mem_zalloc(sizeof(*m), media_destructor);
if (!m)
return ENOMEM;
list_append(&sess->medial, &m->le, m);
m->sdpm = mem_ref(sdpm);
m->sess = sess;
m->sock1 = mem_ref(sock1);
m->sock2 = mem_ref(sock2);
m->proto = proto;
m->ss = sess->ss;
if (sa_isset(&sess->srv, SA_ALL))
err = media_start(sess, m);
if (err)
mem_deref(m);
else {
*mp = m;
++sess->mediac;
}
return err;
}
static int module_init(void)
{
return mnat_register(&mnat, "stun", NULL, session_alloc, media_alloc,
NULL);
}
int stun_module_init(void)
{
return mnat_register(&mnat, "stun", NULL, session_alloc, media_alloc,
NULL);
}
static int module_close(void)
{
mnat = mem_deref(mnat);
return 0;
}
EXPORT_SYM const struct mod_export DECL_EXPORTS(stun) = {
"stun",
"mnat",
module_init,
module_close,
};
关于netpmp,这里就不过多讨论了,因为使用上述三种方案已经能够实现内网穿透。有不明白的可以进群交流。
QQ交流群:697773082