Squashed 'third_party/rawrtc/re/' content from commit f3163ce8b
Change-Id: I6a235e6ac0f03269d951026f9d195da05c40fdab
git-subtree-dir: third_party/rawrtc/re
git-subtree-split: f3163ce8b526a13b35ef71ce4dd6f43585064d8a
diff --git a/src/sip/transp.c b/src/sip/transp.c
new file mode 100644
index 0000000..aed33bb
--- /dev/null
+++ b/src/sip/transp.c
@@ -0,0 +1,985 @@
+/**
+ * @file sip/transp.c SIP Transport
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#include <string.h>
+#include <re_types.h>
+#include <re_mem.h>
+#include <re_mbuf.h>
+#include <re_sa.h>
+#include <re_list.h>
+#include <re_hash.h>
+#include <re_fmt.h>
+#include <re_uri.h>
+#include <re_sys.h>
+#include <re_tmr.h>
+#include <re_udp.h>
+#include <re_stun.h>
+#include <re_srtp.h>
+#include <re_tcp.h>
+#include <re_tls.h>
+#include <re_msg.h>
+#include <re_sip.h>
+#include "sip.h"
+
+
+enum {
+ TCP_ACCEPT_TIMEOUT = 32,
+ TCP_IDLE_TIMEOUT = 900,
+ TCP_KEEPALIVE_TIMEOUT = 10,
+ TCP_KEEPALIVE_INTVAL = 120,
+ TCP_BUFSIZE_MAX = 65536,
+};
+
+
+struct sip_transport {
+ struct le le;
+ struct sa laddr;
+ struct sip *sip;
+ struct tls *tls;
+ void *sock;
+ enum sip_transp tp;
+};
+
+
+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;
+};
+
+
+struct sip_connqent {
+ struct le le;
+ struct mbuf *mb;
+ struct sip_connqent **qentp;
+ sip_transp_h *transph;
+ void *arg;
+};
+
+
+static uint8_t crlfcrlf[4] = {0x0d, 0x0a, 0x0d, 0x0a};
+
+
+static void internal_transport_handler(int err, void *arg)
+{
+ (void)err;
+ (void)arg;
+}
+
+
+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);
+}
+
+
+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);
+ mem_deref(conn->tc);
+ mem_deref(conn->mb);
+}
+
+
+static void qent_destructor(void *arg)
+{
+ struct sip_connqent *qent = arg;
+
+ if (qent->qentp)
+ *qent->qentp = NULL;
+
+ list_unlink(&qent->le);
+ mem_deref(qent->mb);
+}
+
+
+static const struct sip_transport *transp_find(struct sip *sip,
+ enum sip_transp tp,
+ int af, const struct sa *dst)
+{
+ struct le *le;
+ (void)dst;
+
+ for (le = sip->transpl.head; le; le = le->next) {
+
+ const struct sip_transport *transp = le->data;
+
+ if (transp->tp != tp)
+ continue;
+
+ if (af != AF_UNSPEC && sa_af(&transp->laddr) != af)
+ continue;
+
+ return transp;
+ }
+
+ return NULL;
+}
+
+
+static struct sip_conn *conn_find(struct sip *sip, const struct sa *paddr,
+ bool secure)
+{
+ struct le *le;
+
+ le = list_head(hash_list(sip->ht_conn, sa_hash(paddr, SA_ALL)));
+
+ for (; le; le = le->next) {
+
+ struct sip_conn *conn = le->data;
+
+ if (!secure != (conn->sc == NULL))
+ continue;
+
+ if (!sa_cmp(&conn->paddr, paddr, SA_ALL))
+ continue;
+
+ return conn;
+ }
+
+ return NULL;
+}
+
+
+static void conn_close(struct sip_conn *conn, int err)
+{
+ struct le *le;
+
+ 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);
+
+ le = list_head(&conn->ql);
+
+ while (le) {
+
+ struct sip_connqent *qent = le->data;
+ le = le->next;
+
+ if (qent->qentp) {
+ *qent->qentp = NULL;
+ qent->qentp = NULL;
+ }
+
+ qent->transph(err, qent->arg);
+ list_unlink(&qent->le);
+ mem_deref(qent);
+ }
+
+ sip_keepalive_signal(&conn->kal, err);
+}
+
+
+static void conn_tmr_handler(void *arg)
+{
+ struct sip_conn *conn = arg;
+
+ conn_close(conn, ETIMEDOUT);
+ mem_deref(conn);
+}
+
+
+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;
+
+ while (le) {
+ struct sip_lsnr *lsnr = le->data;
+
+ le = le->next;
+
+ if (msg->req != lsnr->req)
+ continue;
+
+ if (lsnr->msgh(msg, lsnr->arg))
+ return;
+ }
+
+ if (msg->req) {
+ (void)re_fprintf(stderr, "unhandeled request from %J: %r %r\n",
+ &msg->src, &msg->met, &msg->ruri);
+
+ if (!pl_strcmp(&msg->met, "CANCEL"))
+ (void)sip_reply(sip, msg,
+ 481, "Transaction Does Not Exist");
+ else
+ (void)sip_reply(sip, msg,
+ 501, "Not Implemented");
+ }
+ else {
+ (void)re_fprintf(stderr, "unhandeled response from %J:"
+ " %u %r (%r)\n", &msg->src,
+ msg->scode, &msg->reason, &msg->cseq.met);
+ }
+}
+
+
+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: %m\n", err);
+ return;
+ }
+
+ msg->sock = mem_ref(transp->sock);
+ msg->src = *src;
+ msg->dst = transp->laddr;
+ msg->tp = SIP_TRANSP_UDP;
+
+ sip_recv(transp->sip, msg);
+
+ mem_deref(msg);
+}
+
+
+static void tcp_recv_handler(struct mbuf *mb, void *arg)
+{
+ struct sip_conn *conn = arg;
+ size_t pos;
+ int err = 0;
+
+ if (conn->mb) {
+ pos = conn->mb->pos;
+
+ conn->mb->pos = conn->mb->end;
+
+ err = mbuf_write_mem(conn->mb, mbuf_buf(mb),mbuf_get_left(mb));
+ if (err)
+ goto out;
+
+ conn->mb->pos = pos;
+
+ if (mbuf_get_left(conn->mb) > TCP_BUFSIZE_MAX) {
+ err = EOVERFLOW;
+ goto out;
+ }
+ }
+ else {
+ conn->mb = mem_ref(mb);
+ }
+
+ for (;;) {
+ struct sip_msg *msg;
+ uint32_t clen;
+ size_t end;
+
+ if (mbuf_get_left(conn->mb) < 2)
+ break;
+
+ if (!memcmp(mbuf_buf(conn->mb), "\r\n", 2)) {
+
+ tmr_start(&conn->tmr, TCP_IDLE_TIMEOUT * 1000,
+ conn_tmr_handler, conn);
+
+ 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;
+
+ conn->mb = mem_deref(conn->mb);
+ break;
+ }
+
+ pos = conn->mb->pos;
+
+ err = sip_msg_decode(&msg, conn->mb);
+ if (err) {
+ if (err == ENODATA)
+ err = 0;
+ break;
+ }
+
+ if (!msg->clen.p) {
+ mem_deref(msg);
+ err = EBADMSG;
+ break;
+ }
+
+ clen = pl_u32(&msg->clen);
+
+ if (mbuf_get_left(conn->mb) < clen) {
+ conn->mb->pos = pos;
+ mem_deref(msg);
+ break;
+ }
+
+ tmr_start(&conn->tmr, TCP_IDLE_TIMEOUT * 1000,
+ conn_tmr_handler, conn);
+
+ end = conn->mb->end;
+
+ msg->mb->end = msg->mb->pos + clen;
+ msg->sock = mem_ref(conn);
+ msg->src = conn->paddr;
+ msg->dst = conn->laddr;
+ msg->tp = conn->sc ? SIP_TRANSP_TLS : SIP_TRANSP_TCP;
+
+ sip_recv(conn->sip, msg);
+ mem_deref(msg);
+
+ if (end <= conn->mb->end) {
+ conn->mb = mem_deref(conn->mb);
+ break;
+ }
+
+ mb = mbuf_alloc(end - conn->mb->end);
+ if (!mb) {
+ err = ENOMEM;
+ goto out;
+ }
+
+ (void)mbuf_write_mem(mb, &conn->mb->buf[conn->mb->end],
+ end - conn->mb->end);
+
+ mb->pos = 0;
+
+ mem_deref(conn->mb);
+ conn->mb = mb;
+ }
+
+ out:
+ if (err) {
+ conn_close(conn, err);
+ mem_deref(conn);
+ }
+}
+
+
+static void tcp_estab_handler(void *arg)
+{
+ struct sip_conn *conn = arg;
+ struct le *le;
+ int err;
+
+#ifdef WIN32
+ tcp_conn_local_get(conn->tc, &conn->laddr);
+#endif
+
+ conn->established = true;
+
+ le = list_head(&conn->ql);
+
+ while (le) {
+
+ struct sip_connqent *qent = le->data;
+ le = le->next;
+
+ if (qent->qentp) {
+ *qent->qentp = NULL;
+ qent->qentp = NULL;
+ }
+
+ err = tcp_send(conn->tc, qent->mb);
+ if (err)
+ qent->transph(err, qent->arg);
+
+ list_unlink(&qent->le);
+ mem_deref(qent);
+ }
+}
+
+
+static void tcp_close_handler(int err, void *arg)
+{
+ struct sip_conn *conn = arg;
+
+ conn_close(conn, err ? err : ECONNRESET);
+ mem_deref(conn);
+}
+
+
+static void tcp_connect_handler(const struct sa *paddr, void *arg)
+{
+ struct sip_transport *transp = arg;
+ struct sip_conn *conn;
+ int err;
+
+ conn = mem_zalloc(sizeof(*conn), conn_destructor);
+ if (!conn) {
+ err = ENOMEM;
+ goto out;
+ }
+
+ hash_append(transp->sip->ht_conn, sa_hash(paddr, SA_ALL),
+ &conn->he, conn);
+
+ conn->paddr = *paddr;
+ conn->sip = transp->sip;
+
+ err = tcp_accept(&conn->tc, transp->sock, tcp_estab_handler,
+ tcp_recv_handler, tcp_close_handler, conn);
+ if (err)
+ goto out;
+
+ err = tcp_conn_local_get(conn->tc, &conn->laddr);
+ if (err)
+ goto out;
+
+#ifdef USE_TLS
+ if (transp->tls) {
+ err = tls_start_tcp(&conn->sc, transp->tls, conn->tc, 0);
+ if (err)
+ goto out;
+ }
+#endif
+
+ tmr_start(&conn->tmr, TCP_ACCEPT_TIMEOUT * 1000,
+ conn_tmr_handler, conn);
+
+ out:
+ if (err) {
+ tcp_reject(transp->sock);
+ mem_deref(conn);
+ }
+}
+
+
+static int conn_send(struct sip_connqent **qentp, struct sip *sip, bool secure,
+ const struct sa *dst, struct mbuf *mb,
+ sip_transp_h *transph, void *arg)
+{
+ struct sip_conn *conn, *new_conn = NULL;
+ struct sip_connqent *qent;
+ int err = 0;
+
+ conn = conn_find(sip, dst, secure);
+ if (conn) {
+ if (!conn->established)
+ goto enqueue;
+
+ return tcp_send(conn->tc, mb);
+ }
+
+ new_conn = conn = mem_zalloc(sizeof(*conn), conn_destructor);
+ if (!conn)
+ return ENOMEM;
+
+ hash_append(sip->ht_conn, sa_hash(dst, SA_ALL), &conn->he, conn);
+ conn->paddr = *dst;
+ conn->sip = sip;
+
+ err = tcp_connect(&conn->tc, dst, tcp_estab_handler, tcp_recv_handler,
+ tcp_close_handler, conn);
+ if (err)
+ goto out;
+
+ err = tcp_conn_local_get(conn->tc, &conn->laddr);
+ if (err)
+ goto out;
+
+#ifdef USE_TLS
+ if (secure) {
+ const struct sip_transport *transp;
+
+ transp = transp_find(sip, SIP_TRANSP_TLS, sa_af(dst), dst);
+ if (!transp || !transp->tls) {
+ err = EPROTONOSUPPORT;
+ goto out;
+ }
+
+ err = tls_start_tcp(&conn->sc, transp->tls, conn->tc, 0);
+ if (err)
+ goto out;
+ }
+#endif
+
+ tmr_start(&conn->tmr, TCP_IDLE_TIMEOUT * 1000, conn_tmr_handler, conn);
+
+ enqueue:
+ qent = mem_zalloc(sizeof(*qent), qent_destructor);
+ if (!qent) {
+ err = ENOMEM;
+ goto out;
+
+ }
+
+ list_append(&conn->ql, &qent->le, qent);
+ qent->mb = mem_ref(mb);
+ qent->transph = transph ? transph : internal_transport_handler;
+ qent->arg = arg;
+
+ if (qentp) {
+ qent->qentp = qentp;
+ *qentp = qent;
+ }
+
+ out:
+ if (err)
+ mem_deref(new_conn);
+
+ return err;
+}
+
+
+int sip_transp_init(struct sip *sip, uint32_t sz)
+{
+ return hash_alloc(&sip->ht_conn, sz);
+}
+
+
+/**
+ * Add a SIP transport
+ *
+ * @param sip SIP stack instance
+ * @param tp SIP Transport
+ * @param laddr Local network address
+ * @param ... Optional transport parameters such as TLS context
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int sip_transp_add(struct sip *sip, enum sip_transp tp,
+ const struct sa *laddr, ...)
+{
+ struct sip_transport *transp;
+ struct tls *tls;
+ va_list ap;
+ int err;
+
+ if (!sip || !laddr || !sa_isset(laddr, SA_ADDR))
+ return EINVAL;
+
+ transp = mem_zalloc(sizeof(*transp), transp_destructor);
+ if (!transp)
+ return ENOMEM;
+
+ list_append(&sip->transpl, &transp->le, transp);
+ transp->sip = sip;
+ transp->tp = tp;
+
+ va_start(ap, laddr);
+
+ switch (tp) {
+
+ case SIP_TRANSP_UDP:
+ err = udp_listen((struct udp_sock **)&transp->sock, laddr,
+ udp_recv_handler, transp);
+ if (err)
+ break;
+
+ err = udp_local_get(transp->sock, &transp->laddr);
+ break;
+
+ case SIP_TRANSP_TLS:
+ tls = va_arg(ap, struct tls *);
+ if (!tls) {
+ err = EINVAL;
+ break;
+ }
+
+ transp->tls = mem_ref(tls);
+
+ /*@fallthrough@*/
+
+ case SIP_TRANSP_TCP:
+ err = tcp_listen((struct tcp_sock **)&transp->sock, laddr,
+ tcp_connect_handler, transp);
+ if (err)
+ break;
+
+ err = tcp_sock_local_get(transp->sock, &transp->laddr);
+ break;
+
+ default:
+ err = EPROTONOSUPPORT;
+ break;
+ }
+
+ va_end(ap);
+
+ if (err)
+ mem_deref(transp);
+
+ return err;
+}
+
+
+/**
+ * Flush all transports of a SIP stack instance
+ *
+ * @param sip SIP stack instance
+ */
+void sip_transp_flush(struct sip *sip)
+{
+ if (!sip)
+ return;
+
+ hash_flush(sip->ht_conn);
+ list_flush(&sip->transpl);
+}
+
+
+int sip_transp_send(struct sip_connqent **qentp, struct sip *sip, void *sock,
+ enum sip_transp tp, const struct sa *dst, struct mbuf *mb,
+ sip_transp_h *transph, void *arg)
+{
+ const struct sip_transport *transp;
+ struct sip_conn *conn;
+ bool secure = false;
+ int err;
+
+ if (!sip || !dst || !mb)
+ return EINVAL;
+
+ switch (tp) {
+
+ case SIP_TRANSP_UDP:
+ if (!sock) {
+ transp = transp_find(sip, tp, sa_af(dst), dst);
+ if (!transp)
+ return EPROTONOSUPPORT;
+
+ sock = transp->sock;
+ }
+
+ err = udp_send(sock, dst, mb);
+ break;
+
+ case SIP_TRANSP_TLS:
+ secure = true;
+ /*@fallthrough@*/
+
+ case SIP_TRANSP_TCP:
+ conn = sock;
+
+ if (conn && conn->tc)
+ err = tcp_send(conn->tc, mb);
+ else
+ err = conn_send(qentp, sip, secure, dst, mb,
+ transph, arg);
+ break;
+
+ default:
+ err = EPROTONOSUPPORT;
+ break;
+ }
+
+ return err;
+}
+
+
+int sip_transp_laddr(struct sip *sip, struct sa *laddr, enum sip_transp tp,
+ const struct sa *dst)
+{
+ const struct sip_transport *transp;
+
+ if (!sip || !laddr)
+ return EINVAL;
+
+ transp = transp_find(sip, tp, sa_af(dst), dst);
+ if (!transp)
+ return EPROTONOSUPPORT;
+
+ *laddr = transp->laddr;
+
+ return 0;
+}
+
+
+bool sip_transp_supported(struct sip *sip, enum sip_transp tp, int af)
+{
+ if (!sip)
+ return false;
+
+ return transp_find(sip, tp, af, NULL) != NULL;
+}
+
+
+/**
+ * Check if network address is part of SIP transports
+ *
+ * @param sip SIP stack instance
+ * @param tp SIP transport
+ * @param laddr Local network address to check
+ *
+ * @return True if part of SIP transports, otherwise false
+ */
+bool sip_transp_isladdr(const struct sip *sip, enum sip_transp tp,
+ const struct sa *laddr)
+{
+ struct le *le;
+
+ if (!sip || !laddr)
+ return false;
+
+ for (le=sip->transpl.head; le; le=le->next) {
+
+ const struct sip_transport *transp = le->data;
+
+ if (tp != SIP_TRANSP_NONE && transp->tp != tp)
+ continue;
+
+ if (!sa_cmp(&transp->laddr, laddr, SA_ALL))
+ continue;
+
+ return true;
+ }
+
+ return false;
+}
+
+
+/**
+ * Get the name of a given SIP Transport
+ *
+ * @param tp SIP Transport
+ *
+ * @return Name of the corresponding SIP Transport
+ */
+const char *sip_transp_name(enum sip_transp tp)
+{
+ switch (tp) {
+
+ case SIP_TRANSP_UDP: return "UDP";
+ case SIP_TRANSP_TCP: return "TCP";
+ case SIP_TRANSP_TLS: return "TLS";
+ default: return "???";
+ }
+}
+
+
+const char *sip_transp_srvid(enum sip_transp tp)
+{
+ switch (tp) {
+
+ case SIP_TRANSP_UDP: return "_sip._udp";
+ case SIP_TRANSP_TCP: return "_sip._tcp";
+ case SIP_TRANSP_TLS: return "_sips._tcp";
+ default: return "???";
+ }
+}
+
+
+/**
+ * Get the transport parameters for a given SIP Transport
+ *
+ * @param tp SIP Transport
+ *
+ * @return Transport parameters of the corresponding SIP Transport
+ */
+const char *sip_transp_param(enum sip_transp tp)
+{
+ switch (tp) {
+
+ case SIP_TRANSP_UDP: return "";
+ case SIP_TRANSP_TCP: return ";transport=tcp";
+ case SIP_TRANSP_TLS: return ";transport=tls";
+ default: return "";
+ }
+}
+
+
+bool sip_transp_reliable(enum sip_transp tp)
+{
+ switch (tp) {
+
+ case SIP_TRANSP_UDP: return false;
+ case SIP_TRANSP_TCP: return true;
+ case SIP_TRANSP_TLS: return true;
+ default: return false;
+ }
+}
+
+
+/**
+ * Get the default port number for a given SIP Transport
+ *
+ * @param tp SIP Transport
+ * @param port Port number
+ *
+ * @return Corresponding port number
+ */
+uint16_t sip_transp_port(enum sip_transp tp, uint16_t port)
+{
+ if (port)
+ return port;
+
+ switch (tp) {
+
+ case SIP_TRANSP_UDP: return SIP_PORT;
+ case SIP_TRANSP_TCP: return SIP_PORT;
+ case SIP_TRANSP_TLS: return SIP_PORT_TLS;
+ default: return 0;
+ }
+}
+
+
+static bool debug_handler(struct le *le, void *arg)
+{
+ const struct sip_transport *transp = le->data;
+ struct re_printf *pf = arg;
+
+ (void)re_hprintf(pf, " %J (%s)\n",
+ &transp->laddr,
+ sip_transp_name(transp->tp));
+
+ return false;
+}
+
+
+int sip_transp_debug(struct re_printf *pf, const struct sip *sip)
+{
+ int err;
+
+ err = re_hprintf(pf, "transports:\n");
+ list_apply(&sip->transpl, true, debug_handler, pf);
+
+ return err;
+}
+
+
+/**
+ * Get the TCP Connection from a SIP Message
+ *
+ * @param msg SIP Message
+ *
+ * @return TCP Connection if reliable transport, otherwise NULL
+ */
+struct tcp_conn *sip_msg_tcpconn(const struct sip_msg *msg)
+{
+ if (!msg || !msg->sock)
+ return NULL;
+
+ switch (msg->tp) {
+
+ case SIP_TRANSP_TCP:
+ case SIP_TRANSP_TLS:
+ return ((struct sip_conn *)msg->sock)->tc;
+
+ default:
+ 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;
+}