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