1 /* 2 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 #pragma ident "%Z%%M% %I% %E% SMI" 7 8 /* 9 * The contents of this file are subject to the Netscape Public 10 * License Version 1.1 (the "License"); you may not use this file 11 * except in compliance with the License. You may obtain a copy of 12 * the License at http://www.mozilla.org/NPL/ 13 * 14 * Software distributed under the License is distributed on an "AS 15 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or 16 * implied. See the License for the specific language governing 17 * rights and limitations under the License. 18 * 19 * The Original Code is Mozilla Communicator client code, released 20 * March 31, 1998. 21 * 22 * The Initial Developer of the Original Code is Netscape 23 * Communications Corporation. Portions created by Netscape are 24 * Copyright (C) 1998-1999 Netscape Communications Corporation. All 25 * Rights Reserved. 26 * 27 * Contributor(s): 28 */ 29 /* 30 * Copyright (c) 1995 Regents of the University of Michigan. 31 * All rights reserved. 32 */ 33 /* 34 * os-ip.c -- platform-specific TCP & UDP related code 35 */ 36 37 #if 0 38 #ifndef lint 39 static char copyright[] = "@(#) Copyright (c) 1995 Regents of the University of Michigan.\nAll rights reserved.\n"; 40 #endif 41 #endif 42 43 #include "ldap-int.h" 44 #ifdef LDAP_CONNECT_MUST_NOT_BE_INTERRUPTED 45 #include <signal.h> 46 #endif 47 48 #ifdef NSLDAPI_HAVE_POLL 49 #include <poll.h> 50 #endif 51 52 53 #ifdef _WINDOWS 54 #define NSLDAPI_INVALID_OS_SOCKET( s ) ((s) == INVALID_SOCKET) 55 #else 56 #define NSLDAPI_INVALID_OS_SOCKET( s ) ((s) < 0 ) 57 #endif 58 59 60 #define NSLDAPI_POLL_ARRAY_GROWTH 5 /* grow arrays 5 elements at a time */ 61 62 63 /* 64 * Structures and union for tracking status of network sockets 65 */ 66 #ifdef NSLDAPI_HAVE_POLL 67 struct nsldapi_os_statusinfo { /* used with native OS poll() */ 68 struct pollfd *ossi_pollfds; 69 int ossi_pollfds_size; 70 }; 71 #else /* NSLDAPI_HAVE_POLL */ 72 struct nsldapi_os_statusinfo { /* used with native OS select() */ 73 fd_set ossi_readfds; 74 fd_set ossi_writefds; 75 fd_set ossi_use_readfds; 76 fd_set ossi_use_writefds; 77 }; 78 #endif /* else NSLDAPI_HAVE_POLL */ 79 80 struct nsldapi_cb_statusinfo { /* used with ext. I/O poll() callback */ 81 LDAP_X_PollFD *cbsi_pollfds; 82 int cbsi_pollfds_size; 83 }; 84 85 /* 86 * NSLDAPI_CB_POLL_MATCH() evaluates to non-zero (true) if the Sockbuf *sdp 87 * matches the LDAP_X_PollFD pollfd. 88 */ 89 #ifdef _WINDOWS 90 #define NSLDAPI_CB_POLL_SD_CAST (unsigned int) 91 #else 92 #define NSLDAPI_CB_POLL_SD_CAST 93 #endif 94 #if defined(LDAP_SASLIO_HOOKS) 95 #define NSLDAPI_CB_POLL_MATCH( sbp, pollfd ) \ 96 ( ((sbp)->sb_sd == NSLDAPI_CB_POLL_SD_CAST ((pollfd).lpoll_fd)) && \ 97 (((sbp)->sb_sasl_fns.lbextiofn_socket_arg == (pollfd).lpoll_socketarg) || \ 98 ((sbp)->sb_ext_io_fns.lbextiofn_socket_arg == (pollfd).lpoll_socketarg) ) ) 99 #else 100 #define NSLDAPI_CB_POLL_MATCH( sbp, pollfd ) \ 101 ((sbp)->sb_sd == NSLDAPI_CB_POLL_SD_CAST ((pollfd).lpoll_fd) && \ 102 (sbp)->sb_ext_io_fns.lbextiofn_socket_arg == (pollfd).lpoll_socketarg) 103 #endif 104 105 106 struct nsldapi_iostatus_info { 107 int ios_type; 108 #define NSLDAPI_IOSTATUS_TYPE_OSNATIVE 1 /* poll() or select() */ 109 #define NSLDAPI_IOSTATUS_TYPE_CALLBACK 2 /* poll()-like */ 110 int ios_read_count; 111 int ios_write_count; 112 union { 113 struct nsldapi_os_statusinfo ios_osinfo; 114 struct nsldapi_cb_statusinfo ios_cbinfo; 115 } ios_status; 116 }; 117 118 119 #ifdef NSLDAPI_HAVE_POLL 120 static int nsldapi_add_to_os_pollfds( int fd, 121 struct nsldapi_os_statusinfo *pip, short events ); 122 static int nsldapi_clear_from_os_pollfds( int fd, 123 struct nsldapi_os_statusinfo *pip, short events ); 124 static int nsldapi_find_in_os_pollfds( int fd, 125 struct nsldapi_os_statusinfo *pip, short revents ); 126 #endif /* NSLDAPI_HAVE_POLL */ 127 128 static int nsldapi_iostatus_init_nolock( LDAP *ld ); 129 static int nsldapi_add_to_cb_pollfds( Sockbuf *sb, 130 struct nsldapi_cb_statusinfo *pip, short events ); 131 static int nsldapi_clear_from_cb_pollfds( Sockbuf *sb, 132 struct nsldapi_cb_statusinfo *pip, short events ); 133 static int nsldapi_find_in_cb_pollfds( Sockbuf *sb, 134 struct nsldapi_cb_statusinfo *pip, short revents ); 135 136 137 #ifdef irix 138 #ifndef _PR_THREADS 139 /* 140 * XXXmcs: on IRIX NSPR's poll() and select() wrappers will crash if NSPR 141 * has not been initialized. We work around the problem by bypassing 142 * the NSPR wrapper functions and going directly to the OS' functions. 143 */ 144 #define NSLDAPI_POLL _poll 145 #define NSLDAPI_SELECT _select 146 extern int _poll(struct pollfd *fds, unsigned long nfds, int timeout); 147 extern int _select(int nfds, fd_set *readfds, fd_set *writefds, 148 fd_set *exceptfds, struct timeval *timeout); 149 #else /* _PR_THREADS */ 150 #define NSLDAPI_POLL poll 151 #define NSLDAPI_SELECT select 152 #endif /* else _PR_THREADS */ 153 #else /* irix */ 154 #define NSLDAPI_POLL poll 155 #define NSLDAPI_SELECT select 156 #endif /* else irix */ 157 158 159 static LBER_SOCKET nsldapi_os_socket( LDAP *ld, int secure, int domain, 160 int type, int protocol ); 161 static int nsldapi_os_ioctl( LBER_SOCKET s, int option, int *statusp ); 162 static int nsldapi_os_connect_with_to( LBER_SOCKET s, struct sockaddr *name, 163 int namelen, LDAP *ld); 164 165 /* 166 * Function typedefs used by nsldapi_try_each_host() 167 */ 168 typedef LBER_SOCKET (NSLDAPI_SOCKET_FN)( LDAP *ld, int secure, int domain, 169 int type, int protocol ); 170 typedef int (NSLDAPI_IOCTL_FN)( LBER_SOCKET s, int option, int *statusp ); 171 typedef int (NSLDAPI_CONNECT_WITH_TO_FN )( LBER_SOCKET s, struct sockaddr *name, 172 int namelen, LDAP *ld); 173 typedef int (NSLDAPI_CONNECT_FN )( LBER_SOCKET s, struct sockaddr *name, 174 int namelen ); 175 typedef int (NSLDAPI_CLOSE_FN )( LBER_SOCKET s ); 176 177 static int nsldapi_try_each_host( LDAP *ld, const char *hostlist, int defport, 178 int secure, NSLDAPI_SOCKET_FN *socketfn, NSLDAPI_IOCTL_FN *ioctlfn, 179 NSLDAPI_CONNECT_WITH_TO_FN *connectwithtofn, 180 NSLDAPI_CONNECT_FN *connectfn, NSLDAPI_CLOSE_FN *closefn ); 181 182 183 static int 184 nsldapi_os_closesocket( LBER_SOCKET s ) 185 { 186 int rc; 187 188 #ifdef _WINDOWS 189 rc = closesocket( s ); 190 #else 191 rc = close( s ); 192 #endif 193 return( rc ); 194 } 195 196 197 static LBER_SOCKET 198 nsldapi_os_socket( LDAP *ld, int secure, int domain, int type, int protocol ) 199 { 200 int s, invalid_socket; 201 char *errmsg = NULL; 202 203 if ( secure ) { 204 LDAP_SET_LDERRNO( ld, LDAP_LOCAL_ERROR, NULL, 205 nsldapi_strdup( dgettext(TEXT_DOMAIN, 206 "secure mode not supported") )); 207 return( -1 ); 208 } 209 210 s = socket( domain, type, protocol ); 211 212 /* 213 * if the socket() call failed or it returned a socket larger 214 * than we can deal with, return a "local error." 215 */ 216 if ( NSLDAPI_INVALID_OS_SOCKET( s )) { 217 errmsg = dgettext(TEXT_DOMAIN, "unable to create a socket"); 218 invalid_socket = 1; 219 } else { /* valid socket -- check for overflow */ 220 invalid_socket = 0; 221 #if !defined(NSLDAPI_HAVE_POLL) && !defined(_WINDOWS) 222 /* not on Windows and do not have poll() */ 223 if ( s >= FD_SETSIZE ) { 224 errmsg = "can't use socket >= FD_SETSIZE"; 225 } 226 #endif 227 } 228 229 if ( errmsg != NULL ) { /* local socket error */ 230 if ( !invalid_socket ) { 231 nsldapi_os_closesocket( s ); 232 } 233 errmsg = nsldapi_strdup( errmsg ); 234 LDAP_SET_LDERRNO( ld, LDAP_LOCAL_ERROR, NULL, errmsg ); 235 return( -1 ); 236 } 237 238 return( s ); 239 } 240 241 242 243 /* 244 * Non-blocking connect call function 245 */ 246 static int 247 nsldapi_os_connect_with_to(LBER_SOCKET sockfd, struct sockaddr *saptr, 248 int salen, LDAP *ld) 249 { 250 #ifndef _WINDOWS 251 int flags; 252 #endif /* _WINDOWS */ 253 int n, error; 254 int len; 255 fd_set rset, wset; 256 struct timeval tval; 257 #ifdef _WINDOWS 258 int nonblock = 1; 259 int block = 0; 260 fd_set eset; 261 #endif /* _WINDOWS */ 262 int msec = ld->ld_connect_timeout; /* milliseconds */ 263 int continue_on_intr = 0; 264 #ifdef _SOLARIS_SDK 265 hrtime_t start_time = 0, tmp_time, tv_time; /* nanoseconds */ 266 #else 267 long start_time = 0, tmp_time; /* seconds */ 268 #endif 269 270 271 LDAPDebug( LDAP_DEBUG_TRACE, "nsldapi_connect_nonblock timeout: %d (msec)\n", 272 msec, 0, 0); 273 274 #ifdef _WINDOWS 275 ioctlsocket(sockfd, FIONBIO, &nonblock); 276 #else 277 flags = fcntl(sockfd, F_GETFL, 0); 278 fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); 279 #endif /* _WINDOWS */ 280 281 error = 0; 282 if ((n = connect(sockfd, saptr, salen)) < 0) 283 #ifdef _WINDOWS 284 if ((n != SOCKET_ERROR) && (WSAGetLastError() != WSAEWOULDBLOCK)) { 285 #else 286 if (errno != EINPROGRESS) { 287 #endif /* _WINDOWS */ 288 #ifdef LDAP_DEBUG 289 if ( ldap_debug & LDAP_DEBUG_TRACE ) { 290 perror("connect"); 291 } 292 #endif 293 return (-1); 294 } 295 296 /* success */ 297 if (n == 0) 298 goto done; 299 300 FD_ZERO(&rset); 301 FD_SET(sockfd, &rset); 302 wset = rset; 303 304 #ifdef _WINDOWS 305 eset = rset; 306 #endif /* _WINDOWS */ 307 308 if (msec < 0 && msec != LDAP_X_IO_TIMEOUT_NO_TIMEOUT) { 309 LDAPDebug( LDAP_DEBUG_TRACE, "Invalid timeout value detected.." 310 "resetting connect timeout to default value " 311 "(LDAP_X_IO_TIMEOUT_NO_TIMEOUT\n", 0, 0, 0); 312 msec = LDAP_X_IO_TIMEOUT_NO_TIMEOUT; 313 } else { 314 if (msec != 0) { 315 tval.tv_sec = msec / MILLISEC; 316 tval.tv_usec = (MICROSEC / MILLISEC) * 317 (msec % MILLISEC); 318 #ifdef _SOLARIS_SDK 319 start_time = gethrtime(); 320 tv_time = (hrtime_t)msec * (NANOSEC / MILLISEC); 321 #else 322 start_time = (long)time(NULL); 323 #endif 324 } else { 325 tval.tv_sec = 0; 326 tval.tv_usec = 0; 327 } 328 } 329 330 /* if timeval structure == NULL, select will block indefinitely */ 331 /* != NULL, and value == 0, select will */ 332 /* not block */ 333 /* Windows is a bit quirky on how it behaves w.r.t nonblocking */ 334 /* connects. If the connect fails, the exception fd, eset, is */ 335 /* set to show the failure. The first argument in select is */ 336 /* ignored */ 337 338 #ifdef _WINDOWS 339 if ((n = select(sockfd +1, &rset, &wset, &eset, 340 (msec != LDAP_X_IO_TIMEOUT_NO_TIMEOUT) ? &tval : NULL)) == 0) { 341 errno = WSAETIMEDOUT; 342 return (-1); 343 } 344 /* if wset is set, the connect worked */ 345 if (FD_ISSET(sockfd, &wset) || FD_ISSET(sockfd, &rset)) { 346 len = sizeof(error); 347 if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (char *)&error, &len) 348 < 0) 349 return (-1); 350 goto done; 351 } 352 353 /* if eset is set, the connect failed */ 354 if (FD_ISSET(sockfd, &eset)) { 355 return (-1); 356 } 357 358 /* failure on select call */ 359 if (n == SOCKET_ERROR) { 360 perror("select error: SOCKET_ERROR returned"); 361 return (-1); 362 } 363 #else 364 /* 365 * if LDAP_BITOPT_RESTART and select() is interrupted 366 * try again. 367 */ 368 do { 369 continue_on_intr = 0; 370 if ((n = select(sockfd +1, &rset, &wset, NULL, 371 (msec != LDAP_X_IO_TIMEOUT_NO_TIMEOUT) ? \ 372 &tval : NULL)) == 0) { 373 errno = ETIMEDOUT; 374 return (-1); 375 } 376 if (n < 0) { 377 if ((ld->ld_options & LDAP_BITOPT_RESTART) && 378 (errno == EINTR)) { 379 continue_on_intr = 1; 380 errno = 0; 381 FD_ZERO(&rset); 382 FD_SET(sockfd, &rset); 383 wset = rset; 384 /* honour the timeout */ 385 if ((msec != LDAP_X_IO_TIMEOUT_NO_TIMEOUT) && 386 (msec != 0)) { 387 #ifdef _SOLARIS_SDK 388 tmp_time = gethrtime(); 389 if ((tv_time -= 390 (tmp_time - start_time)) <= 0) { 391 #else 392 tmp_time = (long)time(NULL); 393 if ((tval.tv_sec -= 394 (tmp_time - start_time)) <= 0) { 395 #endif 396 /* timeout */ 397 errno = ETIMEDOUT; 398 return (-1); 399 } 400 #ifdef _SOLARIS_SDK 401 tval.tv_sec = tv_time / NANOSEC; 402 tval.tv_usec = (tv_time % NANOSEC) / 403 (NANOSEC / MICROSEC); 404 #endif 405 start_time = tmp_time; 406 } 407 } else { 408 #ifdef LDAP_DEBUG 409 perror("select error: "); 410 #endif 411 return (-1); 412 } 413 } 414 } while (continue_on_intr == 1); 415 416 if (FD_ISSET(sockfd, &rset) || FD_ISSET(sockfd, &wset)) { 417 len = sizeof(error); 418 if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (char *)&error, &len) 419 < 0) 420 return (-1); 421 #ifdef LDAP_DEBUG 422 } else if ( ldap_debug & LDAP_DEBUG_TRACE ) { 423 perror("select error: sockfd not set"); 424 #endif 425 } 426 #endif /* _WINDOWS */ 427 428 done: 429 #ifdef _WINDOWS 430 ioctlsocket(sockfd, FIONBIO, &block); 431 #else 432 fcntl(sockfd, F_SETFL, flags); 433 #endif /* _WINDOWS */ 434 435 if (error) { 436 errno = error; 437 return (-1); 438 } 439 440 return (0); 441 } 442 443 444 static int 445 nsldapi_os_ioctl( LBER_SOCKET s, int option, int *statusp ) 446 { 447 int err; 448 #ifdef _WINDOWS 449 u_long iostatus; 450 #endif 451 452 if ( FIONBIO != option ) { 453 return( -1 ); 454 } 455 456 #ifdef _WINDOWS 457 iostatus = *(u_long *)statusp; 458 err = ioctlsocket( s, FIONBIO, &iostatus ); 459 #else 460 err = ioctl( s, FIONBIO, (caddr_t)statusp ); 461 #endif 462 463 return( err ); 464 } 465 466 467 int 468 nsldapi_connect_to_host( LDAP *ld, Sockbuf *sb, const char *hostlist, 469 int defport, int secure, char **krbinstancep ) 470 /* 471 * "defport" must be in host byte order 472 * zero is returned upon success, -1 if fatal error, -2 EINPROGRESS 473 * if -1 is returned, ld_errno is set 474 */ 475 { 476 int s; 477 478 LDAPDebug( LDAP_DEBUG_TRACE, "nsldapi_connect_to_host: %s, port: %d\n", 479 NULL == hostlist ? "NULL" : hostlist, defport, 0 ); 480 481 /* 482 * If an extended I/O connect callback has been defined, just use it. 483 */ 484 if ( NULL != ld->ld_extconnect_fn ) { 485 unsigned long connect_opts = 0; 486 487 if ( ld->ld_options & LDAP_BITOPT_ASYNC) { 488 connect_opts |= LDAP_X_EXTIOF_OPT_NONBLOCKING; 489 } 490 if ( secure ) { 491 connect_opts |= LDAP_X_EXTIOF_OPT_SECURE; 492 } 493 s = ld->ld_extconnect_fn( hostlist, defport, 494 ld->ld_connect_timeout, connect_opts, 495 ld->ld_ext_session_arg, 496 &sb->sb_ext_io_fns.lbextiofn_socket_arg 497 #ifdef _SOLARIS_SDK 498 , NULL ); 499 #else 500 ); 501 #endif /* _SOLARIS_SDK */ 502 503 } else { 504 s = nsldapi_try_each_host( ld, hostlist, 505 defport, secure, nsldapi_os_socket, 506 nsldapi_os_ioctl, nsldapi_os_connect_with_to, 507 NULL, nsldapi_os_closesocket ); 508 } 509 510 if ( s < 0 ) { 511 LDAP_SET_LDERRNO( ld, LDAP_CONNECT_ERROR, NULL, NULL ); 512 return( -1 ); 513 } 514 515 sb->sb_sd = s; 516 517 /* 518 * Set krbinstancep (canonical name of host for use by Kerberos). 519 */ 520 #ifdef KERBEROS 521 char *p; 522 523 if (( *krbinstancep = nsldapi_host_connected_to( sb )) != NULL 524 && ( p = strchr( *krbinstancep, '.' )) != NULL ) { 525 *p = '\0'; 526 } 527 #else /* KERBEROS */ 528 *krbinstancep = NULL; 529 #endif /* KERBEROS */ 530 531 return( 0 ); 532 } 533 534 535 /* 536 * Returns a socket number if successful and -1 if an error occurs. 537 */ 538 static int 539 nsldapi_try_each_host( LDAP *ld, const char *hostlist, 540 int defport, int secure, NSLDAPI_SOCKET_FN *socketfn, 541 NSLDAPI_IOCTL_FN *ioctlfn, NSLDAPI_CONNECT_WITH_TO_FN *connectwithtofn, 542 NSLDAPI_CONNECT_FN *connectfn, NSLDAPI_CLOSE_FN *closefn ) 543 { 544 int rc, i, s, err, connected, use_hp; 545 int parse_err, port; 546 struct sockaddr_in sin; 547 nsldapi_in_addr_t address; 548 char **addrlist, *ldhpbuf, *ldhpbuf_allocd; 549 char *host; 550 LDAPHostEnt ldhent, *ldhp; 551 struct hostent *hp; 552 struct ldap_x_hostlist_status *status; 553 #ifdef GETHOSTBYNAME_BUF_T 554 GETHOSTBYNAME_BUF_T hbuf; 555 struct hostent hent; 556 #endif /* GETHOSTBYNAME_BUF_T */ 557 558 connected = 0; 559 parse_err = ldap_x_hostlist_first( hostlist, defport, &host, &port, 560 &status ); 561 while ( !connected && LDAP_SUCCESS == parse_err && host != NULL ) { 562 ldhpbuf_allocd = NULL; 563 ldhp = NULL; 564 hp = NULL; 565 s = 0; 566 use_hp = 0; 567 addrlist = NULL; 568 569 570 if (( address = inet_addr( host )) == -1 ) { 571 if ( ld->ld_dns_gethostbyname_fn == NULL ) { 572 if (( hp = GETHOSTBYNAME( host, &hent, hbuf, 573 sizeof(hbuf), &err )) != NULL ) { 574 addrlist = hp->h_addr_list; 575 } 576 } else { 577 /* 578 * DNS callback installed... use it. 579 */ 580 #ifdef GETHOSTBYNAME_buf_t 581 /* avoid allocation by using hbuf if large enough */ 582 if ( sizeof( hbuf ) < ld->ld_dns_bufsize ) { 583 ldhpbuf = ldhpbuf_allocd 584 = NSLDAPI_MALLOC( ld->ld_dns_bufsize ); 585 } else { 586 ldhpbuf = (char *)hbuf; 587 } 588 #else /* GETHOSTBYNAME_buf_t */ 589 ldhpbuf = ldhpbuf_allocd = NSLDAPI_MALLOC( 590 ld->ld_dns_bufsize ); 591 #endif /* else GETHOSTBYNAME_buf_t */ 592 593 if ( ldhpbuf == NULL ) { 594 LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, 595 NULL, NULL ); 596 ldap_memfree( host ); 597 ldap_x_hostlist_statusfree( status ); 598 return( -1 ); 599 } 600 601 if (( ldhp = ld->ld_dns_gethostbyname_fn( host, 602 &ldhent, ldhpbuf, ld->ld_dns_bufsize, &err, 603 ld->ld_dns_extradata )) != NULL ) { 604 addrlist = ldhp->ldaphe_addr_list; 605 } 606 } 607 608 if ( addrlist == NULL ) { 609 LDAP_SET_LDERRNO( ld, LDAP_CONNECT_ERROR, NULL, NULL ); 610 LDAP_SET_ERRNO( ld, EHOSTUNREACH ); /* close enough */ 611 if ( ldhpbuf_allocd != NULL ) { 612 NSLDAPI_FREE( ldhpbuf_allocd ); 613 } 614 ldap_memfree( host ); 615 ldap_x_hostlist_statusfree( status ); 616 return( -1 ); 617 } 618 use_hp = 1; 619 } 620 621 rc = -1; 622 for ( i = 0; !use_hp || ( addrlist[ i ] != 0 ); i++ ) { 623 if ( -1 == ( s = (*socketfn)( ld, secure, AF_INET, 624 SOCK_STREAM, 0 ))) { 625 if ( ldhpbuf_allocd != NULL ) { 626 NSLDAPI_FREE( ldhpbuf_allocd ); 627 } 628 ldap_memfree( host ); 629 ldap_x_hostlist_statusfree( status ); 630 return( -1 ); 631 } 632 633 if ( ld->ld_options & LDAP_BITOPT_ASYNC ) { 634 int iostatus = 1; 635 636 err = (*ioctlfn)( s, FIONBIO, &iostatus ); 637 if ( err == -1 ) { 638 LDAPDebug( LDAP_DEBUG_ANY, 639 "FIONBIO ioctl failed on %d\n", 640 s, 0, 0 ); 641 } 642 } 643 644 (void)memset( (char *)&sin, 0, sizeof( struct sockaddr_in )); 645 sin.sin_family = AF_INET; 646 sin.sin_port = htons( (unsigned short)port ); 647 648 SAFEMEMCPY( (char *) &sin.sin_addr.s_addr, 649 ( use_hp ? (char *) addrlist[ i ] : 650 (char *) &address ), sizeof( sin.sin_addr.s_addr) ); 651 652 { 653 #ifdef LDAP_CONNECT_MUST_NOT_BE_INTERRUPTED 654 /* 655 * Block all of the signals that might interrupt connect() since there 656 * is an OS bug that causes connect() to fail if it is restarted. Look in 657 * ns/netsite/ldap/include/portable.h for the definition of 658 * LDAP_CONNECT_MUST_NOT_BE_INTERRUPTED 659 */ 660 sigset_t ints_off, oldset; 661 662 sigemptyset( &ints_off ); 663 sigaddset( &ints_off, SIGALRM ); 664 sigaddset( &ints_off, SIGIO ); 665 sigaddset( &ints_off, SIGCLD ); 666 667 sigprocmask( SIG_BLOCK, &ints_off, &oldset ); 668 #endif /* LDAP_CONNECT_MUST_NOT_BE_INTERRUPTED */ 669 670 if ( NULL != connectwithtofn ) { 671 err = (*connectwithtofn)(s, 672 (struct sockaddr *)&sin, 673 sizeof(struct sockaddr_in), 674 ld); 675 } else { 676 err = (*connectfn)(s, 677 (struct sockaddr *)&sin, 678 sizeof(struct sockaddr_in)); 679 } 680 #ifdef LDAP_CONNECT_MUST_NOT_BE_INTERRUPTED 681 /* 682 * restore original signal mask 683 */ 684 sigprocmask( SIG_SETMASK, &oldset, 0 ); 685 #endif /* LDAP_CONNECT_MUST_NOT_BE_INTERRUPTED */ 686 687 } 688 if ( err >= 0 ) { 689 connected = 1; 690 rc = 0; 691 break; 692 } else { 693 if ( ld->ld_options & LDAP_BITOPT_ASYNC) { 694 #ifdef _WINDOWS 695 if (err == -1 && WSAGetLastError() == WSAEWOULDBLOCK) 696 LDAP_SET_ERRNO( ld, EWOULDBLOCK ); 697 #endif /* _WINDOWS */ 698 err = LDAP_GET_ERRNO( ld ); 699 if ( NSLDAPI_ERRNO_IO_INPROGRESS( err )) { 700 LDAPDebug( LDAP_DEBUG_TRACE, "connect would block...\n", 701 0, 0, 0 ); 702 rc = -2; 703 break; 704 } 705 } 706 707 #ifdef LDAP_DEBUG 708 if ( ldap_debug & LDAP_DEBUG_TRACE ) { 709 perror( (char *)inet_ntoa( sin.sin_addr )); 710 } 711 #endif 712 (*closefn)( s ); 713 if ( !use_hp ) { 714 break; 715 } 716 } 717 } 718 719 ldap_memfree( host ); 720 parse_err = ldap_x_hostlist_next( &host, &port, status ); 721 } 722 723 if ( ldhpbuf_allocd != NULL ) { 724 NSLDAPI_FREE( ldhpbuf_allocd ); 725 } 726 ldap_memfree( host ); 727 ldap_x_hostlist_statusfree( status ); 728 729 if ( connected ) { 730 LDAPDebug( LDAP_DEBUG_TRACE, "sd %d connected to: %s\n", 731 s, inet_ntoa( sin.sin_addr ), 0 ); 732 } 733 734 return( rc == 0 ? s : -1 ); 735 } 736 737 738 void 739 nsldapi_close_connection( LDAP *ld, Sockbuf *sb ) 740 { 741 if ( ld->ld_extclose_fn == NULL ) { 742 nsldapi_os_closesocket( sb->sb_sd ); 743 } else { 744 ld->ld_extclose_fn( sb->sb_sd, 745 sb->sb_ext_io_fns.lbextiofn_socket_arg ); 746 } 747 } 748 749 750 #ifdef KERBEROS 751 char * 752 nsldapi_host_connected_to( Sockbuf *sb ) 753 { 754 struct hostent *hp; 755 char *p; 756 int len; 757 struct sockaddr_in sin; 758 759 (void)memset( (char *)&sin, 0, sizeof( struct sockaddr_in )); 760 len = sizeof( sin ); 761 if ( getpeername( sb->sb_sd, (struct sockaddr *)&sin, &len ) == -1 ) { 762 return( NULL ); 763 } 764 765 /* 766 * do a reverse lookup on the addr to get the official hostname. 767 * this is necessary for kerberos to work right, since the official 768 * hostname is used as the kerberos instance. 769 */ 770 #error XXXmcs: need to use DNS callbacks here 771 if (( hp = gethostbyaddr((char *) &sin.sin_addr, 772 sizeof( sin.sin_addr ), AF_INET)) != NULL ) { 773 if ( hp->h_name != NULL ) { 774 return( nsldapi_strdup( hp->h_name )); 775 } 776 } 777 778 return( NULL ); 779 } 780 #endif /* KERBEROS */ 781 782 783 /* 784 * Returns 0 if all goes well and -1 if an error occurs (error code set in ld) 785 * Also allocates initializes ld->ld_iostatus if needed.. 786 */ 787 int 788 nsldapi_iostatus_interest_write( LDAP *ld, Sockbuf *sb ) 789 { 790 NSLDAPIIOStatus *iosp; 791 792 LDAP_MUTEX_LOCK( ld, LDAP_IOSTATUS_LOCK ); 793 794 if ( ld->ld_iostatus == NULL 795 && nsldapi_iostatus_init_nolock( ld ) < 0 ) { 796 LDAP_MUTEX_UNLOCK( ld, LDAP_IOSTATUS_LOCK ); 797 return( -1 ); 798 } 799 800 iosp = ld->ld_iostatus; 801 802 if ( iosp->ios_type == NSLDAPI_IOSTATUS_TYPE_OSNATIVE ) { 803 #ifdef NSLDAPI_HAVE_POLL 804 if ( nsldapi_add_to_os_pollfds( sb->sb_sd, 805 &iosp->ios_status.ios_osinfo, POLLOUT )) { 806 ++iosp->ios_write_count; 807 } 808 #else /* NSLDAPI_HAVE_POLL */ 809 if ( !FD_ISSET( sb->sb_sd, 810 &iosp->ios_status.ios_osinfo.ossi_writefds )) { 811 FD_SET( sb->sb_sd, 812 &iosp->ios_status.ios_osinfo.ossi_writefds ); 813 ++iosp->ios_write_count; 814 } 815 #endif /* else NSLDAPI_HAVE_POLL */ 816 817 } else if ( iosp->ios_type == NSLDAPI_IOSTATUS_TYPE_CALLBACK ) { 818 if ( nsldapi_add_to_cb_pollfds( sb, 819 &iosp->ios_status.ios_cbinfo, LDAP_X_POLLOUT )) { 820 ++iosp->ios_write_count; 821 } 822 823 } else { 824 LDAPDebug( LDAP_DEBUG_ANY, 825 "nsldapi_iostatus_interest_write: unknown I/O type %d\n", 826 iosp->ios_type, 0, 0 ); 827 } 828 829 LDAP_MUTEX_UNLOCK( ld, LDAP_IOSTATUS_LOCK ); 830 831 return( 0 ); 832 } 833 834 835 /* 836 * Returns 0 if all goes well and -1 if an error occurs (error code set in ld) 837 * Also allocates initializes ld->ld_iostatus if needed.. 838 */ 839 int 840 nsldapi_iostatus_interest_read( LDAP *ld, Sockbuf *sb ) 841 { 842 NSLDAPIIOStatus *iosp; 843 844 LDAP_MUTEX_LOCK( ld, LDAP_IOSTATUS_LOCK ); 845 846 if ( ld->ld_iostatus == NULL 847 && nsldapi_iostatus_init_nolock( ld ) < 0 ) { 848 LDAP_MUTEX_UNLOCK( ld, LDAP_IOSTATUS_LOCK ); 849 return( -1 ); 850 } 851 852 iosp = ld->ld_iostatus; 853 854 if ( iosp->ios_type == NSLDAPI_IOSTATUS_TYPE_OSNATIVE ) { 855 #ifdef NSLDAPI_HAVE_POLL 856 if ( nsldapi_add_to_os_pollfds( sb->sb_sd, 857 &iosp->ios_status.ios_osinfo, POLLIN )) { 858 ++iosp->ios_read_count; 859 } 860 #else /* NSLDAPI_HAVE_POLL */ 861 if ( !FD_ISSET( sb->sb_sd, 862 &iosp->ios_status.ios_osinfo.ossi_readfds )) { 863 FD_SET( sb->sb_sd, 864 &iosp->ios_status.ios_osinfo.ossi_readfds ); 865 ++iosp->ios_read_count; 866 } 867 #endif /* else NSLDAPI_HAVE_POLL */ 868 869 } else if ( iosp->ios_type == NSLDAPI_IOSTATUS_TYPE_CALLBACK ) { 870 if ( nsldapi_add_to_cb_pollfds( sb, 871 &iosp->ios_status.ios_cbinfo, LDAP_X_POLLIN )) { 872 ++iosp->ios_read_count; 873 } 874 } else { 875 LDAPDebug( LDAP_DEBUG_ANY, 876 "nsldapi_iostatus_interest_read: unknown I/O type %d\n", 877 iosp->ios_type, 0, 0 ); 878 } 879 880 LDAP_MUTEX_UNLOCK( ld, LDAP_IOSTATUS_LOCK ); 881 882 return( 0 ); 883 } 884 885 886 /* 887 * Returns 0 if all goes well and -1 if an error occurs (error code set in ld) 888 * Also allocates initializes ld->ld_iostatus if needed.. 889 */ 890 int 891 nsldapi_iostatus_interest_clear( LDAP *ld, Sockbuf *sb ) 892 { 893 NSLDAPIIOStatus *iosp; 894 895 LDAP_MUTEX_LOCK( ld, LDAP_IOSTATUS_LOCK ); 896 897 if ( ld->ld_iostatus == NULL 898 && nsldapi_iostatus_init_nolock( ld ) < 0 ) { 899 LDAP_MUTEX_UNLOCK( ld, LDAP_IOSTATUS_LOCK ); 900 return( -1 ); 901 } 902 903 iosp = ld->ld_iostatus; 904 905 if ( iosp->ios_type == NSLDAPI_IOSTATUS_TYPE_OSNATIVE ) { 906 #ifdef NSLDAPI_HAVE_POLL 907 if ( nsldapi_clear_from_os_pollfds( sb->sb_sd, 908 &iosp->ios_status.ios_osinfo, POLLOUT )) { 909 --iosp->ios_write_count; 910 } 911 if ( nsldapi_clear_from_os_pollfds( sb->sb_sd, 912 &iosp->ios_status.ios_osinfo, POLLIN )) { 913 --iosp->ios_read_count; 914 } 915 #else /* NSLDAPI_HAVE_POLL */ 916 if ( FD_ISSET( sb->sb_sd, 917 &iosp->ios_status.ios_osinfo.ossi_writefds )) { 918 FD_CLR( sb->sb_sd, 919 &iosp->ios_status.ios_osinfo.ossi_writefds ); 920 --iosp->ios_write_count; 921 } 922 if ( FD_ISSET( sb->sb_sd, 923 &iosp->ios_status.ios_osinfo.ossi_readfds )) { 924 FD_CLR( sb->sb_sd, 925 &iosp->ios_status.ios_osinfo.ossi_readfds ); 926 --iosp->ios_read_count; 927 } 928 #endif /* else NSLDAPI_HAVE_POLL */ 929 930 } else if ( iosp->ios_type == NSLDAPI_IOSTATUS_TYPE_CALLBACK ) { 931 if ( nsldapi_clear_from_cb_pollfds( sb, 932 &iosp->ios_status.ios_cbinfo, LDAP_X_POLLOUT )) { 933 --iosp->ios_write_count; 934 } 935 if ( nsldapi_clear_from_cb_pollfds( sb, 936 &iosp->ios_status.ios_cbinfo, LDAP_X_POLLIN )) { 937 --iosp->ios_read_count; 938 } 939 } else { 940 LDAPDebug( LDAP_DEBUG_ANY, 941 "nsldapi_iostatus_interest_clear: unknown I/O type %d\n", 942 iosp->ios_type, 0, 0 ); 943 } 944 945 LDAP_MUTEX_UNLOCK( ld, LDAP_IOSTATUS_LOCK ); 946 947 return( 0 ); 948 } 949 950 951 /* 952 * Return a non-zero value if sb is ready for write. 953 */ 954 int 955 nsldapi_iostatus_is_write_ready( LDAP *ld, Sockbuf *sb ) 956 { 957 int rc; 958 NSLDAPIIOStatus *iosp; 959 960 LDAP_MUTEX_LOCK( ld, LDAP_IOSTATUS_LOCK ); 961 iosp = ld->ld_iostatus; 962 963 if ( iosp->ios_type == NSLDAPI_IOSTATUS_TYPE_OSNATIVE ) { 964 #ifdef NSLDAPI_HAVE_POLL 965 /* 966 * if we are using poll() we do something a little tricky: if 967 * any bits in the socket's returned events field other than 968 * POLLIN (ready for read) are set, we return true. This 969 * is done so we notice when a server closes a connection 970 * or when another error occurs. The actual error will be 971 * noticed later when we call write() or send(). 972 */ 973 rc = nsldapi_find_in_os_pollfds( sb->sb_sd, 974 &iosp->ios_status.ios_osinfo, ~POLLIN ); 975 976 #else /* NSLDAPI_HAVE_POLL */ 977 rc = FD_ISSET( sb->sb_sd, 978 &iosp->ios_status.ios_osinfo.ossi_use_writefds ); 979 #endif /* else NSLDAPI_HAVE_POLL */ 980 981 } else if ( iosp->ios_type == NSLDAPI_IOSTATUS_TYPE_CALLBACK ) { 982 rc = nsldapi_find_in_cb_pollfds( sb, 983 &iosp->ios_status.ios_cbinfo, ~LDAP_X_POLLIN ); 984 985 } else { 986 LDAPDebug( LDAP_DEBUG_ANY, 987 "nsldapi_iostatus_is_write_ready: unknown I/O type %d\n", 988 iosp->ios_type, 0, 0 ); 989 rc = 0; 990 } 991 992 LDAP_MUTEX_UNLOCK( ld, LDAP_IOSTATUS_LOCK ); 993 return( rc ); 994 } 995 996 997 /* 998 * Return a non-zero value if sb is ready for read. 999 */ 1000 int 1001 nsldapi_iostatus_is_read_ready( LDAP *ld, Sockbuf *sb ) 1002 { 1003 int rc; 1004 NSLDAPIIOStatus *iosp; 1005 1006 LDAP_MUTEX_LOCK( ld, LDAP_IOSTATUS_LOCK ); 1007 iosp = ld->ld_iostatus; 1008 1009 if ( iosp->ios_type == NSLDAPI_IOSTATUS_TYPE_OSNATIVE ) { 1010 #ifdef NSLDAPI_HAVE_POLL 1011 /* 1012 * if we are using poll() we do something a little tricky: if 1013 * any bits in the socket's returned events field other than 1014 * POLLOUT (ready for write) are set, we return true. This 1015 * is done so we notice when a server closes a connection 1016 * or when another error occurs. The actual error will be 1017 * noticed later when we call read() or recv(). 1018 */ 1019 rc = nsldapi_find_in_os_pollfds( sb->sb_sd, 1020 &iosp->ios_status.ios_osinfo, ~POLLOUT ); 1021 1022 #else /* NSLDAPI_HAVE_POLL */ 1023 rc = FD_ISSET( sb->sb_sd, 1024 &iosp->ios_status.ios_osinfo.ossi_use_readfds ); 1025 #endif /* else NSLDAPI_HAVE_POLL */ 1026 1027 } else if ( iosp->ios_type == NSLDAPI_IOSTATUS_TYPE_CALLBACK ) { 1028 rc = nsldapi_find_in_cb_pollfds( sb, 1029 &iosp->ios_status.ios_cbinfo, ~LDAP_X_POLLOUT ); 1030 1031 } else { 1032 LDAPDebug( LDAP_DEBUG_ANY, 1033 "nsldapi_iostatus_is_read_ready: unknown I/O type %d\n", 1034 iosp->ios_type, 0, 0 ); 1035 rc = 0; 1036 } 1037 1038 LDAP_MUTEX_UNLOCK( ld, LDAP_IOSTATUS_LOCK ); 1039 return( rc ); 1040 } 1041 1042 1043 /* 1044 * Allocated and initialize ld->ld_iostatus if not already done. 1045 * Should be called with LDAP_IOSTATUS_LOCK locked. 1046 * Returns 0 if all goes well and -1 if not (sets error in ld) 1047 */ 1048 static int 1049 nsldapi_iostatus_init_nolock( LDAP *ld ) 1050 { 1051 NSLDAPIIOStatus *iosp; 1052 1053 if ( ld->ld_iostatus != NULL ) { 1054 return( 0 ); 1055 } 1056 1057 if (( iosp = (NSLDAPIIOStatus *)NSLDAPI_CALLOC( 1, 1058 sizeof( NSLDAPIIOStatus ))) == NULL ) { 1059 LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, NULL ); 1060 return( -1 ); 1061 } 1062 1063 if ( ld->ld_extpoll_fn == NULL ) { 1064 iosp->ios_type = NSLDAPI_IOSTATUS_TYPE_OSNATIVE; 1065 #ifndef NSLDAPI_HAVE_POLL 1066 FD_ZERO( &iosp->ios_status.ios_osinfo.ossi_readfds ); 1067 FD_ZERO( &iosp->ios_status.ios_osinfo.ossi_writefds ); 1068 #endif /* !NSLDAPI_HAVE_POLL */ 1069 1070 } else { 1071 iosp->ios_type = NSLDAPI_IOSTATUS_TYPE_CALLBACK; 1072 } 1073 1074 ld->ld_iostatus = iosp; 1075 return( 0 ); 1076 } 1077 1078 1079 void 1080 nsldapi_iostatus_free( LDAP *ld ) 1081 { 1082 if ( ld == NULL ) { 1083 return; 1084 } 1085 1086 1087 /* clean up classic I/O compatibility glue */ 1088 if ( ld->ld_io_fns_ptr != NULL ) { 1089 if ( ld->ld_ext_session_arg != NULL ) { 1090 NSLDAPI_FREE( ld->ld_ext_session_arg ); 1091 } 1092 NSLDAPI_FREE( ld->ld_io_fns_ptr ); 1093 } 1094 1095 /* clean up I/O status tracking info. */ 1096 if ( ld->ld_iostatus != NULL ) { 1097 NSLDAPIIOStatus *iosp = ld->ld_iostatus; 1098 1099 if ( iosp->ios_type == NSLDAPI_IOSTATUS_TYPE_OSNATIVE ) { 1100 #ifdef NSLDAPI_HAVE_POLL 1101 if ( iosp->ios_status.ios_osinfo.ossi_pollfds 1102 != NULL ) { 1103 NSLDAPI_FREE( 1104 iosp->ios_status.ios_osinfo.ossi_pollfds ); 1105 } 1106 #endif /* NSLDAPI_HAVE_POLL */ 1107 1108 } else if ( iosp->ios_type == NSLDAPI_IOSTATUS_TYPE_CALLBACK ) { 1109 if ( iosp->ios_status.ios_cbinfo.cbsi_pollfds 1110 != NULL ) { 1111 NSLDAPI_FREE( 1112 iosp->ios_status.ios_cbinfo.cbsi_pollfds ); 1113 } 1114 } else { 1115 LDAPDebug( LDAP_DEBUG_ANY, 1116 "nsldapi_iostatus_free: unknown I/O type %d\n", 1117 iosp->ios_type, 0, 0 ); 1118 } 1119 1120 NSLDAPI_FREE( iosp ); 1121 } 1122 } 1123 1124 1125 static int 1126 nsldapi_get_select_table_size( void ) 1127 { 1128 static int tblsize = 0; /* static */ 1129 1130 if ( tblsize == 0 ) { 1131 #if defined(_WINDOWS) || defined(XP_OS2) 1132 tblsize = FOPEN_MAX; /* ANSI spec. */ 1133 #else 1134 #ifdef USE_SYSCONF 1135 tblsize = sysconf( _SC_OPEN_MAX ); 1136 #else /* USE_SYSCONF */ 1137 tblsize = getdtablesize(); 1138 #endif /* else USE_SYSCONF */ 1139 #endif /* else _WINDOWS */ 1140 1141 if ( tblsize >= FD_SETSIZE ) { 1142 /* 1143 * clamp value so we don't overrun the fd_set structure 1144 */ 1145 tblsize = FD_SETSIZE - 1; 1146 } 1147 } 1148 1149 return( tblsize ); 1150 } 1151 1152 static int 1153 nsldapi_tv2ms( struct timeval *tv ) 1154 { 1155 if ( tv == NULL ) { 1156 return( -1 ); /* infinite timout for poll() */ 1157 } 1158 1159 return( tv->tv_sec * 1000 + tv->tv_usec / 1000 ); 1160 } 1161 1162 1163 int 1164 nsldapi_iostatus_poll( LDAP *ld, struct timeval *timeout ) 1165 { 1166 int rc; 1167 NSLDAPIIOStatus *iosp; 1168 1169 LDAPDebug( LDAP_DEBUG_TRACE, "nsldapi_iostatus_poll\n", 0, 0, 0 ); 1170 1171 LDAP_MUTEX_LOCK( ld, LDAP_IOSTATUS_LOCK ); 1172 iosp = ld->ld_iostatus; 1173 1174 if ( iosp == NULL || 1175 ( iosp->ios_read_count <= 0 && iosp->ios_read_count <= 0 )) { 1176 rc = 0; /* simulate a timeout */ 1177 1178 } else if ( iosp->ios_type == NSLDAPI_IOSTATUS_TYPE_OSNATIVE ) { 1179 #ifdef NSLDAPI_HAVE_POLL 1180 1181 rc = NSLDAPI_POLL( iosp->ios_status.ios_osinfo.ossi_pollfds, 1182 iosp->ios_status.ios_osinfo.ossi_pollfds_size, 1183 nsldapi_tv2ms( timeout )); 1184 1185 #else /* NSLDAPI_HAVE_POLL */ 1186 1187 /* two (potentially large) struct copies */ 1188 iosp->ios_status.ios_osinfo.ossi_use_readfds 1189 = iosp->ios_status.ios_osinfo.ossi_readfds; 1190 iosp->ios_status.ios_osinfo.ossi_use_writefds 1191 = iosp->ios_status.ios_osinfo.ossi_writefds; 1192 1193 #ifdef HPUX9 1194 rc = NSLDAPI_SELECT( nsldapi_get_select_table_size(), 1195 (int *)&iosp->ios_status.ios_osinfo.ossi_use_readfds 1196 (int *)&iosp->ios_status.ios_osinfo.ossi_use_writefds, 1197 NULL, timeout ); 1198 #else 1199 rc = NSLDAPI_SELECT( nsldapi_get_select_table_size(), 1200 &iosp->ios_status.ios_osinfo.ossi_use_readfds, 1201 &iosp->ios_status.ios_osinfo.ossi_use_writefds, 1202 NULL, timeout ); 1203 #endif /* else HPUX9 */ 1204 #endif /* else NSLDAPI_HAVE_POLL */ 1205 1206 } else if ( iosp->ios_type == NSLDAPI_IOSTATUS_TYPE_CALLBACK ) { 1207 /* 1208 * We always pass the session extended I/O argument to 1209 * the extended poll() callback. 1210 */ 1211 rc = ld->ld_extpoll_fn( 1212 iosp->ios_status.ios_cbinfo.cbsi_pollfds, 1213 iosp->ios_status.ios_cbinfo.cbsi_pollfds_size, 1214 nsldapi_tv2ms( timeout ), ld->ld_ext_session_arg ); 1215 1216 } else { 1217 LDAPDebug( LDAP_DEBUG_ANY, 1218 "nsldapi_iostatus_poll: unknown I/O type %d\n", 1219 iosp->ios_type, 0, 0 ); 1220 rc = 0; /* simulate a timeout (what else to do?) */ 1221 } 1222 1223 LDAP_MUTEX_UNLOCK( ld, LDAP_IOSTATUS_LOCK ); 1224 return( rc ); 1225 } 1226 1227 1228 #ifdef NSLDAPI_HAVE_POLL 1229 /* 1230 * returns 1 if "fd" was added to pollfds. 1231 * returns 1 if some of the bits in "events" were added to pollfds. 1232 * returns 0 if no changes were made. 1233 */ 1234 static int 1235 nsldapi_add_to_os_pollfds( int fd, struct nsldapi_os_statusinfo *pip, 1236 short events ) 1237 { 1238 int i, openslot; 1239 1240 /* first we check to see if "fd" is already in our pollfds */ 1241 openslot = -1; 1242 for ( i = 0; i < pip->ossi_pollfds_size; ++i ) { 1243 if ( pip->ossi_pollfds[ i ].fd == fd ) { 1244 if (( pip->ossi_pollfds[ i ].events & events ) 1245 != events ) { 1246 pip->ossi_pollfds[ i ].events |= events; 1247 return( 1 ); 1248 } else { 1249 return( 0 ); 1250 } 1251 } 1252 if ( pip->ossi_pollfds[ i ].fd == -1 && openslot == -1 ) { 1253 openslot = i; /* remember for later */ 1254 } 1255 } 1256 1257 /* 1258 * "fd" is not currently being poll'd on -- add to array. 1259 * if we need to expand the pollfds array, we do it in increments of 1260 * NSLDAPI_POLL_ARRAY_GROWTH (#define near the top of this file). 1261 */ 1262 if ( openslot == -1 ) { 1263 struct pollfd *newpollfds; 1264 1265 if ( pip->ossi_pollfds_size == 0 ) { 1266 newpollfds = (struct pollfd *)NSLDAPI_MALLOC( 1267 NSLDAPI_POLL_ARRAY_GROWTH 1268 * sizeof( struct pollfd )); 1269 } else { 1270 newpollfds = (struct pollfd *)NSLDAPI_REALLOC( 1271 pip->ossi_pollfds, (NSLDAPI_POLL_ARRAY_GROWTH 1272 + pip->ossi_pollfds_size) 1273 * sizeof( struct pollfd )); 1274 } 1275 if ( newpollfds == NULL ) { /* XXXmcs: no way to return err! */ 1276 return( 0 ); 1277 } 1278 pip->ossi_pollfds = newpollfds; 1279 openslot = pip->ossi_pollfds_size; 1280 pip->ossi_pollfds_size += NSLDAPI_POLL_ARRAY_GROWTH; 1281 for ( i = openslot + 1; i < pip->ossi_pollfds_size; ++i ) { 1282 pip->ossi_pollfds[ i ].fd = -1; 1283 pip->ossi_pollfds[ i ].events = 1284 pip->ossi_pollfds[ i ].revents = 0; 1285 } 1286 } 1287 pip->ossi_pollfds[ openslot ].fd = fd; 1288 pip->ossi_pollfds[ openslot ].events = events; 1289 pip->ossi_pollfds[ openslot ].revents = 0; 1290 return( 1 ); 1291 } 1292 1293 1294 /* 1295 * returns 1 if any "events" from "fd" were removed from pollfds 1296 * returns 0 of "fd" wasn't in pollfds or if events did not overlap. 1297 */ 1298 static int 1299 nsldapi_clear_from_os_pollfds( int fd, struct nsldapi_os_statusinfo *pip, 1300 short events ) 1301 { 1302 int i; 1303 1304 for ( i = 0; i < pip->ossi_pollfds_size; ++i ) { 1305 if ( pip->ossi_pollfds[i].fd == fd ) { 1306 if (( pip->ossi_pollfds[ i ].events & events ) != 0 ) { 1307 pip->ossi_pollfds[ i ].events &= ~events; 1308 if ( pip->ossi_pollfds[ i ].events == 0 ) { 1309 pip->ossi_pollfds[i].fd = -1; 1310 } 1311 return( 1 ); /* events overlap */ 1312 } else { 1313 return( 0 ); /* events do not overlap */ 1314 } 1315 } 1316 } 1317 1318 return( 0 ); /* "fd" was not found */ 1319 } 1320 1321 1322 /* 1323 * returns 1 if any "revents" from "fd" were set in pollfds revents field. 1324 * returns 0 if not. 1325 */ 1326 static int 1327 nsldapi_find_in_os_pollfds( int fd, struct nsldapi_os_statusinfo *pip, 1328 short revents ) 1329 { 1330 int i; 1331 1332 for ( i = 0; i < pip->ossi_pollfds_size; ++i ) { 1333 if ( pip->ossi_pollfds[i].fd == fd ) { 1334 if (( pip->ossi_pollfds[ i ].revents & revents ) != 0 ) { 1335 return( 1 ); /* revents overlap */ 1336 } else { 1337 return( 0 ); /* revents do not overlap */ 1338 } 1339 } 1340 } 1341 1342 return( 0 ); /* "fd" was not found */ 1343 } 1344 #endif /* NSLDAPI_HAVE_POLL */ 1345 1346 1347 /* 1348 * returns 1 if "sb" was added to pollfds. 1349 * returns 1 if some of the bits in "events" were added to pollfds. 1350 * returns 0 if no changes were made. 1351 */ 1352 static int 1353 nsldapi_add_to_cb_pollfds( Sockbuf *sb, struct nsldapi_cb_statusinfo *pip, 1354 short events ) 1355 { 1356 int i, openslot; 1357 1358 /* first we check to see if "sb" is already in our pollfds */ 1359 openslot = -1; 1360 for ( i = 0; i < pip->cbsi_pollfds_size; ++i ) { 1361 if ( NSLDAPI_CB_POLL_MATCH( sb, pip->cbsi_pollfds[ i ] )) { 1362 if (( pip->cbsi_pollfds[ i ].lpoll_events & events ) 1363 != events ) { 1364 pip->cbsi_pollfds[ i ].lpoll_events |= events; 1365 return( 1 ); 1366 } else { 1367 return( 0 ); 1368 } 1369 } 1370 if ( pip->cbsi_pollfds[ i ].lpoll_fd == -1 && openslot == -1 ) { 1371 openslot = i; /* remember for later */ 1372 } 1373 } 1374 1375 /* 1376 * "sb" is not currently being poll'd on -- add to array. 1377 * if we need to expand the pollfds array, we do it in increments of 1378 * NSLDAPI_POLL_ARRAY_GROWTH (#define near the top of this file). 1379 */ 1380 if ( openslot == -1 ) { 1381 LDAP_X_PollFD *newpollfds; 1382 1383 if ( pip->cbsi_pollfds_size == 0 ) { 1384 newpollfds = (LDAP_X_PollFD *)NSLDAPI_MALLOC( 1385 NSLDAPI_POLL_ARRAY_GROWTH 1386 * sizeof( LDAP_X_PollFD )); 1387 } else { 1388 newpollfds = (LDAP_X_PollFD *)NSLDAPI_REALLOC( 1389 pip->cbsi_pollfds, (NSLDAPI_POLL_ARRAY_GROWTH 1390 + pip->cbsi_pollfds_size) 1391 * sizeof( LDAP_X_PollFD )); 1392 } 1393 if ( newpollfds == NULL ) { /* XXXmcs: no way to return err! */ 1394 return( 0 ); 1395 } 1396 pip->cbsi_pollfds = newpollfds; 1397 openslot = pip->cbsi_pollfds_size; 1398 pip->cbsi_pollfds_size += NSLDAPI_POLL_ARRAY_GROWTH; 1399 for ( i = openslot + 1; i < pip->cbsi_pollfds_size; ++i ) { 1400 pip->cbsi_pollfds[ i ].lpoll_fd = -1; 1401 pip->cbsi_pollfds[ i ].lpoll_socketarg = NULL; 1402 pip->cbsi_pollfds[ i ].lpoll_events = 1403 pip->cbsi_pollfds[ i ].lpoll_revents = 0; 1404 } 1405 } 1406 pip->cbsi_pollfds[ openslot ].lpoll_fd = sb->sb_sd; 1407 pip->cbsi_pollfds[ openslot ].lpoll_socketarg = 1408 sb->sb_ext_io_fns.lbextiofn_socket_arg; 1409 pip->cbsi_pollfds[ openslot ].lpoll_events = events; 1410 pip->cbsi_pollfds[ openslot ].lpoll_revents = 0; 1411 return( 1 ); 1412 } 1413 1414 1415 /* 1416 * returns 1 if any "events" from "sb" were removed from pollfds 1417 * returns 0 of "sb" wasn't in pollfds or if events did not overlap. 1418 */ 1419 static int 1420 nsldapi_clear_from_cb_pollfds( Sockbuf *sb, 1421 struct nsldapi_cb_statusinfo *pip, short events ) 1422 { 1423 int i; 1424 1425 for ( i = 0; i < pip->cbsi_pollfds_size; ++i ) { 1426 if ( NSLDAPI_CB_POLL_MATCH( sb, pip->cbsi_pollfds[ i ] )) { 1427 if (( pip->cbsi_pollfds[ i ].lpoll_events 1428 & events ) != 0 ) { 1429 pip->cbsi_pollfds[ i ].lpoll_events &= ~events; 1430 if ( pip->cbsi_pollfds[ i ].lpoll_events 1431 == 0 ) { 1432 pip->cbsi_pollfds[i].lpoll_fd = -1; 1433 } 1434 return( 1 ); /* events overlap */ 1435 } else { 1436 return( 0 ); /* events do not overlap */ 1437 } 1438 } 1439 } 1440 1441 return( 0 ); /* "sb" was not found */ 1442 } 1443 1444 1445 /* 1446 * returns 1 if any "revents" from "sb" were set in pollfds revents field. 1447 * returns 0 if not. 1448 */ 1449 static int 1450 nsldapi_find_in_cb_pollfds( Sockbuf *sb, struct nsldapi_cb_statusinfo *pip, 1451 short revents ) 1452 { 1453 int i; 1454 1455 for ( i = 0; i < pip->cbsi_pollfds_size; ++i ) { 1456 if ( NSLDAPI_CB_POLL_MATCH( sb, pip->cbsi_pollfds[ i ] )) { 1457 if (( pip->cbsi_pollfds[ i ].lpoll_revents 1458 & revents ) != 0 ) { 1459 return( 1 ); /* revents overlap */ 1460 } else { 1461 return( 0 ); /* revents do not overlap */ 1462 } 1463 } 1464 } 1465 1466 return( 0 ); /* "sb" was not found */ 1467 } 1468 1469 1470 /* 1471 * Install read and write functions into lber layer / sb 1472 */ 1473 int 1474 nsldapi_install_lber_extiofns( LDAP *ld, Sockbuf *sb ) 1475 { 1476 struct lber_x_ext_io_fns lberiofns; 1477 1478 memset( &lberiofns, 0, sizeof(struct lber_x_ext_io_fns) ); 1479 if ( NULL != sb ) { 1480 lberiofns.lbextiofn_size = LBER_X_EXTIO_FNS_SIZE; 1481 lberiofns.lbextiofn_read = ld->ld_extread_fn; 1482 lberiofns.lbextiofn_write = ld->ld_extwrite_fn; 1483 lberiofns.lbextiofn_writev = ld->ld_extwritev_fn; 1484 lberiofns.lbextiofn_socket_arg = ld->ld_ext_session_arg; 1485 1486 if ( ber_sockbuf_set_option( sb, LBER_SOCKBUF_OPT_EXT_IO_FNS, 1487 &lberiofns ) != 0 ) { 1488 return( LDAP_LOCAL_ERROR ); 1489 } 1490 } 1491 1492 return( LDAP_SUCCESS ); 1493 } 1494 1495 1496 /* 1497 ****************************************************************************** 1498 * One struct and several functions to bridge the gap between new extended 1499 * I/O functions that are installed using ldap_set_option( ..., 1500 * LDAP_OPT_EXTIO_FN_PTRS, ... ) and the original "classic" I/O functions 1501 * (installed using LDAP_OPT_IO_FN_PTRS) follow. 1502 * 1503 * Our basic strategy is to use the new extended arg to hold a pointer to a 1504 * structure that contains a pointer to the LDAP * (which contains pointers 1505 * to the old functions so we can call them) as well as a pointer to an 1506 * LBER_SOCKET to hold the socket used by the classic functions (the new 1507 * functions use a simple int for the socket). 1508 */ 1509 typedef struct nsldapi_compat_socket_info { 1510 LBER_SOCKET csi_socket; 1511 LDAP *csi_ld; 1512 } NSLDAPICompatSocketInfo; 1513 1514 static int LDAP_CALLBACK 1515 nsldapi_ext_compat_read( int s, void *buf, int len, 1516 struct lextiof_socket_private *arg ) 1517 { 1518 NSLDAPICompatSocketInfo *csip = (NSLDAPICompatSocketInfo *)arg; 1519 struct ldap_io_fns *iofns = csip->csi_ld->ld_io_fns_ptr; 1520 1521 return( iofns->liof_read( csip->csi_socket, buf, len )); 1522 } 1523 1524 1525 static int LDAP_CALLBACK 1526 nsldapi_ext_compat_write( int s, const void *buf, int len, 1527 struct lextiof_socket_private *arg ) 1528 { 1529 NSLDAPICompatSocketInfo *csip = (NSLDAPICompatSocketInfo *)arg; 1530 struct ldap_io_fns *iofns = csip->csi_ld->ld_io_fns_ptr; 1531 1532 return( iofns->liof_write( csip->csi_socket, buf, len )); 1533 } 1534 1535 1536 static int LDAP_CALLBACK 1537 nsldapi_ext_compat_poll( LDAP_X_PollFD fds[], int nfds, int timeout, 1538 struct lextiof_session_private *arg ) 1539 { 1540 NSLDAPICompatSocketInfo *csip = (NSLDAPICompatSocketInfo *)arg; 1541 struct ldap_io_fns *iofns = csip->csi_ld->ld_io_fns_ptr; 1542 fd_set readfds, writefds; 1543 int i, rc, maxfd = 0; 1544 struct timeval tv, *tvp; 1545 1546 /* 1547 * Prepare fd_sets for select() 1548 */ 1549 FD_ZERO( &readfds ); 1550 FD_ZERO( &writefds ); 1551 for ( i = 0; i < nfds; ++i ) { 1552 if ( fds[ i ].lpoll_fd < 0 ) { 1553 continue; 1554 } 1555 1556 if ( fds[ i ].lpoll_fd >= FD_SETSIZE ) { 1557 LDAP_SET_ERRNO( csip->csi_ld, EINVAL ); 1558 return( -1 ); 1559 } 1560 1561 if ( 0 != ( fds[i].lpoll_events & LDAP_X_POLLIN )) { 1562 FD_SET( fds[i].lpoll_fd, &readfds ); 1563 } 1564 1565 if ( 0 != ( fds[i].lpoll_events & LDAP_X_POLLOUT )) { 1566 FD_SET( fds[i].lpoll_fd, &writefds ); 1567 } 1568 1569 fds[i].lpoll_revents = 0; /* clear revents */ 1570 1571 if ( fds[i].lpoll_fd >= maxfd ) { 1572 maxfd = fds[i].lpoll_fd; 1573 } 1574 } 1575 1576 /* 1577 * select() using callback. 1578 */ 1579 ++maxfd; 1580 if ( timeout == -1 ) { 1581 tvp = NULL; 1582 } else { 1583 tv.tv_sec = timeout / 1000; 1584 tv.tv_usec = 1000 * ( timeout - tv.tv_sec * 1000 ); 1585 tvp = &tv; 1586 } 1587 rc = iofns->liof_select( maxfd, &readfds, &writefds, NULL, tvp ); 1588 if ( rc <= 0 ) { /* timeout or fatal error */ 1589 return( rc ); 1590 } 1591 1592 /* 1593 * Use info. in fd_sets to populate poll() revents. 1594 */ 1595 for ( i = 0; i < nfds; ++i ) { 1596 if ( fds[ i ].lpoll_fd < 0 ) { 1597 continue; 1598 } 1599 1600 if ( 0 != ( fds[i].lpoll_events & LDAP_X_POLLIN ) 1601 && FD_ISSET( fds[i].lpoll_fd, &readfds )) { 1602 fds[i].lpoll_revents |= LDAP_X_POLLIN; 1603 } 1604 1605 if ( 0 != ( fds[i].lpoll_events & LDAP_X_POLLOUT ) 1606 && FD_ISSET( fds[i].lpoll_fd, &writefds )) { 1607 fds[i].lpoll_revents |= LDAP_X_POLLOUT; 1608 } 1609 1610 /* XXXmcs: any other cases to deal with? LDAP_X_POLLERR? */ 1611 } 1612 1613 return( rc ); 1614 } 1615 1616 1617 static LBER_SOCKET 1618 nsldapi_compat_socket( LDAP *ld, int secure, int domain, int type, 1619 int protocol ) 1620 { 1621 int s; 1622 1623 s = ld->ld_io_fns_ptr->liof_socket( domain, type, protocol ); 1624 1625 if ( s >= 0 ) { 1626 char *errmsg = NULL; 1627 1628 #ifdef NSLDAPI_HAVE_POLL 1629 if ( ld->ld_io_fns_ptr->liof_select != NULL 1630 && s >= FD_SETSIZE ) { 1631 errmsg = dgettext(TEXT_DOMAIN, 1632 "can't use socket >= FD_SETSIZE"); 1633 } 1634 #elif !defined(_WINDOWS) /* not on Windows and do not have poll() */ 1635 if ( s >= FD_SETSIZE ) { 1636 errmsg = "can't use socket >= FD_SETSIZE"; 1637 } 1638 #endif 1639 1640 if ( NULL == errmsg && secure && 1641 ld->ld_io_fns_ptr->liof_ssl_enable( s ) < 0 ) { 1642 errmsg = dgettext(TEXT_DOMAIN, 1643 "failed to enable secure mode"); 1644 } 1645 1646 if ( NULL != errmsg ) { 1647 if ( NULL == ld->ld_io_fns_ptr->liof_close ) { 1648 nsldapi_os_closesocket( s ); 1649 } else { 1650 ld->ld_io_fns_ptr->liof_close( s ); 1651 } 1652 LDAP_SET_LDERRNO( ld, LDAP_LOCAL_ERROR, NULL, 1653 nsldapi_strdup( errmsg )); 1654 return( -1 ); 1655 } 1656 } 1657 1658 return( s ); 1659 } 1660 1661 1662 /* 1663 * Note: timeout is ignored because we have no way to pass it via 1664 * the old I/O callback interface. 1665 */ 1666 static int LDAP_CALLBACK 1667 nsldapi_ext_compat_connect( const char *hostlist, int defport, int timeout, 1668 unsigned long options, struct lextiof_session_private *sessionarg, 1669 struct lextiof_socket_private **socketargp 1670 #ifdef _SOLARIS_SDK 1671 , void **not_used ) 1672 #else 1673 ) 1674 #endif /* _SOLARIS_SDK */ 1675 { 1676 NSLDAPICompatSocketInfo *defcsip; 1677 struct ldap_io_fns *iofns; 1678 int s, secure; 1679 NSLDAPI_SOCKET_FN *socketfn; 1680 NSLDAPI_IOCTL_FN *ioctlfn; 1681 NSLDAPI_CONNECT_WITH_TO_FN *connectwithtofn; 1682 NSLDAPI_CONNECT_FN *connectfn; 1683 NSLDAPI_CLOSE_FN *closefn; 1684 1685 defcsip = (NSLDAPICompatSocketInfo *)sessionarg; 1686 iofns = defcsip->csi_ld->ld_io_fns_ptr; 1687 1688 if ( 0 != ( options & LDAP_X_EXTIOF_OPT_SECURE )) { 1689 if ( NULL == iofns->liof_ssl_enable ) { 1690 LDAP_SET_ERRNO( defcsip->csi_ld, EINVAL ); 1691 return( -1 ); 1692 } 1693 secure = 1; 1694 } else { 1695 secure = 0; 1696 } 1697 1698 socketfn = ( iofns->liof_socket == NULL ) ? 1699 nsldapi_os_socket : nsldapi_compat_socket; 1700 ioctlfn = ( iofns->liof_ioctl == NULL ) ? 1701 nsldapi_os_ioctl : (NSLDAPI_IOCTL_FN *)(iofns->liof_ioctl); 1702 if ( NULL == iofns->liof_connect ) { 1703 connectwithtofn = nsldapi_os_connect_with_to; 1704 connectfn = NULL; 1705 } else { 1706 connectwithtofn = NULL; 1707 connectfn = iofns->liof_connect; 1708 } 1709 closefn = ( iofns->liof_close == NULL ) ? 1710 nsldapi_os_closesocket : iofns->liof_close; 1711 1712 s = nsldapi_try_each_host( defcsip->csi_ld, hostlist, defport, 1713 secure, socketfn, ioctlfn, connectwithtofn, 1714 connectfn, closefn ); 1715 1716 if ( s >= 0 ) { 1717 NSLDAPICompatSocketInfo *csip; 1718 1719 if (( csip = (NSLDAPICompatSocketInfo *)NSLDAPI_CALLOC( 1, 1720 sizeof( NSLDAPICompatSocketInfo ))) == NULL ) { 1721 (*closefn)( s ); 1722 LDAP_SET_LDERRNO( defcsip->csi_ld, LDAP_NO_MEMORY, 1723 NULL, NULL ); 1724 return( -1 ); 1725 } 1726 1727 csip->csi_socket = s; 1728 csip->csi_ld = defcsip->csi_ld; 1729 *socketargp = (void *)csip; 1730 1731 /* 1732 * We always return 1, which is a valid but not unique socket 1733 * (file descriptor) number. The extended I/O functions only 1734 * require that the combination of the void *arg and the int 1735 * socket be unique. Since we allocate the 1736 * NSLDAPICompatSocketInfo that we assign to arg, we meet 1737 * that requirement. 1738 */ 1739 s = 1; 1740 } 1741 1742 return( s ); 1743 } 1744 1745 1746 static int LDAP_CALLBACK 1747 nsldapi_ext_compat_close( int s, struct lextiof_socket_private *arg ) 1748 { 1749 NSLDAPICompatSocketInfo *csip = (NSLDAPICompatSocketInfo *)arg; 1750 struct ldap_io_fns *iofns = csip->csi_ld->ld_io_fns_ptr; 1751 int rc; 1752 1753 rc = iofns->liof_close( csip->csi_socket ); 1754 1755 NSLDAPI_FREE( csip ); 1756 1757 return( rc ); 1758 } 1759 1760 /* 1761 * Install the I/O functions. 1762 * Return an LDAP error code (LDAP_SUCCESS if all goes well). 1763 */ 1764 int 1765 nsldapi_install_compat_io_fns( LDAP *ld, struct ldap_io_fns *iofns ) 1766 { 1767 NSLDAPICompatSocketInfo *defcsip; 1768 1769 if (( defcsip = (NSLDAPICompatSocketInfo *)NSLDAPI_CALLOC( 1, 1770 sizeof( NSLDAPICompatSocketInfo ))) == NULL ) { 1771 return( LDAP_NO_MEMORY ); 1772 } 1773 1774 defcsip->csi_socket = -1; 1775 defcsip->csi_ld = ld; 1776 1777 if ( ld->ld_io_fns_ptr != NULL ) { 1778 (void)memset( (char *)ld->ld_io_fns_ptr, 0, 1779 sizeof( struct ldap_io_fns )); 1780 } else if (( ld->ld_io_fns_ptr = (struct ldap_io_fns *)NSLDAPI_CALLOC( 1781 1, sizeof( struct ldap_io_fns ))) == NULL ) { 1782 NSLDAPI_FREE( defcsip ); 1783 return( LDAP_NO_MEMORY ); 1784 } 1785 1786 /* struct copy */ 1787 *(ld->ld_io_fns_ptr) = *iofns; 1788 1789 ld->ld_extio_size = LBER_X_EXTIO_FNS_SIZE; 1790 ld->ld_ext_session_arg = defcsip; 1791 ld->ld_extread_fn = nsldapi_ext_compat_read; 1792 ld->ld_extwrite_fn = nsldapi_ext_compat_write; 1793 ld->ld_extpoll_fn = nsldapi_ext_compat_poll; 1794 ld->ld_extconnect_fn = nsldapi_ext_compat_connect; 1795 ld->ld_extclose_fn = nsldapi_ext_compat_close; 1796 1797 return( nsldapi_install_lber_extiofns( ld, ld->ld_sbp )); 1798 } 1799 /* 1800 * end of compat I/O functions 1801 ****************************************************************************** 1802 */ 1803 #ifdef _SOLARIS_SDK 1804 /* 1805 * _ns_gethostbyaddr is a helper function for the ssl layer so that 1806 * it can use the ldap layer's gethostbyaddr resolver. 1807 */ 1808 1809 LDAPHostEnt * 1810 _ns_gethostbyaddr(LDAP *ld, const char *addr, int length, int type, 1811 LDAPHostEnt *result, char *buffer, int buflen, int *statusp, 1812 void *extradata) 1813 { 1814 if (ld == NULL || ld->ld_dns_gethostbyaddr_fn == NULL) 1815 return (NULL); 1816 return (ld->ld_dns_gethostbyaddr_fn(addr, length, type, 1817 result, buffer, buflen, statusp, extradata)); 1818 } 1819 1820 #endif /* _SOLARIS_SDK */ 1821 1822 1823