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/bfcp/attr.c b/src/bfcp/attr.c
new file mode 100644
index 0000000..7607fe0
--- /dev/null
+++ b/src/bfcp/attr.c
@@ -0,0 +1,746 @@
+/**
+ * @file bfcp/attr.c BFCP Attributes
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#include <string.h>
+#include <re_types.h>
+#include <re_fmt.h>
+#include <re_mem.h>
+#include <re_mbuf.h>
+#include <re_sa.h>
+#include <re_list.h>
+#include <re_tmr.h>
+#include <re_bfcp.h>
+#include "bfcp.h"
+
+
+enum {
+ BFCP_ATTR_HDR_SIZE = 2,
+};
+
+
+static void destructor(void *arg)
+{
+ struct bfcp_attr *attr = arg;
+
+ switch (attr->type) {
+
+ case BFCP_ERROR_CODE:
+ mem_deref(attr->v.errcode.details);
+ break;
+
+ case BFCP_ERROR_INFO:
+ case BFCP_PART_PROV_INFO:
+ case BFCP_STATUS_INFO:
+ case BFCP_USER_DISP_NAME:
+ case BFCP_USER_URI:
+ mem_deref(attr->v.str);
+ break;
+
+ case BFCP_SUPPORTED_ATTRS:
+ mem_deref(attr->v.supattr.attrv);
+ break;
+
+ case BFCP_SUPPORTED_PRIMS:
+ mem_deref(attr->v.supprim.primv);
+ break;
+
+ default:
+ break;
+ }
+
+ list_flush(&attr->attrl);
+ list_unlink(&attr->le);
+}
+
+
+static int attr_encode(struct mbuf *mb, bool mand, enum bfcp_attrib type,
+ const void *v)
+{
+ const struct bfcp_reqstatus *reqstatus = v;
+ const struct bfcp_errcode *errcode = v;
+ const struct bfcp_supattr *supattr = v;
+ const struct bfcp_supprim *supprim = v;
+ const enum bfcp_priority *priority = v;
+ const uint16_t *u16 = v;
+ size_t start, len, i;
+ int err;
+
+ start = mb->pos;
+ mb->pos += BFCP_ATTR_HDR_SIZE;
+
+ switch (type) {
+
+ case BFCP_BENEFICIARY_ID:
+ case BFCP_FLOOR_ID:
+ case BFCP_FLOOR_REQUEST_ID:
+ case BFCP_BENEFICIARY_INFO:
+ case BFCP_FLOOR_REQ_INFO:
+ case BFCP_REQUESTED_BY_INFO:
+ case BFCP_FLOOR_REQ_STATUS:
+ case BFCP_OVERALL_REQ_STATUS:
+ err = mbuf_write_u16(mb, htons(*u16));
+ break;
+
+ case BFCP_PRIORITY:
+ err = mbuf_write_u8(mb, *priority << 5);
+ err |= mbuf_write_u8(mb, 0x00);
+ break;
+
+ case BFCP_REQUEST_STATUS:
+ err = mbuf_write_u8(mb, reqstatus->status);
+ err |= mbuf_write_u8(mb, reqstatus->qpos);
+ break;
+
+ case BFCP_ERROR_CODE:
+ err = mbuf_write_u8(mb, errcode->code);
+ if (errcode->details && errcode->len)
+ err |= mbuf_write_mem(mb, errcode->details,
+ errcode->len);
+ break;
+
+ case BFCP_ERROR_INFO:
+ case BFCP_PART_PROV_INFO:
+ case BFCP_STATUS_INFO:
+ case BFCP_USER_DISP_NAME:
+ case BFCP_USER_URI:
+ err = mbuf_write_str(mb, v);
+ break;
+
+ case BFCP_SUPPORTED_ATTRS:
+ for (i=0, err=0; i<supattr->attrc; i++)
+ err |= mbuf_write_u8(mb, supattr->attrv[i] << 1);
+ break;
+
+ case BFCP_SUPPORTED_PRIMS:
+ for (i=0, err=0; i<supprim->primc; i++)
+ err |= mbuf_write_u8(mb, supprim->primv[i]);
+ break;
+
+ default:
+ err = EINVAL;
+ break;
+ }
+
+ /* header */
+ len = mb->pos - start;
+
+ mb->pos = start;
+ err |= mbuf_write_u8(mb, (type<<1) | (mand ? 1 : 0));
+ err |= mbuf_write_u8(mb, len);
+ mb->pos += (len - BFCP_ATTR_HDR_SIZE);
+
+ /* padding */
+ while ((mb->pos - start) & 0x03)
+ err |= mbuf_write_u8(mb, 0x00);
+
+ return err;
+}
+
+
+/**
+ * Encode BFCP Attributes with variable arguments
+ *
+ * @param mb Mbuf to encode into
+ * @param attrc Number of attributes
+ * @param ap Variable argument of attributes
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int bfcp_attrs_vencode(struct mbuf *mb, unsigned attrc, va_list *ap)
+{
+ unsigned i;
+
+ if (!mb)
+ return EINVAL;
+
+ for (i=0; i<attrc; i++) {
+
+ int type = va_arg(*ap, int);
+ unsigned subc = va_arg(*ap, unsigned);
+ const void *v = va_arg(*ap, const void *);
+ size_t start, len;
+ int err;
+
+ if (!v)
+ continue;
+
+ start = mb->pos;
+
+ if (type == BFCP_ENCODE_HANDLER) {
+
+ const struct bfcp_encode *enc = v;
+
+ if (enc->ench) {
+ err = enc->ench(mb, enc->arg);
+ if (err)
+ return err;
+ }
+
+ continue;
+ }
+
+ err = attr_encode(mb, type>>7, type & 0x7f, v);
+ if (err)
+ return err;
+
+ if (subc == 0)
+ continue;
+
+ err = bfcp_attrs_vencode(mb, subc, ap);
+ if (err)
+ return err;
+
+ /* update total length for grouped attributes */
+ len = mb->pos - start;
+
+ mb->pos = start + 1;
+ err = mbuf_write_u8(mb, (uint8_t)len);
+ if (err)
+ return err;
+
+ mb->pos += (len - BFCP_ATTR_HDR_SIZE);
+ }
+
+ return 0;
+}
+
+
+/**
+ * Encode BFCP Attributes
+ *
+ * @param mb Mbuf to encode into
+ * @param attrc Number of attributes
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int bfcp_attrs_encode(struct mbuf *mb, unsigned attrc, ...)
+{
+ va_list ap;
+ int err;
+
+ va_start(ap, attrc);
+ err = bfcp_attrs_vencode(mb, attrc, &ap);
+ va_end(ap);
+
+ return err;
+}
+
+
+static int attr_decode(struct bfcp_attr **attrp, struct mbuf *mb,
+ struct bfcp_unknown_attr *uma)
+{
+ struct bfcp_attr *attr;
+ union bfcp_union *v;
+ size_t i, start, len;
+ int err = 0;
+ uint8_t b;
+
+ if (mbuf_get_left(mb) < BFCP_ATTR_HDR_SIZE)
+ return EBADMSG;
+
+ attr = mem_zalloc(sizeof(*attr), destructor);
+ if (!attr)
+ return ENOMEM;
+
+ start = mb->pos;
+
+ b = mbuf_read_u8(mb);
+ attr->type = b >> 1;
+ attr->mand = b & 1;
+ len = mbuf_read_u8(mb);
+
+ if (len < BFCP_ATTR_HDR_SIZE)
+ goto badmsg;
+
+ len -= BFCP_ATTR_HDR_SIZE;
+
+ if (mbuf_get_left(mb) < len)
+ goto badmsg;
+
+ v = &attr->v;
+
+ switch (attr->type) {
+
+ case BFCP_BENEFICIARY_ID:
+ case BFCP_FLOOR_ID:
+ case BFCP_FLOOR_REQUEST_ID:
+ if (len < 2)
+ goto badmsg;
+
+ v->u16 = ntohs(mbuf_read_u16(mb));
+ break;
+
+ case BFCP_PRIORITY:
+ if (len < 2)
+ goto badmsg;
+
+ v->priority = mbuf_read_u8(mb) >> 5;
+ (void)mbuf_read_u8(mb);
+ break;
+
+ case BFCP_REQUEST_STATUS:
+ if (len < 2)
+ goto badmsg;
+
+ v->reqstatus.status = mbuf_read_u8(mb);
+ v->reqstatus.qpos = mbuf_read_u8(mb);
+ break;
+
+ case BFCP_ERROR_CODE:
+ if (len < 1)
+ goto badmsg;
+
+ v->errcode.code = mbuf_read_u8(mb);
+ v->errcode.len = len - 1;
+
+ if (v->errcode.len == 0)
+ break;
+
+ v->errcode.details = mem_alloc(v->errcode.len, NULL);
+ if (!v->errcode.details) {
+ err = ENOMEM;
+ goto error;
+ }
+
+ (void)mbuf_read_mem(mb, v->errcode.details,
+ v->errcode.len);
+ break;
+
+ case BFCP_ERROR_INFO:
+ case BFCP_PART_PROV_INFO:
+ case BFCP_STATUS_INFO:
+ case BFCP_USER_DISP_NAME:
+ case BFCP_USER_URI:
+ err = mbuf_strdup(mb, &v->str, len);
+ break;
+
+ case BFCP_SUPPORTED_ATTRS:
+ v->supattr.attrc = len;
+ v->supattr.attrv = mem_alloc(len*sizeof(*v->supattr.attrv),
+ NULL);
+ if (!v->supattr.attrv) {
+ err = ENOMEM;
+ goto error;
+ }
+
+ for (i=0; i<len; i++)
+ v->supattr.attrv[i] = mbuf_read_u8(mb) >> 1;
+ break;
+
+ case BFCP_SUPPORTED_PRIMS:
+ v->supprim.primc = len;
+ v->supprim.primv = mem_alloc(len * sizeof(*v->supprim.primv),
+ NULL);
+ if (!v->supprim.primv) {
+ err = ENOMEM;
+ goto error;
+ }
+
+ for (i=0; i<len; i++)
+ v->supprim.primv[i] = mbuf_read_u8(mb);
+ break;
+
+ /* grouped attributes */
+
+ case BFCP_BENEFICIARY_INFO:
+ case BFCP_FLOOR_REQ_INFO:
+ case BFCP_REQUESTED_BY_INFO:
+ case BFCP_FLOOR_REQ_STATUS:
+ case BFCP_OVERALL_REQ_STATUS:
+ if (len < 2)
+ goto badmsg;
+
+ v->u16 = ntohs(mbuf_read_u16(mb));
+ err = bfcp_attrs_decode(&attr->attrl, mb, len - 2, uma);
+ break;
+
+ default:
+ mb->pos += len;
+
+ if (!attr->mand)
+ break;
+
+ if (uma && uma->typec < ARRAY_SIZE(uma->typev))
+ uma->typev[uma->typec++] = attr->type<<1;
+ break;
+ }
+
+ if (err)
+ goto error;
+
+ /* padding */
+ while (((mb->pos - start) & 0x03) && mbuf_get_left(mb))
+ ++mb->pos;
+
+ *attrp = attr;
+
+ return 0;
+
+ badmsg:
+ err = EBADMSG;
+ error:
+ mem_deref(attr);
+
+ return err;
+}
+
+
+int bfcp_attrs_decode(struct list *attrl, struct mbuf *mb, size_t len,
+ struct bfcp_unknown_attr *uma)
+{
+ int err = 0;
+ size_t end;
+
+ if (!attrl || !mb || mbuf_get_left(mb) < len)
+ return EINVAL;
+
+ end = mb->end;
+ mb->end = mb->pos + len;
+
+ while (mbuf_get_left(mb) >= BFCP_ATTR_HDR_SIZE) {
+
+ struct bfcp_attr *attr;
+
+ err = attr_decode(&attr, mb, uma);
+ if (err)
+ break;
+
+ list_append(attrl, &attr->le, attr);
+ }
+
+ mb->end = end;
+
+ return err;
+}
+
+
+struct bfcp_attr *bfcp_attrs_find(const struct list *attrl,
+ enum bfcp_attrib type)
+{
+ struct le *le = list_head(attrl);
+
+ while (le) {
+ struct bfcp_attr *attr = le->data;
+
+ le = le->next;
+
+ if (attr->type == type)
+ return attr;
+ }
+
+ return NULL;
+}
+
+
+struct bfcp_attr *bfcp_attrs_apply(const struct list *attrl,
+ bfcp_attr_h *h, void *arg)
+{
+ struct le *le = list_head(attrl);
+
+ while (le) {
+ struct bfcp_attr *attr = le->data;
+
+ le = le->next;
+
+ if (h && h(attr, arg))
+ return attr;
+ }
+
+ return NULL;
+}
+
+
+/**
+ * Get a BFCP sub-attribute from a BFCP attribute
+ *
+ * @param attr BFCP attribute
+ * @param type Attribute type
+ *
+ * @return Matching BFCP attribute if found, otherwise NULL
+ */
+struct bfcp_attr *bfcp_attr_subattr(const struct bfcp_attr *attr,
+ enum bfcp_attrib type)
+{
+ if (!attr)
+ return NULL;
+
+ return bfcp_attrs_find(&attr->attrl, type);
+}
+
+
+/**
+ * Apply a function handler to all sub-attributes in a BFCP attribute
+ *
+ * @param attr BFCP attribute
+ * @param h Handler
+ * @param arg Handler argument
+ *
+ * @return BFCP attribute returned by handler, or NULL
+ */
+struct bfcp_attr *bfcp_attr_subattr_apply(const struct bfcp_attr *attr,
+ bfcp_attr_h *h, void *arg)
+{
+ if (!attr)
+ return NULL;
+
+ return bfcp_attrs_apply(&attr->attrl, h, arg);
+}
+
+
+/**
+ * Print a BFCP attribute
+ *
+ * @param pf Print function
+ * @param attr BFCP attribute
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int bfcp_attr_print(struct re_printf *pf, const struct bfcp_attr *attr)
+{
+ const union bfcp_union *v;
+ size_t i;
+ int err;
+
+ if (!attr)
+ return 0;
+
+ err = re_hprintf(pf, "%c%-28s", attr->mand ? '*' : ' ',
+ bfcp_attr_name(attr->type));
+
+ v = &attr->v;
+
+ switch (attr->type) {
+
+ case BFCP_BENEFICIARY_ID:
+ case BFCP_FLOOR_ID:
+ case BFCP_FLOOR_REQUEST_ID:
+ err |= re_hprintf(pf, "%u", v->u16);
+ break;
+
+ case BFCP_PRIORITY:
+ err |= re_hprintf(pf, "%d", v->priority);
+ break;
+
+ case BFCP_REQUEST_STATUS:
+ err |= re_hprintf(pf, "%s (%d), qpos=%u",
+ bfcp_reqstatus_name(v->reqstatus.status),
+ v->reqstatus.status,
+ v->reqstatus.qpos);
+ break;
+
+ case BFCP_ERROR_CODE:
+ err |= re_hprintf(pf, "%d (%s)", v->errcode.code,
+ bfcp_errcode_name(v->errcode.code));
+
+ if (v->errcode.code == BFCP_UNKNOWN_MAND_ATTR) {
+
+ for (i=0; i<v->errcode.len; i++) {
+
+ uint8_t type = v->errcode.details[i] >> 1;
+
+ err |= re_hprintf(pf, " %s",
+ bfcp_attr_name(type));
+ }
+ }
+ break;
+
+ case BFCP_ERROR_INFO:
+ case BFCP_PART_PROV_INFO:
+ case BFCP_STATUS_INFO:
+ case BFCP_USER_DISP_NAME:
+ case BFCP_USER_URI:
+ err |= re_hprintf(pf, "\"%s\"", v->str);
+ break;
+
+ case BFCP_SUPPORTED_ATTRS:
+ err |= re_hprintf(pf, "%zu:", v->supattr.attrc);
+
+ for (i=0; i<v->supattr.attrc; i++) {
+
+ const enum bfcp_attrib type = v->supattr.attrv[i];
+
+ err |= re_hprintf(pf, " %s", bfcp_attr_name(type));
+ }
+ break;
+
+ case BFCP_SUPPORTED_PRIMS:
+ err |= re_hprintf(pf, "%zu:", v->supprim.primc);
+
+ for (i=0; i<v->supprim.primc; i++) {
+
+ const enum bfcp_prim prim = v->supprim.primv[i];
+
+ err |= re_hprintf(pf, " %s", bfcp_prim_name(prim));
+ }
+ break;
+
+ /* Grouped Attributes */
+
+ case BFCP_BENEFICIARY_INFO:
+ err |= re_hprintf(pf, "beneficiary-id=%u", v->beneficiaryid);
+ break;
+
+ case BFCP_FLOOR_REQ_INFO:
+ err |= re_hprintf(pf, "floor-request-id=%u", v->floorreqid);
+ break;
+
+ case BFCP_REQUESTED_BY_INFO:
+ err |= re_hprintf(pf, "requested-by-id=%u", v->reqbyid);
+ break;
+
+ case BFCP_FLOOR_REQ_STATUS:
+ err |= re_hprintf(pf, "floor-id=%u", v->floorid);
+ break;
+
+ case BFCP_OVERALL_REQ_STATUS:
+ err |= re_hprintf(pf, "floor-request-id=%u", v->floorreqid);
+ break;
+
+ default:
+ err |= re_hprintf(pf, "???");
+ break;
+ }
+
+ return err;
+}
+
+
+int bfcp_attrs_print(struct re_printf *pf, const struct list *attrl,
+ unsigned level)
+{
+ struct le *le;
+ int err = 0;
+
+ for (le=list_head(attrl); le; le=le->next) {
+
+ const struct bfcp_attr *attr = le->data;
+ unsigned i;
+
+ for (i=0; i<level; i++)
+ err |= re_hprintf(pf, " ");
+
+ err |= re_hprintf(pf, "%H\n", bfcp_attr_print, attr);
+ err |= bfcp_attrs_print(pf, &attr->attrl, level + 1);
+ }
+
+ return err;
+}
+
+
+/**
+ * Get the BFCP attribute name
+ *
+ * @param type BFCP attribute type
+ *
+ * @return String with BFCP attribute name
+ */
+const char *bfcp_attr_name(enum bfcp_attrib type)
+{
+ switch (type) {
+
+ case BFCP_BENEFICIARY_ID: return "BENEFICIARY-ID";
+ case BFCP_FLOOR_ID: return "FLOOR-ID";
+ case BFCP_FLOOR_REQUEST_ID: return "FLOOR-REQUEST-ID";
+ case BFCP_PRIORITY: return "PRIORITY";
+ case BFCP_REQUEST_STATUS: return "REQUEST-STATUS";
+ case BFCP_ERROR_CODE: return "ERROR-CODE";
+ case BFCP_ERROR_INFO: return "ERROR-INFO";
+ case BFCP_PART_PROV_INFO: return "PARTICIPANT-PROVIDED-INFO";
+ case BFCP_STATUS_INFO: return "STATUS-INFO";
+ case BFCP_SUPPORTED_ATTRS: return "SUPPORTED-ATTRIBUTES";
+ case BFCP_SUPPORTED_PRIMS: return "SUPPORTED-PRIMITIVES";
+ case BFCP_USER_DISP_NAME: return "USER-DISPLAY-NAME";
+ case BFCP_USER_URI: return "USER-URI";
+ case BFCP_BENEFICIARY_INFO: return "BENEFICIARY-INFORMATION";
+ case BFCP_FLOOR_REQ_INFO: return "FLOOR-REQUEST-INFORMATION";
+ case BFCP_REQUESTED_BY_INFO: return "REQUESTED-BY-INFORMATION";
+ case BFCP_FLOOR_REQ_STATUS: return "FLOOR-REQUEST-STATUS";
+ case BFCP_OVERALL_REQ_STATUS: return "OVERALL-REQUEST-STATUS";
+ default: return "???";
+ }
+}
+
+
+/**
+ * Get the BFCP Request status name
+ *
+ * @param status Request status
+ *
+ * @return String with BFCP Request status name
+ */
+const char *bfcp_reqstatus_name(enum bfcp_reqstat status)
+{
+ switch (status) {
+
+ case BFCP_PENDING: return "Pending";
+ case BFCP_ACCEPTED: return "Accepted";
+ case BFCP_GRANTED: return "Granted";
+ case BFCP_DENIED: return "Denied";
+ case BFCP_CANCELLED: return "Cancelled";
+ case BFCP_RELEASED: return "Released";
+ case BFCP_REVOKED: return "Revoked";
+ default: return "???";
+ }
+}
+
+
+/**
+ * Get the BFCP Error code name
+ *
+ * @param code BFCP Error code
+ *
+ * @return String with error code
+ */
+const char *bfcp_errcode_name(enum bfcp_err code)
+{
+ switch (code) {
+
+ case BFCP_CONF_NOT_EXIST:
+ return "Conference does not Exist";
+
+ case BFCP_USER_NOT_EXIST:
+ return "User does not Exist";
+
+ case BFCP_UNKNOWN_PRIM:
+ return "Unknown Primitive";
+
+ case BFCP_UNKNOWN_MAND_ATTR:
+ return "Unknown Mandatory Attribute";
+
+ case BFCP_UNAUTH_OPERATION:
+ return "Unauthorized Operation";
+
+ case BFCP_INVALID_FLOOR_ID:
+ return "Invalid Floor ID";
+
+ case BFCP_FLOOR_REQ_ID_NOT_EXIST:
+ return "Floor Request ID Does Not Exist";
+
+ case BFCP_MAX_FLOOR_REQ_REACHED:
+ return "You have Already Reached the Maximum Number "
+ "of Ongoing Floor Requests for this Floor";
+
+ case BFCP_USE_TLS:
+ return "Use TLS";
+
+ case BFCP_PARSE_ERROR:
+ return "Unable to Parse Message";
+
+ case BFCP_USE_DTLS:
+ return "Use DTLS";
+
+ case BFCP_UNSUPPORTED_VERSION:
+ return "Unsupported Version";
+
+ case BFCP_BAD_LENGTH:
+ return "Incorrect Message Length";
+
+ case BFCP_GENERIC_ERROR:
+ return "Generic Error";
+
+ default:
+ return "???";
+ }
+}
diff --git a/src/bfcp/bfcp.h b/src/bfcp/bfcp.h
new file mode 100644
index 0000000..7e76e0d
--- /dev/null
+++ b/src/bfcp/bfcp.h
@@ -0,0 +1,48 @@
+/**
+ * @file bfcp.h Internal interface to Binary Floor Control Protocol (BFCP)
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+
+struct bfcp_strans {
+ enum bfcp_prim prim;
+ uint32_t confid;
+ uint16_t tid;
+ uint16_t userid;
+};
+
+struct bfcp_conn {
+ struct bfcp_strans st;
+ struct list ctransl;
+ struct tmr tmr1;
+ struct tmr tmr2;
+ struct udp_sock *us;
+ struct mbuf *mb;
+ bfcp_recv_h *recvh;
+ void *arg;
+ enum bfcp_transp tp;
+ unsigned txc;
+ uint16_t tid;
+};
+
+
+/* attributes */
+int bfcp_attrs_decode(struct list *attrl, struct mbuf *mb, size_t len,
+ struct bfcp_unknown_attr *uma);
+struct bfcp_attr *bfcp_attrs_find(const struct list *attrl,
+ enum bfcp_attrib type);
+struct bfcp_attr *bfcp_attrs_apply(const struct list *attrl,
+ bfcp_attr_h *h, void *arg);
+int bfcp_attrs_print(struct re_printf *pf, const struct list *attrl,
+ unsigned level);
+
+
+/* connection */
+int bfcp_send(struct bfcp_conn *bc, const struct sa *dst, struct mbuf *mb);
+
+
+/* request */
+bool bfcp_handle_response(struct bfcp_conn *bc, const struct bfcp_msg *msg);
+int bfcp_vrequest(struct bfcp_conn *bc, const struct sa *dst, uint8_t ver,
+ enum bfcp_prim prim, uint32_t confid, uint16_t userid,
+ bfcp_resp_h *resph, void *arg, unsigned attrc, va_list *ap);
diff --git a/src/bfcp/conn.c b/src/bfcp/conn.c
new file mode 100644
index 0000000..6e4051d
--- /dev/null
+++ b/src/bfcp/conn.c
@@ -0,0 +1,156 @@
+/**
+ * @file bfcp/conn.c BFCP Connection
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#include <string.h>
+#include <re_types.h>
+#include <re_fmt.h>
+#include <re_mem.h>
+#include <re_mbuf.h>
+#include <re_list.h>
+#include <re_sa.h>
+#include <re_udp.h>
+#include <re_tmr.h>
+#include <re_bfcp.h>
+#include "bfcp.h"
+
+
+static void destructor(void *arg)
+{
+ struct bfcp_conn *bc = arg;
+
+ list_flush(&bc->ctransl);
+ tmr_cancel(&bc->tmr1);
+ tmr_cancel(&bc->tmr2);
+ mem_deref(bc->us);
+ mem_deref(bc->mb);
+}
+
+
+static bool strans_cmp(const struct bfcp_strans *st,
+ const struct bfcp_msg *msg)
+{
+ if (st->tid != msg->tid)
+ return false;
+
+ if (st->prim != msg->prim)
+ return false;
+
+ if (st->confid != msg->confid)
+ return false;
+
+ if (st->userid != msg->userid)
+ return false;
+
+ return true;
+}
+
+
+static void udp_recv_handler(const struct sa *src, struct mbuf *mb, void *arg)
+{
+ struct bfcp_conn *bc = arg;
+ struct bfcp_msg *msg;
+ int err;
+
+ err = bfcp_msg_decode(&msg, mb);
+ if (err)
+ return;
+
+ msg->src = *src;
+
+ if (bfcp_handle_response(bc, msg))
+ goto out;
+
+ if (bc->mb && strans_cmp(&bc->st, msg)) {
+ (void)bfcp_send(bc, &msg->src, bc->mb);
+ goto out;
+ }
+
+ if (bc->recvh)
+ bc->recvh(msg, bc->arg);
+
+out:
+ mem_deref(msg);
+}
+
+
+/**
+ * Create BFCP connection
+ *
+ * @param bcp Pointer to BFCP connection
+ * @param tp BFCP Transport type
+ * @param laddr Optional listening address/port
+ * @param tls TLS Context (optional)
+ * @param recvh Receive handler
+ * @param arg Receive handler argument
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int bfcp_listen(struct bfcp_conn **bcp, enum bfcp_transp tp, struct sa *laddr,
+ struct tls *tls, bfcp_recv_h *recvh, void *arg)
+{
+ struct bfcp_conn *bc;
+ int err;
+ (void)tls;
+
+ if (!bcp)
+ return EINVAL;
+
+ bc = mem_zalloc(sizeof(*bc), destructor);
+ if (!bc)
+ return ENOMEM;
+
+ bc->tp = tp;
+ bc->recvh = recvh;
+ bc->arg = arg;
+
+ switch (bc->tp) {
+
+ case BFCP_UDP:
+ err = udp_listen(&bc->us, laddr, udp_recv_handler, bc);
+ if (err)
+ goto out;
+
+ if (laddr) {
+ err = udp_local_get(bc->us, laddr);
+ if (err)
+ goto out;
+ }
+ break;
+
+ default:
+ err = ENOSYS;
+ goto out;
+ }
+
+ out:
+ if (err)
+ mem_deref(bc);
+ else
+ *bcp = bc;
+
+ return err;
+}
+
+
+int bfcp_send(struct bfcp_conn *bc, const struct sa *dst, struct mbuf *mb)
+{
+ if (!bc || !dst || !mb)
+ return EINVAL;
+
+ switch (bc->tp) {
+
+ case BFCP_UDP:
+ return udp_send(bc->us, dst, mb);
+
+ default:
+ return ENOSYS;
+ }
+}
+
+
+void *bfcp_sock(const struct bfcp_conn *bc)
+{
+ return bc ? bc->us : NULL;
+}
diff --git a/src/bfcp/mod.mk b/src/bfcp/mod.mk
new file mode 100644
index 0000000..b533415
--- /dev/null
+++ b/src/bfcp/mod.mk
@@ -0,0 +1,11 @@
+#
+# mod.mk
+#
+# Copyright (C) 2010 Creytiv.com
+#
+
+SRCS += bfcp/attr.c
+SRCS += bfcp/conn.c
+SRCS += bfcp/msg.c
+SRCS += bfcp/reply.c
+SRCS += bfcp/request.c
diff --git a/src/bfcp/msg.c b/src/bfcp/msg.c
new file mode 100644
index 0000000..03f0b72
--- /dev/null
+++ b/src/bfcp/msg.c
@@ -0,0 +1,288 @@
+/**
+ * @file bfcp/msg.c BFCP Message
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#include <string.h>
+#include <re_types.h>
+#include <re_fmt.h>
+#include <re_mem.h>
+#include <re_mbuf.h>
+#include <re_list.h>
+#include <re_sa.h>
+#include <re_tmr.h>
+#include <re_bfcp.h>
+#include "bfcp.h"
+
+
+enum {
+ BFCP_HDR_SIZE = 12,
+};
+
+
+static void destructor(void *arg)
+{
+ struct bfcp_msg *msg = arg;
+
+ list_flush(&msg->attrl);
+}
+
+
+static int hdr_encode(struct mbuf *mb, uint8_t ver, bool r,
+ enum bfcp_prim prim, uint16_t len, uint32_t confid,
+ uint16_t tid, uint16_t userid)
+{
+ int err;
+
+ err = mbuf_write_u8(mb, (ver << 5) | ((r ? 1 : 0) << 4));
+ err |= mbuf_write_u8(mb, prim);
+ err |= mbuf_write_u16(mb, htons(len));
+ err |= mbuf_write_u32(mb, htonl(confid));
+ err |= mbuf_write_u16(mb, htons(tid));
+ err |= mbuf_write_u16(mb, htons(userid));
+
+ return err;
+}
+
+
+static int hdr_decode(struct bfcp_msg *msg, struct mbuf *mb)
+{
+ uint8_t b;
+
+ if (mbuf_get_left(mb) < BFCP_HDR_SIZE)
+ return ENODATA;
+
+ b = mbuf_read_u8(mb);
+
+ msg->ver = b >> 5;
+ msg->r = (b >> 4) & 1;
+ msg->f = (b >> 3) & 1;
+ msg->prim = mbuf_read_u8(mb);
+ msg->len = ntohs(mbuf_read_u16(mb));
+ msg->confid = ntohl(mbuf_read_u32(mb));
+ msg->tid = ntohs(mbuf_read_u16(mb));
+ msg->userid = ntohs(mbuf_read_u16(mb));
+
+ if (msg->ver != BFCP_VER1 && msg->ver != BFCP_VER2)
+ return EBADMSG;
+
+ /* fragmentation not supported */
+ if (msg->f)
+ return ENOSYS;
+
+ if (mbuf_get_left(mb) < (size_t)(4*msg->len))
+ return ENODATA;
+
+ return 0;
+}
+
+
+/**
+ * Encode a BFCP message with variable arguments
+ *
+ * @param mb Mbuf to encode into
+ * @param ver Protocol version
+ * @param r Transaction responder flag
+ * @param prim BFCP Primitive
+ * @param confid Conference ID
+ * @param tid Transaction ID
+ * @param userid User ID
+ * @param attrc Number of attributes
+ * @param ap Variable argument of attributes
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int bfcp_msg_vencode(struct mbuf *mb, uint8_t ver, bool r, enum bfcp_prim prim,
+ uint32_t confid, uint16_t tid, uint16_t userid,
+ unsigned attrc, va_list *ap)
+{
+ size_t start, len;
+ int err;
+
+ if (!mb)
+ return EINVAL;
+
+ start = mb->pos;
+ mb->pos += BFCP_HDR_SIZE;
+
+ err = bfcp_attrs_vencode(mb, attrc, ap);
+ if (err)
+ return err;
+
+ /* header */
+ len = mb->pos - start - BFCP_HDR_SIZE;
+ mb->pos = start;
+ err = hdr_encode(mb, ver, r, prim, (uint16_t)(len/4), confid, tid,
+ userid);
+ mb->pos += len;
+
+ return err;
+}
+
+
+/**
+ * Encode a BFCP message
+ *
+ * @param mb Mbuf to encode into
+ * @param ver Protocol version
+ * @param r Transaction responder flag
+ * @param prim BFCP Primitive
+ * @param confid Conference ID
+ * @param tid Transaction ID
+ * @param userid User ID
+ * @param attrc Number of attributes
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int bfcp_msg_encode(struct mbuf *mb, uint8_t ver, bool r, enum bfcp_prim prim,
+ uint32_t confid, uint16_t tid, uint16_t userid,
+ unsigned attrc, ...)
+{
+ va_list ap;
+ int err;
+
+ va_start(ap, attrc);
+ err = bfcp_msg_vencode(mb, ver, r, prim, confid, tid, userid,
+ attrc, &ap);
+ va_end(ap);
+
+ return err;
+}
+
+
+/**
+ * Decode a BFCP message from a buffer
+ *
+ * @param msgp Pointer to allocated and decoded BFCP message
+ * @param mb Mbuf to decode from
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int bfcp_msg_decode(struct bfcp_msg **msgp, struct mbuf *mb)
+{
+ struct bfcp_msg *msg;
+ size_t start;
+ int err;
+
+ if (!msgp || !mb)
+ return EINVAL;
+
+ msg = mem_zalloc(sizeof(*msg), destructor);
+ if (!msg)
+ return ENOMEM;
+
+ start = mb->pos;
+
+ err = hdr_decode(msg, mb);
+ if (err) {
+ mb->pos = start;
+ goto out;
+ }
+
+ err = bfcp_attrs_decode(&msg->attrl, mb, 4*msg->len, &msg->uma);
+ if (err)
+ goto out;
+
+ out:
+ if (err)
+ mem_deref(msg);
+ else
+ *msgp = msg;
+
+ return err;
+}
+
+
+/**
+ * Get a BFCP attribute from a BFCP message
+ *
+ * @param msg BFCP message
+ * @param type Attribute type
+ *
+ * @return Matching BFCP attribute if found, otherwise NULL
+ */
+struct bfcp_attr *bfcp_msg_attr(const struct bfcp_msg *msg,
+ enum bfcp_attrib type)
+{
+ if (!msg)
+ return NULL;
+
+ return bfcp_attrs_find(&msg->attrl, type);
+}
+
+
+/**
+ * Apply a function handler to all attributes in a BFCP message
+ *
+ * @param msg BFCP message
+ * @param h Handler
+ * @param arg Handler argument
+ *
+ * @return BFCP attribute returned by handler, or NULL
+ */
+struct bfcp_attr *bfcp_msg_attr_apply(const struct bfcp_msg *msg,
+ bfcp_attr_h *h, void *arg)
+{
+ if (!msg)
+ return NULL;
+
+ return bfcp_attrs_apply(&msg->attrl, h, arg);
+}
+
+
+/**
+ * Print a BFCP message
+ *
+ * @param pf Print function
+ * @param msg BFCP message
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int bfcp_msg_print(struct re_printf *pf, const struct bfcp_msg *msg)
+{
+ int err;
+
+ if (!msg)
+ return 0;
+
+ err = re_hprintf(pf, "%s (confid=%u tid=%u userid=%u)\n",
+ bfcp_prim_name(msg->prim), msg->confid,
+ msg->tid, msg->userid);
+
+ err |= bfcp_attrs_print(pf, &msg->attrl, 0);
+
+ return err;
+}
+
+
+/**
+ * Get the BFCP primitive name
+ *
+ * @param prim BFCP primitive
+ *
+ * @return String with BFCP primitive name
+ */
+const char *bfcp_prim_name(enum bfcp_prim prim)
+{
+ switch (prim) {
+
+ case BFCP_FLOOR_REQUEST: return "FloorRequest";
+ case BFCP_FLOOR_RELEASE: return "FloorRelease";
+ case BFCP_FLOOR_REQUEST_QUERY: return "FloorRequestQuery";
+ case BFCP_FLOOR_REQUEST_STATUS: return "FloorRequestStatus";
+ case BFCP_USER_QUERY: return "UserQuery";
+ case BFCP_USER_STATUS: return "UserStatus";
+ case BFCP_FLOOR_QUERY: return "FloorQuery";
+ case BFCP_FLOOR_STATUS: return "FloorStatus";
+ case BFCP_CHAIR_ACTION: return "ChairAction";
+ case BFCP_CHAIR_ACTION_ACK: return "ChairActionAck";
+ case BFCP_HELLO: return "Hello";
+ case BFCP_HELLO_ACK: return "HelloAck";
+ case BFCP_ERROR: return "Error";
+ case BFCP_FLOOR_REQ_STATUS_ACK: return "FloorRequestStatusAck";
+ case BFCP_FLOOR_STATUS_ACK: return "FloorStatusAck";
+ case BFCP_GOODBYE: return "Goodbye";
+ case BFCP_GOODBYE_ACK: return "GoodbyeAck";
+ default: return "???";
+ }
+}
diff --git a/src/bfcp/reply.c b/src/bfcp/reply.c
new file mode 100644
index 0000000..321429c
--- /dev/null
+++ b/src/bfcp/reply.c
@@ -0,0 +1,124 @@
+/**
+ * @file bfcp/reply.c BFCP Reply
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#include <string.h>
+#include <re_types.h>
+#include <re_fmt.h>
+#include <re_mem.h>
+#include <re_mbuf.h>
+#include <re_list.h>
+#include <re_sa.h>
+#include <re_tmr.h>
+#include <re_bfcp.h>
+#include "bfcp.h"
+
+
+enum {
+ BFCP_T2 = 10000,
+};
+
+
+static void tmr_handler(void *arg)
+{
+ struct bfcp_conn *bc = arg;
+
+ bc->mb = mem_deref(bc->mb);
+}
+
+
+/**
+ * Send a BFCP response
+ *
+ * @param bc BFCP connection
+ * @param req BFCP request message
+ * @param prim BFCP Primitive
+ * @param attrc Number of attributes
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int bfcp_reply(struct bfcp_conn *bc, const struct bfcp_msg *req,
+ enum bfcp_prim prim, unsigned attrc, ...)
+{
+ va_list ap;
+ int err;
+
+ if (!bc || !req)
+ return EINVAL;
+
+ bc->mb = mem_deref(bc->mb);
+ tmr_cancel(&bc->tmr2);
+
+ bc->mb = mbuf_alloc(64);
+ if (!bc->mb)
+ return ENOMEM;
+
+ va_start(ap, attrc);
+ err = bfcp_msg_vencode(bc->mb, req->ver, true, prim, req->confid,
+ req->tid, req->userid, attrc, &ap);
+ va_end(ap);
+
+ if (err)
+ goto out;
+
+ bc->mb->pos = 0;
+
+ err = bfcp_send(bc, &req->src, bc->mb);
+ if (err)
+ goto out;
+
+ bc->st.prim = req->prim;
+ bc->st.confid = req->confid;
+ bc->st.tid = req->tid;
+ bc->st.userid = req->userid;
+
+ tmr_start(&bc->tmr2, BFCP_T2, tmr_handler, bc);
+
+ out:
+ if (err)
+ bc->mb = mem_deref(bc->mb);
+
+ return err;
+}
+
+
+/**
+ * Send a BFCP error response with details
+ *
+ * @param bc BFCP connection
+ * @param req BFCP request message
+ * @param code Error code
+ * @param details Error details
+ * @param len Details length
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int bfcp_edreply(struct bfcp_conn *bc, const struct bfcp_msg *req,
+ enum bfcp_err code, const uint8_t *details, size_t len)
+{
+ struct bfcp_errcode errcode;
+
+ errcode.code = code;
+ errcode.details = (uint8_t *)details;
+ errcode.len = len;
+
+ return bfcp_reply(bc, req, BFCP_ERROR, 1,
+ BFCP_ERROR_CODE, 0, &errcode);
+}
+
+
+/**
+ * Send a BFCP error response
+ *
+ * @param bc BFCP connection
+ * @param req BFCP request message
+ * @param code Error code
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int bfcp_ereply(struct bfcp_conn *bc, const struct bfcp_msg *req,
+ enum bfcp_err code)
+{
+ return bfcp_edreply(bc, req, code, NULL, 0);
+}
diff --git a/src/bfcp/request.c b/src/bfcp/request.c
new file mode 100644
index 0000000..cfe066e
--- /dev/null
+++ b/src/bfcp/request.c
@@ -0,0 +1,257 @@
+/**
+ * @file bfcp/request.c BFCP Request
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#include <string.h>
+#include <re_types.h>
+#include <re_fmt.h>
+#include <re_mem.h>
+#include <re_mbuf.h>
+#include <re_list.h>
+#include <re_sa.h>
+#include <re_tmr.h>
+#include <re_bfcp.h>
+#include "bfcp.h"
+
+
+enum {
+ BFCP_T1 = 500,
+ BFCP_TXC = 4,
+};
+
+
+struct bfcp_ctrans {
+ struct le le;
+ struct sa dst;
+ struct mbuf *mb;
+ bfcp_resp_h *resph;
+ void *arg;
+ uint32_t confid;
+ uint16_t userid;
+ uint16_t tid;
+};
+
+
+static void tmr_handler(void *arg);
+
+
+static void dummy_resp_handler(int err, const struct bfcp_msg *msg, void *arg)
+{
+ (void)err;
+ (void)msg;
+ (void)arg;
+}
+
+
+static void destructor(void *arg)
+{
+ struct bfcp_ctrans *ct = arg;
+
+ list_unlink(&ct->le);
+ mem_deref(ct->mb);
+}
+
+
+static void dispatch(struct bfcp_conn *bc)
+{
+ struct le *le = bc->ctransl.head;
+
+ while (le) {
+ struct bfcp_ctrans *ct = le->data;
+ int err;
+
+ le = le->next;
+
+ err = bfcp_send(bc, &ct->dst, ct->mb);
+ if (err) {
+ ct->resph(err, NULL, ct->arg);
+ mem_deref(ct);
+ continue;
+ }
+
+ tmr_start(&bc->tmr1, BFCP_T1, tmr_handler, bc);
+ bc->txc = 1;
+ break;
+ }
+}
+
+
+static void tmr_handler(void *arg)
+{
+ struct bfcp_conn *bc = arg;
+ struct bfcp_ctrans *ct;
+ uint32_t timeout;
+ int err;
+
+ ct = list_ledata(bc->ctransl.head);
+ if (!ct)
+ return;
+
+ timeout = BFCP_T1<<bc->txc;
+
+ if (++bc->txc > BFCP_TXC) {
+ err = ETIMEDOUT;
+ goto out;
+ }
+
+ err = bfcp_send(bc, &ct->dst, ct->mb);
+ if (err)
+ goto out;
+
+ tmr_start(&bc->tmr1, timeout, tmr_handler, bc);
+ return;
+
+ out:
+ ct->resph(err, NULL, ct->arg);
+ mem_deref(ct);
+ dispatch(bc);
+}
+
+
+bool bfcp_handle_response(struct bfcp_conn *bc, const struct bfcp_msg *msg)
+{
+ struct bfcp_ctrans *ct;
+
+ if (!bc || !msg)
+ return false;
+
+ ct = list_ledata(bc->ctransl.head);
+ if (!ct)
+ return false;
+
+ if (msg->tid != ct->tid)
+ return false;
+
+ if (msg->confid != ct->confid)
+ return false;
+
+ if (msg->userid != ct->userid)
+ return false;
+
+ tmr_cancel(&bc->tmr1);
+
+ ct->resph(0, msg, ct->arg);
+ mem_deref(ct);
+
+ dispatch(bc);
+
+ return true;
+}
+
+
+int bfcp_vrequest(struct bfcp_conn *bc, const struct sa *dst, uint8_t ver,
+ enum bfcp_prim prim, uint32_t confid, uint16_t userid,
+ bfcp_resp_h *resph, void *arg, unsigned attrc, va_list *ap)
+{
+ struct bfcp_ctrans *ct;
+ int err;
+
+ if (!bc || !dst)
+ return EINVAL;
+
+ ct = mem_zalloc(sizeof(*ct), destructor);
+ if (!ct)
+ return ENOMEM;
+
+ if (bc->tid == 0)
+ bc->tid = 1;
+
+ ct->dst = *dst;
+ ct->confid = confid;
+ ct->userid = userid;
+ ct->tid = bc->tid++;
+ ct->resph = resph ? resph : dummy_resp_handler;
+ ct->arg = arg;
+
+ ct->mb = mbuf_alloc(128);
+ if (!ct->mb) {
+ err = ENOMEM;
+ goto out;
+ }
+
+ err = bfcp_msg_vencode(ct->mb, ver, false, prim, confid, ct->tid,
+ userid, attrc, ap);
+ if (err)
+ goto out;
+
+ ct->mb->pos = 0;
+
+ if (!bc->ctransl.head) {
+
+ err = bfcp_send(bc, &ct->dst, ct->mb);
+ if (err)
+ goto out;
+
+ tmr_start(&bc->tmr1, BFCP_T1, tmr_handler, bc);
+ bc->txc = 1;
+ }
+
+ list_append(&bc->ctransl, &ct->le, ct);
+
+ out:
+ if (err)
+ mem_deref(ct);
+
+ return err;
+}
+
+
+/**
+ * Send a BFCP request
+ *
+ * @param bc BFCP connection
+ * @param dst Destination address
+ * @param ver BFCP Version
+ * @param prim BFCP Primitive
+ * @param confid Conference ID
+ * @param userid User ID
+ * @param resph Response handler
+ * @param arg Response handler argument
+ * @param attrc Number of attributes
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int bfcp_request(struct bfcp_conn *bc, const struct sa *dst, uint8_t ver,
+ enum bfcp_prim prim, uint32_t confid, uint16_t userid,
+ bfcp_resp_h *resph, void *arg, unsigned attrc, ...)
+{
+ va_list ap;
+ int err;
+
+ va_start(ap, attrc);
+ err = bfcp_vrequest(bc, dst, ver, prim, confid, userid, resph, arg,
+ attrc, &ap);
+ va_end(ap);
+
+ return err;
+}
+
+
+/**
+ * Send a BFCP notification/subsequent response
+ *
+ * @param bc BFCP connection
+ * @param dst Destination address
+ * @param ver BFCP Version
+ * @param prim BFCP Primitive
+ * @param confid Conference ID
+ * @param userid User ID
+ * @param attrc Number of attributes
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int bfcp_notify(struct bfcp_conn *bc, const struct sa *dst, uint8_t ver,
+ enum bfcp_prim prim, uint32_t confid, uint16_t userid,
+ unsigned attrc, ...)
+{
+ va_list ap;
+ int err;
+
+ va_start(ap, attrc);
+ err = bfcp_vrequest(bc, dst, ver, prim, confid, userid, NULL, NULL,
+ attrc, &ap);
+ va_end(ap);
+
+ return err;
+}