]> git.sur5r.net Git - openldap/blob - libraries/libldap/os-ip.c
6bb3ac48214e530d3a4721378c6a1176b7b0d7eb
[openldap] / libraries / libldap / os-ip.c
1 /*
2  * Copyright 1998-1999 The OpenLDAP Foundation, All Rights Reserved.
3  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
4  */
5 /*  Portions
6  *  Copyright (c) 1995 Regents of the University of Michigan.
7  *  All rights reserved.
8  *
9  *  os-ip.c -- platform-specific TCP & UDP related code
10  */
11
12 #include "portable.h"
13
14 #include <stdio.h>
15
16 #include <ac/stdlib.h>
17
18 #include <ac/errno.h>
19 #include <ac/socket.h>
20 #include <ac/string.h>
21 #include <ac/time.h>
22 #include <ac/unistd.h>
23
24 #ifdef HAVE_IO_H
25 #include <io.h>
26 #endif /* HAVE_IO_H */
27
28 #if defined( HAVE_FCNTL_H )
29 #include <fcntl.h>
30 #ifndef O_NONBLOCK
31 #define O_NONBLOCK O_NDELAY
32 #endif
33 #endif /* HAVE_FCNTL_H */
34
35 #if defined( HAVE_SYS_FILIO_H )
36 #include <sys/filio.h>
37 #elif defined( HAVE_SYS_IOCTL_H )
38 #include <sys/ioctl.h>
39 #endif
40
41 #include "ldap-int.h"
42
43 int ldap_int_tblsize = 0;
44
45 /*
46  * nonblock connect code
47  * written by Lars Uffmann, <lars.uffmann@mediaway.net>.
48  *
49  * Copyright 1999, Lars Uffmann, All rights reserved.
50  * This software is not subject to any license of my employer
51  * mediaWays GmbH.
52  *
53  * OpenLDAP COPYING RESTRICTIONS APPLY, see COPYRIGHT file
54  *
55  * Read about the rationale in ldap_connect_timeout: 
56  * ftp://koobera.math.uic.edu/www/docs/connect.html.
57  */
58
59 #define osip_debug(ld,fmt,arg1,arg2,arg3) \
60 do { \
61         ldap_log_printf(ld, LDAP_DEBUG_TRACE, fmt, arg1, arg2, arg3); \
62 } while(0)
63
64 static void
65 ldap_pvt_set_errno(int err)
66 {
67         errno = err;
68 }
69
70 int
71 ldap_int_timeval_dup( struct timeval **dest, const struct timeval *src )
72 {
73         struct timeval *new;
74
75         assert( dest != NULL );
76
77         if (src == NULL) {
78                 *dest = NULL;
79                 return 0;
80         }
81
82         new = (struct timeval *) malloc(sizeof(struct timeval));
83
84         if( new == NULL ) {
85                 *dest = NULL;
86                 return 1;
87         }
88
89         SAFEMEMCPY( (char *) new, (char *) src, sizeof(struct timeval));
90
91         *dest = new;
92         return 0;
93 }
94
95 static int
96 ldap_pvt_ndelay_on(LDAP *ld, int fd)
97 {
98         osip_debug(ld, "ldap_ndelay_on: %d\n",fd,0,0);
99 #ifdef notyet
100 /* #if defined( HAVE_FCNTL_H ) */
101         return fcntl(fd,F_SETFL,fcntl(fd,F_GETFL,0) | O_NONBLOCK);
102 #else
103 {
104         ioctl_t status = 1;
105         return ioctl( fd, FIONBIO, &status );
106 }
107 #endif
108         return 0;
109 }
110    
111 static int
112 ldap_pvt_ndelay_off(LDAP *ld, int fd)
113 {
114         osip_debug(ld, "ldap_ndelay_off: %d\n",fd,0,0);
115 #ifdef notyet
116 /* #if defined( HAVE_FCNTL_H ) */
117         return fcntl(fd,F_SETFL,fcntl(fd,F_GETFL,0) & ~O_NONBLOCK);
118 #else
119 {
120         ioctl_t status = 0;
121         return ioctl( fd, FIONBIO, &status );
122 }
123 #endif
124 }
125
126 static ber_socket_t
127 ldap_pvt_socket(LDAP *ld)
128 {
129         ber_socket_t s = socket(AF_INET, SOCK_STREAM, 0);
130         osip_debug(ld, "ldap_new_socket: %d\n",s,0,0);
131         return ( s );
132 }
133
134 static int
135 ldap_pvt_close_socket(LDAP *ld, int s)
136 {
137         osip_debug(ld, "ldap_close_socket: %d\n",s,0,0);
138         return tcp_close(s);
139 }
140
141 static int
142 ldap_pvt_prepare_socket(LDAP *ld, int fd)
143 {
144         osip_debug(ld, "ldap_prepare_socket: %d\n",fd,0,0);
145
146 #ifdef TCP_NODELAY
147 {
148         int dummy = 1;
149         if ( setsockopt( fd, IPPROTO_TCP, TCP_NODELAY, (char*) &dummy, sizeof(dummy) ) == -1 )
150                 return -1;
151 }
152 #endif
153         return 0;
154 }
155
156 /*
157  * check the socket for errors after select returned.
158  */
159 static int
160 ldap_pvt_is_socket_ready(LDAP *ld, int s)
161 {
162         osip_debug(ld, "ldap_is_sock_ready: %d\n",s,0,0);
163
164 #define TRACE \
165 { \
166         osip_debug(ld, \
167                 "ldap_is_socket_ready: errror on socket %d: errno: %d (%s)\n", \
168                 s, \
169                 errno, \
170                 strerror(errno) ); \
171 }
172
173 #ifdef notyet
174 /* #ifdef SO_ERROR */
175 {
176         int so_errno;
177         int dummy = sizeof(so_errno);
178         if ( getsockopt(s,SOL_SOCKET,SO_ERROR,&so_errno,&dummy) == -1 )
179                 return -1;
180         if ( so_errno ) {
181                 ldap_pvt_set_errno(so_errno);
182                 TRACE;
183                 return -1;
184         }
185         return 0;
186 }
187 #else
188 {
189         /* error slippery */
190         struct sockaddr_in sin;
191         char ch;
192         int dummy = sizeof(sin);
193         if ( getpeername(s, (struct sockaddr *) &sin, &dummy) == -1 ) {
194                 read(s, &ch, 1);
195 #ifdef HAVE_WINSOCK
196                 ldap_pvt_set_errno( WSAGetLastError() );
197 #endif
198                 TRACE;
199                 return -1;
200                 }
201         return 0;
202 }
203 #endif
204         return -1;
205 #undef TRACE
206 }
207
208 static int
209 ldap_pvt_connect(LDAP *ld, ber_socket_t s, struct sockaddr_in *sin, int async)
210 {
211         struct timeval  tv, *opt_tv=NULL;
212         fd_set          wfds, *z=NULL;
213
214         if ( (opt_tv = ld->ld_options.ldo_tm_net) != NULL ) {
215                 tv.tv_usec = opt_tv->tv_usec;
216                 tv.tv_sec = opt_tv->tv_sec;
217         }
218
219         osip_debug(ld, "ldap_connect_timeout: fd: %d tm: %d async: %d\n",
220                         s, opt_tv ? tv.tv_sec : -1, async);
221
222         if ( ldap_pvt_ndelay_on(ld, s) == -1 )
223                 return ( -1 );
224
225         if ( connect(s, (struct sockaddr *) sin, sizeof(struct sockaddr_in)) == 0 )
226         {
227                 if ( ldap_pvt_ndelay_off(ld, s) == -1 )
228                         return ( -1 );
229                 return ( 0 );
230         }
231
232 #ifdef HAVE_WINSOCK
233         ldap_pvt_set_errno( WSAGetLastError() );
234 #endif
235
236         if ( errno != EINPROGRESS && errno != EWOULDBLOCK ) {
237                 return ( -1 );
238         }
239         
240 #ifdef notyet
241         if ( async ) return ( -2 );
242 #endif
243
244         FD_ZERO(&wfds);
245         FD_SET(s, &wfds );
246
247         if ( select(s + 1, z, &wfds, z, opt_tv ? &tv : NULL) == -1)
248                 return ( -1 );
249
250         if ( FD_ISSET(s, &wfds) ) {
251                 if ( ldap_pvt_is_socket_ready(ld, s) == -1 )
252                         return ( -1 );
253                 if ( ldap_pvt_ndelay_off(ld, s) == -1 )
254                         return ( -1 );
255                 return ( 0 );
256         }
257         osip_debug(ld, "ldap_connect_timeout: timed out\n",0,0,0);
258         ldap_pvt_set_errno( ETIMEDOUT );
259         return ( -1 );
260 }
261
262 #ifndef HAVE_INET_ATON
263 int
264 ldap_pvt_inet_aton( const char *host, struct in_addr *in)
265 {
266         unsigned long u = inet_addr( host );
267         if ( u != 0xffffffff || u != (unsigned long) -1 ) {
268                 in->s_addr = u;
269                 return 1;
270         }
271         return 0;
272 }
273 #endif
274
275
276 int
277 ldap_connect_to_host(LDAP *ld, Sockbuf *sb, const char *host,
278                 unsigned long address, int port, int async)
279 {
280         struct sockaddr_in      sin;
281         struct in_addr          in;
282         ber_socket_t            s = AC_SOCKET_INVALID;
283         int                     rc, i, use_hp = 0;
284         struct hostent          *hp, he_buf;
285         int                     local_h_errno;
286         char                    *ha_buf=NULL, *p, *q;
287
288         osip_debug(ld, "ldap_connect_to_host\n",0,0,0);
289         
290         if (host != NULL) {
291                 if (! inet_aton( host, &in) ) {
292                         rc = ldap_pvt_gethostbyname_a(host, &he_buf, &ha_buf,
293                                         &hp, &local_h_errno);
294
295                         if ( rc < 0 )
296                                 ; /*XXX NO MEMORY? */
297
298                         if ( (rc < 0) || (hp == NULL) ) {
299 #ifdef HAVE_WINSOCK
300                                 ldap_pvt_set_errno( WSAGetLastError() );
301 #else
302                                 /* not exactly right, but... */
303                                 ldap_pvt_set_errno( EHOSTUNREACH );
304 #endif
305                                 if (ha_buf) LDAP_FREE(ha_buf);
306                                 return -1;
307                         }
308                         use_hp = 1;
309                 }
310                 address = in.s_addr;
311         }
312
313         rc = s = -1;
314         for ( i = 0; !use_hp || (hp->h_addr_list[i] != 0); ++i, rc = -1 ) {
315
316                 if ( (s = ldap_pvt_socket( ld )) == -1 )
317                         /* use_hp ? continue : break; */
318                         break;
319            
320                 if ( ldap_pvt_prepare_socket(ld, s) == -1 ) {
321                         ldap_pvt_close_socket(ld, s);
322                         /* use_hp ? continue : break; */
323                         break;
324                 }
325
326                 (void)memset((char *)&sin, 0, sizeof(struct sockaddr_in));
327                 sin.sin_family = AF_INET;
328                 sin.sin_port = port;
329                 p = (char *)&sin.sin_addr.s_addr;
330                 q = use_hp ? (char *)hp->h_addr_list[i] : (char *)&address;
331                 SAFEMEMCPY(p, q, sizeof(p) );
332
333                 osip_debug(ld, "ldap_connect_to_host: Trying %s:%d\n", 
334                                 inet_ntoa(sin.sin_addr),ntohs(sin.sin_port),0);
335
336                 rc = ldap_pvt_connect(ld, s, &sin, async);
337    
338                 if ( (rc == 0) || (rc == -2) ) {
339                         ber_pvt_sb_set_desc( sb, s );
340                         break;
341                 }
342
343                 ldap_pvt_close_socket(ld, s);
344
345                 if (!use_hp)
346                         break;
347         }
348         if (ha_buf) LDAP_FREE(ha_buf);
349         return rc;
350 }
351
352 void
353 ldap_close_connection( Sockbuf *sb )
354 {
355         ber_pvt_sb_close( sb );
356 }
357
358
359 #if defined( HAVE_KERBEROS ) || defined( HAVE_TLS )
360 char *
361 ldap_host_connected_to( Sockbuf *sb )
362 {
363         struct hostent          *hp;
364         char                    *p;
365         socklen_t               len;
366         struct sockaddr_in      sin;
367
368         /* buffers for gethostbyaddr_r */
369         struct hostent          he_buf;
370         int                     local_h_errno;
371         char                    *ha_buf=NULL;
372 #define DO_RETURN(x) if (ha_buf) LDAP_FREE(ha_buf); return (x);
373    
374         (void)memset( (char *)&sin, 0, sizeof( struct sockaddr_in ));
375         len = sizeof( sin );
376
377         if ( getpeername( ber_pvt_sb_get_desc(sb), (struct sockaddr *)&sin, &len ) == -1 ) {
378                 return( NULL );
379         }
380
381         /*
382          * do a reverse lookup on the addr to get the official hostname.
383          * this is necessary for kerberos to work right, since the official
384          * hostname is used as the kerberos instance.
385          */
386         if ((ldap_pvt_gethostbyaddr_a( (char *) &sin.sin_addr,
387                 sizeof( sin.sin_addr ), 
388                 AF_INET, &he_buf, &ha_buf,
389                 &hp,&local_h_errno ) ==0 ) && (hp != NULL) )
390         {
391                 if ( hp->h_name != NULL ) {
392                         char *host = LDAP_STRDUP( hp->h_name );   
393                         DO_RETURN( host );
394                 }
395         }
396
397         DO_RETURN( NULL );
398 }
399 #undef DO_RETURN   
400    
401 #endif /* HAVE_KERBEROS || HAVE_TLS */
402
403
404 /* for UNIX */
405 struct selectinfo {
406         fd_set  si_readfds;
407         fd_set  si_writefds;
408         fd_set  si_use_readfds;
409         fd_set  si_use_writefds;
410 };
411
412
413 void
414 ldap_mark_select_write( LDAP *ld, Sockbuf *sb )
415 {
416         struct selectinfo       *sip;
417
418         sip = (struct selectinfo *)ld->ld_selectinfo;
419         
420         if ( !FD_ISSET( ber_pvt_sb_get_desc(sb), &sip->si_writefds )) {
421                 FD_SET( (u_int) sb->sb_sd, &sip->si_writefds );
422         }
423 }
424
425
426 void
427 ldap_mark_select_read( LDAP *ld, Sockbuf *sb )
428 {
429         struct selectinfo       *sip;
430
431         sip = (struct selectinfo *)ld->ld_selectinfo;
432
433         if ( !FD_ISSET( ber_pvt_sb_get_desc(sb), &sip->si_readfds )) {
434                 FD_SET( (u_int) sb->sb_sd, &sip->si_readfds );
435         }
436 }
437
438
439 void
440 ldap_mark_select_clear( LDAP *ld, Sockbuf *sb )
441 {
442         struct selectinfo       *sip;
443
444         sip = (struct selectinfo *)ld->ld_selectinfo;
445
446         FD_CLR( (u_int) ber_pvt_sb_get_desc(sb), &sip->si_writefds );
447         FD_CLR( (u_int) ber_pvt_sb_get_desc(sb), &sip->si_readfds );
448 }
449
450
451 int
452 ldap_is_write_ready( LDAP *ld, Sockbuf *sb )
453 {
454         struct selectinfo       *sip;
455
456         sip = (struct selectinfo *)ld->ld_selectinfo;
457
458         return( FD_ISSET( ber_pvt_sb_get_desc(sb), &sip->si_use_writefds ));
459 }
460
461
462 int
463 ldap_is_read_ready( LDAP *ld, Sockbuf *sb )
464 {
465         struct selectinfo       *sip;
466
467         sip = (struct selectinfo *)ld->ld_selectinfo;
468
469         return( FD_ISSET( ber_pvt_sb_get_desc(sb), &sip->si_use_readfds ));
470 }
471
472
473 void *
474 ldap_new_select_info( void )
475 {
476         struct selectinfo       *sip;
477
478         if (( sip = (struct selectinfo *)LDAP_CALLOC( 1,
479             sizeof( struct selectinfo ))) != NULL ) {
480                 FD_ZERO( &sip->si_readfds );
481                 FD_ZERO( &sip->si_writefds );
482         }
483
484         return( (void *)sip );
485 }
486
487
488 void
489 ldap_free_select_info( void *sip )
490 {
491         LDAP_FREE( sip );
492 }
493
494
495 void
496 ldap_int_ip_init( void )
497 {
498         int tblsize;
499 #if defined( HAVE_SYSCONF )
500         tblsize = sysconf( _SC_OPEN_MAX );
501 #elif defined( HAVE_GETDTABLESIZE )
502         tblsize = getdtablesize();
503 #else
504         tblsize = FD_SETSIZE;
505 #endif /* !USE_SYSCONF */
506
507 #ifdef FD_SETSIZE
508         if( tblsize > FD_SETSIZE )
509                 tblsize = FD_SETSIZE;
510 #endif  /* FD_SETSIZE*/
511         ldap_int_tblsize = tblsize;
512 }
513
514
515 int
516 do_ldap_select( LDAP *ld, struct timeval *timeout )
517 {
518         struct selectinfo       *sip;
519
520         Debug( LDAP_DEBUG_TRACE, "do_ldap_select\n", 0, 0, 0 );
521
522         if ( ldap_int_tblsize == 0 )
523                 ldap_int_ip_init();
524
525         sip = (struct selectinfo *)ld->ld_selectinfo;
526         sip->si_use_readfds = sip->si_readfds;
527         sip->si_use_writefds = sip->si_writefds;
528         
529         return( select( ldap_int_tblsize,
530                         &sip->si_use_readfds, &sip->si_use_writefds,
531                         NULL, timeout ));
532 }