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 #include <stdlib.h> 30 #include <assert.h> 31 #include <errno.h> 32 #include <pthread.h> 33 #include <strings.h> 34 35 #include "sip_parse_uri.h" 36 #include "sip_msg.h" 37 #include "sip_miscdefs.h" 38 #include "sip_xaction.h" 39 #include "sip_hash.h" 40 41 #define RFC_3261_BRANCH "z9hG4bK" 42 43 /* 44 * The transaction hash table 45 */ 46 sip_hash_t sip_xaction_hash[SIP_HASH_SZ]; 47 48 int (*sip_xaction_ulp_trans_err)(sip_transaction_t, int, void *) = NULL; 49 void (*sip_xaction_ulp_state_cb)(sip_transaction_t, sip_msg_t, int, int) = NULL; 50 51 int sip_xaction_add(sip_xaction_t *, char *, _sip_msg_t *, sip_method_t); 52 static boolean_t sip_is_conn_obj_cache(sip_conn_object_t, void *); 53 54 /* 55 * Get the md5 hash of the required fields 56 */ 57 int 58 sip_find_md5_digest(char *bid, _sip_msg_t *msg, uint16_t *hindex, 59 sip_method_t method) 60 { 61 boolean_t is_2543; 62 63 is_2543 = (bid == NULL || 64 strncmp(bid, RFC_3261_BRANCH, strlen(RFC_3261_BRANCH)) != 0); 65 66 if (is_2543 && msg == NULL) 67 return (EINVAL); 68 if (is_2543) { 69 _sip_header_t *from = NULL; 70 _sip_header_t *cid = NULL; 71 _sip_header_t *via = NULL; 72 const sip_str_t *to_uri = NULL; 73 int cseq; 74 int error = 0; 75 76 /* 77 * Since the response might contain parameters not in the 78 * request, just use the to URI. 79 */ 80 to_uri = sip_get_to_uri_str((sip_msg_t)msg, &error); 81 if (to_uri == NULL || error != 0) 82 return (EINVAL); 83 cseq = sip_get_callseq_num((sip_msg_t)msg, &error); 84 if (cseq < 0 || error != 0) 85 return (EINVAL); 86 (void) pthread_mutex_lock(&msg->sip_msg_mutex); 87 via = sip_search_for_header(msg, SIP_VIA, NULL); 88 from = sip_search_for_header(msg, SIP_FROM, NULL); 89 cid = sip_search_for_header(msg, SIP_CALL_ID, NULL); 90 (void) pthread_mutex_unlock(&msg->sip_msg_mutex); 91 if (via == NULL || from == NULL || cid == NULL) 92 return (EINVAL); 93 sip_md5_hash(via->sip_hdr_start, 94 via->sip_hdr_end - via->sip_hdr_start, 95 cid->sip_hdr_start, 96 cid->sip_hdr_end - cid->sip_hdr_start, 97 from->sip_hdr_start, 98 from->sip_hdr_end - from->sip_hdr_start, 99 (char *)&cseq, sizeof (int), 100 (char *)&method, sizeof (sip_method_t), 101 to_uri->sip_str_ptr, to_uri->sip_str_len, 102 (uchar_t *)hindex); 103 } else { 104 sip_md5_hash(bid, strlen(bid), (char *)&method, 105 sizeof (sip_method_t), NULL, 0, NULL, 0, NULL, 0, NULL, 0, 106 (uchar_t *)hindex); 107 } 108 return (0); 109 } 110 111 /* 112 * Add object to the connection cache object. Not checking for duplicates!! 113 */ 114 int 115 sip_add_conn_obj_cache(sip_conn_object_t obj, void *cobj) 116 { 117 void **obj_val; 118 sip_conn_obj_pvt_t *pvt_data; 119 sip_conn_cache_t *xaction_list; 120 sip_xaction_t *sip_trans = (sip_xaction_t *)cobj; 121 122 /* 123 * Is already cached 124 */ 125 if (sip_trans->sip_xaction_conn_obj != NULL) { 126 if (sip_is_conn_obj_cache(sip_trans->sip_xaction_conn_obj, 127 (void *)sip_trans)) { 128 return (0); 129 } 130 /* 131 * Transaction has cached a different conn_obj, release it 132 */ 133 sip_del_conn_obj_cache(sip_trans->sip_xaction_conn_obj, 134 (void *)sip_trans); 135 } 136 137 xaction_list = malloc(sizeof (sip_conn_cache_t)); 138 if (xaction_list == NULL) 139 return (ENOMEM); 140 xaction_list->obj = cobj; 141 xaction_list->next = xaction_list->prev = NULL; 142 143 obj_val = (void *)obj; 144 pvt_data = (sip_conn_obj_pvt_t *)*obj_val; 145 if (pvt_data == NULL) { 146 free(xaction_list); 147 return (EINVAL); 148 } 149 (void) pthread_mutex_lock(&pvt_data->sip_conn_obj_cache_lock); 150 151 if (pvt_data->sip_conn_obj_cache == NULL) { 152 pvt_data->sip_conn_obj_cache = xaction_list; 153 } else { 154 xaction_list->next = pvt_data->sip_conn_obj_cache; 155 pvt_data->sip_conn_obj_cache->prev = xaction_list; 156 pvt_data->sip_conn_obj_cache = xaction_list; 157 } 158 sip_refhold_conn(obj); 159 sip_trans->sip_xaction_conn_obj = obj; 160 (void) pthread_mutex_unlock(&pvt_data->sip_conn_obj_cache_lock); 161 return (0); 162 } 163 164 /* 165 * Walk thru the list of transactions that have cached this obj and 166 * and return true if 'cobj' is one of them. 167 */ 168 static boolean_t 169 sip_is_conn_obj_cache(sip_conn_object_t obj, void *cobj) 170 { 171 void **obj_val; 172 sip_conn_obj_pvt_t *pvt_data; 173 sip_conn_cache_t *xaction_list; 174 sip_xaction_t *trans; 175 sip_xaction_t *ctrans = (sip_xaction_t *)cobj; 176 177 obj_val = (void *)obj; 178 pvt_data = (sip_conn_obj_pvt_t *)*obj_val; 179 if (pvt_data == NULL) 180 return (B_FALSE); 181 (void) pthread_mutex_lock(&pvt_data->sip_conn_obj_cache_lock); 182 xaction_list = pvt_data->sip_conn_obj_cache; 183 while (xaction_list != NULL) { 184 trans = (sip_xaction_t *)xaction_list->obj; 185 if (ctrans != trans) { 186 xaction_list = xaction_list->next; 187 continue; 188 } 189 (void) pthread_mutex_unlock(&pvt_data->sip_conn_obj_cache_lock); 190 return (B_TRUE); 191 } 192 (void) pthread_mutex_unlock(&pvt_data->sip_conn_obj_cache_lock); 193 return (B_FALSE); 194 } 195 196 197 /* 198 * Walk thru the list of transactions that have cached this obj and 199 * refrele the objs. 200 */ 201 void 202 sip_del_conn_obj_cache(sip_conn_object_t obj, void *cobj) 203 { 204 void **obj_val; 205 sip_conn_obj_pvt_t *pvt_data; 206 sip_conn_cache_t *xaction_list; 207 sip_conn_cache_t *tmp_list; 208 sip_xaction_t *trans; 209 sip_xaction_t *ctrans = NULL; 210 211 if (cobj != NULL) 212 ctrans = (sip_xaction_t *)cobj; 213 214 obj_val = (void *)obj; 215 pvt_data = (sip_conn_obj_pvt_t *)*obj_val; 216 if (pvt_data == NULL) { /* ASSERT FALSE if ctrans != NULL?? */ 217 if (ctrans != NULL) { 218 sip_refrele_conn(obj); 219 ctrans->sip_xaction_conn_obj = NULL; 220 } 221 return; 222 } 223 (void) pthread_mutex_lock(&pvt_data->sip_conn_obj_cache_lock); 224 xaction_list = pvt_data->sip_conn_obj_cache; 225 while (xaction_list != NULL) { 226 tmp_list = xaction_list; 227 trans = (sip_xaction_t *)xaction_list->obj; 228 assert(trans != NULL); 229 if (ctrans != NULL && ctrans != trans) { 230 xaction_list = xaction_list->next; 231 continue; 232 } 233 if (ctrans == NULL) 234 (void) pthread_mutex_lock(&trans->sip_xaction_mutex); 235 assert(trans->sip_xaction_conn_obj == obj); 236 sip_refrele_conn(obj); 237 trans->sip_xaction_conn_obj = NULL; 238 if (ctrans == NULL) 239 (void) pthread_mutex_unlock(&trans->sip_xaction_mutex); 240 xaction_list = xaction_list->next; 241 242 /* 243 * Take the obj out of the list 244 */ 245 if (tmp_list == pvt_data->sip_conn_obj_cache) { 246 if (xaction_list == NULL) { 247 pvt_data->sip_conn_obj_cache = NULL; 248 } else { 249 xaction_list->prev = NULL; 250 pvt_data->sip_conn_obj_cache = xaction_list; 251 } 252 } else if (xaction_list == NULL) { 253 assert(tmp_list->prev != NULL); 254 tmp_list->prev->next = NULL; 255 } else { 256 assert(tmp_list->prev != NULL); 257 tmp_list->prev->next = xaction_list; 258 xaction_list->prev = tmp_list->prev; 259 } 260 tmp_list->prev = NULL; 261 tmp_list->next = NULL; 262 tmp_list->obj = NULL; 263 264 free(tmp_list); 265 } 266 (void) pthread_mutex_unlock(&pvt_data->sip_conn_obj_cache_lock); 267 } 268 269 /* 270 * Check for a transaction match. Passed to sip_hash_find(). 271 */ 272 boolean_t 273 sip_xaction_match(void *obj, void *hindex) 274 { 275 sip_xaction_t *tmp = (sip_xaction_t *)obj; 276 277 tmp = (sip_xaction_t *)obj; 278 279 if (SIP_IS_XACTION_TERMINATED(tmp->sip_xaction_state)) 280 return (B_FALSE); 281 if (bcmp(tmp->sip_xaction_hash_digest, hindex, 282 sizeof (tmp->sip_xaction_hash_digest)) == 0) { 283 SIP_XACTION_REFCNT_INCR(tmp); 284 return (B_TRUE); 285 } 286 return (B_FALSE); 287 } 288 289 290 /* 291 * Find a transaction 292 */ 293 static sip_xaction_t * 294 sip_xaction_find(char *branchid, _sip_msg_t *msg, int which) 295 { 296 sip_xaction_t *tmp; 297 uint16_t hash_index[8]; 298 int hindex; 299 sip_method_t method; 300 int error; 301 sip_message_type_t *sip_msg_info; 302 303 sip_msg_info = msg->sip_msg_req_res; 304 method = sip_get_callseq_method((sip_msg_t)msg, &error); 305 if (error != 0) 306 return (NULL); 307 308 /* 309 * If we are getting a ACK/CANCEL we need to match with the 310 * corresponding INVITE, if any. 311 */ 312 if (sip_msg_info->is_request && which == SIP_SERVER_TRANSACTION && 313 (method == ACK || method == CANCEL)) { 314 method = INVITE; 315 } 316 if (sip_find_md5_digest(branchid, msg, hash_index, method) != 0) 317 return (NULL); 318 hindex = SIP_DIGEST_TO_HASH(hash_index); 319 tmp = (sip_xaction_t *)sip_hash_find(sip_xaction_hash, 320 (void *)hash_index, hindex, sip_xaction_match); 321 return (tmp); 322 } 323 324 /* 325 * create a transaction. 326 */ 327 static sip_xaction_t * 328 sip_xaction_create(sip_conn_object_t obj, _sip_msg_t *msg, char *branchid, 329 int *error) 330 { 331 sip_xaction_t *trans; 332 sip_message_type_t *sip_msg_info; 333 int state = 0; 334 int prev_state = 0; 335 sip_method_t method; 336 int ret; 337 int timer1 = sip_timer_T1; 338 int timer4 = sip_timer_T4; 339 int timerd = sip_timer_TD; 340 341 if (error != NULL) 342 *error = 0; 343 /* 344 * Make sure we are not creating a transaction for 345 * an ACK request. 346 */ 347 trans = (sip_xaction_t *)malloc(sizeof (sip_xaction_t)); 348 if (trans == NULL) { 349 if (error != NULL) 350 *error = ENOMEM; 351 return (NULL); 352 } 353 bzero(trans, sizeof (sip_xaction_t)); 354 if (branchid == NULL) { 355 trans->sip_xaction_branch_id = (char *)sip_branchid(NULL); 356 if (trans->sip_xaction_branch_id == NULL) { 357 free(trans); 358 if (error != NULL) 359 *error = ENOMEM; 360 return (NULL); 361 } 362 } else { 363 trans->sip_xaction_branch_id = (char *)malloc(strlen(branchid) 364 + 1); 365 if (trans->sip_xaction_branch_id == NULL) { 366 free(trans); 367 if (error != NULL) 368 *error = ENOMEM; 369 return (NULL); 370 } 371 (void) strncpy(trans->sip_xaction_branch_id, branchid, 372 strlen(branchid)); 373 trans->sip_xaction_branch_id[strlen(branchid)] = '\0'; 374 } 375 (void) pthread_mutex_init(&trans->sip_xaction_mutex, NULL); 376 SIP_MSG_REFCNT_INCR(msg); 377 trans->sip_xaction_orig_msg = msg; 378 assert(msg->sip_msg_req_res != NULL); 379 sip_msg_info = msg->sip_msg_req_res; 380 if (sip_msg_info->is_request) { 381 method = sip_msg_info->sip_req_method; 382 } else { 383 method = sip_get_callseq_method((sip_msg_t)msg, &ret); 384 if (ret != 0) { 385 free(trans->sip_xaction_branch_id); 386 free(trans); 387 if (error != NULL) 388 *error = ret; 389 return (NULL); 390 } 391 if (method == INVITE) 392 state = SIP_SRV_INV_PROCEEDING; 393 else 394 state = SIP_SRV_TRYING; 395 } 396 trans->sip_xaction_method = method; 397 trans->sip_xaction_state = state; 398 399 /* 400 * Get connection object specific timeouts, if present 401 */ 402 if (sip_conn_timer1 != NULL) 403 timer1 = sip_conn_timer1(obj); 404 if (sip_conn_timer4 != NULL) 405 timer4 = sip_conn_timer4(obj); 406 if (sip_conn_timerd != NULL) 407 timerd = sip_conn_timerd(obj); 408 409 SIP_INIT_TIMER(trans->sip_xaction_TA, 2 * timer1); 410 SIP_INIT_TIMER(trans->sip_xaction_TB, 64 * timer1) 411 SIP_INIT_TIMER(trans->sip_xaction_TD, timerd); 412 SIP_INIT_TIMER(trans->sip_xaction_TE, timer1); 413 SIP_INIT_TIMER(trans->sip_xaction_TF, 64 * timer1); 414 SIP_INIT_TIMER(trans->sip_xaction_TG, 2 * timer1); 415 SIP_INIT_TIMER(trans->sip_xaction_TH, 64 * timer1); 416 SIP_INIT_TIMER(trans->sip_xaction_TI, timer4); 417 SIP_INIT_TIMER(trans->sip_xaction_TJ, 64 * timer1); 418 SIP_INIT_TIMER(trans->sip_xaction_TK, timer4); 419 420 if ((ret = sip_xaction_add(trans, branchid, msg, method)) != 0) { 421 (void) pthread_mutex_destroy(&trans->sip_xaction_mutex); 422 free(trans->sip_xaction_branch_id); 423 free(trans); 424 if (error != NULL) 425 *error = ret; 426 return (NULL); 427 } 428 if (sip_xaction_ulp_state_cb != NULL && 429 prev_state != trans->sip_xaction_state) { 430 sip_xaction_ulp_state_cb((sip_transaction_t)trans, 431 (sip_msg_t)msg, prev_state, trans->sip_xaction_state); 432 } 433 return (trans); 434 } 435 436 /* 437 * Find a transaction, create if asked for 438 */ 439 sip_xaction_t * 440 sip_xaction_get(sip_conn_object_t obj, sip_msg_t msg, boolean_t create, 441 int which, int *error) 442 { 443 char *branchid; 444 sip_xaction_t *sip_trans; 445 _sip_msg_t *_msg; 446 sip_message_type_t *sip_msg_info; 447 448 if (error != NULL) 449 *error = 0; 450 451 _msg = (_sip_msg_t *)msg; 452 sip_msg_info = ((_sip_msg_t *)msg)->sip_msg_req_res; 453 454 branchid = sip_get_branchid(msg, NULL); 455 sip_trans = sip_xaction_find(branchid, _msg, which); 456 if (sip_trans == NULL && create) { 457 /* 458 * If we are sending a request, must be conformant to RFC 3261. 459 */ 460 if (sip_msg_info->is_request && 461 (branchid == NULL || strncmp(branchid, 462 RFC_3261_BRANCH, strlen(RFC_3261_BRANCH) != 0))) { 463 if (error != NULL) 464 *error = EINVAL; 465 if (branchid != NULL) 466 free(branchid); 467 return (NULL); 468 } 469 sip_trans = sip_xaction_create(obj, _msg, branchid, error); 470 if (sip_trans != NULL) 471 SIP_XACTION_REFCNT_INCR(sip_trans); 472 } 473 if (branchid != NULL) 474 free(branchid); 475 return (sip_trans); 476 } 477 478 479 /* 480 * Delete a transaction if the reference count is 0. Passed to 481 * sip_hash_delete(). 482 */ 483 boolean_t 484 sip_xaction_remove(void *obj, void *hindex, int *found) 485 { 486 sip_xaction_t *tmp = (sip_xaction_t *)obj; 487 488 *found = 0; 489 tmp = (sip_xaction_t *)obj; 490 (void) pthread_mutex_lock(&tmp->sip_xaction_mutex); 491 if (bcmp(tmp->sip_xaction_hash_digest, hindex, 492 sizeof (tmp->sip_xaction_hash_digest)) == 0) { 493 *found = 1; 494 if (tmp->sip_xaction_ref_cnt != 0) { 495 (void) pthread_mutex_unlock(&tmp->sip_xaction_mutex); 496 return (B_FALSE); 497 } 498 (void) pthread_mutex_destroy(&tmp->sip_xaction_mutex); 499 SIP_CANCEL_TIMER(tmp->sip_xaction_TA); 500 SIP_CANCEL_TIMER(tmp->sip_xaction_TB); 501 SIP_CANCEL_TIMER(tmp->sip_xaction_TD); 502 SIP_CANCEL_TIMER(tmp->sip_xaction_TE); 503 SIP_CANCEL_TIMER(tmp->sip_xaction_TF); 504 SIP_CANCEL_TIMER(tmp->sip_xaction_TG); 505 SIP_CANCEL_TIMER(tmp->sip_xaction_TH); 506 SIP_CANCEL_TIMER(tmp->sip_xaction_TI); 507 SIP_CANCEL_TIMER(tmp->sip_xaction_TJ); 508 SIP_CANCEL_TIMER(tmp->sip_xaction_TK); 509 free(tmp->sip_xaction_branch_id); 510 if (tmp->sip_xaction_last_msg != NULL) { 511 SIP_MSG_REFCNT_DECR(tmp->sip_xaction_last_msg); 512 tmp->sip_xaction_last_msg = NULL; 513 } 514 if (tmp->sip_xaction_orig_msg != NULL) { 515 SIP_MSG_REFCNT_DECR(tmp->sip_xaction_orig_msg); 516 tmp->sip_xaction_orig_msg = NULL; 517 } 518 if (tmp->sip_xaction_conn_obj != NULL) { 519 sip_del_conn_obj_cache(tmp->sip_xaction_conn_obj, 520 (void *)tmp); 521 } 522 free(tmp); 523 return (B_TRUE); 524 } 525 (void) pthread_mutex_unlock(&tmp->sip_xaction_mutex); 526 return (B_FALSE); 527 } 528 529 /* 530 * Delete a SIP transaction 531 */ 532 void 533 sip_xaction_delete(sip_xaction_t *trans) 534 { 535 int hindex; 536 537 (void) pthread_mutex_lock(&trans->sip_xaction_mutex); 538 hindex = SIP_DIGEST_TO_HASH(trans->sip_xaction_hash_digest); 539 if (trans->sip_xaction_ref_cnt != 0) { 540 (void) pthread_mutex_unlock(&trans->sip_xaction_mutex); 541 return; 542 } 543 (void) pthread_mutex_unlock(&trans->sip_xaction_mutex); 544 sip_hash_delete(sip_xaction_hash, trans->sip_xaction_hash_digest, 545 hindex, sip_xaction_remove); 546 } 547 548 /* 549 * Add a SIP transaction into the hash list. 550 */ 551 int 552 sip_xaction_add(sip_xaction_t *trans, char *branchid, _sip_msg_t *msg, 553 sip_method_t method) 554 { 555 uint16_t hash_index[8]; 556 557 if (sip_find_md5_digest(branchid, msg, hash_index, method) != 0) 558 return (EINVAL); 559 560 /* 561 * trans is not in the list as yet, so no need to hold the lock 562 */ 563 bcopy(hash_index, trans->sip_xaction_hash_digest, sizeof (hash_index)); 564 565 if (sip_hash_add(sip_xaction_hash, (void *)trans, 566 SIP_DIGEST_TO_HASH(hash_index)) != 0) { 567 return (ENOMEM); 568 } 569 return (0); 570 } 571 572 573 /* 574 * Given a state, return the string - This is mostly for debug purposes 575 */ 576 char * 577 sip_get_xaction_state(int state) 578 { 579 switch (state) { 580 case SIP_CLNT_CALLING: 581 return ("SIP_CLNT_CALLING"); 582 case SIP_CLNT_INV_PROCEEDING: 583 return ("SIP_CLNT_INV_PROCEEDING"); 584 case SIP_CLNT_INV_TERMINATED: 585 return ("SIP_CLNT_INV_TERMINATED"); 586 case SIP_CLNT_INV_COMPLETED: 587 return ("SIP_CLNT_INV_COMPLETED"); 588 case SIP_CLNT_TRYING: 589 return ("SIP_CLNT_TRYING"); 590 case SIP_CLNT_NONINV_PROCEEDING: 591 return ("SIP_CLNT_NONINV_PROCEEDING"); 592 case SIP_CLNT_NONINV_TERMINATED: 593 return ("SIP_CLNT_NONINV_TERMINATED"); 594 case SIP_CLNT_NONINV_COMPLETED: 595 return ("SIP_CLNT_NONINV_COMPLETED"); 596 case SIP_SRV_INV_PROCEEDING: 597 return ("SIP_SRV_INV_PROCEEDING"); 598 case SIP_SRV_INV_COMPLETED: 599 return ("SIP_SRV_INV_COMPLETED"); 600 case SIP_SRV_CONFIRMED: 601 return ("SIP_SRV_CONFIRMED"); 602 case SIP_SRV_INV_TERMINATED: 603 return ("SIP_SRV_INV_TERMINATED"); 604 case SIP_SRV_TRYING: 605 return ("SIP_SRV_TRYING"); 606 case SIP_SRV_NONINV_PROCEEDING: 607 return ("SIP_SRV_NONINV_PROCEEDING"); 608 case SIP_SRV_NONINV_COMPLETED: 609 return ("SIP_SRV_NONINV_COMPLETED"); 610 case SIP_SRV_NONINV_TERMINATED: 611 return ("SIP_SRV_NONINV_TERMINATED"); 612 default : 613 return ("unknown"); 614 } 615 } 616 617 /* 618 * Initialize the hash table etc. 619 */ 620 void 621 sip_xaction_init(int (*ulp_trans_err)(sip_transaction_t, int, void *), 622 void (*ulp_state_cb)(sip_transaction_t, sip_msg_t, int, int)) 623 { 624 int cnt; 625 626 for (cnt = 0; cnt < SIP_HASH_SZ; cnt++) { 627 sip_xaction_hash[cnt].hash_count = 0; 628 sip_xaction_hash[cnt].hash_head = NULL; 629 sip_xaction_hash[cnt].hash_tail = NULL; 630 (void) pthread_mutex_init( 631 &sip_xaction_hash[cnt].sip_hash_mutex, NULL); 632 } 633 if (ulp_trans_err != NULL) 634 sip_xaction_ulp_trans_err = ulp_trans_err; 635 if (ulp_state_cb != NULL) 636 sip_xaction_ulp_state_cb = ulp_state_cb; 637 } 638