diff --git a/Makefile b/Makefile index 474435f..ac52c76 100644 --- a/Makefile +++ b/Makefile @@ -28,6 +28,7 @@ MODULES += udp sa net tcp tls MODULES += list mbuf hash MODULES += fmt tmr main mem dbg sys lock mqueue MODULES += mod conf +MODULES += bfcp INSTALL := install ifeq ($(DESTDIR),) diff --git a/docs/README b/docs/README index e6b24c9..f3d6b9a 100644 --- a/docs/README +++ b/docs/README @@ -88,6 +88,7 @@ Features: * RFC 3994 - Indication of Message Composition for Instant Messaging * RFC 4346 - The TLS Protocol Version 1.1 * RFC 4566 - SDP: Session Description Protocol +* RFC 4582 - The Binary Floor Control Protocol (BFCP) * RFC 4585 - Extended RTP Profile for RTCP-Based Feedback * RFC 4733 - RTP Payload for DTMF Digits, Telephony Tones, and Teleph. Signals * RFC 4961 - Symmetric RTP / RTP Control Protocol (RTCP) diff --git a/include/re.h b/include/re.h index cf6e36e..ffca789 100644 --- a/include/re.h +++ b/include/re.h @@ -17,6 +17,7 @@ extern "C" { /* Library modules */ #include "re_base64.h" +#include "re_bfcp.h" #include "re_conf.h" #include "re_crc32.h" #include "re_dns.h" diff --git a/include/re_bfcp.h b/include/re_bfcp.h new file mode 100644 index 0000000..7de1f97 --- /dev/null +++ b/include/re_bfcp.h @@ -0,0 +1,236 @@ +/** + * @file re_bfcp.h Interface to Binary Floor Control Protocol (BFCP) + * + * Copyright (C) 2010 Creytiv.com + */ + + +enum {BFCP_VERSION = 1}; + +/** BFCP Primitives */ +enum bfcp_prim { + BFCP_FLOOR_REQUEST = 1, + BFCP_FLOOR_RELEASE = 2, + BFCP_FLOOR_REQUEST_QUERY = 3, + BFCP_FLOOR_REQUEST_STAT = 4, + BFCP_USER_QUERY = 5, + BFCP_USER_STATUS = 6, + BFCP_FLOOR_QUERY = 7, + BFCP_FLOOR_STATUS = 8, + BFCP_CHAIR_ACTION = 9, + BFCP_CHAIR_ACTION_ACK = 10, + BFCP_HELLO = 11, + BFCP_HELLO_ACK = 12, + BFCP_ERROR = 13, +}; + +/** BFCP Attributes */ +enum bfcp_attrib { + BFCP_BENEFICIARY_ID = 1, + BFCP_FLOOR_ID = 2, + BFCP_FLOOR_REQUEST_ID = 3, + BFCP_PRIORITY = 4, + BFCP_REQUEST_STATUS = 5, + BFCP_ERROR_CODE = 6, + BFCP_ERROR_INFO = 7, + BFCP_PARTICIPANT_PROV_INFO = 8, + BFCP_STATUS_INFO = 9, + BFCP_SUPPORTED_ATTRIBUTES = 10, + BFCP_SUPPORTED_PRIMITIVES = 11, + BFCP_USER_DISPLAY_NAME = 12, + BFCP_USER_URI = 13, + /* grouped: */ + BFCP_BENEFICIARY_INFO = 14, + BFCP_FLOOR_REQUEST_INFO = 15, + BFCP_REQUESTED_BY_INFO = 16, + BFCP_FLOOR_REQUEST_STATUS = 17, + BFCP_OVERALL_REQUEST_STATUS = 18, +}; + +/** BFCP Request Status */ +enum bfcp_rstat { + BFCP_PENDING = 1, + BFCP_ACCEPTED = 2, + BFCP_GRANTED = 3, + BFCP_DENIED = 4, + BFCP_CANCELLED = 5, + BFCP_RELEASED = 6, + BFCP_REVOKED = 7 +}; + +/** BFCP Error Codes */ +enum bfcp_err { + BFCP_ERR_CONF_NOT_EXIST = 1, + BFCP_ERR_USER_NOT_EXIST = 2, + BFCP_ERR_UNKNOWN_PRIM = 3, + BFCP_ERR_UNKNOWN_MAND_ATTR = 4, + BFCP_ERR_UNAUTH_OPERATION = 5, + BFCP_ERR_INVALID_FLOOR_ID = 6, + BFCP_ERR_FLOOR_REQ_ID_NOT_EXIST = 7, + BFCP_ERR_MAX_FLOOR_REQ_REACHED = 8, + BFCP_ERR_USE_TLS = 9 +}; + +enum bfcp_prio { + BFCP_PRIO_LOWEST = 0, + BFCP_PRIO_LOW = 1, + BFCP_PRIO_NORMAL = 2, + BFCP_PRIO_HIGH = 3, + BFCP_PRIO_HIGHEST = 4 +}; + +struct bfcp_reqstat { + enum bfcp_rstat stat; + uint8_t qpos; +}; + +struct bfcp_errcode { + enum bfcp_err code; + uint8_t *details; /* optional */ + size_t len; +}; + +struct bfcp_supattr { + enum bfcp_attrib *attrv; + size_t attrc; +}; + +struct bfcp_supprim { + enum bfcp_prim *primv; + size_t primc; +}; + +struct bfcp_overall_reqstat { + uint16_t freqid; + struct bfcp_reqstat reqstat; + char *statinfo; +}; + +struct bfcp_beneficiary_info { + uint16_t bfid; + char *dname; + char *uri; +}; + +struct bfcp_reqby_info { + uint16_t rbid; + char *dname; + char *uri; +}; + +struct bfcp_floor_reqstat { + uint16_t floorid; + struct bfcp_reqstat reqstat; + char *statinfo; +}; + +struct bfcp_floor_reqinfo { + uint16_t freqid; + struct bfcp_overall_reqstat ors; + struct bfcp_floor_reqstat *frsv; + size_t frsc; + struct bfcp_beneficiary_info bfi; + struct bfcp_reqby_info rbi; + uint8_t prio; + char *ppi; +}; + +struct bfcp_attr { + struct le le; + enum bfcp_attrib type; + bool mand; + union bfcp_union { + /* generic types */ + char *str; + uint16_t u16; + + /* actual attributes */ + uint16_t bfid; + uint16_t floorid; + uint16_t freqid; + uint8_t prio; + struct bfcp_reqstat reqstat; + struct bfcp_errcode errcode; + char *errinfo; + char *ppi; + char *statinfo; + struct bfcp_supattr supattr; + struct bfcp_supprim supprim; + char *userdname; + char *useruri; + + /* grouped attributes */ + struct bfcp_beneficiary_info bfi; + struct bfcp_floor_reqinfo fri; + struct bfcp_reqby_info rbi; + struct bfcp_floor_reqstat frs; + struct bfcp_overall_reqstat ors; + } v; +}; + +enum bfcp_transp { + BFCP_TRANSP_TCP = 0, + BFCP_TRANSP_TLS = 1 +}; + + +/* BFCP Message */ + +struct bfcp_msg; + +typedef bool (bfcp_attr_h)(const struct bfcp_attr *attr, void *arg); + +int bfcp_msg_vencode(struct mbuf *mb, enum bfcp_prim prim, + uint32_t confid, uint16_t tid, uint16_t userid, + uint32_t attrc, va_list ap); +int bfcp_msg_encode(struct mbuf *mb, enum bfcp_prim prim, uint32_t confid, + uint16_t tid, uint16_t userid, uint32_t attrc, ...); +int bfcp_msg_decode(struct bfcp_msg **msgp, struct mbuf *mb); +struct bfcp_attr *bfcp_msg_attr(const struct bfcp_msg *msg, + enum bfcp_attrib type); +struct bfcp_attr *bfcp_msg_attr_apply(const struct bfcp_msg *msg, + bfcp_attr_h *h, void *arg); +int bfcp_msg_print(struct re_printf *pf, const struct bfcp_msg *msg); +enum bfcp_prim bfcp_msg_prim(const struct bfcp_msg *msg); +uint32_t bfcp_msg_confid(const struct bfcp_msg *msg); +uint16_t bfcp_msg_tid(const struct bfcp_msg *msg); +uint16_t bfcp_msg_userid(const struct bfcp_msg *msg); +void bfcp_msg_set_src(struct bfcp_msg *msg, const struct sa *src); +const struct sa *bfcp_msg_src(const struct bfcp_msg *msg); + + +/* BFCP supplement */ + +const char *bfcp_prim_name(enum bfcp_prim prim); +const char *bfcp_attr_name(enum bfcp_attrib attr); +const char *bfcp_reqstat_name(enum bfcp_rstat stat); +const char *bfcp_errcode_name(enum bfcp_err code); + + +/* BFCP Transport */ + +bool bfcp_transp_reliable(enum bfcp_transp tp); +const char *bfcp_transp_proto(enum bfcp_transp tp); + + +/* BFCP Socket */ + +struct tls; +struct bfcp_sock; +struct bfcp_ctrans; + +typedef void (bfcp_msg_h)(const struct bfcp_msg *msg, void *arg); +typedef void (bfcp_resp_h)(int err, const struct bfcp_msg *msg, void *arg); + + +int bfcp_listen(struct bfcp_sock **sockp, enum bfcp_transp transp, + struct tls *tls, const struct sa *laddr, + bfcp_msg_h *msgh, void *arg); +int bfcp_request(struct bfcp_ctrans **ctp, struct bfcp_sock *sock, + const struct sa *dst, + enum bfcp_prim prim, uint32_t confid, uint16_t userid, + bfcp_resp_h *resph, void *arg, uint32_t attrc, ...); +int bfcp_reply(struct bfcp_sock *sock, const struct bfcp_msg *req, + enum bfcp_prim prim, uint32_t attrc, ...); +int bfcp_ereply(struct bfcp_sock *sock, const struct bfcp_msg *req, + enum bfcp_err code, ...); diff --git a/mk/symbian/bld.inf b/mk/symbian/bld.inf index 1a735a1..fcfb8c3 100644 --- a/mk/symbian/bld.inf +++ b/mk/symbian/bld.inf @@ -8,6 +8,7 @@ PRJ_EXPORTS ..\..\include\re.h \epoc32\include\re\re.h ..\..\include\re_base64.h \epoc32\include\re\re_base64.h +..\..\include\re_bfcp.h \epoc32\include\re\re_bfcp.h ..\..\include\re_bitv.h \epoc32\include\re\re_bitv.h ..\..\include\re_conf.h \epoc32\include\re\re_conf.h ..\..\include\re_crc32.h \epoc32\include\re\re_crc32.h @@ -51,6 +52,7 @@ PRJ_EXPORTS PRJ_MMPFILES re.mmp +rebfcp.mmp redns.mmp resdp.mmp resip.mmp diff --git a/mk/symbian/rebfcp.mmp b/mk/symbian/rebfcp.mmp new file mode 100644 index 0000000..d5d9a26 --- /dev/null +++ b/mk/symbian/rebfcp.mmp @@ -0,0 +1,43 @@ +/** + * @file rebfcp.mmp Symbian makefile for libre BFCP + * + * Copyright (C) 2010 Creytiv.com + */ +TARGET rebfcp.lib +TARGETTYPE lib +TARGETPATH system\libs +UID 0x10000fd3 0x20011308 + +#ifdef EKA2 +VENDORID 0 +CAPABILITY NetworkServices +#endif + +MACRO HAVE_SYS_TIME_H +MACRO HAVE_UNISTD_H +MACRO HAVE_ACTSCHED + +#ifndef EKA2 +SOURCEPATH . +SOURCE dll.cpp +#endif + +SOURCEPATH ..\..\src\bfcp +SOURCE attr.c +SOURCE hdr.c +SOURCE msg.c +SOURCE rep.c +SOURCE req.c +SOURCE sock.c +SOURCE transp.c + +USERINCLUDE . ..\..\include +SYSTEMINCLUDE \epoc32\include +SYSTEMINCLUDE \epoc32\include\libc +SYSTEMINCLUDE ..\..\include +#ifndef EKA2 +LIBRARY estlib.lib euser.lib +LIBRARY esock.lib insock.lib +#endif + +EXPORTUNFROZEN diff --git a/mk/win32/re.vcproj b/mk/win32/re.vcproj index de43786..d003d9a 100644 --- a/mk/win32/re.vcproj +++ b/mk/win32/re.vcproj @@ -108,6 +108,31 @@ RelativePath="..\..\src\base64\b64.c"> + + + + + + + + + + + + + + + + diff --git a/src/bfcp/attr.c b/src/bfcp/attr.c new file mode 100644 index 0000000..71f0a9c --- /dev/null +++ b/src/bfcp/attr.c @@ -0,0 +1,763 @@ +/** + * @file bfcp/attr.c BFCP Attributes + * + * Copyright (C) 2010 Creytiv.com + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "bfcp.h" + + +static int attr_decode(struct bfcp_attr *attr, union bfcp_union *v, + struct mbuf *mb); + + +static void destructor(void *arg) +{ + struct bfcp_attr *attr = arg; + size_t i; + + switch (attr->type) { + + case BFCP_ERROR_INFO: + case BFCP_PARTICIPANT_PROV_INFO: + case BFCP_STATUS_INFO: + case BFCP_USER_DISPLAY_NAME: + case BFCP_USER_URI: + mem_deref(attr->v.str); + break; + + case BFCP_SUPPORTED_ATTRIBUTES: + mem_deref(attr->v.supattr.attrv); + break; + + case BFCP_SUPPORTED_PRIMITIVES: + mem_deref(attr->v.supprim.primv); + break; + + case BFCP_ERROR_CODE: + mem_deref(attr->v.errcode.details); + break; + + /* grouped */ + + case BFCP_BENEFICIARY_INFO: + mem_deref(attr->v.bfi.dname); + mem_deref(attr->v.bfi.uri); + break; + + case BFCP_FLOOR_REQUEST_INFO: + mem_deref(attr->v.fri.ors.statinfo); + for (i=0; iv.fri.frsc; i++) + mem_deref(attr->v.fri.frsv[i].statinfo); + mem_deref(attr->v.fri.frsv); + mem_deref(attr->v.fri.bfi.dname); + mem_deref(attr->v.fri.bfi.uri); + mem_deref(attr->v.fri.rbi.dname); + mem_deref(attr->v.fri.rbi.uri); + mem_deref(attr->v.fri.ppi); + break; + + case BFCP_REQUESTED_BY_INFO: + mem_deref(attr->v.rbi.dname); + mem_deref(attr->v.rbi.uri); + break; + + case BFCP_FLOOR_REQUEST_STATUS: + mem_deref(attr->v.frs.statinfo); + break; + + case BFCP_OVERALL_REQUEST_STATUS: + mem_deref(attr->v.ors.statinfo); + break; + + default: + /* nothing allocated */ + break; + } +} + + +int bfcp_attr_encode(struct mbuf *mb, bool mand, enum bfcp_attrib type, + const void *v) +{ + const struct bfcp_errcode *ec = v; + const struct bfcp_supattr *sa = v; + const struct bfcp_supprim *sp = v; + const struct bfcp_reqstat *rs = v; + const struct bfcp_beneficiary_info *bfi = v; + const struct bfcp_floor_reqinfo *fri = v; + const struct bfcp_reqby_info *rbi = v; + const struct bfcp_floor_reqstat *frs = v; + const struct bfcp_overall_reqstat *ors = v; + const unsigned int *num = v; + size_t start, len, i; + int err = 0; + + if (!mb || !v) + return EINVAL; + + start = mb->pos; + mb->pos += ATTR_HDR_SIZE; + + switch (type) { + + case BFCP_BENEFICIARY_ID: + case BFCP_FLOOR_ID: + case BFCP_FLOOR_REQUEST_ID: + err |= mbuf_write_u16(mb, htons(*num)); + break; + + case BFCP_PRIORITY: + err |= mbuf_write_u8(mb, *num << 5); + err |= mbuf_write_u8(mb, 0x00); + break; + + case BFCP_REQUEST_STATUS: + err |= mbuf_write_u8(mb, rs->stat); + err |= mbuf_write_u8(mb, rs->qpos); + break; + + case BFCP_ERROR_CODE: + err |= mbuf_write_u8(mb, ec->code); + if (ec->details && ec->len) + err |= mbuf_write_mem(mb, ec->details, ec->len); + break; + + case BFCP_ERROR_INFO: + case BFCP_PARTICIPANT_PROV_INFO: + case BFCP_STATUS_INFO: + case BFCP_USER_DISPLAY_NAME: + case BFCP_USER_URI: + err |= mbuf_write_str(mb, v); + break; + + case BFCP_SUPPORTED_ATTRIBUTES: + for (i=0; iattrc; i++) + err |= mbuf_write_u8(mb, sa->attrv[i] << 1); + break; + + case BFCP_SUPPORTED_PRIMITIVES: + for (i=0; iprimc; i++) + err |= mbuf_write_u8(mb, sp->primv[i]); + break; + + /* grouped attributes: */ + + case BFCP_BENEFICIARY_INFO: + err |= mbuf_write_u16(mb, htons(bfi->bfid)); + + if (bfi->dname) + err |= bfcp_attr_encode(mb, mand, + BFCP_USER_DISPLAY_NAME, + bfi->dname); + if (bfi->uri) + err |= bfcp_attr_encode(mb, mand, + BFCP_USER_URI, + bfi->uri); + break; + + case BFCP_FLOOR_REQUEST_INFO: + err |= mbuf_write_u16(mb, htons(fri->freqid)); + + if (fri->ors.freqid) + err |= bfcp_attr_encode(mb, mand, + BFCP_OVERALL_REQUEST_STATUS, + &fri->ors); + + for (i=0; ifrsc; i++) { + err |= bfcp_attr_encode(mb, mand, + BFCP_FLOOR_REQUEST_STATUS, + &fri->frsv[i]); + } + + if (fri->bfi.bfid) + err |= bfcp_attr_encode(mb, mand, + BFCP_BENEFICIARY_INFO, + &fri->bfi); + + if (fri->rbi.rbid) + err |= bfcp_attr_encode(mb, mand, + BFCP_REQUESTED_BY_INFO, + &fri->rbi); + + err |= bfcp_attr_encode(mb, mand, BFCP_PRIORITY, &fri->prio); + + if (fri->ppi) + err |= bfcp_attr_encode(mb, mand, + BFCP_PARTICIPANT_PROV_INFO, + fri->ppi); + break; + + case BFCP_REQUESTED_BY_INFO: + err |= mbuf_write_u16(mb, htons(rbi->rbid)); + + if (rbi->dname) + err |= bfcp_attr_encode(mb, mand, + BFCP_USER_DISPLAY_NAME, + rbi->dname); + if (rbi->uri) + err |= bfcp_attr_encode(mb, mand, + BFCP_USER_URI, + rbi->uri); + break; + + case BFCP_FLOOR_REQUEST_STATUS: + err |= mbuf_write_u16(mb, htons(frs->floorid)); + + if (frs->reqstat.stat) + err |= bfcp_attr_encode(mb, mand, + BFCP_REQUEST_STATUS, + &frs->reqstat); + if (frs->statinfo) + err |= bfcp_attr_encode(mb, mand, + BFCP_STATUS_INFO, + frs->statinfo); + break; + + case BFCP_OVERALL_REQUEST_STATUS: + err |= mbuf_write_u16(mb, htons(ors->freqid)); + + if (ors->reqstat.stat) + err |= bfcp_attr_encode(mb, mand, + BFCP_REQUEST_STATUS, + &ors->reqstat); + if (ors->statinfo) + err |= bfcp_attr_encode(mb, mand, + BFCP_STATUS_INFO, + ors->statinfo); + break; + + default: + err = EINVAL; + break; + } + + if (err) + return err; + + len = mb->pos - start; + + /* padding */ + while ((mb->pos - start) & 0x03) + err |= mbuf_write_u8(mb, 0x00); + + if (bfcp_attr_isgrouped(type)) + len = mb->pos - start; + + /* header */ + mb->pos = start; + err |= mbuf_write_u8(mb, (type<<1) | (mand&1)); + err |= mbuf_write_u8(mb, len); + mb->pos = mb->end; + + return err; +} + + +static int decv(struct mbuf *mb, uint8_t type, void **vp, + size_t elemsz, size_t *count) +{ + size_t n = 0; + void *v = NULL; + int err = 0; + + while (mbuf_get_left(mb) >= ATTR_HDR_SIZE) { + + struct bfcp_attr attr; + uint8_t *bp; + + if (type != (mbuf_buf(mb)[0] >> 1)) + break; + + ++n; + + if (v) { + void *v2 = mem_realloc(v, n * elemsz); + if (!v2) { + err = ENOMEM; + break; + } + + v = v2; + } + else { + v = mem_zalloc(1 * elemsz, NULL); + if (!v) { + err = ENOMEM; + break; + } + } + + bp = ((uint8_t *)v) + (n-1) * elemsz; + + err = attr_decode(&attr, (void *)bp, mb); + if (err) + break; + } + + if (err) + mem_deref(v); + else { + *vp = v; + *count = n; + } + + return err; +} + + +/* Decode a Nested attribute */ +static int decn(struct mbuf *mb, uint8_t type, void *p) +{ + struct bfcp_attr attr; + int err; + + /* sanity check of attribute type */ + if (mbuf_get_left(mb) < 1 || type != (mbuf_buf(mb)[0] >> 1)) + return 0; + + err = attr_decode(&attr, p, mb); + if (err) + return err; + + return 0; +} + + +static int attr_decode(struct bfcp_attr *attr, union bfcp_union *v, + struct mbuf *mb) +{ + size_t i, start, len; + uint8_t u8; + int err = 0; + + if (!attr || !v || !mb) + return EINVAL; + + if (mbuf_get_left(mb) < ATTR_HDR_SIZE) + return EBADMSG; + + start = mb->pos; + + u8 = mbuf_read_u8(mb); + attr->type = u8 >> 1; + attr->mand = u8 & 1; + len = mbuf_read_u8(mb) - ATTR_HDR_SIZE; + + if (mbuf_get_left(mb) < len) + goto badmsg; + + 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->prio = mbuf_read_u8(mb) >> 5; + (void)mbuf_read_u8(mb); + break; + + case BFCP_REQUEST_STATUS: + if (len < 2) + goto badmsg; + + v->reqstat.stat = mbuf_read_u8(mb); + v->reqstat.qpos = mbuf_read_u8(mb); + break; + + case BFCP_ERROR_CODE: + if (len < 1) + goto badmsg; + + v->errcode.len = len - 1; + v->errcode.code = mbuf_read_u8(mb); + + if (v->errcode.len > 0) { + + 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_PARTICIPANT_PROV_INFO: + case BFCP_STATUS_INFO: + case BFCP_USER_DISPLAY_NAME: + case BFCP_USER_URI: + err = mbuf_strdup(mb, &v->str, len); + break; + + case BFCP_SUPPORTED_ATTRIBUTES: + v->supattr.attrv = mem_alloc(len * sizeof(*v->supattr.attrv), + NULL); + if (!v->supattr.attrv) { + err = ENOMEM; + goto error; + } + + v->supattr.attrc = (uint32_t)len; + for (i=0; isupattr.attrv[i] = mbuf_read_u8(mb) >> 1; + break; + + case BFCP_SUPPORTED_PRIMITIVES: + v->supprim.primv = mem_alloc(len * sizeof(*v->supprim.primv), + NULL); + if (!v->supprim.primv) { + err = ENOMEM; + goto error; + } + + v->supprim.primc = (uint32_t)len; + for (i=0; isupprim.primv[i] = mbuf_read_u8(mb); + break; + + /* grouped attributes */ + + case BFCP_BENEFICIARY_INFO: + if (len < 2) + goto badmsg; + + v->bfi.bfid = ntohs(mbuf_read_u16(mb)); + err |= decn(mb, BFCP_USER_DISPLAY_NAME, &v->bfi.dname); + err |= decn(mb, BFCP_USER_URI, &v->bfi.uri); + break; + + case BFCP_FLOOR_REQUEST_INFO: + if (len < 2) + goto badmsg; + + v->fri.freqid = ntohs(mbuf_read_u16(mb)); + err |= decn(mb, BFCP_OVERALL_REQUEST_STATUS, &v->fri.ors); + err |= decv(mb, BFCP_FLOOR_REQUEST_STATUS, + (void *)&v->fri.frsv, sizeof(*v->fri.frsv), + &v->fri.frsc); + err |= decn(mb, BFCP_BENEFICIARY_INFO, &v->fri.bfi); + err |= decn(mb, BFCP_REQUESTED_BY_INFO, &v->fri.rbi); + err |= decn(mb, BFCP_PRIORITY, &v->fri.prio); + err |= decn(mb, BFCP_PARTICIPANT_PROV_INFO, &v->fri.ppi); + break; + + case BFCP_REQUESTED_BY_INFO: + if (len < 2) + goto badmsg; + + v->rbi.rbid = ntohs(mbuf_read_u16(mb)); + err |= decn(mb, BFCP_USER_DISPLAY_NAME, &v->rbi.dname); + err |= decn(mb, BFCP_USER_URI, &v->rbi.uri); + break; + + case BFCP_FLOOR_REQUEST_STATUS: + if (len < 2) + goto badmsg; + + v->frs.floorid = ntohs(mbuf_read_u16(mb)); + err |= decn(mb, BFCP_REQUEST_STATUS, &v->frs.reqstat); + err |= decn(mb, BFCP_STATUS_INFO, &v->frs.statinfo); + break; + + case BFCP_OVERALL_REQUEST_STATUS: + if (len < 2) + goto badmsg; + + v->ors.freqid = ntohs(mbuf_read_u16(mb)); + err |= decn(mb, BFCP_REQUEST_STATUS, &v->ors.reqstat); + err |= decn(mb, BFCP_STATUS_INFO, &v->ors.statinfo); + break; + + default: + mb->pos += len; + (void)re_fprintf(stderr, "bfcp decode: unknown attribute %d\n", + attr->type); + break; + } + + if (err) + goto error; + + /* padding */ + while (((mb->pos - start) & 0x03) && mbuf_get_left(mb)) + ++mb->pos; + + return 0; + + badmsg: + err = EBADMSG; + error: + return err; +} + + +int bfcp_attr_decode(struct bfcp_attr **attrp, struct mbuf *mb) +{ + struct bfcp_attr *attr; + int err; + + if (!attrp || !mb) + return EINVAL; + + attr = mem_zalloc(sizeof(*attr), destructor); + if (!attr) + return ENOMEM; + + err = attr_decode(attr, &attr->v, mb); + + if (err) + mem_deref(attr); + else + *attrp = attr; + + return err; +} + + +const char *bfcp_attr_name(enum bfcp_attrib attr) +{ + switch (attr) { + + 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_PARTICIPANT_PROV_INFO: return "PARTICIPANT-PROVIDED-INFO"; + case BFCP_STATUS_INFO: return "STATUS-INFO"; + case BFCP_SUPPORTED_ATTRIBUTES: return "SUPPORTED-ATTRIBUTES"; + case BFCP_SUPPORTED_PRIMITIVES: return "SUPPORTED-PRIMITIVES"; + case BFCP_USER_DISPLAY_NAME: return "USER-DISPLAY-NAME"; + case BFCP_USER_URI: return "USER-URI"; + case BFCP_BENEFICIARY_INFO: return "BENEFICIARY-INFORMATION"; + case BFCP_FLOOR_REQUEST_INFO: return "FLOOR-REQUEST-INFORMATION"; + case BFCP_REQUESTED_BY_INFO: return "REQUESTED-BY-INFORMATION"; + case BFCP_FLOOR_REQUEST_STATUS: return "FLOOR-REQUEST-STATUS"; + case BFCP_OVERALL_REQUEST_STATUS: return "OVERALL-REQUEST-STATUS"; + default: return "???"; + } +} + + +static int leadh(struct re_printf *pf, void *arg) +{ + int16_t level = *(int16_t *)arg; + int err = 0; + + while (level--) + err |= re_hprintf(pf, " "); + + return err; +} + + +static int attr_print(int16_t level, struct re_printf *pf, + uint8_t type, const void *p) +{ + const union bfcp_union *v = p; + uint32_t i; + int err = 0; + + if (!v) + return EINVAL; + + ++level; + + err |= re_hprintf(pf, "%H%-28s", leadh, &level, bfcp_attr_name(type)); + + if (bfcp_attr_isgrouped(type)) { + const uint16_t level2 = level + 1; + err |= re_hprintf(pf, "\n%H{\n%H", leadh, &level, + leadh, &level2); + } + + switch (type) { + + case BFCP_BENEFICIARY_ID: + case BFCP_FLOOR_ID: + case BFCP_FLOOR_REQUEST_ID: + case BFCP_PRIORITY: + err |= re_hprintf(pf, "%u", v->u16); + break; + + case BFCP_REQUEST_STATUS: + err |= re_hprintf(pf, "%s (%d), qpos=%u", + bfcp_reqstat_name(v->reqstat.stat), + v->reqstat.stat, + v->reqstat.qpos); + break; + + case BFCP_ERROR_CODE: + err |= re_hprintf(pf, "%u (%s)", v->errcode.code, + bfcp_errcode_name(v->errcode.code)); + if (v->errcode.code == BFCP_ERR_UNKNOWN_MAND_ATTR) { + for (i=0; ierrcode.len; i++) { + uint8_t t = v->errcode.details[i] >> 1; + err |= re_hprintf(pf, " %s", + bfcp_attr_name(t)); + } + } + break; + + case BFCP_ERROR_INFO: + case BFCP_PARTICIPANT_PROV_INFO: + case BFCP_STATUS_INFO: + case BFCP_USER_DISPLAY_NAME: + case BFCP_USER_URI: + err |= re_hprintf(pf, "\"%s\"", v->str); + break; + + case BFCP_SUPPORTED_ATTRIBUTES: + err |= re_hprintf(pf, "%u:", v->supattr.attrc); + for (i=0; isupattr.attrc; i++) { + const enum bfcp_attrib attr = v->supattr.attrv[i]; + err |= re_hprintf(pf, " %s", bfcp_attr_name(attr)); + } + break; + + case BFCP_SUPPORTED_PRIMITIVES: + err |= re_hprintf(pf, "%u:", v->supprim.primc); + for (i=0; isupprim.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, "bfid=%u\n", v->bfi.bfid); + err |= attr_print(level, pf, BFCP_USER_DISPLAY_NAME, + &v->bfi.dname); + err |= attr_print(level, pf, BFCP_USER_URI, &v->bfi.uri); + break; + + case BFCP_FLOOR_REQUEST_INFO: + err |= re_hprintf(pf, "freqid=%u\n", v->fri.freqid); + err |= attr_print(level, pf, BFCP_OVERALL_REQUEST_STATUS, + &v->fri.ors); + for (i=0; ifri.frsc; i++) { + err |= attr_print(level, pf, BFCP_FLOOR_REQUEST_STATUS, + &v->fri.frsv[i]); + } + err |= attr_print(level, pf, BFCP_BENEFICIARY_INFO, + &v->fri.bfi); + err |= attr_print(level, pf, BFCP_REQUESTED_BY_INFO, + &v->fri.rbi); + err |= attr_print(level, pf, BFCP_PRIORITY, &v->fri.prio); + err |= attr_print(level, pf, BFCP_PARTICIPANT_PROV_INFO, + &v->fri.ppi); + break; + + case BFCP_REQUESTED_BY_INFO: + err |= re_hprintf(pf, "rbid=%u\n", v->rbi.rbid); + err |= attr_print(level, pf, BFCP_USER_DISPLAY_NAME, + &v->rbi.dname); + err |= attr_print(level, pf, BFCP_USER_URI, &v->rbi.uri); + break; + + case BFCP_FLOOR_REQUEST_STATUS: + err |= re_hprintf(pf, "floorid=%u\n", v->frs.floorid); + err |= attr_print(level, pf, BFCP_REQUEST_STATUS, + &v->frs.reqstat); + err |= attr_print(level, pf, BFCP_STATUS_INFO, + &v->frs.statinfo); + break; + + case BFCP_OVERALL_REQUEST_STATUS: + err |= re_hprintf(pf, "freqid=%u\n", v->ors.freqid); + err |= attr_print(level, pf, BFCP_REQUEST_STATUS, + &v->ors.reqstat); + err |= attr_print(level, pf, BFCP_STATUS_INFO, + &v->ors.statinfo); + break; + + default: + err |= re_hprintf(pf, "?"); + break; + } + + if (bfcp_attr_isgrouped(type)) + err |= re_hprintf(pf, "%H}", leadh, &level); + + err |= re_hprintf(pf, "\n"); + + return err; +} + + +int bfcp_attr_print(struct re_printf *pf, const struct bfcp_attr *a) +{ + if (!a) + return 0; + + return attr_print(0, pf, a->type, &a->v); +} + + +bool bfcp_attr_isgrouped(enum bfcp_attrib attr) +{ + switch (attr) { + + case BFCP_BENEFICIARY_INFO: + case BFCP_FLOOR_REQUEST_INFO: + case BFCP_REQUESTED_BY_INFO: + case BFCP_FLOOR_REQUEST_STATUS: + case BFCP_OVERALL_REQUEST_STATUS: + return true; + + default: + return false; + } +} + + +const char *bfcp_errcode_name(enum bfcp_err code) +{ + switch (code) { + + case BFCP_ERR_CONF_NOT_EXIST: + return "Conference does not Exist"; + case BFCP_ERR_USER_NOT_EXIST: + return "User does not Exist"; + case BFCP_ERR_UNKNOWN_PRIM: + return "Unknown Primitive"; + case BFCP_ERR_UNKNOWN_MAND_ATTR: + return "Unknown Mandatory Attribute"; + case BFCP_ERR_UNAUTH_OPERATION: + return "Unauthorized Operation"; + case BFCP_ERR_INVALID_FLOOR_ID: + return "Invalid Floor ID"; + case BFCP_ERR_FLOOR_REQ_ID_NOT_EXIST: + return "Floor Request ID Does Not Exist"; + case BFCP_ERR_MAX_FLOOR_REQ_REACHED: + return "You have Already Reached the Maximum Number" + " of Ongoing Floor Requests for this Floor"; + case BFCP_ERR_USE_TLS: + return "Use TLS"; + default: + return "???"; + } +} diff --git a/src/bfcp/bfcp.h b/src/bfcp/bfcp.h new file mode 100644 index 0000000..bc0cbed --- /dev/null +++ b/src/bfcp/bfcp.h @@ -0,0 +1,60 @@ +/** + * @file bfcp.h Internal interface to Binary Floor Control Protocol (BFCP) + * + * Copyright (C) 2010 Creytiv.com + */ + + +/* header */ +enum { + BFCP_HDR_SIZE = 12, + ATTR_HDR_SIZE = 2 +}; + + +struct bfcp_hdr { + uint8_t ver; + unsigned i:1; + enum bfcp_prim prim; + uint16_t len; + uint32_t confid; + uint16_t tid; + uint16_t userid; +}; + +int bfcp_hdr_encode(struct mbuf *mb, enum bfcp_prim prim, uint16_t len, + uint32_t confid, uint16_t tid, uint16_t userid); +int bfcp_hdr_decode(struct mbuf *mb, struct bfcp_hdr *hdr); + + +/* attributes */ +int bfcp_attr_encode(struct mbuf *mb, bool mand, enum bfcp_attrib type, + const void *v); +int bfcp_attr_decode(struct bfcp_attr **attrp, struct mbuf *mb); +int bfcp_attr_print(struct re_printf *pf, const struct bfcp_attr *a); +bool bfcp_attr_isgrouped(enum bfcp_attrib attr); + + +/* socket */ + +struct bfcp_sock { + struct list transl; + struct list connl; + struct tcp_sock *ts; + struct tls *tls; + enum bfcp_transp transp; + uint16_t tidc; + bool active; + bfcp_msg_h *msgh; + void *arg; +}; + + +int bfcp_send(struct bfcp_sock *sock, const struct sa *dst, struct mbuf *mb); + + +/* ctrans request */ + +void bfcp_ctrans_completed(struct bfcp_ctrans *ct, int err, + const struct bfcp_msg *msg); +struct bfcp_ctrans *bfcp_ctrans_find(struct bfcp_sock *sock, uint16_t tid); diff --git a/src/bfcp/hdr.c b/src/bfcp/hdr.c new file mode 100644 index 0000000..13c2350 --- /dev/null +++ b/src/bfcp/hdr.c @@ -0,0 +1,57 @@ +/** + * @file hdr.c BFCP Message header + * + * Copyright (C) 2010 Creytiv.com + */ +#include +#include +#include +#include +#include +#include +#include +#include "bfcp.h" + + +int bfcp_hdr_encode(struct mbuf *mb, enum bfcp_prim prim, uint16_t len, + uint32_t confid, uint16_t tid, uint16_t userid) +{ + int err; + + err = mbuf_write_u8(mb, BFCP_VERSION << 5); + 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; +} + + +int bfcp_hdr_decode(struct mbuf *mb, struct bfcp_hdr *hdr) +{ + uint8_t b; + + if (mbuf_get_left(mb) < BFCP_HDR_SIZE) + return EBADMSG; + + b = mbuf_read_u8(mb); + hdr->ver = b >> 5; + hdr->i = (b >> 4) & 1; + hdr->prim = mbuf_read_u8(mb); + hdr->len = ntohs(mbuf_read_u16(mb)); + hdr->confid = ntohl(mbuf_read_u32(mb)); + hdr->tid = ntohs(mbuf_read_u16(mb)); + hdr->userid = ntohs(mbuf_read_u16(mb)); + + if (hdr->ver != BFCP_VERSION) + return EBADMSG; + + if (mbuf_get_left(mb) < (size_t)(hdr->len*4)) { + + return ENODATA; + } + + return 0; +} diff --git a/src/bfcp/mod.mk b/src/bfcp/mod.mk new file mode 100644 index 0000000..f46e564 --- /dev/null +++ b/src/bfcp/mod.mk @@ -0,0 +1,13 @@ +# +# mod.mk +# +# Copyright (C) 2010 Creytiv.com +# + +SRCS += bfcp/attr.c +SRCS += bfcp/hdr.c +SRCS += bfcp/msg.c +SRCS += bfcp/rep.c +SRCS += bfcp/req.c +SRCS += bfcp/sock.c +SRCS += bfcp/transp.c diff --git a/src/bfcp/msg.c b/src/bfcp/msg.c new file mode 100644 index 0000000..c931b68 --- /dev/null +++ b/src/bfcp/msg.c @@ -0,0 +1,260 @@ +/** + * @file bfcp/msg.c BFCP Message + * + * Copyright (C) 2010 Creytiv.com + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "bfcp.h" + + +struct bfcp_msg { + struct sa src; + struct bfcp_hdr hdr; + struct list attrl; +}; + + +static void destructor(void *arg) +{ + struct bfcp_msg *msg = arg; + + list_flush(&msg->attrl); +} + + +int bfcp_msg_vencode(struct mbuf *mb, enum bfcp_prim prim, + uint32_t confid, uint16_t tid, uint16_t userid, + uint32_t attrc, va_list ap) +{ + size_t start; + uint32_t i; + uint16_t len; + int err = 0; + + if (!mb) + return EINVAL; + + start = mb->pos; + mb->pos += BFCP_HDR_SIZE; + + for (i=0; ipos - start - BFCP_HDR_SIZE) / 4; + mb->pos = start; + err = bfcp_hdr_encode(mb, prim, len, confid, tid, userid); + + mb->pos = mb->end; + + return err; +} + + +int bfcp_msg_encode(struct mbuf *mb, enum bfcp_prim prim, uint32_t confid, + uint16_t tid, uint16_t userid, uint32_t attrc, ...) +{ + va_list ap; + int err; + + va_start(ap, attrc); + err = bfcp_msg_vencode(mb, prim, confid, tid, userid, attrc, ap); + va_end(ap); + + return err; +} + + +int bfcp_msg_decode(struct bfcp_msg **msgp, struct mbuf *mb) +{ + struct bfcp_msg *msg; + size_t start, extra; + int err; + + if (!msgp || !mb) + return EINVAL; + + start = mb->pos; + + msg = mem_zalloc(sizeof(*msg), destructor); + if (!msg) + return ENOMEM; + + err = bfcp_hdr_decode(mb, &msg->hdr); + if (err) { + mb->pos = start; + goto out; + } + + extra = mbuf_get_left(mb) - 4*msg->hdr.len; + + while (mbuf_get_left(mb) - extra >= ATTR_HDR_SIZE) { + + struct bfcp_attr *attr; + + err = bfcp_attr_decode(&attr, mb); + if (err) + break; + + list_append(&msg->attrl, &attr->le, attr); + } + + out: + if (err) + mem_deref(msg); + else + *msgp = msg; + + return err; +} + + +static bool attr_match(const struct bfcp_attr *attr, void *arg) +{ + return attr->type == *(uint8_t *)arg; +} + + +struct bfcp_attr *bfcp_msg_attr(const struct bfcp_msg *msg, + enum bfcp_attrib type) +{ + return bfcp_msg_attr_apply(msg, attr_match, &type); +} + + +struct bfcp_attr *bfcp_msg_attr_apply(const struct bfcp_msg *msg, + bfcp_attr_h *h, void *arg) +{ + struct le *le = msg ? list_head(&msg->attrl) : NULL; + + while (le) { + struct bfcp_attr *attr = le->data; + + le = le->next; + + if (h && h(attr, arg)) + return attr; + } + + return NULL; +} + + +static bool attr_print(const struct bfcp_attr *attr, void *arg) +{ + struct re_printf *pf = arg; + + return 0 != bfcp_attr_print(pf, attr); +} + + +int bfcp_msg_print(struct re_printf *pf, const struct bfcp_msg *msg) +{ + int err; + + if (!msg) + return 0; + + err = re_hprintf(pf, "%s (len=%u confid=%u tid=%u userid=%u)\n", + bfcp_prim_name(msg->hdr.prim), msg->hdr.len, + msg->hdr.confid, msg->hdr.tid, msg->hdr.userid); + + bfcp_msg_attr_apply(msg, attr_print, pf); + + return err; +} + + +enum bfcp_prim bfcp_msg_prim(const struct bfcp_msg *msg) +{ + return msg ? msg->hdr.prim : 0; +} + + +uint32_t bfcp_msg_confid(const struct bfcp_msg *msg) +{ + return msg ? msg->hdr.confid : 0; +} + + +uint16_t bfcp_msg_tid(const struct bfcp_msg *msg) +{ + return msg ? msg->hdr.tid : 0; +} + + +uint16_t bfcp_msg_userid(const struct bfcp_msg *msg) +{ + return msg ? msg->hdr.userid : 0; +} + + +const char *bfcp_reqstat_name(enum bfcp_rstat stat) +{ + switch (stat) { + + 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 "???"; + } +} + + +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_STAT: 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"; + default: return "???"; + } +} + + +void bfcp_msg_set_src(struct bfcp_msg *msg, const struct sa *src) +{ + if (!msg || !src) + return; + + msg->src = *src; +} + + +const struct sa *bfcp_msg_src(const struct bfcp_msg *msg) +{ + return msg ? &msg->src : NULL; +} diff --git a/src/bfcp/rep.c b/src/bfcp/rep.c new file mode 100644 index 0000000..f296bfa --- /dev/null +++ b/src/bfcp/rep.c @@ -0,0 +1,70 @@ +/** + * @file bfcp/rep.c BFCP Reply + * + * Copyright (C) 2010 Creytiv.com + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "bfcp.h" + + +int bfcp_reply(struct bfcp_sock *sock, const struct bfcp_msg *req, + enum bfcp_prim prim, uint32_t attrc, ...) +{ + struct mbuf *mb; + va_list ap; + int err; + + if (!sock || !req) + return EINVAL; + + mb = mbuf_alloc(64); + if (!mb) + return ENOMEM; + + va_start(ap, attrc); + err = bfcp_msg_vencode(mb, prim, bfcp_msg_confid(req), + bfcp_msg_tid(req), bfcp_msg_userid(req), + attrc, ap); + va_end(ap); + + if (err) + goto out; + + mb->pos = 0; + + err = bfcp_send(sock, bfcp_msg_src(req), mb); + + out: + mem_deref(mb); + + return err; +} + + +int bfcp_ereply(struct bfcp_sock *sock, const struct bfcp_msg *req, + enum bfcp_err code, ...) +{ + struct bfcp_errcode ec; + va_list ap; + + va_start(ap, code); + + memset(&ec, 0, sizeof(ec)); + ec.code = code; + + if (code == BFCP_ERR_UNKNOWN_MAND_ATTR) { + ec.details = va_arg(ap, uint8_t *); + ec.len = va_arg(ap, size_t); + } + + va_end(ap); + + return bfcp_reply(sock, req, BFCP_ERROR, 1, BFCP_ERROR_CODE, &ec); +} diff --git a/src/bfcp/req.c b/src/bfcp/req.c new file mode 100644 index 0000000..d464a01 --- /dev/null +++ b/src/bfcp/req.c @@ -0,0 +1,156 @@ +/** + * @file bfcp/req.c BFCP Client request + * + * Copyright (C) 2010 Creytiv.com + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "bfcp.h" + + +struct bfcp_ctrans { + struct le le; + struct tmr tmr; + struct bfcp_ctrans **ctp; + uint16_t tid; + bfcp_resp_h *resph; + void *arg; +}; + + +static void destructor(void *arg) +{ + struct bfcp_ctrans *ct = arg; + + list_unlink(&ct->le); + tmr_cancel(&ct->tmr); +} + + +static void timeout(void *arg) +{ + struct bfcp_ctrans *ct = arg; + + bfcp_ctrans_completed(ct, ETIMEDOUT, NULL); +} + + +static struct bfcp_ctrans *ctrans_new(struct bfcp_sock *sock, + bfcp_resp_h *resph, void *arg) +{ + struct bfcp_ctrans *ct; + + ct = mem_zalloc(sizeof(*ct), destructor); + if (!ct) + return NULL; + + list_append(&sock->transl, &ct->le, ct); + + sock->tidc++; + + if (sock->tidc == 0) + sock->tidc++; + + ct->tid = sock->tidc; + ct->resph = resph; + ct->arg = arg; + + tmr_start(&ct->tmr, 10000, timeout, ct); + + return ct; +} + + +void bfcp_ctrans_completed(struct bfcp_ctrans *ct, int err, + const struct bfcp_msg *msg) +{ + bfcp_resp_h *resph = ct->resph; + void *arg = ct->arg; + + list_unlink(&ct->le); + tmr_cancel(&ct->tmr); + + if (ct->ctp) { + *ct->ctp = NULL; + ct->ctp = NULL; + } + + ct->resph = NULL; + + mem_deref(ct); + + if (resph) + resph(err, msg, arg); +} + + +struct bfcp_ctrans *bfcp_ctrans_find(struct bfcp_sock *sock, uint16_t tid) +{ + struct le *le; + + for (le = sock->transl.head; le; le = le->next) { + + struct bfcp_ctrans *ct = le->data; + + if (ct->tid == tid) + return ct; + } + + return NULL; +} + + +int bfcp_request(struct bfcp_ctrans **ctp, struct bfcp_sock *sock, + const struct sa *dst, + enum bfcp_prim prim, uint32_t confid, uint16_t userid, + bfcp_resp_h *resph, void *arg, uint32_t attrc, ...) +{ + struct bfcp_ctrans *ct; + struct mbuf *mb; + va_list ap; + int err; + + if (!sock || !dst || !confid || !userid) + return EINVAL; + + ct = ctrans_new(sock, resph, arg); + if (!ct) + return ENOMEM; + + mb = mbuf_alloc(512); + if (!mb) { + err = ENOMEM; + goto out; + } + + va_start(ap, attrc); + err = bfcp_msg_vencode(mb, prim, confid, ct->tid, userid, + attrc, ap); + va_end(ap); + if (err) + goto out; + + mb->pos = 0; + + err = bfcp_send(sock, dst, mb); + if (err) + goto out; + + out: + if (err) + mem_deref(ct); + else if (ctp) { + ct->ctp = ctp; + *ctp = ct; + } + + mem_deref(mb); + return err; +} diff --git a/src/bfcp/sock.c b/src/bfcp/sock.c new file mode 100644 index 0000000..d235052 --- /dev/null +++ b/src/bfcp/sock.c @@ -0,0 +1,343 @@ +/** + * @file bfcp/sock.c BFCP Socket + * + * Copyright (C) 2010 Creytiv.com + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "bfcp.h" + + +struct bfcp_conn { + struct le le; + struct sa paddr; + struct mbuf *mbtx; + struct mbuf *mbrx; + struct tcp_conn *tc; + struct tls_conn *sc; + struct bfcp_sock *bs; + bool established; +}; + + +static void destructor(void *arg) +{ + struct bfcp_sock *sock = arg; + + list_flush(&sock->transl); + list_flush(&sock->connl); + mem_deref(sock->tls); + mem_deref(sock->ts); +} + + +static void conn_destructor(void *arg) +{ + struct bfcp_conn *conn = arg; + + list_unlink(&conn->le); + mem_deref(conn->mbtx); + mem_deref(conn->mbrx); + mem_deref(conn->sc); + mem_deref(conn->tc); +} + + +static struct bfcp_conn *conn_add(struct bfcp_sock *bs, + const struct sa *paddr) +{ + struct bfcp_conn *bc = mem_zalloc(sizeof(*bc), conn_destructor); + if (!bc) + return NULL; + + list_append(&bs->connl, &bc->le, bc); + + bc->bs = bs; + bc->paddr = *paddr; + + return bc; +} + + +static void tcp_estab_handler(void *arg) +{ + struct bfcp_conn *conn = arg; + +#ifdef USE_TLS + if (conn->sc) { + char cn[256]; + int err; + + err = tls_verify_cert(conn->sc, cn, sizeof(cn)); + + (void)re_printf("CN: '%s' (%sverified)\n", + cn, err ? "not " : ""); + } +#endif + + conn->established = true; + + /* flush transmit buffer */ + if (conn->mbtx) { + + conn->mbtx->pos = 0; + if (tcp_send(conn->tc, conn->mbtx)) + return; + + conn->mbtx = mem_deref(conn->mbtx); + } +} + + +static void tcp_recv_handler(struct mbuf *mb, void *arg) +{ + struct bfcp_conn *conn = arg; + size_t pos; + int err = 0; + + if (conn->mbrx) { + pos = conn->mbrx->pos; + + conn->mbrx->pos = conn->mbrx->end; + + err = mbuf_write_mem(conn->mbrx, + mbuf_buf(mb), mbuf_get_left(mb)); + if (err) + goto out; + + conn->mbrx->pos = pos; + } + else { + conn->mbrx = mem_ref(mb); + } + + for (;;) { + struct bfcp_msg *msg; + struct bfcp_ctrans *ct; + + pos = conn->mbrx->pos; + + err = bfcp_msg_decode(&msg, conn->mbrx); + if (err) { + if (err == ENODATA) { + conn->mbrx->pos = pos; + err = 0; + } + break; + } + + bfcp_msg_set_src(msg, &conn->paddr); + + ct = bfcp_ctrans_find(conn->bs, bfcp_msg_tid(msg)); + if (ct) { + bfcp_ctrans_completed(ct, 0, msg); + } + else { + if (conn->bs->msgh) + conn->bs->msgh(msg, conn->bs->arg); + } + + mem_deref(msg); + + if (0 == mbuf_get_left(conn->mbrx)) { + conn->mbrx = mem_deref(conn->mbrx); + break; + } + } + + out: + if (err) + mem_deref(conn); +} + + +static void tcp_close_handler(int err, void *arg) +{ + struct bfcp_conn *conn = arg; + + (void)re_printf("BFCP connection closed: %s\n", strerror(err)); + + mem_deref(conn); +} + + +static void tcp_conn_handler(const struct sa *addr, void *arg) +{ + struct bfcp_sock *bs = arg; + struct bfcp_conn *conn; + + (void)re_printf("bfcpd: Connection from %J via %s\n", addr, + bs->transp == BFCP_TRANSP_TLS ? "TLS" : "TCP"); + + conn = conn_add(bs, addr); + if (conn) { + if (tcp_accept(&conn->tc, bs->ts, tcp_estab_handler, + tcp_recv_handler, tcp_close_handler, conn)) + goto error; + +#ifdef USE_TLS + if (bs->transp == BFCP_TRANSP_TLS) { + if (tls_start_tcp(&conn->sc, bs->tls, conn->tc)) + goto error; + } +#endif + + return; + } + + error: + tcp_reject(bs->ts); + mem_deref(conn); +} + + +static struct bfcp_conn *findconn(const struct bfcp_sock *bs, + const struct sa *peer) +{ + struct le *le; + + for (le = bs->connl.head; le; le = le->next) { + + struct bfcp_conn *bc = le->data; + + if (sa_cmp(&bc->paddr, peer, SA_ALL)) + return bc; + } + + return NULL; +} + + +int bfcp_listen(struct bfcp_sock **sockp, enum bfcp_transp transp, + struct tls *tls, const struct sa *laddr, + bfcp_msg_h *msgh, void *arg) +{ + struct bfcp_sock *sock; + int err = 0; + + if (!sockp) + return EINVAL; + + sock = mem_zalloc(sizeof(*sock), destructor); + if (!sock) + return ENOMEM; + + sock->transp = transp; + sock->tls = mem_ref(tls); + sock->msgh = msgh; + sock->arg = arg; + + /* Server */ + if (laddr) { + switch (transp) { + + case BFCP_TRANSP_TLS: + if (!tls) { + err = EINVAL; + goto out; + } + /*@fallthrough@*/ + + case BFCP_TRANSP_TCP: + sock->active = false; + err = tcp_listen(&sock->ts, laddr, tcp_conn_handler, + sock); + break; + + default: + err = EPROTONOSUPPORT; + break; + } + } + else { + sock->active = true; + } + + out: + if (err) + mem_deref(sock); + else if (sockp) + *sockp = sock; + + return err; +} + + +int bfcp_send(struct bfcp_sock *sock, const struct sa *dst, struct mbuf *mb) +{ + struct bfcp_conn *conn = NULL; + int err = 0; + + if (!sock || !dst || !mb) + return EINVAL; + + switch (sock->transp) { + + case BFCP_TRANSP_TCP: + case BFCP_TRANSP_TLS: + + conn = findconn(sock, dst); + + if (!conn) { + + if (!sock->active) + return ENOTCONN; + + conn = conn_add(sock, dst); + if (!conn) { + err = ENOMEM; + goto out; + } + + err = tcp_connect(&conn->tc, dst, tcp_estab_handler, + tcp_recv_handler, + tcp_close_handler, conn); + if (err) + goto out; + +#ifdef USE_TLS + if (sock->transp == BFCP_TRANSP_TLS) { + + err = tls_start_tcp(&conn->sc, sock->tls, + conn->tc); + if (err) + goto out; + } +#endif + } + + if (conn->established) { + err = tcp_send(conn->tc, mb); + } + else { + if (!conn->mbtx) { + conn->mbtx = mem_ref(mb); + } + else { + conn->mbtx->pos = conn->mbtx->end; + err = mbuf_write_mem(conn->mbtx, mbuf_buf(mb), + mbuf_get_left(mb)); + } + } + break; + + default: + err = EPROTONOSUPPORT; + break; + } + + out: + if (err) + mem_deref(conn); + + return err; +} diff --git a/src/bfcp/transp.c b/src/bfcp/transp.c new file mode 100644 index 0000000..a561705 --- /dev/null +++ b/src/bfcp/transp.c @@ -0,0 +1,38 @@ +/** + * @file transp.c BFCP Transport + * + * Copyright (C) 2010 Creytiv.com + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "bfcp.h" + + +bool bfcp_transp_reliable(enum bfcp_transp tp) +{ + switch (tp) { + + case BFCP_TRANSP_TCP: return true; + case BFCP_TRANSP_TLS: return true; + default: return false; + } +} + + +const char *bfcp_transp_proto(enum bfcp_transp tp) +{ + switch (tp) { + + case BFCP_TRANSP_TCP: return "TCP/BFCP"; + case BFCP_TRANSP_TLS: return "TCP/TLS/BFCP"; + default: return "???"; + } +}