diff --git a/docs/README b/docs/README index ded92a9..594e9ac 100644 --- a/docs/README +++ b/docs/README @@ -95,6 +95,7 @@ Features: * RFC 5118 - SIP Torture Test Messages for IPv6 * RFC 5245 - Interactive Connectivity Establishment (ICE) * RFC 5389 - Session Traversal Utilities for NAT (STUN) +* RFC 5626 - Managing Client-Initiated Connections in SIP * RFC 5761 - Multiplexing RTP Data and Control Packets on a Single Port * RFC 5766 - Traversal Using Relays around NAT (TURN) * RFC 5768 - Indicating Support for ICE in SIP diff --git a/docs/TODO b/docs/TODO index de043a7..0cdaa73 100644 --- a/docs/TODO +++ b/docs/TODO @@ -3,7 +3,6 @@ TODO ------------------------------------------------------------------------------- Version v0.1.1 - SIP Outbound support - client+server tmr: scaling using binary heap or hash Add support for SIP SUBSCRIBE/NOTIFY Add support for Call-Transfer in SIP Session diff --git a/include/re_sip.h b/include/re_sip.h index e5524f3..8250911 100644 --- a/include/re_sip.h +++ b/include/re_sip.h @@ -45,6 +45,7 @@ enum sip_hdrid { SIP_HDR_ERROR_INFO = 21, SIP_HDR_EVENT = 3286, SIP_HDR_EXPIRES = 1983, + SIP_HDR_FLOW_TIMER = 584, SIP_HDR_FROM = 1963, SIP_HDR_HIDE = 283, SIP_HDR_HISTORY_INFO = 2582, @@ -210,6 +211,7 @@ struct sip_request; struct sip_strans; struct sip_auth; struct sip_dialog; +struct sip_keepalive; struct dnsc; typedef bool(sip_msg_h)(const struct sip_msg *msg, void *arg); @@ -222,6 +224,7 @@ typedef int(sip_auth_h)(char **username, char **password, const char *realm, void *arg); typedef bool(sip_hdr_h)(const struct sip_hdr *hdr, const struct sip_msg *msg, void *arg); +typedef void(sip_keepalive_h)(int err, void *arg); /* sip */ @@ -312,6 +315,10 @@ const struct sip_hdr *sip_msg_xhdr_apply(const struct sip_msg *msg, sip_hdr_h *h, void *arg); uint32_t sip_msg_hdr_count(const struct sip_msg *msg, enum sip_hdrid id); uint32_t sip_msg_xhdr_count(const struct sip_msg *msg, const char *name); +bool sip_msg_hdr_has_value(const struct sip_msg *msg, enum sip_hdrid id, + const char *value); +bool sip_msg_xhdr_has_value(const struct sip_msg *msg, const char *name, + const char *value); struct tcp_conn *sip_msg_tcpconn(const struct sip_msg *msg); void sip_msg_dump(const struct sip_msg *msg); @@ -320,3 +327,9 @@ int sip_via_decode(struct sip_via *via, const struct pl *pl); int sip_cseq_decode(struct sip_cseq *cseq, const struct pl *pl); int sip_param_decode(const struct pl *pl, const char *name, struct pl *val); int sip_param_exists(const struct pl *pl, const char *name, struct pl *end); + + +/* keepalive */ +int sip_keepalive_start(struct sip_keepalive **kap, struct sip *sip, + const struct sip_msg *msg, uint32_t interval, + sip_keepalive_h *kah, void *arg); diff --git a/include/re_sipreg.h b/include/re_sipreg.h index 9df4725..765d158 100644 --- a/include/re_sipreg.h +++ b/include/re_sipreg.h @@ -10,6 +10,6 @@ struct sipreg; int sipreg_register(struct sipreg **regp, struct sip *sip, const char *reg_uri, const char *to_uri, const char *from_uri, uint32_t expires, const char *cuser, const char *routev[], uint32_t routec, - sip_auth_h *authh, void *aarg, bool ref, + int regid, sip_auth_h *authh, void *aarg, bool aref, sip_resp_h *resph, void *arg, const char *params, const char *fmt, ...); diff --git a/mk/symbian/resip.mmp b/mk/symbian/resip.mmp index 5353694..3251c63 100644 --- a/mk/symbian/resip.mmp +++ b/mk/symbian/resip.mmp @@ -26,6 +26,8 @@ SOURCE auth.c SOURCE cseq.c SOURCE ctrans.c SOURCE dialog.c +SOURCE keepalive.c +SOURCE keepalive_udp.c SOURCE msg.c SOURCE param.c SOURCE reply.c diff --git a/src/sip/auth.c b/src/sip/auth.c index 30ae464..3f4c187 100644 --- a/src/sip/auth.c +++ b/src/sip/auth.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include "sip.h" diff --git a/src/sip/keepalive.c b/src/sip/keepalive.c new file mode 100644 index 0000000..d2d233e --- /dev/null +++ b/src/sip/keepalive.c @@ -0,0 +1,102 @@ +/** + * @file keepalive.c SIP Keepalive + * + * Copyright (C) 2010 Creytiv.com + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sip.h" + + +static void destructor(void *arg) +{ + struct sip_keepalive *ka = arg; + + if (ka->kap) + *ka->kap = NULL; + + list_unlink(&ka->le); +} + + +void sip_keepalive_signal(struct list *kal, int err) +{ + struct le *le = list_head(kal); + + while (le) { + + struct sip_keepalive *ka = le->data; + sip_keepalive_h *kah = ka->kah; + void *arg = ka->arg; + + le = le->next; + + list_unlink(&ka->le); + mem_deref(ka); + + kah(err, arg); + } +} + + +uint64_t sip_keepalive_wait(uint32_t interval) +{ + return interval * (800 + rand_u16() % 201); +} + + +int sip_keepalive_start(struct sip_keepalive **kap, struct sip *sip, + const struct sip_msg *msg, uint32_t interval, + sip_keepalive_h *kah, void *arg) +{ + struct sip_keepalive *ka; + int err; + + if (!kap || !sip || !msg || !kah) + return EINVAL; + + ka = mem_zalloc(sizeof(*ka), destructor); + if (!ka) + return ENOMEM; + + ka->kah = kah; + ka->arg = arg; + + switch (msg->tp) { + + case SIP_TRANSP_UDP: + err = sip_keepalive_udp(ka, sip, (struct udp_sock *)msg->sock, + &msg->src, interval); + break; + + case SIP_TRANSP_TCP: + case SIP_TRANSP_TLS: + err = sip_keepalive_tcp(ka, (struct sip_conn *)msg->sock, + interval); + break; + + default: + err = EPROTONOSUPPORT; + break; + } + + if (err) { + mem_deref(ka); + } + else { + ka->kap = kap; + *kap = ka; + } + + return err; +} diff --git a/src/sip/keepalive_udp.c b/src/sip/keepalive_udp.c new file mode 100644 index 0000000..b6bd250 --- /dev/null +++ b/src/sip/keepalive_udp.c @@ -0,0 +1,187 @@ +/** + * @file keepalive_udp.c SIP UDP Keepalive + * + * Copyright (C) 2010 Creytiv.com + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sip.h" + + +enum { + UDP_KEEPALIVE_INTVAL = 29, +}; + + +struct sip_udpconn { + struct le he; + struct list kal; + struct tmr tmr_ka; + struct sa maddr; + struct sa paddr; + struct udp_sock *us; + struct stun_ctrans *ct; + struct stun *stun; + uint32_t ka_interval; +}; + + +static void udpconn_keepalive_handler(void *arg); + + +static void destructor(void *arg) +{ + struct sip_udpconn *uc = arg; + + list_flush(&uc->kal); + hash_unlink(&uc->he); + tmr_cancel(&uc->tmr_ka); + mem_deref(uc->ct); + mem_deref(uc->us); + mem_deref(uc->stun); +} + + +static void udpconn_close(struct sip_udpconn *uc, int err) +{ + sip_keepalive_signal(&uc->kal, err); + hash_unlink(&uc->he); + tmr_cancel(&uc->tmr_ka); + uc->ct = mem_deref(uc->ct); + uc->us = mem_deref(uc->us); + uc->stun = mem_deref(uc->stun); +} + + +static void stun_response_handler(int err, uint16_t scode, const char *reason, + const struct stun_msg *msg, void *arg) +{ + struct sip_udpconn *uc = arg; + struct stun_attr *attr; + (void)reason; + + if (err || scode) { + err = err ? err : EPROTO; + goto out; + } + + attr = stun_msg_attr(msg, STUN_ATTR_XOR_MAPPED_ADDR); + if (!attr) { + attr = stun_msg_attr(msg, STUN_ATTR_MAPPED_ADDR); + if (!attr) { + err = EPROTO; + goto out; + } + } + + if (!sa_isset(&uc->maddr, SA_ALL)) { + uc->maddr = attr->v.sa; + } + else if (!sa_cmp(&uc->maddr, &attr->v.sa, SA_ALL)) { + err = ENOTCONN; + goto out; + } + + out: + if (err) { + udpconn_close(uc, err); + mem_deref(uc); + } + else { + tmr_start(&uc->tmr_ka, sip_keepalive_wait(uc->ka_interval), + udpconn_keepalive_handler, uc); + } +} + + +static void udpconn_keepalive_handler(void *arg) +{ + struct sip_udpconn *uc = arg; + int err; + + if (!uc->kal.head) { + /* no need for us anymore */ + udpconn_close(uc, 0); + mem_deref(uc); + return; + } + + err = stun_request(&uc->ct, uc->stun, IPPROTO_UDP, uc->us, + &uc->paddr, 0, STUN_METHOD_BINDING, NULL, 0, + false, stun_response_handler, uc, 1, + STUN_ATTR_SOFTWARE, stun_software); + if (err) { + udpconn_close(uc, err); + mem_deref(uc); + } +} + + +static struct sip_udpconn *udpconn_find(struct sip *sip, struct udp_sock *us, + const struct sa *paddr) +{ + struct le *le; + + le = list_head(hash_list(sip->ht_udpconn, sa_hash(paddr, SA_ALL))); + + for (; le; le = le->next) { + + struct sip_udpconn *uc = le->data; + + if (!sa_cmp(&uc->paddr, paddr, SA_ALL)) + continue; + + if (uc->us != us) + continue; + + return uc; + } + + return NULL; +} + + +int sip_keepalive_udp(struct sip_keepalive *ka, struct sip *sip, + struct udp_sock *us, const struct sa *paddr, + uint32_t interval) +{ + struct sip_udpconn *uc; + + if (!ka || !sip || !us || !paddr) + return EINVAL; + + uc = udpconn_find(sip, us, paddr); + if (!uc) { + uc = mem_zalloc(sizeof(*uc), destructor); + if (!uc) + return ENOMEM; + + hash_append(sip->ht_udpconn, sa_hash(paddr, SA_ALL), + &uc->he, uc); + + uc->paddr = *paddr; + uc->stun = mem_ref(sip->stun); + uc->us = mem_ref(us); + uc->ka_interval = interval ? interval : UDP_KEEPALIVE_INTVAL; + + /* learn mapped address immediately */ + tmr_start(&uc->tmr_ka, 0, udpconn_keepalive_handler, uc); + } + + list_append(&uc->kal, &ka->le, ka); + + return 0; +} diff --git a/src/sip/mod.mk b/src/sip/mod.mk index 08132f3..b89d9b9 100644 --- a/src/sip/mod.mk +++ b/src/sip/mod.mk @@ -8,6 +8,8 @@ SRCS += sip/auth.c SRCS += sip/cseq.c SRCS += sip/ctrans.c SRCS += sip/dialog.c +SRCS += sip/keepalive.c +SRCS += sip/keepalive_udp.c SRCS += sip/msg.c SRCS += sip/param.c SRCS += sip/reply.c diff --git a/src/sip/msg.c b/src/sip/msg.c index ae2dd9a..ee28dbf 100644 --- a/src/sip/msg.c +++ b/src/sip/msg.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include "sip.h" @@ -534,6 +535,31 @@ uint32_t sip_msg_xhdr_count(const struct sip_msg *msg, const char *name) } +static bool value_handler(const struct sip_hdr *hdr, const struct sip_msg *msg, + void *arg) +{ + (void)msg; + + return 0 == pl_strcasecmp(&hdr->val, (const char *)arg); +} + + +bool sip_msg_hdr_has_value(const struct sip_msg *msg, enum sip_hdrid id, + const char *value) +{ + return NULL != sip_msg_hdr_apply(msg, true, id, value_handler, + (void *)value); +} + + +bool sip_msg_xhdr_has_value(const struct sip_msg *msg, const char *name, + const char *value) +{ + return NULL != sip_msg_xhdr_apply(msg, true, name, value_handler, + (void *)value); +} + + void sip_msg_dump(const struct sip_msg *msg) { struct le *le; diff --git a/src/sip/sip.c b/src/sip/sip.c index 39d8991..676e4b1 100644 --- a/src/sip/sip.c +++ b/src/sip/sip.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include "sip.h" @@ -42,11 +43,15 @@ static void destructor(void *arg) hash_flush(sip->ht_conn); mem_deref(sip->ht_conn); + hash_flush(sip->ht_udpconn); + mem_deref(sip->ht_udpconn); + list_flush(&sip->transpl); list_flush(&sip->lsnrl); mem_deref(sip->software); mem_deref(sip->dnsc); + mem_deref(sip->stun); } @@ -87,6 +92,14 @@ int sip_alloc(struct sip **sipp, struct dnsc *dnsc, uint32_t ctsz, if (err) goto out; + err = hash_alloc(&sip->ht_udpconn, tcsz); + if (err) + goto out; + + err = stun_alloc(&sip->stun, NULL, NULL, NULL); + if (err) + goto out; + if (software) { err = str_dup(&sip->software, software); if (err) diff --git a/src/sip/sip.h b/src/sip/sip.h index 1db4663..bbe8397 100644 --- a/src/sip/sip.h +++ b/src/sip/sip.h @@ -12,7 +12,9 @@ struct sip { struct hash *ht_ctrans; struct hash *ht_strans; struct hash *ht_conn; + struct hash *ht_udpconn; struct dnsc *dnsc; + struct stun *stun; char *software; sip_exit_h *exith; void *arg; @@ -29,6 +31,14 @@ struct sip_lsnr { }; +struct sip_keepalive { + struct le le; + struct sip_keepalive **kap; + sip_keepalive_h *kah; + void *arg; +}; + + /* request */ void sip_request_close(struct sip *sip); @@ -78,3 +88,15 @@ int sip_dialog_encode(struct mbuf *mb, struct sip_dialog *dlg, uint32_t cseq, const char *met); const char *sip_dialog_uri(const struct sip_dialog *dlg); const struct uri *sip_dialog_route(const struct sip_dialog *dlg); + + +/* keepalive */ +struct sip_conn; + +void sip_keepalive_signal(struct list *kal, int err); +uint64_t sip_keepalive_wait(uint32_t interval); +int sip_keepalive_tcp(struct sip_keepalive *ka, struct sip_conn *conn, + uint32_t interval); +int sip_keepalive_udp(struct sip_keepalive *ka, struct sip *sip, + struct udp_sock *us, const struct sa *paddr, + uint32_t interval); diff --git a/src/sip/transp.c b/src/sip/transp.c index eb0e111..97b0b07 100644 --- a/src/sip/transp.c +++ b/src/sip/transp.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -22,7 +23,9 @@ enum { - TCP_ACCEPT_TIMEOUT = 32000, + TCP_ACCEPT_TIMEOUT = 32, + TCP_KEEPALIVE_TIMEOUT = 10, + TCP_KEEPALIVE_INTVAL = 120, }; @@ -39,13 +42,16 @@ struct sip_transport { struct sip_conn { struct le he; struct list ql; + struct list kal; struct tmr tmr; + struct tmr tmr_ka; struct sa laddr; struct sa paddr; struct tls_conn *sc; struct tcp_conn *tc; struct mbuf *mb; struct sip *sip; + uint32_t ka_interval; bool established; }; @@ -59,6 +65,9 @@ struct sip_connqent { }; +static uint8_t crlfcrlf[4] = {0x0d, 0x0a, 0x0d, 0x0a}; + + static void internal_transport_handler(int err, void *arg) { (void)err; @@ -70,6 +79,9 @@ static void transp_destructor(void *arg) { struct sip_transport *transp = arg; + if (transp->tp == SIP_TRANSP_UDP) + udp_handler_set(transp->sock, NULL, NULL); + list_unlink(&transp->le); mem_deref(transp->sock); mem_deref(transp->tls); @@ -80,7 +92,9 @@ static void conn_destructor(void *arg) { struct sip_conn *conn = arg; + tmr_cancel(&conn->tmr_ka); tmr_cancel(&conn->tmr); + list_flush(&conn->kal); list_flush(&conn->ql); hash_unlink(&conn->he); mem_deref(conn->sc); @@ -155,6 +169,7 @@ static void conn_close(struct sip_conn *conn, int err) conn->sc = mem_deref(conn->sc); conn->tc = mem_deref(conn->tc); + tmr_cancel(&conn->tmr_ka); tmr_cancel(&conn->tmr); hash_unlink(&conn->he); @@ -174,6 +189,8 @@ static void conn_close(struct sip_conn *conn, int err) list_unlink(&qent->le); mem_deref(qent); } + + sip_keepalive_signal(&conn->kal, err); } @@ -186,6 +203,31 @@ static void conn_tmr_handler(void *arg) } +static void conn_keepalive_handler(void *arg) +{ + struct sip_conn *conn = arg; + struct mbuf mb; + int err; + + mb.buf = crlfcrlf; + mb.size = sizeof(crlfcrlf); + mb.pos = 0; + mb.end = 4; + + err = tcp_send(conn->tc, &mb); + if (err) { + conn_close(conn, err); + mem_deref(conn); + return; + } + + tmr_start(&conn->tmr, TCP_KEEPALIVE_TIMEOUT * 1000, + conn_tmr_handler, conn); + tmr_start(&conn->tmr_ka, sip_keepalive_wait(conn->ka_interval), + conn_keepalive_handler, conn); +} + + static void sip_recv(struct sip *sip, const struct sip_msg *msg) { struct le *le = sip->lsnrl.head; @@ -224,12 +266,42 @@ static void sip_recv(struct sip *sip, const struct sip_msg *msg) static void udp_recv_handler(const struct sa *src, struct mbuf *mb, void *arg) { struct sip_transport *transp = arg; + struct stun_unknown_attr ua; + struct stun_msg *stun_msg; struct sip_msg *msg; int err; if (mb->end <= 4) return; + if (!stun_msg_decode(&stun_msg, mb, &ua)) { + + if (stun_msg_method(stun_msg) == STUN_METHOD_BINDING) { + + switch (stun_msg_class(stun_msg)) { + + case STUN_CLASS_REQUEST: + (void)stun_reply(IPPROTO_UDP, transp->sock, + src, 0, stun_msg, + NULL, 0, false, 2, + STUN_ATTR_XOR_MAPPED_ADDR, + src, + STUN_ATTR_SOFTWARE, + transp->sip->software); + break; + + default: + (void)stun_ctrans_recv(transp->sip->stun, + stun_msg, &ua); + break; + } + } + + mem_deref(stun_msg); + + return; + } + err = sip_msg_decode(&msg, mb); if (err) { (void)re_fprintf(stderr, "sip: msg decode err: %s\n", @@ -281,7 +353,26 @@ static void tcp_recv_handler(struct mbuf *mb, void *arg) break; if (!memcmp(mbuf_buf(conn->mb), "\r\n", 2)) { + conn->mb->pos += 2; + + if (mbuf_get_left(conn->mb) >= 2 && + !memcmp(mbuf_buf(conn->mb), "\r\n", 2)) { + + struct mbuf mbr; + + conn->mb->pos += 2; + + mbr.buf = crlfcrlf; + mbr.size = sizeof(crlfcrlf); + mbr.pos = 0; + mbr.end = 2; + + err = tcp_send(conn->tc, &mbr); + if (err) + break; + } + if (mbuf_get_left(conn->mb)) continue; @@ -436,7 +527,8 @@ static void tcp_connect_handler(const struct sa *paddr, void *arg) } #endif - tmr_start(&conn->tmr, TCP_ACCEPT_TIMEOUT, conn_tmr_handler, conn); + tmr_start(&conn->tmr, TCP_ACCEPT_TIMEOUT * 1000, + conn_tmr_handler, conn); out: if (err) { @@ -781,3 +873,29 @@ struct tcp_conn *sip_msg_tcpconn(const struct sip_msg *msg) return NULL; } } + + +int sip_keepalive_tcp(struct sip_keepalive *ka, struct sip_conn *conn, + uint32_t interval) +{ + if (!ka || !conn) + return EINVAL; + + if (!conn->tc || !conn->established) + return ENOTCONN; + + list_append(&conn->kal, &ka->le, ka); + + if (!tmr_isrunning(&conn->tmr_ka)) { + + interval = MAX(interval ? interval : TCP_KEEPALIVE_INTVAL, + TCP_KEEPALIVE_TIMEOUT * 2); + + conn->ka_interval = interval; + + tmr_start(&conn->tmr_ka, sip_keepalive_wait(conn->ka_interval), + conn_keepalive_handler, conn); + } + + return 0; +} diff --git a/src/sipreg/reg.c b/src/sipreg/reg.c index a108c0e..8665ae4 100644 --- a/src/sipreg/reg.c +++ b/src/sipreg/reg.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -18,7 +19,6 @@ enum { DEFAULT_EXPIRES = 3600, - FAIL_WAIT = 60 * 1000, }; @@ -27,6 +27,7 @@ struct sipreg { struct sa laddr; struct tmr tmr; struct sip *sip; + struct sip_keepalive *ka; struct sip_request *req; struct sip_dialog *dlg; struct sip_auth *auth; @@ -35,11 +36,13 @@ struct sipreg { sip_resp_h *resph; void *arg; uint32_t expires; + uint32_t failc; uint32_t wait; enum sip_transp tp; bool registered; bool terminated; char *params; + int regid; }; @@ -76,6 +79,7 @@ static void destructor(void *arg) } } + mem_deref(reg->ka); mem_deref(reg->dlg); mem_deref(reg->auth); mem_deref(reg->cuser); @@ -85,6 +89,12 @@ static void destructor(void *arg) } +static uint32_t failwait(uint32_t failc) +{ + return min(1800, (30 * (1<tmr, FAIL_WAIT, tmr_handler, reg); + tmr_start(®->tmr, failwait(++reg->failc), tmr_handler, reg); reg->resph(err, NULL, reg->arg); } } +static void keepalive_handler(int err, void *arg) +{ + struct sipreg *reg = arg; + + /* failure will be handled in response handler */ + if (reg->req || reg->terminated) + return; + + tmr_start(®->tmr, failwait(++reg->failc), tmr_handler, reg); + reg->resph(err, NULL, reg->arg); +} + + +static void start_outbound(struct sipreg *reg, const struct sip_msg *msg) +{ + const struct sip_hdr *flowtimer; + + if (!sip_msg_hdr_has_value(msg, SIP_HDR_REQUIRE, "outbound")) + return; + + flowtimer = sip_msg_hdr(msg, SIP_HDR_FLOW_TIMER); + + (void)sip_keepalive_start(®->ka, reg->sip, msg, + flowtimer ? pl_u32(&flowtimer->val) : 0, + keepalive_handler, reg); +} + + static bool contact_handler(const struct sip_hdr *hdr, const struct sip_msg *msg, void *arg) { @@ -133,10 +171,12 @@ static void response_handler(int err, const struct sip_msg *msg, void *arg) const struct sip_hdr *minexp; struct sipreg *reg = arg; - reg->wait = FAIL_WAIT; + reg->wait = failwait(reg->failc + 1); - if (err || sip_request_loops(®->ls, msg->scode)) + if (err || sip_request_loops(®->ls, msg->scode)) { + reg->failc++; goto out; + } if (msg->scode < 200) { return; @@ -147,6 +187,10 @@ static void response_handler(int err, const struct sip_msg *msg, void *arg) sip_msg_hdr_apply(msg, true, SIP_HDR_CONTACT, contact_handler, reg); reg->wait *= 900; + reg->failc = 0; + + if (reg->regid > 0 && !reg->terminated && !reg->ka) + start_outbound(reg, msg); } else { if (reg->terminated && !reg->registered) @@ -185,6 +229,8 @@ static void response_handler(int err, const struct sip_msg *msg, void *arg) return; } + + ++reg->failc; } out: @@ -206,6 +252,8 @@ static int send_handler(enum sip_transp tp, const struct sa *src, const struct sa *dst, struct mbuf *mb, void *arg) { struct sipreg *reg = arg; + int err; + (void)dst; if (reg->expires > 0) { @@ -213,11 +261,18 @@ static int send_handler(enum sip_transp tp, const struct sa *src, reg->tp = tp; } - return mbuf_printf(mb, "Contact: ;expires=%u%s%s\r\n", - reg->cuser, ®->laddr, sip_transp_param(reg->tp), - reg->expires, - reg->params ? ";" : "", - reg->params ? reg->params : ""); + err = mbuf_printf(mb, "Contact: ;expires=%u%s%s", + reg->cuser, ®->laddr, sip_transp_param(reg->tp), + reg->expires, + reg->params ? ";" : "", + reg->params ? reg->params : ""); + + if (reg->regid > 0) + err |= mbuf_printf(mb, ";reg-id=%d", reg->regid); + + err |= mbuf_printf(mb, "\r\n"); + + return err; } @@ -231,9 +286,12 @@ static int request(struct sipreg *reg, bool reset_ls) return sip_drequestf(®->req, reg->sip, true, "REGISTER", reg->dlg, 0, reg->auth, send_handler, response_handler, reg, + "%s" "%b" "Content-Length: 0\r\n" "\r\n", + reg->regid > 0 + ? "Supported: outbound, path\r\n" : "", reg->hdrs ? mbuf_buf(reg->hdrs) : NULL, reg->hdrs ? mbuf_get_left(reg->hdrs) : 0); } @@ -242,7 +300,7 @@ static int request(struct sipreg *reg, bool reset_ls) int sipreg_register(struct sipreg **regp, struct sip *sip, const char *reg_uri, const char *to_uri, const char *from_uri, uint32_t expires, const char *cuser, const char *routev[], uint32_t routec, - sip_auth_h *authh, void *aarg, bool aref, + int regid, sip_auth_h *authh, void *aarg, bool aref, sip_resp_h *resph, void *arg, const char *params, const char *fmt, ...) { @@ -295,6 +353,7 @@ int sipreg_register(struct sipreg **regp, struct sip *sip, const char *reg_uri, reg->expires = expires; reg->resph = resph ? resph : dummy_handler; reg->arg = arg; + reg->regid = regid; err = request(reg, true); if (err)