]> git.sur5r.net Git - openldap/blob - libraries/libldap/os-ip.c
140f333e749b15442141ad184fbc26bd74ee15f7
[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, (caddr_t)&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, (caddr_t)&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,&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, int 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 #ifdef notyet
240         if ( async ) return ( -2 );
241 #endif
242
243         FD_ZERO(&wfds); FD_SET(s, &wfds );
244
245         if ( select(s + 1, z, &wfds, z, opt_tv ? &tv : NULL) == -1)
246                 return ( -1 );
247
248         if ( FD_ISSET(s, &wfds) ) {
249                 if ( ldap_pvt_is_socket_ready(ld, s) == -1 )
250                         return ( -1 );
251                 if ( ldap_pvt_ndelay_off(ld, s) == -1 )
252                         return ( -1 );
253                 return ( 0 );
254         }
255         osip_debug(ld, "ldap_connect_timeout: timed out\n",0,0,0);
256         ldap_pvt_set_errno( ETIMEDOUT );
257         return ( -1 );
258 }
259
260 static int
261 ldap_pvt_inet_aton( LDAP *ld, const char *host, struct in_addr *in)
262 {
263 #ifdef notyet
264 /* #ifdef HAVE_INET_ATON */
265         return inet_aton( host, in );
266 #else
267 {
268         unsigned long u = inet_addr( host );
269         if ( u != 0xffffffff || u != (unsigned long) -1 ) {
270                 in->s_addr = u;
271                 return 1;
272         }
273 }
274 #endif
275         return 0;
276 }
277
278
279 int
280 ldap_connect_to_host(LDAP *ld, Sockbuf *sb, const char *host,
281                 unsigned long address, int port, int async)
282 {
283         struct sockaddr_in      sin;
284         struct in_addr          in;
285         ber_socket_t            s = AC_SOCKET_INVALID;
286         int                     rc, i, use_hp = 0;
287         struct hostent          *hp, he_buf;
288         int                     local_h_errno;
289         char                    *ha_buf=NULL, *p, *q;
290
291         osip_debug(ld, "ldap_connect_to_host\n",0,0,0);
292         
293         if (host != NULL) {
294                 if (! ldap_pvt_inet_aton( ld, host, &in) ) {
295                         rc = ldap_pvt_gethostbyname_a(host, &he_buf, &ha_buf,
296                                         &hp, &local_h_errno);
297
298                         if ( rc < 0 )
299                                 ; /*XXX NO MEMORY? */
300
301                         if ( (rc < 0) || (hp == NULL) ) {
302 #ifdef HAVE_WINSOCK
303                                 ldap_pvt_set_errno( WSAGetLastError() );
304 #else
305                                 /* not exactly right, but... */
306                                 ldap_pvt_set_errno( EHOSTUNREACH );
307 #endif
308                                 if (ha_buf) LDAP_FREE(ha_buf);
309                                 return -1;
310                         }
311                         use_hp = 1;
312                 }
313                 address = in.s_addr;
314         }
315
316         rc = s = -1;
317         for ( i = 0; !use_hp || (hp->h_addr_list[i] != 0); ++i, rc = -1 ) {
318
319                 if ( (s = ldap_pvt_socket( ld )) == -1 )
320                         /* use_hp ? continue : break; */
321                         break;
322            
323                 if ( ldap_pvt_prepare_socket(ld, s) == -1 ) {
324                         ldap_pvt_close_socket(ld, s);
325                         /* use_hp ? continue : break; */
326                         break;
327                 }
328
329                 (void)memset((char *)&sin, 0, sizeof(struct sockaddr_in));
330                 sin.sin_family = AF_INET;
331                 sin.sin_port = port;
332                 p = (char *)&sin.sin_addr.s_addr;
333                 q = use_hp ? (char *)hp->h_addr_list[i] : (char *)&address;
334                 SAFEMEMCPY(p, q, sizeof(p) );
335
336                 osip_debug(ld, "ldap_connect_to_host: Trying %s:%d\n", 
337                                 inet_ntoa(sin.sin_addr),ntohs(sin.sin_port),0);
338
339                 rc = ldap_pvt_connect(ld, s, &sin, async);
340    
341                 if ( (rc == 0) || (rc == -2) ) {
342                         ber_pvt_sb_set_desc( sb, s );
343                         break;
344                 }
345
346                 ldap_pvt_close_socket(ld, s);
347
348                 if (!use_hp)
349                         break;
350         }
351         if (ha_buf) LDAP_FREE(ha_buf);
352         return rc;
353 }
354
355 void
356 ldap_close_connection( Sockbuf *sb )
357 {
358         ber_pvt_sb_close( sb );
359 }
360
361
362 #if defined( HAVE_KERBEROS ) || defined( HAVE_TLS )
363 char *
364 ldap_host_connected_to( Sockbuf *sb )
365 {
366         struct hostent          *hp;
367         char                    *p;
368         socklen_t               len;
369         struct sockaddr_in      sin;
370
371         /* buffers for gethostbyaddr_r */
372         struct hostent          he_buf;
373         int                     local_h_errno;
374         char                    *ha_buf=NULL;
375 #define DO_RETURN(x) if (ha_buf) LDAP_FREE(ha_buf); return (x);
376    
377         (void)memset( (char *)&sin, 0, sizeof( struct sockaddr_in ));
378         len = sizeof( sin );
379
380         if ( getpeername( ber_pvt_sb_get_desc(sb), (struct sockaddr *)&sin, &len ) == -1 ) {
381                 return( NULL );
382         }
383
384         /*
385          * do a reverse lookup on the addr to get the official hostname.
386          * this is necessary for kerberos to work right, since the official
387          * hostname is used as the kerberos instance.
388          */
389         if ((ldap_pvt_gethostbyaddr_a( (char *) &sin.sin_addr,
390                 sizeof( sin.sin_addr ), 
391                 AF_INET, &he_buf, &ha_buf,
392                 &hp,&local_h_errno ) ==0 ) && (hp != NULL) )
393         {
394                 if ( hp->h_name != NULL ) {
395                         char *host = LDAP_STRDUP( hp->h_name );   
396                         DO_RETURN( host );
397                 }
398         }
399
400         DO_RETURN( NULL );
401 }
402 #undef DO_RETURN   
403    
404 #endif /* HAVE_KERBEROS || HAVE_TLS */
405
406
407 /* for UNIX */
408 struct selectinfo {
409         fd_set  si_readfds;
410         fd_set  si_writefds;
411         fd_set  si_use_readfds;
412         fd_set  si_use_writefds;
413 };
414
415
416 void
417 ldap_mark_select_write( LDAP *ld, Sockbuf *sb )
418 {
419         struct selectinfo       *sip;
420
421         sip = (struct selectinfo *)ld->ld_selectinfo;
422         
423         if ( !FD_ISSET( ber_pvt_sb_get_desc(sb), &sip->si_writefds )) {
424                 FD_SET( (u_int) sb->sb_sd, &sip->si_writefds );
425         }
426 }
427
428
429 void
430 ldap_mark_select_read( LDAP *ld, Sockbuf *sb )
431 {
432         struct selectinfo       *sip;
433
434         sip = (struct selectinfo *)ld->ld_selectinfo;
435
436         if ( !FD_ISSET( ber_pvt_sb_get_desc(sb), &sip->si_readfds )) {
437                 FD_SET( (u_int) sb->sb_sd, &sip->si_readfds );
438         }
439 }
440
441
442 void
443 ldap_mark_select_clear( LDAP *ld, Sockbuf *sb )
444 {
445         struct selectinfo       *sip;
446
447         sip = (struct selectinfo *)ld->ld_selectinfo;
448
449         FD_CLR( (u_int) ber_pvt_sb_get_desc(sb), &sip->si_writefds );
450         FD_CLR( (u_int) ber_pvt_sb_get_desc(sb), &sip->si_readfds );
451 }
452
453
454 int
455 ldap_is_write_ready( LDAP *ld, Sockbuf *sb )
456 {
457         struct selectinfo       *sip;
458
459         sip = (struct selectinfo *)ld->ld_selectinfo;
460
461         return( FD_ISSET( ber_pvt_sb_get_desc(sb), &sip->si_use_writefds ));
462 }
463
464
465 int
466 ldap_is_read_ready( LDAP *ld, Sockbuf *sb )
467 {
468         struct selectinfo       *sip;
469
470         sip = (struct selectinfo *)ld->ld_selectinfo;
471
472         return( FD_ISSET( ber_pvt_sb_get_desc(sb), &sip->si_use_readfds ));
473 }
474
475
476 void *
477 ldap_new_select_info( void )
478 {
479         struct selectinfo       *sip;
480
481         if (( sip = (struct selectinfo *)LDAP_CALLOC( 1,
482             sizeof( struct selectinfo ))) != NULL ) {
483                 FD_ZERO( &sip->si_readfds );
484                 FD_ZERO( &sip->si_writefds );
485         }
486
487         return( (void *)sip );
488 }
489
490
491 void
492 ldap_free_select_info( void *sip )
493 {
494         LDAP_FREE( sip );
495 }
496
497
498 void
499 ldap_int_ip_init( void )
500 {
501         int tblsize;
502 #if defined( HAVE_SYSCONF )
503         tblsize = sysconf( _SC_OPEN_MAX );
504 #elif defined( HAVE_GETDTABLESIZE )
505         tblsize = getdtablesize();
506 #else
507         tblsize = FD_SETSIZE;
508 #endif /* !USE_SYSCONF */
509
510 #ifdef FD_SETSIZE
511         if( tblsize > FD_SETSIZE )
512                 tblsize = FD_SETSIZE;
513 #endif  /* FD_SETSIZE*/
514         ldap_int_tblsize = tblsize;
515 }
516
517
518 int
519 do_ldap_select( LDAP *ld, struct timeval *timeout )
520 {
521         struct selectinfo       *sip;
522
523         Debug( LDAP_DEBUG_TRACE, "do_ldap_select\n", 0, 0, 0 );
524
525         if ( ldap_int_tblsize == 0 )
526                 ldap_int_ip_init();
527
528         sip = (struct selectinfo *)ld->ld_selectinfo;
529         sip->si_use_readfds = sip->si_readfds;
530         sip->si_use_writefds = sip->si_writefds;
531         
532         return( select( ldap_int_tblsize,
533                         &sip->si_use_readfds, &sip->si_use_writefds,
534                         NULL, timeout ));
535 }