os-ip.c revision 1.1
1/* os-ip.c -- platform-specific TCP & UDP related code */
2/* $OpenLDAP: pkg/ldap/libraries/libldap/os-ip.c,v 1.118.2.7 2008/04/15 00:00:36 quanah Exp $ */
3/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4 *
5 * Copyright 1998-2008 The OpenLDAP Foundation.
6 * Portions Copyright 1999 Lars Uffmann.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted only as authorized by the OpenLDAP
11 * Public License.
12 *
13 * A copy of this license is available in the file LICENSE in the
14 * top-level directory of the distribution or, alternatively, at
15 * <http://www.OpenLDAP.org/license.html>.
16 */
17/* Portions Copyright (c) 1995 Regents of the University of Michigan.
18 * All rights reserved.
19 */
20/* Significant additional contributors include:
21 *    Lars Uffman
22 */
23
24#include "portable.h"
25
26#include <stdio.h>
27
28#include <ac/stdlib.h>
29
30#include <ac/errno.h>
31#include <ac/socket.h>
32#include <ac/string.h>
33#include <ac/time.h>
34#include <ac/unistd.h>
35
36#ifdef HAVE_IO_H
37#include <io.h>
38#endif /* HAVE_IO_H */
39
40#include "ldap-int.h"
41
42#if defined( HAVE_GETADDRINFO ) && defined( HAVE_INET_NTOP )
43#  ifdef LDAP_PF_INET6
44int ldap_int_inet4or6 = AF_UNSPEC;
45#  else
46int ldap_int_inet4or6 = AF_INET;
47#  endif
48#endif
49
50#ifdef LDAP_DEBUG
51
52#define osip_debug(ld,fmt,arg1,arg2,arg3) \
53do { \
54	ldap_log_printf(NULL, LDAP_DEBUG_TRACE, fmt, arg1, arg2, arg3); \
55} while(0)
56
57#else
58
59#define osip_debug(ld,fmt,arg1,arg2,arg3) ((void)0)
60
61#endif /* LDAP_DEBUG */
62
63static void
64ldap_pvt_set_errno(int err)
65{
66	sock_errset(err);
67}
68
69int
70ldap_int_timeval_dup( struct timeval **dest, const struct timeval *src )
71{
72	struct timeval *new;
73
74	assert( dest != NULL );
75
76	if (src == NULL) {
77		*dest = NULL;
78		return 0;
79	}
80
81	new = (struct timeval *) LDAP_MALLOC(sizeof(struct timeval));
82
83	if( new == NULL ) {
84		*dest = NULL;
85		return 1;
86	}
87
88	AC_MEMCPY( (char *) new, (const char *) src, sizeof(struct timeval));
89
90	*dest = new;
91	return 0;
92}
93
94static int
95ldap_pvt_ndelay_on(LDAP *ld, int fd)
96{
97	osip_debug(ld, "ldap_ndelay_on: %d\n",fd,0,0);
98	return ber_pvt_socket_set_nonblock( fd, 1 );
99}
100
101static int
102ldap_pvt_ndelay_off(LDAP *ld, int fd)
103{
104	osip_debug(ld, "ldap_ndelay_off: %d\n",fd,0,0);
105	return ber_pvt_socket_set_nonblock( fd, 0 );
106}
107
108static ber_socket_t
109ldap_int_socket(LDAP *ld, int family, int type )
110{
111	ber_socket_t s = socket(family, type, 0);
112	osip_debug(ld, "ldap_new_socket: %d\n",s,0,0);
113	return ( s );
114}
115
116static int
117ldap_pvt_close_socket(LDAP *ld, int s)
118{
119	osip_debug(ld, "ldap_close_socket: %d\n",s,0,0);
120	return tcp_close(s);
121}
122
123static int
124ldap_int_prepare_socket(LDAP *ld, int s, int proto )
125{
126	osip_debug( ld, "ldap_prepare_socket: %d\n", s, 0, 0 );
127
128#if defined( SO_KEEPALIVE ) || defined( TCP_NODELAY )
129	if ( proto == LDAP_PROTO_TCP ) {
130		int dummy = 1;
131#ifdef SO_KEEPALIVE
132		if ( setsockopt( s, SOL_SOCKET, SO_KEEPALIVE,
133			(char*) &dummy, sizeof(dummy) ) == AC_SOCKET_ERROR )
134		{
135			osip_debug( ld, "ldap_prepare_socket: "
136				"setsockopt(%d, SO_KEEPALIVE) failed (ignored).\n",
137				s, 0, 0 );
138		}
139#endif /* SO_KEEPALIVE */
140#ifdef TCP_NODELAY
141		if ( setsockopt( s, IPPROTO_TCP, TCP_NODELAY,
142			(char*) &dummy, sizeof(dummy) ) == AC_SOCKET_ERROR )
143		{
144			osip_debug( ld, "ldap_prepare_socket: "
145				"setsockopt(%d, TCP_NODELAY) failed (ignored).\n",
146				s, 0, 0 );
147		}
148#endif /* TCP_NODELAY */
149	}
150#endif /* SO_KEEPALIVE || TCP_NODELAY */
151
152	return 0;
153}
154
155#ifndef HAVE_WINSOCK
156
157#undef TRACE
158#define TRACE do { \
159	osip_debug(ld, \
160		"ldap_is_socket_ready: error on socket %d: errno: %d (%s)\n", \
161		s, \
162		errno, \
163		sock_errstr(errno) ); \
164} while( 0 )
165
166/*
167 * check the socket for errors after select returned.
168 */
169static int
170ldap_pvt_is_socket_ready(LDAP *ld, int s)
171{
172	osip_debug(ld, "ldap_is_sock_ready: %d\n",s,0,0);
173
174#if defined( notyet ) /* && defined( SO_ERROR ) */
175{
176	int so_errno;
177	ber_socklen_t dummy = sizeof(so_errno);
178	if ( getsockopt( s, SOL_SOCKET, SO_ERROR, &so_errno, &dummy )
179		== AC_SOCKET_ERROR )
180	{
181		return -1;
182	}
183	if ( so_errno ) {
184		ldap_pvt_set_errno(so_errno);
185		TRACE;
186		return -1;
187	}
188	return 0;
189}
190#else
191{
192	/* error slippery */
193#ifdef LDAP_PF_INET6
194	struct sockaddr_storage sin;
195#else
196	struct sockaddr_in sin;
197#endif
198	char ch;
199	ber_socklen_t dummy = sizeof(sin);
200	if ( getpeername( s, (struct sockaddr *) &sin, &dummy )
201		== AC_SOCKET_ERROR )
202	{
203		/* XXX: needs to be replace with ber_stream_read() */
204		read(s, &ch, 1);
205		TRACE;
206		return -1;
207	}
208	return 0;
209}
210#endif
211	return -1;
212}
213#undef TRACE
214
215#endif /* HAVE_WINSOCK */
216
217/* NOTE: this is identical to analogous code in os-local.c */
218int
219ldap_int_poll(
220	LDAP *ld,
221	ber_socket_t s,
222	struct timeval *tvp )
223{
224	int		rc;
225
226
227	osip_debug(ld, "ldap_int_poll: fd: %d tm: %ld\n",
228		s, tvp ? tvp->tv_sec : -1L, 0);
229
230#ifdef HAVE_POLL
231	{
232		struct pollfd fd;
233		int timeout = INFTIM;
234
235		fd.fd = s;
236		fd.events = POLL_WRITE;
237
238		if ( tvp != NULL ) {
239			timeout = TV2MILLISEC( tvp );
240		}
241		do {
242			fd.revents = 0;
243			rc = poll( &fd, 1, timeout );
244
245		} while ( rc == AC_SOCKET_ERROR && errno == EINTR &&
246			LDAP_BOOL_GET( &ld->ld_options, LDAP_BOOL_RESTART ) );
247
248		if ( rc == AC_SOCKET_ERROR ) {
249			return rc;
250		}
251
252		if ( timeout == 0 && rc == 0 ) {
253			return -2;
254		}
255
256		if ( fd.revents & POLL_WRITE ) {
257			if ( ldap_pvt_is_socket_ready( ld, s ) == -1 ) {
258				return -1;
259			}
260
261			if ( ldap_pvt_ndelay_off( ld, s ) == -1 ) {
262				return -1;
263			}
264			return 0;
265		}
266	}
267#else
268	{
269		fd_set		wfds, *z = NULL;
270#ifdef HAVE_WINSOCK
271		fd_set		efds;
272#endif
273		struct timeval	tv = { 0 };
274
275#if defined( FD_SETSIZE ) && !defined( HAVE_WINSOCK )
276		if ( s >= FD_SETSIZE ) {
277			rc = AC_SOCKET_ERROR;
278			tcp_close( s );
279			ldap_pvt_set_errno( EMFILE );
280			return rc;
281		}
282#endif
283
284		if ( tvp != NULL ) {
285			tv = *tvp;
286		}
287
288		do {
289			FD_ZERO(&wfds);
290			FD_SET(s, &wfds );
291
292#ifdef HAVE_WINSOCK
293			FD_ZERO(&efds);
294			FD_SET(s, &efds );
295#endif
296
297			rc = select( ldap_int_tblsize, z, &wfds,
298#ifdef HAVE_WINSOCK
299				&efds,
300#else
301				z,
302#endif
303				tvp ? &tv : NULL );
304		} while ( rc == AC_SOCKET_ERROR && errno == EINTR &&
305			LDAP_BOOL_GET( &ld->ld_options, LDAP_BOOL_RESTART ) );
306
307		if ( rc == AC_SOCKET_ERROR ) {
308			return rc;
309		}
310
311		if ( rc == 0 && tvp && tvp->tv_sec == 0 && tvp->tv_usec == 0 ) {
312			return -2;
313		}
314
315#ifdef HAVE_WINSOCK
316		/* This means the connection failed */
317		if ( FD_ISSET(s, &efds) ) {
318			int so_errno;
319			ber_socklen_t dummy = sizeof(so_errno);
320			if ( getsockopt( s, SOL_SOCKET, SO_ERROR,
321				(char *) &so_errno, &dummy ) == AC_SOCKET_ERROR || !so_errno )
322			{
323				/* impossible */
324				so_errno = WSAGetLastError();
325			}
326			ldap_pvt_set_errno( so_errno );
327			osip_debug(ld, "ldap_int_poll: error on socket %d: "
328			       "errno: %d (%s)\n", s, errno, sock_errstr( errno ));
329			return -1;
330		}
331#endif
332		if ( FD_ISSET(s, &wfds) ) {
333#ifndef HAVE_WINSOCK
334			if ( ldap_pvt_is_socket_ready( ld, s ) == -1 ) {
335				return -1;
336			}
337#endif
338			if ( ldap_pvt_ndelay_off(ld, s) == -1 ) {
339				return -1;
340			}
341			return 0;
342		}
343	}
344#endif
345
346	osip_debug(ld, "ldap_int_poll: timed out\n",0,0,0);
347	ldap_pvt_set_errno( ETIMEDOUT );
348	return -1;
349}
350
351static int
352ldap_pvt_connect(LDAP *ld, ber_socket_t s,
353	struct sockaddr *sin, ber_socklen_t addrlen,
354	int async)
355{
356	int rc, err;
357	struct timeval	tv, *opt_tv = NULL;
358
359#ifdef LDAP_CONNECTIONLESS
360	/* We could do a connect() but that would interfere with
361	 * attempts to poll a broadcast address
362	 */
363	if (LDAP_IS_UDP(ld)) {
364		if (ld->ld_options.ldo_peer)
365			ldap_memfree(ld->ld_options.ldo_peer);
366		ld->ld_options.ldo_peer=ldap_memalloc(sizeof(struct sockaddr));
367		AC_MEMCPY(ld->ld_options.ldo_peer,sin,sizeof(struct sockaddr));
368		return ( 0 );
369	}
370#endif
371	if ( ld->ld_options.ldo_tm_net.tv_sec >= 0 ) {
372		tv = ld->ld_options.ldo_tm_net;
373		opt_tv = &tv;
374	}
375
376	osip_debug(ld, "ldap_pvt_connect: fd: %d tm: %ld async: %d\n",
377			s, opt_tv ? tv.tv_sec : -1L, async);
378
379	if ( opt_tv && ldap_pvt_ndelay_on(ld, s) == -1 )
380		return ( -1 );
381
382	if ( connect(s, sin, addrlen) != AC_SOCKET_ERROR ) {
383		if ( opt_tv && ldap_pvt_ndelay_off(ld, s) == -1 )
384			return ( -1 );
385		return ( 0 );
386	}
387
388	err = sock_errno();
389	if ( err != EINPROGRESS && err != EWOULDBLOCK ) {
390		return ( -1 );
391	}
392
393	if ( async ) {
394		/* caller will call ldap_int_poll() as appropriate? */
395		return ( -2 );
396	}
397
398	rc = ldap_int_poll( ld, s, opt_tv );
399
400	osip_debug(ld, "ldap_pvt_connect: %d\n", rc, 0, 0);
401
402	return rc;
403}
404
405#ifndef HAVE_INET_ATON
406int
407ldap_pvt_inet_aton( const char *host, struct in_addr *in)
408{
409	unsigned long u = inet_addr( host );
410
411#ifdef INADDR_NONE
412	if ( u == INADDR_NONE ) return 0;
413#endif
414	if ( u == 0xffffffffUL || u == (unsigned long) -1L ) return 0;
415
416	in->s_addr = u;
417	return 1;
418}
419#endif
420
421
422int
423ldap_connect_to_host(LDAP *ld, Sockbuf *sb,
424	int proto,
425	const char *host, int port,
426	int async )
427{
428	int	rc;
429	int	socktype;
430	ber_socket_t		s = AC_SOCKET_INVALID;
431
432#if defined( HAVE_GETADDRINFO ) && defined( HAVE_INET_NTOP )
433	char serv[7];
434	int err;
435	struct addrinfo hints, *res, *sai;
436#else
437	int i;
438	int use_hp = 0;
439	struct hostent *hp = NULL;
440	struct hostent he_buf;
441	struct in_addr in;
442	char *ha_buf=NULL;
443#endif
444
445	if( host == NULL ) host = "localhost";
446
447	switch(proto) {
448	case LDAP_PROTO_TCP: socktype = SOCK_STREAM;
449		osip_debug( ld,
450			"ldap_connect_to_host: TCP %s:%d\n",
451			host, port, 0);
452		break;
453	case LDAP_PROTO_UDP: socktype = SOCK_DGRAM;
454		osip_debug( ld,
455			"ldap_connect_to_host: UDP %s:%d\n",
456			host, port, 0);
457		break;
458	default:
459		osip_debug( ld, "ldap_connect_to_host: unknown proto: %d\n",
460			proto, 0, 0 );
461		return -1;
462	}
463
464#if defined( HAVE_GETADDRINFO ) && defined( HAVE_INET_NTOP )
465	memset( &hints, '\0', sizeof(hints) );
466#ifdef USE_AI_ATTRCONFIG /* FIXME: configure test needed */
467	/* Use AI_ATTRCONFIG only on systems where its known to be needed. */
468	hints.ai_flags = AI_ATTRCONFIG;
469#endif
470	hints.ai_family = ldap_int_inet4or6;
471	hints.ai_socktype = socktype;
472	snprintf(serv, sizeof serv, "%d", port );
473
474#ifdef LDAP_R_COMPILE
475	/* most getaddrinfo(3) use non-threadsafe resolver libraries */
476	ldap_pvt_thread_mutex_lock(&ldap_int_resolv_mutex);
477#endif
478
479	err = getaddrinfo( host, serv, &hints, &res );
480
481#ifdef LDAP_R_COMPILE
482	ldap_pvt_thread_mutex_unlock(&ldap_int_resolv_mutex);
483#endif
484
485	if ( err != 0 ) {
486		osip_debug(ld, "ldap_connect_to_host: getaddrinfo failed: %s\n",
487			AC_GAI_STRERROR(err), 0, 0);
488		return -1;
489	}
490	rc = -1;
491
492	for( sai=res; sai != NULL; sai=sai->ai_next) {
493		if( sai->ai_addr == NULL ) {
494			osip_debug(ld, "ldap_connect_to_host: getaddrinfo "
495				"ai_addr is NULL?\n", 0, 0, 0);
496			continue;
497		}
498
499		/* we assume AF_x and PF_x are equal for all x */
500		s = ldap_int_socket( ld, sai->ai_family, socktype );
501		if ( s == AC_SOCKET_INVALID ) {
502			continue;
503		}
504
505		if ( ldap_int_prepare_socket(ld, s, proto ) == -1 ) {
506			ldap_pvt_close_socket(ld, s);
507			break;
508		}
509
510		switch (sai->ai_family) {
511#ifdef LDAP_PF_INET6
512			case AF_INET6: {
513				char addr[INET6_ADDRSTRLEN];
514				inet_ntop( AF_INET6,
515					&((struct sockaddr_in6 *)sai->ai_addr)->sin6_addr,
516					addr, sizeof addr);
517				osip_debug(ld, "ldap_connect_to_host: Trying %s %s\n",
518					addr, serv, 0);
519			} break;
520#endif
521			case AF_INET: {
522				char addr[INET_ADDRSTRLEN];
523				inet_ntop( AF_INET,
524					&((struct sockaddr_in *)sai->ai_addr)->sin_addr,
525					addr, sizeof addr);
526				osip_debug(ld, "ldap_connect_to_host: Trying %s:%s\n",
527					addr, serv, 0);
528			} break;
529		}
530
531		rc = ldap_pvt_connect( ld, s,
532			sai->ai_addr, sai->ai_addrlen, async );
533		if ( rc == 0 || rc == -2 ) {
534			ber_sockbuf_ctrl( sb, LBER_SB_OPT_SET_FD, &s );
535			break;
536		}
537		ldap_pvt_close_socket(ld, s);
538	}
539	freeaddrinfo(res);
540
541#else
542	if (! inet_aton( host, &in ) ) {
543		int local_h_errno;
544		rc = ldap_pvt_gethostbyname_a( host, &he_buf, &ha_buf,
545			&hp, &local_h_errno );
546
547		if ( (rc < 0) || (hp == NULL) ) {
548#ifdef HAVE_WINSOCK
549			ldap_pvt_set_errno( WSAGetLastError() );
550#else
551			/* not exactly right, but... */
552			ldap_pvt_set_errno( EHOSTUNREACH );
553#endif
554			if (ha_buf) LDAP_FREE(ha_buf);
555			return -1;
556		}
557
558		use_hp = 1;
559	}
560
561	rc = s = -1;
562	for ( i = 0; !use_hp || (hp->h_addr_list[i] != 0); ++i, rc = -1 ) {
563		struct sockaddr_in	sin;
564
565		s = ldap_int_socket( ld, PF_INET, socktype );
566		if ( s == AC_SOCKET_INVALID ) {
567			/* use_hp ? continue : break; */
568			break;
569		}
570
571		if ( ldap_int_prepare_socket( ld, s, proto ) == -1 ) {
572			ldap_pvt_close_socket(ld, s);
573			break;
574		}
575
576		(void)memset((char *)&sin, '\0', sizeof sin);
577		sin.sin_family = AF_INET;
578		sin.sin_port = htons((unsigned short) port);
579
580		if( use_hp ) {
581			AC_MEMCPY( &sin.sin_addr, hp->h_addr_list[i],
582				sizeof(sin.sin_addr) );
583		} else {
584			AC_MEMCPY( &sin.sin_addr, &in.s_addr,
585				sizeof(sin.sin_addr) );
586		}
587
588#ifdef HAVE_INET_NTOA_B
589		{
590			/* for VxWorks */
591			char address[INET_ADDR_LEN];
592			inet_ntoa_b(sin.sin_address, address);
593			osip_debug(ld, "ldap_connect_to_host: Trying %s:%d\n",
594				address, port, 0);
595		}
596#else
597		osip_debug(ld, "ldap_connect_to_host: Trying %s:%d\n",
598			inet_ntoa(sin.sin_addr), port, 0);
599#endif
600
601		rc = ldap_pvt_connect(ld, s,
602			(struct sockaddr *)&sin, sizeof(sin),
603			async);
604
605		if ( (rc == 0) || (rc == -2) ) {
606			ber_sockbuf_ctrl( sb, LBER_SB_OPT_SET_FD, &s );
607			break;
608		}
609
610		ldap_pvt_close_socket(ld, s);
611
612		if (!use_hp) break;
613	}
614	if (ha_buf) LDAP_FREE(ha_buf);
615#endif
616
617	return rc;
618}
619
620#if defined( HAVE_CYRUS_SASL )
621char *
622ldap_host_connected_to( Sockbuf *sb, const char *host )
623{
624	ber_socklen_t	len;
625#ifdef LDAP_PF_INET6
626	struct sockaddr_storage sabuf;
627#else
628	struct sockaddr sabuf;
629#endif
630	struct sockaddr	*sa = (struct sockaddr *) &sabuf;
631	ber_socket_t	sd;
632
633	(void)memset( (char *)sa, '\0', sizeof sabuf );
634	len = sizeof sabuf;
635
636	ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_FD, &sd );
637	if ( getpeername( sd, sa, &len ) == -1 ) {
638		return( NULL );
639	}
640
641	/*
642	 * do a reverse lookup on the addr to get the official hostname.
643	 * this is necessary for kerberos to work right, since the official
644	 * hostname is used as the kerberos instance.
645	 */
646
647	switch (sa->sa_family) {
648#ifdef LDAP_PF_LOCAL
649	case AF_LOCAL:
650		return LDAP_STRDUP( ldap_int_hostname );
651#endif
652#ifdef LDAP_PF_INET6
653	case AF_INET6:
654		{
655			struct in6_addr localhost = IN6ADDR_LOOPBACK_INIT;
656			if( memcmp ( &((struct sockaddr_in6 *)sa)->sin6_addr,
657				&localhost, sizeof(localhost)) == 0 )
658			{
659				return LDAP_STRDUP( ldap_int_hostname );
660			}
661		}
662		break;
663#endif
664	case AF_INET:
665		{
666			struct in_addr localhost;
667			localhost.s_addr = htonl( INADDR_ANY );
668
669			if( memcmp ( &((struct sockaddr_in *)sa)->sin_addr,
670				&localhost, sizeof(localhost) ) == 0 )
671			{
672				return LDAP_STRDUP( ldap_int_hostname );
673			}
674
675#ifdef INADDR_LOOPBACK
676			localhost.s_addr = htonl( INADDR_LOOPBACK );
677
678			if( memcmp ( &((struct sockaddr_in *)sa)->sin_addr,
679				&localhost, sizeof(localhost) ) == 0 )
680			{
681				return LDAP_STRDUP( ldap_int_hostname );
682			}
683#endif
684		}
685		break;
686
687	default:
688		return( NULL );
689		break;
690	}
691
692	{
693		char *herr;
694#ifdef NI_MAXHOST
695		char hbuf[NI_MAXHOST];
696#elif defined( MAXHOSTNAMELEN
697		char hbuf[MAXHOSTNAMELEN];
698#else
699		char hbuf[256];
700#endif
701		hbuf[0] = 0;
702
703		if (ldap_pvt_get_hname( sa, len, hbuf, sizeof(hbuf), &herr ) == 0
704			&& hbuf[0] )
705		{
706			return LDAP_STRDUP( hbuf );
707		}
708	}
709
710	return host ? LDAP_STRDUP( host ) : NULL;
711}
712#endif
713
714
715struct selectinfo {
716#ifdef HAVE_POLL
717	/* for UNIX poll(2) */
718	int si_maxfd;
719	struct pollfd si_fds[FD_SETSIZE];
720#else
721	/* for UNIX select(2) */
722	fd_set	si_readfds;
723	fd_set	si_writefds;
724	fd_set	si_use_readfds;
725	fd_set	si_use_writefds;
726#endif
727};
728
729void
730ldap_mark_select_write( LDAP *ld, Sockbuf *sb )
731{
732	struct selectinfo	*sip;
733	ber_socket_t		sd;
734
735	sip = (struct selectinfo *)ld->ld_selectinfo;
736
737	ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_FD, &sd );
738
739#ifdef HAVE_POLL
740	/* for UNIX poll(2) */
741	{
742		int empty=-1;
743		int i;
744		for(i=0; i < sip->si_maxfd; i++) {
745			if( sip->si_fds[i].fd == sd ) {
746				sip->si_fds[i].events |= POLL_WRITE;
747				return;
748			}
749			if( empty==-1 && sip->si_fds[i].fd == -1 ) {
750				empty=i;
751			}
752		}
753
754		if( empty == -1 ) {
755			if( sip->si_maxfd >= FD_SETSIZE ) {
756				/* FIXME */
757				return;
758			}
759			empty = sip->si_maxfd++;
760		}
761
762		sip->si_fds[empty].fd = sd;
763		sip->si_fds[empty].events = POLL_WRITE;
764	}
765#else
766	/* for UNIX select(2) */
767	if ( !FD_ISSET( sd, &sip->si_writefds )) {
768		FD_SET( sd, &sip->si_writefds );
769	}
770#endif
771}
772
773
774void
775ldap_mark_select_read( LDAP *ld, Sockbuf *sb )
776{
777	struct selectinfo	*sip;
778	ber_socket_t		sd;
779
780	sip = (struct selectinfo *)ld->ld_selectinfo;
781
782	ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_FD, &sd );
783
784#ifdef HAVE_POLL
785	/* for UNIX poll(2) */
786	{
787		int empty=-1;
788		int i;
789		for(i=0; i < sip->si_maxfd; i++) {
790			if( sip->si_fds[i].fd == sd ) {
791				sip->si_fds[i].events |= POLL_READ;
792				return;
793			}
794			if( empty==-1 && sip->si_fds[i].fd == -1 ) {
795				empty=i;
796			}
797		}
798
799		if( empty == -1 ) {
800			if( sip->si_maxfd >= FD_SETSIZE ) {
801				/* FIXME */
802				return;
803			}
804			empty = sip->si_maxfd++;
805		}
806
807		sip->si_fds[empty].fd = sd;
808		sip->si_fds[empty].events = POLL_READ;
809	}
810#else
811	/* for UNIX select(2) */
812	if ( !FD_ISSET( sd, &sip->si_readfds )) {
813		FD_SET( sd, &sip->si_readfds );
814	}
815#endif
816}
817
818
819void
820ldap_mark_select_clear( LDAP *ld, Sockbuf *sb )
821{
822	struct selectinfo	*sip;
823	ber_socket_t		sd;
824
825	sip = (struct selectinfo *)ld->ld_selectinfo;
826
827	ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_FD, &sd );
828
829#ifdef HAVE_POLL
830	/* for UNIX poll(2) */
831	{
832		int i;
833		for(i=0; i < sip->si_maxfd; i++) {
834			if( sip->si_fds[i].fd == sd ) {
835				sip->si_fds[i].fd = -1;
836			}
837		}
838	}
839#else
840	/* for UNIX select(2) */
841	FD_CLR( sd, &sip->si_writefds );
842	FD_CLR( sd, &sip->si_readfds );
843#endif
844}
845
846
847int
848ldap_is_write_ready( LDAP *ld, Sockbuf *sb )
849{
850	struct selectinfo	*sip;
851	ber_socket_t		sd;
852
853	sip = (struct selectinfo *)ld->ld_selectinfo;
854
855	ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_FD, &sd );
856
857#ifdef HAVE_POLL
858	/* for UNIX poll(2) */
859	{
860		int i;
861		for(i=0; i < sip->si_maxfd; i++) {
862			if( sip->si_fds[i].fd == sd ) {
863				return sip->si_fds[i].revents & POLL_WRITE;
864			}
865		}
866
867		return 0;
868	}
869#else
870	/* for UNIX select(2) */
871	return( FD_ISSET( sd, &sip->si_use_writefds ));
872#endif
873}
874
875
876int
877ldap_is_read_ready( LDAP *ld, Sockbuf *sb )
878{
879	struct selectinfo	*sip;
880	ber_socket_t		sd;
881
882	sip = (struct selectinfo *)ld->ld_selectinfo;
883
884	ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_FD, &sd );
885
886#ifdef HAVE_POLL
887	/* for UNIX poll(2) */
888	{
889		int i;
890		for(i=0; i < sip->si_maxfd; i++) {
891			if( sip->si_fds[i].fd == sd ) {
892				return sip->si_fds[i].revents & POLL_READ;
893			}
894		}
895
896		return 0;
897	}
898#else
899	/* for UNIX select(2) */
900	return( FD_ISSET( sd, &sip->si_use_readfds ));
901#endif
902}
903
904
905void *
906ldap_new_select_info( void )
907{
908	struct selectinfo	*sip;
909
910	sip = (struct selectinfo *)LDAP_CALLOC( 1, sizeof( struct selectinfo ));
911
912	if ( sip == NULL ) return NULL;
913
914#ifdef HAVE_POLL
915	/* for UNIX poll(2) */
916	/* sip->si_maxfd=0 */
917#else
918	/* for UNIX select(2) */
919	FD_ZERO( &sip->si_readfds );
920	FD_ZERO( &sip->si_writefds );
921#endif
922
923	return( (void *)sip );
924}
925
926
927void
928ldap_free_select_info( void *sip )
929{
930	LDAP_FREE( sip );
931}
932
933
934#ifndef HAVE_POLL
935int ldap_int_tblsize = 0;
936
937void
938ldap_int_ip_init( void )
939{
940#if defined( HAVE_SYSCONF )
941	long tblsize = sysconf( _SC_OPEN_MAX );
942	if( tblsize > INT_MAX ) tblsize = INT_MAX;
943
944#elif defined( HAVE_GETDTABLESIZE )
945	int tblsize = getdtablesize();
946#else
947	int tblsize = FD_SETSIZE;
948#endif /* !USE_SYSCONF */
949
950#ifdef FD_SETSIZE
951	if( tblsize > FD_SETSIZE ) tblsize = FD_SETSIZE;
952#endif	/* FD_SETSIZE */
953
954	ldap_int_tblsize = tblsize;
955}
956#endif
957
958
959int
960ldap_int_select( LDAP *ld, struct timeval *timeout )
961{
962	int rc;
963	struct selectinfo	*sip;
964
965	Debug( LDAP_DEBUG_TRACE, "ldap_int_select\n", 0, 0, 0 );
966
967#ifndef HAVE_POLL
968	if ( ldap_int_tblsize == 0 ) ldap_int_ip_init();
969#endif
970
971	sip = (struct selectinfo *)ld->ld_selectinfo;
972	assert( sip != NULL );
973
974#ifdef HAVE_POLL
975	{
976		int to = timeout ? TV2MILLISEC( timeout ) : INFTIM;
977		rc = poll( sip->si_fds, sip->si_maxfd, to );
978	}
979#else
980	sip->si_use_readfds = sip->si_readfds;
981	sip->si_use_writefds = sip->si_writefds;
982
983	rc = select( ldap_int_tblsize,
984		&sip->si_use_readfds, &sip->si_use_writefds,
985		NULL, timeout );
986#endif
987
988	return rc;
989}
990