1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * Parses the SDP description as per the SDP grammar defined in Section 9 of 31 * RFC 4566 32 */ 33 34 #include <errno.h> 35 #include <stdlib.h> 36 #include <string.h> 37 #include <ctype.h> 38 #include <sdp.h> 39 40 #include "sdp_parse.h" 41 #include "commp_util.h" 42 43 /* 44 * proto-version-field (v=) 45 * %x76 "=" 1*DIGIT CRLF 46 */ 47 static void 48 sdp_parse_version(int *version, const char *begin, const char *end, 49 uint_t *p_error) 50 { 51 if (*begin++ != COMMP_EQUALS || commp_atoi(begin, end, version) != 0) 52 *p_error |= SDP_VERSION_ERROR; 53 } 54 55 /* 56 * session-name-field (s=) 57 * %x73 "=" text CRLF 58 * text = byte-string 59 * byte-string = 1*(%x01-09/%x0B-0C/%x0E-FF) 60 * ;any byte except NUL, CR, or LF 61 */ 62 static void 63 sdp_parse_name(char **name, const char *begin, const char *end, 64 uint_t *p_error) 65 { 66 int len; 67 68 if (*begin++ != COMMP_EQUALS) { 69 *p_error |= SDP_NAME_ERROR; 70 return; 71 } 72 /* there can be only one name field */ 73 if (*name != NULL) 74 return; 75 len = end - begin; 76 if (len < 1) { 77 *p_error |= SDP_NAME_ERROR; 78 } else { 79 COMMP_COPY_STR(*name, begin, len); 80 if (*name == NULL) { 81 *p_error |= SDP_MEMORY_ERROR; 82 return; 83 } 84 } 85 } 86 87 /* 88 * information-field (i=) 89 * [%x69 "=" text CRLF] 90 * text = byte-string 91 * byte-string = 1*(%x01-09/%x0B-0C/%x0E-FF) 92 * any byte except NUL, CR, or LF 93 */ 94 static void 95 sdp_parse_info(char **info, const char *begin, const char *end, 96 uint_t *p_error) 97 { 98 int len; 99 100 if (*begin++ != COMMP_EQUALS) { 101 *p_error |= SDP_INFO_ERROR; 102 return; 103 } 104 /* There can be only one info field */ 105 if (*info != NULL) 106 return; 107 len = end - begin; 108 if (len < 1) { 109 *p_error |= SDP_INFO_ERROR; 110 } else { 111 COMMP_COPY_STR(*info, begin, len); 112 if (*info == NULL) { 113 *p_error |= SDP_MEMORY_ERROR; 114 return; 115 } 116 } 117 } 118 119 /* 120 * uri-field (u=) 121 * [%x75 "=" uri CRLF] 122 * anything between "=" and "CRLF" is considered to be URI. 123 */ 124 static void 125 sdp_parse_uri(char **uri, const char *begin, const char *end, uint_t *p_error) 126 { 127 int len; 128 129 if (*begin++ != COMMP_EQUALS) { 130 *p_error |= SDP_URI_ERROR; 131 return; 132 } 133 /* There can be only one uri field */ 134 if (*uri != NULL) 135 return; 136 len = end - begin; 137 if (len < 1 || isspace(*begin) || isspace (*(end - 1))) { 138 *p_error |= SDP_URI_ERROR; 139 } else { 140 COMMP_COPY_STR(*uri, begin, len); 141 if (*uri == NULL) { 142 *p_error |= SDP_MEMORY_ERROR; 143 return; 144 } 145 } 146 } 147 148 /* 149 * phone-fields (p=) 150 * *(%x70 "=" phone-number CRLF) 151 * anything between "=" and "CRLF" is considered to be phone-number 152 */ 153 static void 154 sdp_parse_phone(sdp_list_t **phone, const char *begin, const char *end, 155 uint_t *p_error) 156 { 157 int len; 158 sdp_list_t *new_phone = NULL; 159 sdp_list_t *tmp = NULL; 160 161 if (*begin++ != COMMP_EQUALS) { 162 *p_error |= SDP_PHONE_ERROR; 163 return; 164 } 165 len = end - begin; 166 if (len < 1 || isspace(*begin) || isspace(*(end - 1))) { 167 *p_error |= SDP_PHONE_ERROR; 168 } else { 169 new_phone = calloc(1, sizeof (sdp_list_t)); 170 if (new_phone == NULL) { 171 *p_error |= SDP_MEMORY_ERROR; 172 return; 173 } 174 COMMP_COPY_STR(new_phone->value, begin, len); 175 if (new_phone->value == NULL) { 176 free(new_phone); 177 *p_error |= SDP_MEMORY_ERROR; 178 return; 179 } 180 if (*phone == NULL) { 181 *phone = new_phone; 182 } else { 183 tmp = *phone; 184 while (tmp->next != NULL) 185 tmp = tmp->next; 186 tmp->next = new_phone; 187 } 188 } 189 } 190 191 /* 192 * email-fields (e=) 193 * *(%x65 "=" email-address CRLF) 194 * anything between "=" and "CRLF" is considered to be email-address 195 */ 196 static void 197 sdp_parse_email(sdp_list_t **email, const char *begin, const char *end, 198 uint_t *p_error) 199 { 200 int len; 201 sdp_list_t *new_email = NULL; 202 sdp_list_t *tmp = NULL; 203 204 if (*begin++ != COMMP_EQUALS) { 205 *p_error |= SDP_EMAIL_ERROR; 206 return; 207 } 208 len = end - begin; 209 if (len < 1 || isspace(*begin) || isspace(*(end - 1))) { 210 *p_error |= SDP_EMAIL_ERROR; 211 } else { 212 new_email = calloc(1, sizeof (sdp_list_t)); 213 if (new_email == NULL) { 214 *p_error |= SDP_MEMORY_ERROR; 215 return; 216 } 217 COMMP_COPY_STR(new_email->value, begin, len); 218 if (new_email->value == NULL) { 219 free(new_email); 220 *p_error |= SDP_MEMORY_ERROR; 221 return; 222 } 223 if (*email == NULL) { 224 *email = new_email; 225 } else { 226 tmp = *email; 227 while (tmp->next != NULL) 228 tmp = tmp->next; 229 tmp->next = new_email; 230 } 231 } 232 } 233 234 /* 235 * origin-field (o=) 236 * %x6f "=" username SP sess-id SP sess-version SP nettype SP addrtype SP 237 * unicast-address CRLF 238 * 239 * username = non-ws-string 240 * sess-id = 1*DIGIT 241 * sess-version = 1*DIGIT 242 * nettype = token 243 * addrtype = token 244 * token = 1*(token-char) 245 * token-char = %x21 / %x23-27 / %x2A-2B / %x2D-2E / %x30-39 / %x41-5A / %x5E-7E 246 * i.e. no space in token-char 247 */ 248 static void 249 sdp_parse_origin(sdp_origin_t **origin, const char *begin, const char *end, 250 uint_t *p_error) 251 { 252 const char *current = NULL; 253 sdp_origin_t *new_origin = NULL; 254 255 if (*begin++ != COMMP_EQUALS) { 256 *p_error |= SDP_ORIGIN_ERROR; 257 return; 258 } 259 /* There can be only one origin field */ 260 if (*origin != NULL) 261 return; 262 new_origin = calloc(1, sizeof (sdp_origin_t)); 263 if (new_origin == NULL) { 264 *p_error |= SDP_MEMORY_ERROR; 265 return; 266 } 267 /* Get username */ 268 current = begin; 269 if (commp_find_token(&begin, ¤t, end, COMMP_SP, B_FALSE) != 0) { 270 goto err_ret; 271 } else { 272 COMMP_COPY_STR(new_origin->o_username, begin, current - begin); 273 if (new_origin->o_username == NULL) { 274 sdp_free_origin(new_origin); 275 *p_error |= SDP_MEMORY_ERROR; 276 return; 277 } 278 } 279 /* Get Session-ID */ 280 begin = ++current; 281 if (commp_find_token(&begin, ¤t, end, COMMP_SP, B_FALSE) != 0) 282 goto err_ret; 283 if (commp_strtoull(begin, current, &new_origin->o_id) != 0) 284 goto err_ret; 285 /* Get Version */ 286 begin = ++current; 287 if (commp_find_token(&begin, ¤t, end, COMMP_SP, B_FALSE) != 0) 288 goto err_ret; 289 if (commp_strtoull(begin, current, &new_origin->o_version) != 0) 290 goto err_ret; 291 /* Get nettype */ 292 begin = ++current; 293 if (commp_find_token(&begin, ¤t, end, COMMP_SP, B_FALSE) != 0) { 294 goto err_ret; 295 } else { 296 COMMP_COPY_STR(new_origin->o_nettype, begin, current - begin); 297 if (new_origin->o_nettype == NULL) { 298 sdp_free_origin(new_origin); 299 *p_error |= SDP_MEMORY_ERROR; 300 return; 301 } 302 } 303 /* Get addrtype */ 304 begin = ++current; 305 if (commp_find_token(&begin, ¤t, end, COMMP_SP, B_FALSE) != 0) { 306 goto err_ret; 307 } else { 308 COMMP_COPY_STR(new_origin->o_addrtype, begin, current - begin); 309 if (new_origin->o_addrtype == NULL) { 310 sdp_free_origin(new_origin); 311 *p_error |= SDP_MEMORY_ERROR; 312 return; 313 } 314 } 315 /* Get address. Its the last sub-field */ 316 begin = ++current; 317 if (commp_find_token(&begin, ¤t, end, COMMP_SP, B_TRUE) != 0) 318 goto err_ret; 319 COMMP_COPY_STR(new_origin->o_address, begin, current - begin); 320 if (new_origin->o_address == NULL) { 321 sdp_free_origin(new_origin); 322 *p_error |= SDP_MEMORY_ERROR; 323 return; 324 } 325 *origin = new_origin; 326 return; 327 err_ret: 328 *p_error |= SDP_ORIGIN_ERROR; 329 sdp_free_origin(new_origin); 330 } 331 332 /* 333 * time-fields (t=) 334 * 1*( %x74 "=" start-time SP stop-time CRLF) 335 * start-time = time / "0" 336 * stop-time = time / "0" 337 * time = POS-DIGIT 9*DIGIT 338 * POS-DIGIT = %x31-39 ; 1 - 9 339 */ 340 static sdp_time_t * 341 sdp_parse_time(sdp_time_t **time, const char *begin, const char *end, 342 uint_t *p_error) 343 { 344 const char *current; 345 sdp_time_t *new_time; 346 sdp_time_t *tmp; 347 348 if (*begin++ != COMMP_EQUALS) { 349 *p_error |= SDP_TIME_ERROR; 350 return (NULL); 351 } 352 new_time = calloc(1, sizeof (sdp_time_t)); 353 if (new_time == NULL) { 354 *p_error |= SDP_MEMORY_ERROR; 355 return (NULL); 356 } 357 /* Get start-time */ 358 current = begin; 359 if (commp_find_token(&begin, ¤t, end, COMMP_SP, B_FALSE) != 0) 360 goto err_ret; 361 if (commp_strtoull(begin, current, &new_time->t_start) != 0) 362 goto err_ret; 363 /* Get stop-time */ 364 begin = ++current; 365 if (commp_find_token(&begin, ¤t, end, COMMP_SP, B_TRUE) != 0) 366 goto err_ret; 367 if (commp_strtoull(begin, current, &new_time->t_stop) != 0) 368 goto err_ret; 369 /* Now assign time to session structure */ 370 if (*time == NULL) { 371 *time = new_time; 372 } else { 373 tmp = *time; 374 while (tmp->t_next != NULL) 375 tmp = tmp->t_next; 376 tmp->t_next = new_time; 377 } 378 return (new_time); 379 err_ret: 380 *p_error |= SDP_TIME_ERROR; 381 sdp_free_time(new_time); 382 return (NULL); 383 } 384 385 /* 386 * connection-field (c=) 387 * [%x63 "=" nettype SP addrtype SP connection-address CRLF] 388 * nettype = token 389 * addrtype = token 390 * connection-address = multicast-address / unicast-address 391 * here, connection-address is parsed as a string. 392 */ 393 static void 394 sdp_parse_connection(sdp_conn_t **conn, const char *begin, const char *end, 395 uint_t *p_error) 396 { 397 const char *current; 398 const char *t_begin; 399 const char *t_current; 400 sdp_conn_t *new_conn; 401 sdp_conn_t *tmp; 402 boolean_t is_IP4 = B_FALSE; 403 boolean_t is_IP6 = B_FALSE; 404 405 if (*begin++ != COMMP_EQUALS) { 406 *p_error |= SDP_CONNECTION_ERROR; 407 return; 408 } 409 new_conn = calloc(1, sizeof (sdp_conn_t)); 410 if (new_conn == NULL) { 411 *p_error |= SDP_MEMORY_ERROR; 412 return; 413 } 414 /* Get NetworkType */ 415 current = begin; 416 if (commp_find_token(&begin, ¤t, end, COMMP_SP, B_FALSE) != 0) { 417 goto err_ret; 418 } else { 419 COMMP_COPY_STR(new_conn->c_nettype, begin, current - begin); 420 if (new_conn->c_nettype == NULL) { 421 sdp_free_connection(new_conn); 422 *p_error |= SDP_MEMORY_ERROR; 423 return; 424 } 425 } 426 /* Get AddressType */ 427 begin = ++current; 428 if (commp_find_token(&begin, ¤t, end, COMMP_SP, B_FALSE) != 0) { 429 goto err_ret; 430 } else { 431 COMMP_COPY_STR(new_conn->c_addrtype, begin, current - begin); 432 if (new_conn->c_addrtype == NULL) { 433 sdp_free_connection(new_conn); 434 *p_error |= SDP_MEMORY_ERROR; 435 return; 436 } 437 } 438 if ((strlen(COMMP_ADDRTYPE_IP4) == strlen(new_conn->c_addrtype)) && 439 (strncasecmp(new_conn->c_addrtype, COMMP_ADDRTYPE_IP4, 440 strlen(COMMP_ADDRTYPE_IP4)) == 0)) { 441 is_IP4 = B_TRUE; 442 } else if ((strlen(COMMP_ADDRTYPE_IP6) == strlen(new_conn-> 443 c_addrtype)) && (strncasecmp(new_conn->c_addrtype, 444 COMMP_ADDRTYPE_IP6, strlen(COMMP_ADDRTYPE_IP6)) == 0)) { 445 is_IP6 = B_TRUE; 446 } 447 /* Get Address. Parsing depends if its IP4,IP6 or something else */ 448 begin = ++current; 449 if (!is_IP4 && !is_IP6) { 450 if (commp_find_token(&begin, ¤t, end, COMMP_SP, 451 B_TRUE) != 0) { 452 goto err_ret; 453 } 454 } else { 455 if (commp_find_token(&begin, ¤t, end, COMMP_SLASH, 456 B_FALSE) != 0) { 457 goto err_ret; 458 } 459 if (current != end) { 460 /* SLASH is present. Needs further parsing */ 461 t_current = current; 462 t_begin = ++t_current; 463 if (commp_find_token(&t_begin, &t_current, end, 464 COMMP_SLASH, B_FALSE) != 0) { 465 goto err_ret; 466 } 467 if (t_current != end) { 468 /* 469 * Another SLASH present. If is_IP4 true then 470 * this is Address count. If is_IP6 true then 471 * incorrect field as per RFC. 472 */ 473 if (is_IP6) { 474 goto err_ret; 475 } else { 476 if (commp_atoi((t_current + 1), end, 477 &new_conn->c_addrcount) != 0) { 478 goto err_ret; 479 } 480 } 481 } 482 if (is_IP6) { 483 if (commp_atoi((current + 1), t_current, 484 &new_conn->c_addrcount) != 0) { 485 goto err_ret; 486 } 487 } else { 488 if (commp_strtoub((current + 1), t_current, 489 &new_conn->c_ttl) != 0) { 490 goto err_ret; 491 } 492 if (new_conn->c_addrcount == 0) 493 new_conn->c_addrcount = 1; 494 } 495 } 496 } 497 COMMP_COPY_STR(new_conn->c_address, begin, current - begin); 498 if (new_conn->c_address == NULL) { 499 sdp_free_connection(new_conn); 500 *p_error |= SDP_MEMORY_ERROR; 501 return; 502 } 503 if (*conn == NULL) { 504 *conn = new_conn; 505 } else { 506 tmp = *conn; 507 while (tmp->c_next != NULL) 508 tmp = tmp->c_next; 509 tmp->c_next = new_conn; 510 } 511 return; 512 err_ret: 513 *p_error |= SDP_CONNECTION_ERROR; 514 sdp_free_connection(new_conn); 515 } 516 517 /* 518 * bandwidth-fields (b=) 519 * *(%x62 "=" bwtype ":" bandwidth CRLF) 520 * bwtype = token 521 * bandwidth = 1*DIGIT 522 */ 523 static void 524 sdp_parse_bandwidth(sdp_bandwidth_t **bw, const char *begin, const char *end, 525 uint_t *p_error) 526 { 527 const char *current; 528 sdp_bandwidth_t *new_bw = NULL; 529 sdp_bandwidth_t *tmp = NULL; 530 531 if (*begin++ != COMMP_EQUALS) { 532 *p_error |= SDP_BANDWIDTH_ERROR; 533 return; 534 } 535 new_bw = calloc(1, sizeof (sdp_bandwidth_t)); 536 if (new_bw == NULL) { 537 *p_error |= SDP_MEMORY_ERROR; 538 return; 539 } 540 current = begin; 541 if (commp_find_token(&begin, ¤t, end, COMMP_COLON, 542 B_FALSE) != 0) { 543 goto err_ret; 544 } else { 545 COMMP_COPY_STR(new_bw->b_type, begin, current - begin); 546 if (new_bw->b_type == NULL) { 547 sdp_free_bandwidth(new_bw); 548 *p_error |= SDP_MEMORY_ERROR; 549 return; 550 } 551 } 552 if (current == end) 553 goto err_ret; 554 begin = ++current; 555 if (commp_find_token(&begin, ¤t, end, COMMP_SP, B_TRUE) != 0) 556 goto err_ret; 557 if (commp_strtoull(begin, current, &new_bw->b_value) != 0) 558 goto err_ret; 559 if (*bw == NULL) { 560 *bw = new_bw; 561 } else { 562 tmp = *bw; 563 while (tmp->b_next != NULL) 564 tmp = tmp->b_next; 565 tmp->b_next = new_bw; 566 } 567 return; 568 err_ret: 569 *p_error |= SDP_BANDWIDTH_ERROR; 570 sdp_free_bandwidth(new_bw); 571 } 572 573 /* 574 * repeat-fields (r=) 575 * Not stand-alone. One or more repeat field appear after time field. 576 * %x72 "=" repeat-interval SP typed-time 1*(SP typed-time) 577 * repeat-interval = POS-DIGIT *DIGIT [fixed-len-time-unit] 578 * typed-time = 1*DIGIT [fixed-len-time-unit] 579 * fixed-len-time-unit = %x64 / %x68 / %x6d / %x73 580 */ 581 static void 582 sdp_parse_repeat(sdp_time_t *time, const char *begin, const char *end, 583 uint_t *p_error) 584 { 585 const char *current; 586 sdp_repeat_t *repeat; 587 sdp_repeat_t *new_repeat; 588 int ret; 589 590 if (*begin++ != COMMP_EQUALS) { 591 *p_error |= SDP_REPEAT_TIME_ERROR; 592 return; 593 } 594 /* 595 * A time field should be present before this field can occur, if 596 * time is NULL then repeat field has occured before time field and 597 * hence fields are out of order. 598 */ 599 if (time == NULL) 600 return; 601 /* 602 * Get the latest time field and associate this repeat field 603 * with it. 604 */ 605 while (time->t_next != NULL) 606 time = time->t_next; 607 new_repeat = calloc(1, sizeof (sdp_repeat_t)); 608 if (new_repeat == NULL) { 609 *p_error |= SDP_MEMORY_ERROR; 610 return; 611 } 612 /* 613 * for a given time field, there could be several repeat fields 614 * add the new repeat field at the end of it. 615 */ 616 repeat = time->t_repeat; 617 if (repeat == NULL) { 618 time->t_repeat = new_repeat; 619 } else { 620 while (repeat->r_next != NULL) 621 repeat = repeat->r_next; 622 repeat->r_next = new_repeat; 623 } 624 /* 625 * Populate the elements of sdp_repeat. 626 * Get time-interval 627 */ 628 current = begin; 629 if (commp_find_token(&begin, ¤t, end, COMMP_SP, B_FALSE) != 0) 630 goto err_ret; 631 if (commp_time_to_secs(begin, current, &new_repeat->r_interval) != 0) 632 goto err_ret; 633 /* Get duration. It could be the last sub-field */ 634 begin = ++current; 635 if (commp_find_token(&begin, ¤t, end, COMMP_SP, B_FALSE) != 0) 636 goto err_ret; 637 if (commp_time_to_secs(begin, current, &new_repeat->r_duration) != 0) 638 goto err_ret; 639 ++current; 640 /* Get offsets into sdp_list */ 641 if (current >= end) 642 goto err_ret; 643 while (current < end) { 644 begin = current; 645 if (commp_find_token(&begin, ¤t, end, COMMP_SP, 646 B_FALSE) != 0) { 647 goto err_ret; 648 } 649 if ((ret = add_value_to_list(&new_repeat->r_offset, begin, 650 current - begin, B_FALSE)) != 0) { 651 if (ret == ENOMEM) { 652 *p_error |= SDP_MEMORY_ERROR; 653 return; 654 } else { 655 goto err_ret; 656 } 657 } 658 ++current; 659 } 660 /* check for trailing white space character. */ 661 if (isspace(*(end - 1))) 662 goto err_ret; 663 return; 664 err_ret: 665 *p_error |= SDP_REPEAT_TIME_ERROR; 666 if (repeat != NULL) 667 repeat->r_next = NULL; 668 else 669 time->t_repeat = NULL; 670 sdp_free_repeat(new_repeat); 671 } 672 673 /* 674 * zone-adjustments (z=) 675 * %x7a "=" time SP ["-"] typed-time *(SP time SP ["-"] typed-time) 676 */ 677 static void 678 sdp_parse_zone(sdp_zone_t **zone, const char *begin, const char *end, 679 uint_t *p_error) 680 { 681 const char *current; 682 sdp_zone_t *new_zone = NULL; 683 sdp_zone_t *tmp = NULL; 684 685 if (*begin++ != COMMP_EQUALS) { 686 *p_error |= SDP_ZONE_ERROR; 687 return; 688 } 689 /* There can be atmost one zone field. */ 690 if (*zone != NULL) 691 return; 692 /* Get time and offset */ 693 current = begin; 694 while (current < end) { 695 new_zone = calloc(1, sizeof (sdp_zone_t)); 696 if (new_zone == NULL) { 697 *p_error |= SDP_MEMORY_ERROR; 698 return; 699 } 700 if (*zone == NULL) { 701 *zone = new_zone; 702 tmp = *zone; 703 } else { 704 tmp->z_next = new_zone; 705 tmp = new_zone; 706 } 707 begin = current; 708 if (commp_find_token(&begin, ¤t, end, COMMP_SP, 709 B_FALSE) != 0) { 710 goto err_ret; 711 } 712 if (commp_strtoull(begin, current, &new_zone->z_time) != 0) 713 goto err_ret; 714 begin = ++current; 715 if (commp_find_token(&begin, ¤t, end, COMMP_SP, 716 B_FALSE) != 0) { 717 goto err_ret; 718 } else { 719 COMMP_COPY_STR(new_zone->z_offset, begin, current - 720 begin); 721 if (new_zone->z_offset == NULL) { 722 *p_error |= SDP_MEMORY_ERROR; 723 return; 724 } 725 726 } 727 ++current; 728 } 729 if (isspace(*(end - 1))) 730 goto err_ret; 731 return; 732 err_ret: 733 *p_error |= SDP_ZONE_ERROR; 734 sdp_free_zone(*zone); 735 *zone = NULL; 736 } 737 738 /* 739 * key-field (k=) 740 * [%x6b "=" key-type CRLF] 741 * key-type = %x70 %x72 %x6f %x6d %x70 %x74 / ; "prompt" 742 * %x63 %x6c %x65 %x61 %x72 ":" text / ; "clear:" 743 * %x62 %x61 %x73 %x65 "64:" base64 / ; "base64:" 744 * %x75 %x72 %x69 ":" uri ; "uri:" 745 */ 746 static void 747 sdp_parse_key(sdp_key_t **key, const char *begin, const char *end, 748 uint_t *p_error) 749 { 750 const char *current; 751 sdp_key_t *new_key; 752 753 if (*begin++ != COMMP_EQUALS) { 754 *p_error |= SDP_KEY_ERROR; 755 return; 756 } 757 /* There can be only one key field */ 758 if (*key != NULL) 759 return; 760 new_key = calloc(1, sizeof (sdp_key_t)); 761 if (new_key == NULL) { 762 *p_error |= SDP_MEMORY_ERROR; 763 return; 764 } 765 /* Get Method name */ 766 current = begin; 767 if (commp_find_token(&begin, ¤t, end, COMMP_COLON, 768 B_FALSE) != 0) { 769 goto err_ret; 770 } else { 771 COMMP_COPY_STR(new_key->k_method, begin, current - begin); 772 if (new_key->k_method == NULL) { 773 sdp_free_key(new_key); 774 *p_error |= SDP_MEMORY_ERROR; 775 return; 776 } 777 } 778 /* Get key, if exists. */ 779 if (*current == COMMP_COLON) { 780 ++current; 781 if (current == end) 782 goto err_ret; 783 COMMP_COPY_STR(new_key->k_enckey, current, end - current); 784 if (new_key->k_enckey == NULL) { 785 sdp_free_key(new_key); 786 *p_error |= SDP_MEMORY_ERROR; 787 return; 788 } 789 } 790 *key = new_key; 791 return; 792 err_ret: 793 *p_error |= SDP_KEY_ERROR; 794 sdp_free_key(new_key); 795 } 796 797 /* 798 * attribute-fields (a=) 799 * *(%x61 "=" attribute CRLF) 800 * attribute = (att-field ":" att-value) / att-field 801 * att-field = token 802 * att-value = byte-string 803 */ 804 static void 805 sdp_parse_attribute(sdp_attr_t **attr, const char *begin, const char *end, 806 uint_t *p_error) 807 { 808 const char *current; 809 sdp_attr_t *new_attr; 810 sdp_attr_t *tmp; 811 812 if (*begin++ != COMMP_EQUALS) { 813 *p_error |= SDP_ATTRIBUTE_ERROR; 814 return; 815 } 816 new_attr = calloc(1, sizeof (sdp_attr_t)); 817 if (new_attr == NULL) { 818 *p_error |= SDP_MEMORY_ERROR; 819 return; 820 } 821 /* Get Attribute Name */ 822 current = begin; 823 if (commp_find_token(&begin, ¤t, end, COMMP_COLON, 824 B_FALSE) != 0) { 825 goto err_ret; 826 } else { 827 COMMP_COPY_STR(new_attr->a_name, begin, current - begin); 828 if (new_attr->a_name == NULL) { 829 sdp_free_attribute(new_attr); 830 *p_error |= SDP_MEMORY_ERROR; 831 return; 832 } 833 } 834 /* Get Attribute Value */ 835 if (*current == COMMP_COLON) { 836 ++current; 837 if (current == end) 838 goto err_ret; 839 COMMP_COPY_STR(new_attr->a_value, current, end - current); 840 if (new_attr->a_value == NULL) { 841 sdp_free_attribute(new_attr); 842 *p_error |= SDP_MEMORY_ERROR; 843 return; 844 } 845 } 846 if (*attr == NULL) { 847 *attr = new_attr; 848 } else { 849 tmp = *attr; 850 while (tmp->a_next != NULL) 851 tmp = tmp->a_next; 852 tmp->a_next = new_attr; 853 } 854 return; 855 err_ret: 856 *p_error |= SDP_ATTRIBUTE_ERROR; 857 sdp_free_attribute(new_attr); 858 } 859 860 /* 861 * media-field (m=) 862 * %x6d "=" media SP port ["/" integer] SP proto 1*(SP fmt) CRLF 863 */ 864 static sdp_media_t * 865 sdp_parse_media(sdp_session_t *session, const char *begin, const char *end, 866 uint_t *p_error) 867 { 868 const char *current; 869 const char *fake_end; 870 sdp_media_t *new_media; 871 sdp_media_t *tmp; 872 873 if (*begin++ != COMMP_EQUALS) { 874 *p_error |= SDP_MEDIA_ERROR; 875 return (NULL); 876 } 877 878 new_media = calloc(1, sizeof (sdp_media_t)); 879 if (new_media == NULL) { 880 *p_error |= SDP_MEMORY_ERROR; 881 return (NULL); 882 } 883 new_media->m_session = session; 884 /* Get media name */ 885 current = begin; 886 if (commp_find_token(&begin, ¤t, end, COMMP_SP, B_FALSE) != 0) { 887 goto err_ret; 888 } else { 889 COMMP_COPY_STR(new_media->m_name, begin, current - begin); 890 if (new_media->m_name == NULL) { 891 sdp_free_media(new_media); 892 *p_error |= SDP_MEMORY_ERROR; 893 return (NULL); 894 } 895 } 896 /* Get port */ 897 begin = ++current; 898 if (commp_find_token(&begin, ¤t, end, COMMP_SP, B_FALSE) != 0) 899 goto err_ret; 900 fake_end = current; 901 current = begin; 902 if (commp_find_token(&begin, ¤t, fake_end, COMMP_SLASH, 903 B_FALSE) != 0) { 904 goto err_ret; 905 } 906 if (commp_atoui(begin, current, &new_media->m_port) != 0) 907 goto err_ret; 908 /* Get portcount */ 909 if (*current == COMMP_SLASH) { 910 begin = ++current; 911 if (commp_find_token(&begin, ¤t, fake_end, COMMP_SP, 912 B_FALSE) != 0) { 913 goto err_ret; 914 } 915 if (commp_atoi(begin, current, &new_media->m_portcount) != 0) 916 goto err_ret; 917 } else { 918 new_media->m_portcount = 1; 919 } 920 /* Get Protocol */ 921 begin = ++current; 922 if (commp_find_token(&begin, ¤t, end, COMMP_SP, B_FALSE) != 0) { 923 goto err_ret; 924 } else { 925 COMMP_COPY_STR(new_media->m_proto, begin, current - begin); 926 if (new_media->m_proto == NULL) { 927 sdp_free_media(new_media); 928 *p_error |= SDP_MEMORY_ERROR; 929 return (NULL); 930 } 931 } 932 ++current; 933 /* Get format list */ 934 if (current >= end) 935 goto err_ret; 936 while (current < end) { 937 begin = current; 938 if (commp_find_token(&begin, ¤t, end, COMMP_SP, 939 B_FALSE) != 0) { 940 goto err_ret; 941 } 942 if (add_value_to_list(&new_media->m_format, begin, 943 current - begin, B_TRUE) != 0) { 944 sdp_free_media(new_media); 945 *p_error |= SDP_MEMORY_ERROR; 946 return (NULL); 947 } 948 ++current; 949 } 950 /* check for trailing white space character. */ 951 if (isspace(*(end - 1))) 952 goto err_ret; 953 /* Assign new media to the media list */ 954 tmp = session->s_media; 955 if (tmp == NULL) { 956 session->s_media = new_media; 957 } else { 958 while (tmp->m_next != NULL) 959 tmp = tmp->m_next; 960 tmp->m_next = new_media; 961 } 962 return (new_media); 963 err_ret: 964 *p_error |= SDP_MEDIA_ERROR; 965 sdp_free_media(new_media); 966 return (NULL); 967 } 968 969 /* 970 * This function ensures that a field is in the right order in SDP descripton. 971 * It also identifies cases where a field ('v', 'o, 'i', et al) that must occur 972 * once but occurs several times in SDP description. error cannot be NULL. 973 */ 974 static void 975 sdp_check_order(char prev, char *order, int *error) 976 { 977 *error = 0; 978 while (*order != '\0') { 979 if (*order++ == prev) 980 return; 981 } 982 *error = 1; 983 } 984 985 /* 986 * This function determines the SDP field and calls the appropriate parse 987 * function. It also ensures that the SDP fields are in strict order. 988 */ 989 static void 990 sdp_handle_fields(sdp_description_t *description, sdp_session_t *_session, 991 const char *begin, const char *end) 992 { 993 boolean_t u_field = B_FALSE; 994 int error = 0; /* fields order error */ 995 char prev = description->d_prev; 996 char m_prev = description->d_mprev; 997 998 switch (*begin) { 999 case SDP_VERSION_FIELD: 1000 sdp_check_order(prev, SDP_VERSION_ORDER, &error); 1001 description->d_version = B_TRUE; 1002 sdp_parse_version(&_session->s_version, begin + 1, end, 1003 &description->d_perror); 1004 break; 1005 case SDP_ORIGIN_FIELD: 1006 sdp_check_order(prev, SDP_ORIGIN_ORDER, &error); 1007 description->d_origin = B_TRUE; 1008 sdp_parse_origin(&_session->s_origin, begin + 1, end, 1009 &description->d_perror); 1010 break; 1011 case SDP_NAME_FIELD: 1012 sdp_check_order(prev, SDP_NAME_ORDER, &error); 1013 description->d_name = B_TRUE; 1014 sdp_parse_name(&_session->s_name, begin + 1, end, 1015 &description->d_perror); 1016 break; 1017 case SDP_INFO_FIELD: 1018 if (description->d_mparsed) { 1019 sdp_check_order(m_prev, SDP_M_INFO_ORDER, 1020 &error); 1021 if (description->d_lmedia == NULL) 1022 break; 1023 sdp_parse_info(&(description->d_lmedia-> 1024 m_info), begin + 1, end, &description-> 1025 d_perror); 1026 } else { 1027 sdp_check_order(prev, SDP_INFO_ORDER, &error); 1028 sdp_parse_info(&_session->s_info, begin + 1, 1029 end, &description->d_perror); 1030 } 1031 break; 1032 case SDP_URI_FIELD: 1033 sdp_check_order(prev, SDP_URI_ORDER, &error); 1034 sdp_parse_uri(&_session->s_uri, begin + 1, end, 1035 &description->d_perror); 1036 break; 1037 case SDP_EMAIL_FIELD: 1038 sdp_check_order(prev, SDP_EMAIL_ORDER, &error); 1039 sdp_parse_email(&_session->s_email, begin + 1, end, 1040 &description->d_perror); 1041 break; 1042 case SDP_PHONE_FIELD: 1043 sdp_check_order(prev, SDP_PHONE_ORDER, &error); 1044 sdp_parse_phone(&_session->s_phone, begin + 1, end, 1045 &description->d_perror); 1046 break; 1047 case SDP_CONNECTION_FIELD: 1048 if (description->d_mparsed) { 1049 sdp_check_order(m_prev, SDP_M_CONN_ORDER, 1050 &error); 1051 --description->d_mccount; 1052 if (description->d_lmedia == NULL) 1053 break; 1054 sdp_parse_connection(&(description->d_lmedia-> 1055 m_conn), begin + 1, end, 1056 &description->d_perror); 1057 } else { 1058 /* 1059 * RFC - 4566 says that session section should 1060 * have only one connection field, while media 1061 * section can have many 1062 */ 1063 sdp_check_order(prev, SDP_CONN_ORDER, &error); 1064 description->d_conn = B_TRUE; 1065 if (_session->s_conn != NULL) 1066 break; 1067 sdp_parse_connection(&_session->s_conn, 1068 begin + 1, end, &description->d_perror); 1069 } 1070 break; 1071 case SDP_BANDWIDTH_FIELD: 1072 if (description->d_mparsed) { 1073 sdp_check_order(m_prev, SDP_M_BW_ORDER, &error); 1074 if (description->d_lmedia == NULL) 1075 break; 1076 sdp_parse_bandwidth(&(description->d_lmedia-> 1077 m_bw), begin + 1, end, 1078 &description->d_perror); 1079 } else { 1080 sdp_check_order(prev, SDP_BW_ORDER, &error); 1081 sdp_parse_bandwidth(&_session->s_bw, 1082 begin + 1, end, &description->d_perror); 1083 } 1084 break; 1085 case SDP_TIME_FIELD: 1086 if (!description->d_tparsed || description->d_prev != 1087 SDP_REPEAT_FIELD) { 1088 sdp_check_order(prev, SDP_TIME_ORDER, &error); 1089 } 1090 description->d_tparsed = B_TRUE; 1091 description->d_ltime = sdp_parse_time(&_session-> 1092 s_time, begin + 1, end, &description->d_perror); 1093 break; 1094 case SDP_REPEAT_FIELD: 1095 sdp_check_order(prev, SDP_REPEAT_ORDER, &error); 1096 if (description->d_ltime == NULL) 1097 break; 1098 /* we pass time, as repeat is associated with time */ 1099 sdp_parse_repeat(description->d_ltime, begin + 1, end, 1100 &description->d_perror); 1101 break; 1102 case SDP_ZONE_FIELD: 1103 sdp_check_order(prev, SDP_ZONE_ORDER, &error); 1104 sdp_parse_zone(&_session->s_zone, begin + 1, end, 1105 &description->d_perror); 1106 break; 1107 case SDP_KEY_FIELD: 1108 if (description->d_mparsed) { 1109 sdp_check_order(m_prev, SDP_M_KEY_ORDER, 1110 &error); 1111 if (description->d_lmedia == NULL) 1112 break; 1113 sdp_parse_key(&(description->d_lmedia->m_key), 1114 begin + 1, end, &description->d_perror); 1115 } else { 1116 sdp_check_order(prev, SDP_KEY_ORDER, &error); 1117 sdp_parse_key(&_session->s_key, begin + 1, end, 1118 &description->d_perror); 1119 } 1120 break; 1121 case SDP_ATTRIBUTE_FIELD: 1122 if (description->d_mparsed) { 1123 sdp_check_order(m_prev, SDP_M_ATTR_ORDER, 1124 &error); 1125 if (description->d_lmedia == NULL) 1126 break; 1127 sdp_parse_attribute(&(description->d_lmedia-> 1128 m_attr), begin + 1, end, 1129 &description->d_perror); 1130 } else { 1131 sdp_check_order(prev, SDP_ATTR_ORDER, &error); 1132 sdp_parse_attribute(&_session->s_attr, 1133 begin + 1, end, &description->d_perror); 1134 } 1135 break; 1136 case SDP_MEDIA_FIELD: 1137 if (!description->d_mparsed) { 1138 sdp_check_order(prev, SDP_MEDIA_ORDER, &error); 1139 description->d_mccount = 1; 1140 } else { 1141 if (description->d_mccount == 1) 1142 description->d_mconn = B_FALSE; 1143 description->d_mccount = 1; 1144 } 1145 description->d_mparsed = B_TRUE; 1146 description->d_lmedia = sdp_parse_media(_session, 1147 begin + 1, end, &description->d_perror); 1148 break; 1149 default: 1150 /* Unknown field type. Ignore it */ 1151 u_field = B_TRUE; 1152 break; 1153 } 1154 if (error) 1155 description->d_perror |= SDP_FIELDS_ORDER_ERROR; 1156 if (!u_field) { 1157 if (!description->d_mparsed) 1158 description->d_prev = *begin; 1159 else 1160 description->d_mprev = *begin; 1161 } 1162 } 1163 1164 /* 1165 * Parses the SDP info 1166 */ 1167 int 1168 sdp_parse(const char *sdp_info, int len, int flags, sdp_session_t **session, 1169 uint_t *p_error) 1170 { 1171 1172 const char *f_begin; 1173 const char *f_end; 1174 sdp_description_t *description; 1175 const char *start; 1176 const char *end; 1177 const char *current; 1178 1179 if (sdp_info == NULL || len == 0 || p_error == NULL || flags != 0 || 1180 session == NULL) { 1181 if (session != NULL) 1182 *session = NULL; 1183 return (EINVAL); 1184 } 1185 *session = NULL; 1186 *p_error = 0; 1187 description = calloc(1, sizeof (sdp_description_t)); 1188 if (description == NULL) { 1189 return (ENOMEM); 1190 } 1191 /* Needed later to check for mandatory fields */ 1192 description->d_prev = COMMP_SP; 1193 description->d_mconn = B_TRUE; 1194 *session = sdp_new_session(); 1195 if (*session == NULL) { 1196 free(description); 1197 return (ENOMEM); 1198 } 1199 start = sdp_info; 1200 end = start + len; 1201 if (commp_skip_white_space(&start, end) != 0) { 1202 free(description); 1203 free(*session); 1204 *session = NULL; 1205 return (EINVAL); 1206 } 1207 current = start; 1208 f_begin = current; 1209 while ((current < end) && !(description->d_perror & 1210 SDP_MEMORY_ERROR)) { 1211 /* 1212 * RFC says parser SHOULD be tolerant to records ending 1213 * with a single newline character too. 1214 */ 1215 if (strncmp(COMMP_CRLF, current, strlen(COMMP_CRLF)) == 0) { 1216 f_end = current; 1217 sdp_handle_fields(description, *session, f_begin, 1218 f_end); 1219 COMMP_SKIP_CRLF(current); 1220 (void) commp_skip_white_space(¤t, end); 1221 f_begin = current; 1222 } else if (strncmp(COMMP_LF, current, strlen(COMMP_LF)) == 0) { 1223 f_end = current; 1224 sdp_handle_fields(description, *session, f_begin, 1225 f_end); 1226 COMMP_SKIP_LF(current); 1227 (void) commp_skip_white_space(¤t, end); 1228 f_begin = current; 1229 } else { 1230 current++; 1231 } 1232 } 1233 if (description->d_perror & SDP_MEMORY_ERROR) { 1234 free(description); 1235 sdp_free_session(*session); 1236 *session = NULL; 1237 return (ENOMEM); 1238 } 1239 /* 1240 * Check for mandatory fields v, o, s, t fields. For connection field, 1241 * RFC says; a connection field must be present in every media 1242 * description or at the session-level 1243 */ 1244 if (description->d_mccount == 1) 1245 description->d_mconn = B_FALSE; 1246 if (!(description->d_version && description->d_origin && 1247 description->d_name && description->d_tparsed && 1248 (description->d_conn || (description->d_mparsed && 1249 description->d_mconn)))) { 1250 description->d_perror |= SDP_MISSING_FIELDS; 1251 } 1252 *p_error = description->d_perror; 1253 free(description); 1254 return (0); 1255 } 1256