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