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