1 /**
2  * @file bfcp/attr.c BFCP Attributes
3  *
4  * Copyright (C) 2010 Creytiv.com
5  */
6 #include <string.h>
7 #include <re_types.h>
8 #include <re_fmt.h>
9 #include <re_mem.h>
10 #include <re_mbuf.h>
11 #include <re_sa.h>
12 #include <re_list.h>
13 #include <re_tmr.h>
14 #include <re_bfcp.h>
15 #include "bfcp.h"
16 
17 
18 enum {
19 	BFCP_ATTR_HDR_SIZE = 2,
20 };
21 
22 
destructor(void * arg)23 static void destructor(void *arg)
24 {
25 	struct bfcp_attr *attr = arg;
26 
27 	switch (attr->type) {
28 
29 	case BFCP_ERROR_CODE:
30 		mem_deref(attr->v.errcode.details);
31 		break;
32 
33 	case BFCP_ERROR_INFO:
34 	case BFCP_PART_PROV_INFO:
35 	case BFCP_STATUS_INFO:
36 	case BFCP_USER_DISP_NAME:
37 	case BFCP_USER_URI:
38 		mem_deref(attr->v.str);
39 		break;
40 
41 	case BFCP_SUPPORTED_ATTRS:
42 		mem_deref(attr->v.supattr.attrv);
43 		break;
44 
45 	case BFCP_SUPPORTED_PRIMS:
46 		mem_deref(attr->v.supprim.primv);
47 		break;
48 
49 	default:
50 		break;
51 	}
52 
53 	list_flush(&attr->attrl);
54 	list_unlink(&attr->le);
55 }
56 
57 
attr_encode(struct mbuf * mb,bool mand,enum bfcp_attrib type,const void * v)58 static int attr_encode(struct mbuf *mb, bool mand, enum bfcp_attrib type,
59 		       const void *v)
60 {
61 	const struct bfcp_reqstatus *reqstatus = v;
62 	const struct bfcp_errcode *errcode = v;
63 	const struct bfcp_supattr *supattr = v;
64 	const struct bfcp_supprim *supprim = v;
65 	const enum bfcp_priority *priority = v;
66 	const uint16_t *u16 = v;
67 	size_t start, len, i;
68 	int err;
69 
70 	start = mb->pos;
71 	mb->pos += BFCP_ATTR_HDR_SIZE;
72 
73 	switch (type) {
74 
75 	case BFCP_BENEFICIARY_ID:
76 	case BFCP_FLOOR_ID:
77 	case BFCP_FLOOR_REQUEST_ID:
78 	case BFCP_BENEFICIARY_INFO:
79 	case BFCP_FLOOR_REQ_INFO:
80 	case BFCP_REQUESTED_BY_INFO:
81 	case BFCP_FLOOR_REQ_STATUS:
82 	case BFCP_OVERALL_REQ_STATUS:
83 		err = mbuf_write_u16(mb, htons(*u16));
84 		break;
85 
86 	case BFCP_PRIORITY:
87 		err  = mbuf_write_u8(mb, *priority << 5);
88 		err |= mbuf_write_u8(mb, 0x00);
89 		break;
90 
91 	case BFCP_REQUEST_STATUS:
92 		err  = mbuf_write_u8(mb, reqstatus->status);
93 		err |= mbuf_write_u8(mb, reqstatus->qpos);
94 		break;
95 
96 	case BFCP_ERROR_CODE:
97 		err = mbuf_write_u8(mb, errcode->code);
98 		if (errcode->details && errcode->len)
99 			err |= mbuf_write_mem(mb, errcode->details,
100 					      errcode->len);
101 		break;
102 
103 	case BFCP_ERROR_INFO:
104 	case BFCP_PART_PROV_INFO:
105 	case BFCP_STATUS_INFO:
106 	case BFCP_USER_DISP_NAME:
107 	case BFCP_USER_URI:
108 		err = mbuf_write_str(mb, v);
109 		break;
110 
111 	case BFCP_SUPPORTED_ATTRS:
112 		for (i=0, err=0; i<supattr->attrc; i++)
113 			err |= mbuf_write_u8(mb, supattr->attrv[i] << 1);
114 		break;
115 
116 	case BFCP_SUPPORTED_PRIMS:
117 		for (i=0, err=0; i<supprim->primc; i++)
118 			err |= mbuf_write_u8(mb, supprim->primv[i]);
119 		break;
120 
121 	default:
122 		err = EINVAL;
123 		break;
124 	}
125 
126 	/* header */
127 	len = mb->pos - start;
128 
129 	mb->pos = start;
130 	err |= mbuf_write_u8(mb, (type<<1) | (mand ? 1 : 0));
131 	err |= mbuf_write_u8(mb, len);
132 	mb->pos += (len - BFCP_ATTR_HDR_SIZE);
133 
134 	/* padding */
135 	while ((mb->pos - start) & 0x03)
136 		err |= mbuf_write_u8(mb, 0x00);
137 
138 	return err;
139 }
140 
141 
142 /**
143  * Encode BFCP Attributes with variable arguments
144  *
145  * @param mb    Mbuf to encode into
146  * @param attrc Number of attributes
147  * @param ap    Variable argument of attributes
148  *
149  * @return 0 if success, otherwise errorcode
150  */
bfcp_attrs_vencode(struct mbuf * mb,unsigned attrc,va_list * ap)151 int bfcp_attrs_vencode(struct mbuf *mb, unsigned attrc, va_list *ap)
152 {
153 	unsigned i;
154 
155 	if (!mb)
156 		return EINVAL;
157 
158 	for (i=0; i<attrc; i++) {
159 
160 		int  type     = va_arg(*ap, int);
161 		unsigned subc = va_arg(*ap, unsigned);
162 		const void *v = va_arg(*ap, const void *);
163 		size_t start, len;
164 		int err;
165 
166 		if (!v)
167 			continue;
168 
169 		start = mb->pos;
170 
171 		if (type == BFCP_ENCODE_HANDLER) {
172 
173 			const struct bfcp_encode *enc = v;
174 
175 			if (enc->ench) {
176 				err = enc->ench(mb, enc->arg);
177 				if (err)
178 					return err;
179 			}
180 
181 			continue;
182 		}
183 
184 		err = attr_encode(mb, type>>7, type & 0x7f, v);
185 		if (err)
186 			return err;
187 
188 		if (subc == 0)
189 			continue;
190 
191 		err = bfcp_attrs_vencode(mb, subc, ap);
192 		if (err)
193 			return err;
194 
195 		/* update total length for grouped attributes */
196 		len = mb->pos - start;
197 
198 		mb->pos = start + 1;
199 		err = mbuf_write_u8(mb, (uint8_t)len);
200 		if (err)
201 			return err;
202 
203 		mb->pos += (len - BFCP_ATTR_HDR_SIZE);
204 	}
205 
206 	return 0;
207 }
208 
209 
210 /**
211  * Encode BFCP Attributes
212  *
213  * @param mb      Mbuf to encode into
214  * @param attrc   Number of attributes
215  *
216  * @return 0 if success, otherwise errorcode
217  */
bfcp_attrs_encode(struct mbuf * mb,unsigned attrc,...)218 int bfcp_attrs_encode(struct mbuf *mb, unsigned attrc, ...)
219 {
220 	va_list ap;
221 	int err;
222 
223 	va_start(ap, attrc);
224 	err = bfcp_attrs_vencode(mb, attrc, &ap);
225 	va_end(ap);
226 
227 	return err;
228 }
229 
230 
attr_decode(struct bfcp_attr ** attrp,struct mbuf * mb,struct bfcp_unknown_attr * uma)231 static int attr_decode(struct bfcp_attr **attrp, struct mbuf *mb,
232 		       struct bfcp_unknown_attr *uma)
233 {
234 	struct bfcp_attr *attr;
235 	union bfcp_union *v;
236 	size_t i, start, len;
237 	int err = 0;
238 	uint8_t b;
239 
240 	if (mbuf_get_left(mb) < BFCP_ATTR_HDR_SIZE)
241 		return EBADMSG;
242 
243 	attr = mem_zalloc(sizeof(*attr), destructor);
244 	if (!attr)
245 		return ENOMEM;
246 
247 	start = mb->pos;
248 
249 	b = mbuf_read_u8(mb);
250 	attr->type = b >> 1;
251 	attr->mand = b & 1;
252 	len = mbuf_read_u8(mb);
253 
254 	if (len < BFCP_ATTR_HDR_SIZE)
255 		goto badmsg;
256 
257 	len -= BFCP_ATTR_HDR_SIZE;
258 
259 	if (mbuf_get_left(mb) < len)
260 		goto badmsg;
261 
262 	v = &attr->v;
263 
264 	switch (attr->type) {
265 
266 	case BFCP_BENEFICIARY_ID:
267 	case BFCP_FLOOR_ID:
268 	case BFCP_FLOOR_REQUEST_ID:
269 		if (len < 2)
270 			goto badmsg;
271 
272 		v->u16 = ntohs(mbuf_read_u16(mb));
273 		break;
274 
275 	case BFCP_PRIORITY:
276 		if (len < 2)
277 			goto badmsg;
278 
279 		v->priority = mbuf_read_u8(mb) >> 5;
280 		(void)mbuf_read_u8(mb);
281 		break;
282 
283 	case BFCP_REQUEST_STATUS:
284 		if (len < 2)
285 			goto badmsg;
286 
287 		v->reqstatus.status = mbuf_read_u8(mb);
288 		v->reqstatus.qpos   = mbuf_read_u8(mb);
289 		break;
290 
291 	case BFCP_ERROR_CODE:
292 		if (len < 1)
293 			goto badmsg;
294 
295 		v->errcode.code = mbuf_read_u8(mb);
296 		v->errcode.len  = len - 1;
297 
298 		if (v->errcode.len == 0)
299 			break;
300 
301 		v->errcode.details = mem_alloc(v->errcode.len, NULL);
302 		if (!v->errcode.details) {
303 			err = ENOMEM;
304 			goto error;
305 		}
306 
307 		(void)mbuf_read_mem(mb, v->errcode.details,
308 				    v->errcode.len);
309 		break;
310 
311 	case BFCP_ERROR_INFO:
312 	case BFCP_PART_PROV_INFO:
313 	case BFCP_STATUS_INFO:
314 	case BFCP_USER_DISP_NAME:
315 	case BFCP_USER_URI:
316 		err = mbuf_strdup(mb, &v->str, len);
317 		break;
318 
319 	case BFCP_SUPPORTED_ATTRS:
320 		v->supattr.attrc = len;
321 		v->supattr.attrv = mem_alloc(len*sizeof(*v->supattr.attrv),
322 					     NULL);
323 		if (!v->supattr.attrv) {
324 			err = ENOMEM;
325 			goto error;
326 		}
327 
328 		for (i=0; i<len; i++)
329 			v->supattr.attrv[i] = mbuf_read_u8(mb) >> 1;
330 		break;
331 
332 	case BFCP_SUPPORTED_PRIMS:
333 		v->supprim.primc = len;
334 		v->supprim.primv = mem_alloc(len * sizeof(*v->supprim.primv),
335 					     NULL);
336 		if (!v->supprim.primv) {
337 			err = ENOMEM;
338 			goto error;
339 		}
340 
341 		for (i=0; i<len; i++)
342 			v->supprim.primv[i] = mbuf_read_u8(mb);
343 		break;
344 
345 		/* grouped attributes */
346 
347 	case BFCP_BENEFICIARY_INFO:
348 	case BFCP_FLOOR_REQ_INFO:
349 	case BFCP_REQUESTED_BY_INFO:
350 	case BFCP_FLOOR_REQ_STATUS:
351 	case BFCP_OVERALL_REQ_STATUS:
352 		if (len < 2)
353 			goto badmsg;
354 
355 		v->u16 = ntohs(mbuf_read_u16(mb));
356 		err = bfcp_attrs_decode(&attr->attrl, mb, len - 2, uma);
357 		break;
358 
359 	default:
360 		mb->pos += len;
361 
362 		if (!attr->mand)
363 			break;
364 
365 		if (uma && uma->typec < ARRAY_SIZE(uma->typev))
366 			uma->typev[uma->typec++] = attr->type<<1;
367 		break;
368 	}
369 
370 	if (err)
371 		goto error;
372 
373 	/* padding */
374 	while (((mb->pos - start) & 0x03) && mbuf_get_left(mb))
375 		++mb->pos;
376 
377 	*attrp = attr;
378 
379 	return 0;
380 
381  badmsg:
382 	err = EBADMSG;
383  error:
384 	mem_deref(attr);
385 
386 	return err;
387 }
388 
389 
bfcp_attrs_decode(struct list * attrl,struct mbuf * mb,size_t len,struct bfcp_unknown_attr * uma)390 int bfcp_attrs_decode(struct list *attrl, struct mbuf *mb, size_t len,
391 		      struct bfcp_unknown_attr *uma)
392 {
393 	int err = 0;
394 	size_t end;
395 
396 	if (!attrl || !mb || mbuf_get_left(mb) < len)
397 		return EINVAL;
398 
399 	end     = mb->end;
400 	mb->end = mb->pos + len;
401 
402 	while (mbuf_get_left(mb) >= BFCP_ATTR_HDR_SIZE) {
403 
404 		struct bfcp_attr *attr;
405 
406 		err = attr_decode(&attr, mb, uma);
407 		if (err)
408 			break;
409 
410 		list_append(attrl, &attr->le, attr);
411 	}
412 
413 	mb->end = end;
414 
415 	return err;
416 }
417 
418 
bfcp_attrs_find(const struct list * attrl,enum bfcp_attrib type)419 struct bfcp_attr *bfcp_attrs_find(const struct list *attrl,
420 				  enum bfcp_attrib type)
421 {
422 	struct le *le = list_head(attrl);
423 
424 	while (le) {
425 		struct bfcp_attr *attr = le->data;
426 
427 		le = le->next;
428 
429 		if (attr->type == type)
430 			return attr;
431 	}
432 
433 	return NULL;
434 }
435 
436 
bfcp_attrs_apply(const struct list * attrl,bfcp_attr_h * h,void * arg)437 struct bfcp_attr *bfcp_attrs_apply(const struct list *attrl,
438 				   bfcp_attr_h *h, void *arg)
439 {
440 	struct le *le = list_head(attrl);
441 
442 	while (le) {
443 		struct bfcp_attr *attr = le->data;
444 
445 		le = le->next;
446 
447 		if (h && h(attr, arg))
448 			return attr;
449 	}
450 
451 	return NULL;
452 }
453 
454 
455 /**
456  * Get a BFCP sub-attribute from a BFCP attribute
457  *
458  * @param attr BFCP attribute
459  * @param type Attribute type
460  *
461  * @return Matching BFCP attribute if found, otherwise NULL
462  */
bfcp_attr_subattr(const struct bfcp_attr * attr,enum bfcp_attrib type)463 struct bfcp_attr *bfcp_attr_subattr(const struct bfcp_attr *attr,
464 				    enum bfcp_attrib type)
465 {
466 	if (!attr)
467 		return NULL;
468 
469 	return bfcp_attrs_find(&attr->attrl, type);
470 }
471 
472 
473 /**
474  * Apply a function handler to all sub-attributes in a BFCP attribute
475  *
476  * @param attr BFCP attribute
477  * @param h    Handler
478  * @param arg  Handler argument
479  *
480  * @return BFCP attribute returned by handler, or NULL
481  */
bfcp_attr_subattr_apply(const struct bfcp_attr * attr,bfcp_attr_h * h,void * arg)482 struct bfcp_attr *bfcp_attr_subattr_apply(const struct bfcp_attr *attr,
483 					  bfcp_attr_h *h, void *arg)
484 {
485 	if (!attr)
486 		return NULL;
487 
488 	return bfcp_attrs_apply(&attr->attrl, h, arg);
489 }
490 
491 
492 /**
493  * Print a BFCP attribute
494  *
495  * @param pf   Print function
496  * @param attr BFCP attribute
497  *
498  * @return 0 if success, otherwise errorcode
499  */
bfcp_attr_print(struct re_printf * pf,const struct bfcp_attr * attr)500 int bfcp_attr_print(struct re_printf *pf, const struct bfcp_attr *attr)
501 {
502 	const union bfcp_union *v;
503 	size_t i;
504 	int err;
505 
506 	if (!attr)
507 		return 0;
508 
509 	err = re_hprintf(pf, "%c%-28s", attr->mand ? '*' : ' ',
510 			 bfcp_attr_name(attr->type));
511 
512 	v = &attr->v;
513 
514 	switch (attr->type) {
515 
516 	case BFCP_BENEFICIARY_ID:
517 	case BFCP_FLOOR_ID:
518 	case BFCP_FLOOR_REQUEST_ID:
519 		err |= re_hprintf(pf, "%u", v->u16);
520 		break;
521 
522 	case BFCP_PRIORITY:
523 		err |= re_hprintf(pf, "%d", v->priority);
524 		break;
525 
526 	case BFCP_REQUEST_STATUS:
527 		err |= re_hprintf(pf, "%s (%d), qpos=%u",
528 				  bfcp_reqstatus_name(v->reqstatus.status),
529 				  v->reqstatus.status,
530 				  v->reqstatus.qpos);
531 		break;
532 
533 	case BFCP_ERROR_CODE:
534 		err |= re_hprintf(pf, "%d (%s)", v->errcode.code,
535 				  bfcp_errcode_name(v->errcode.code));
536 
537 		if (v->errcode.code == BFCP_UNKNOWN_MAND_ATTR) {
538 
539 			for (i=0; i<v->errcode.len; i++) {
540 
541 				uint8_t type = v->errcode.details[i] >> 1;
542 
543 				err |= re_hprintf(pf, " %s",
544 						  bfcp_attr_name(type));
545 			}
546 		}
547 		break;
548 
549 	case BFCP_ERROR_INFO:
550 	case BFCP_PART_PROV_INFO:
551 	case BFCP_STATUS_INFO:
552 	case BFCP_USER_DISP_NAME:
553 	case BFCP_USER_URI:
554 		err |= re_hprintf(pf, "\"%s\"", v->str);
555 		break;
556 
557 	case BFCP_SUPPORTED_ATTRS:
558 		err |= re_hprintf(pf, "%zu:", v->supattr.attrc);
559 
560 		for (i=0; i<v->supattr.attrc; i++) {
561 
562 			const enum bfcp_attrib type = v->supattr.attrv[i];
563 
564 			err |= re_hprintf(pf, " %s", bfcp_attr_name(type));
565 		}
566 		break;
567 
568 	case BFCP_SUPPORTED_PRIMS:
569 		err |= re_hprintf(pf, "%zu:", v->supprim.primc);
570 
571 		for (i=0; i<v->supprim.primc; i++) {
572 
573 			const enum bfcp_prim prim = v->supprim.primv[i];
574 
575 			err |= re_hprintf(pf, " %s", bfcp_prim_name(prim));
576 		}
577 		break;
578 
579 		/* Grouped Attributes */
580 
581 	case BFCP_BENEFICIARY_INFO:
582 		err |= re_hprintf(pf, "beneficiary-id=%u", v->beneficiaryid);
583 		break;
584 
585 	case BFCP_FLOOR_REQ_INFO:
586 		err |= re_hprintf(pf, "floor-request-id=%u", v->floorreqid);
587 		break;
588 
589 	case BFCP_REQUESTED_BY_INFO:
590 		err |= re_hprintf(pf, "requested-by-id=%u", v->reqbyid);
591 		break;
592 
593 	case BFCP_FLOOR_REQ_STATUS:
594 		err |= re_hprintf(pf, "floor-id=%u", v->floorid);
595 		break;
596 
597 	case BFCP_OVERALL_REQ_STATUS:
598 		err |= re_hprintf(pf, "floor-request-id=%u", v->floorreqid);
599 		break;
600 
601 	default:
602 		err |= re_hprintf(pf, "???");
603 		break;
604 	}
605 
606 	return err;
607 }
608 
609 
bfcp_attrs_print(struct re_printf * pf,const struct list * attrl,unsigned level)610 int bfcp_attrs_print(struct re_printf *pf, const struct list *attrl,
611 		     unsigned level)
612 {
613 	struct le *le;
614 	int err = 0;
615 
616 	for (le=list_head(attrl); le; le=le->next) {
617 
618 		const struct bfcp_attr *attr = le->data;
619 		unsigned i;
620 
621 		for (i=0; i<level; i++)
622 			err |= re_hprintf(pf, "    ");
623 
624 		err |= re_hprintf(pf, "%H\n", bfcp_attr_print, attr);
625 		err |= bfcp_attrs_print(pf, &attr->attrl, level + 1);
626 	}
627 
628 	return err;
629 }
630 
631 
632 /**
633  * Get the BFCP attribute name
634  *
635  * @param type BFCP attribute type
636  *
637  * @return String with BFCP attribute name
638  */
bfcp_attr_name(enum bfcp_attrib type)639 const char *bfcp_attr_name(enum bfcp_attrib type)
640 {
641 	switch (type) {
642 
643 	case BFCP_BENEFICIARY_ID:     return "BENEFICIARY-ID";
644 	case BFCP_FLOOR_ID:           return "FLOOR-ID";
645 	case BFCP_FLOOR_REQUEST_ID:   return "FLOOR-REQUEST-ID";
646 	case BFCP_PRIORITY:           return "PRIORITY";
647 	case BFCP_REQUEST_STATUS:     return "REQUEST-STATUS";
648 	case BFCP_ERROR_CODE:         return "ERROR-CODE";
649 	case BFCP_ERROR_INFO:         return "ERROR-INFO";
650 	case BFCP_PART_PROV_INFO:     return "PARTICIPANT-PROVIDED-INFO";
651 	case BFCP_STATUS_INFO:        return "STATUS-INFO";
652 	case BFCP_SUPPORTED_ATTRS:    return "SUPPORTED-ATTRIBUTES";
653 	case BFCP_SUPPORTED_PRIMS:    return "SUPPORTED-PRIMITIVES";
654 	case BFCP_USER_DISP_NAME:     return "USER-DISPLAY-NAME";
655 	case BFCP_USER_URI:           return "USER-URI";
656 	case BFCP_BENEFICIARY_INFO:   return "BENEFICIARY-INFORMATION";
657 	case BFCP_FLOOR_REQ_INFO:     return "FLOOR-REQUEST-INFORMATION";
658 	case BFCP_REQUESTED_BY_INFO:  return "REQUESTED-BY-INFORMATION";
659 	case BFCP_FLOOR_REQ_STATUS:   return "FLOOR-REQUEST-STATUS";
660 	case BFCP_OVERALL_REQ_STATUS: return "OVERALL-REQUEST-STATUS";
661 	default:                      return "???";
662 	}
663 }
664 
665 
666 /**
667  * Get the BFCP Request status name
668  *
669  * @param status Request status
670  *
671  * @return String with BFCP Request status name
672  */
bfcp_reqstatus_name(enum bfcp_reqstat status)673 const char *bfcp_reqstatus_name(enum bfcp_reqstat status)
674 {
675 	switch (status) {
676 
677 	case BFCP_PENDING:   return "Pending";
678 	case BFCP_ACCEPTED:  return "Accepted";
679 	case BFCP_GRANTED:   return "Granted";
680 	case BFCP_DENIED:    return "Denied";
681 	case BFCP_CANCELLED: return "Cancelled";
682 	case BFCP_RELEASED:  return "Released";
683 	case BFCP_REVOKED:   return "Revoked";
684 	default:             return "???";
685 	}
686 }
687 
688 
689 /**
690  * Get the BFCP Error code name
691  *
692  * @param code BFCP Error code
693  *
694  * @return String with error code
695  */
bfcp_errcode_name(enum bfcp_err code)696 const char *bfcp_errcode_name(enum bfcp_err code)
697 {
698 	switch (code) {
699 
700 	case BFCP_CONF_NOT_EXIST:
701 		return "Conference does not Exist";
702 
703 	case BFCP_USER_NOT_EXIST:
704 		return "User does not Exist";
705 
706 	case BFCP_UNKNOWN_PRIM:
707 		return "Unknown Primitive";
708 
709 	case BFCP_UNKNOWN_MAND_ATTR:
710 		return "Unknown Mandatory Attribute";
711 
712 	case BFCP_UNAUTH_OPERATION:
713 		return "Unauthorized Operation";
714 
715 	case BFCP_INVALID_FLOOR_ID:
716 		return "Invalid Floor ID";
717 
718 	case BFCP_FLOOR_REQ_ID_NOT_EXIST:
719 		return "Floor Request ID Does Not Exist";
720 
721 	case BFCP_MAX_FLOOR_REQ_REACHED:
722 		return "You have Already Reached the Maximum Number "
723 		       "of Ongoing Floor Requests for this Floor";
724 
725 	case BFCP_USE_TLS:
726 		return "Use TLS";
727 
728 	case BFCP_PARSE_ERROR:
729 		return "Unable to Parse Message";
730 
731 	case BFCP_USE_DTLS:
732 		return "Use DTLS";
733 
734 	case BFCP_UNSUPPORTED_VERSION:
735 		return "Unsupported Version";
736 
737 	case BFCP_BAD_LENGTH:
738 		return "Incorrect Message Length";
739 
740 	case BFCP_GENERIC_ERROR:
741 		return "Generic Error";
742 
743 	default:
744 		return "???";
745 	}
746 }
747