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