From: Howard Chu Date: Fri, 28 Sep 2001 00:18:40 +0000 (+0000) Subject: Resurrection/rewrite of CLDAP (RFC1798 Connectionless LDAP). X-Git-Tag: LDBM_PRE_GIANT_RWLOCK~1044 X-Git-Url: https://git.sur5r.net/?a=commitdiff_plain;h=647b5f84eee3a910c99518080e5fe562d4f7a32c;p=openldap Resurrection/rewrite of CLDAP (RFC1798 Connectionless LDAP). Compile with -DLDAP_CONNECTIONLESS to use this code. For slapd, use "-h cldap://" to listen on UDP. For ldapsearch, use "-H cldap://" to query on UDP. Client-side support is very minimal: no automatic timeout/retries no basedn wildcard expansion on results no support for specifying multiple servers at once. --- diff --git a/include/lber.h b/include/lber.h index 8ff10d51c9..129dfc0dd3 100644 --- a/include/lber.h +++ b/include/lber.h @@ -502,6 +502,9 @@ LBER_F( Sockbuf_IO ) ber_sockbuf_io_tcp; LBER_F( Sockbuf_IO ) ber_sockbuf_io_readahead; LBER_F( Sockbuf_IO ) ber_sockbuf_io_fd; LBER_F( Sockbuf_IO ) ber_sockbuf_io_debug; +#ifdef LDAP_CONNECTIONLESS +LBER_F( Sockbuf_IO ) ber_sockbuf_io_udp; +#endif /* * LBER memory.c diff --git a/libraries/liblber/sockbuf.c b/libraries/liblber/sockbuf.c index e201157433..5ce23ac656 100644 --- a/libraries/liblber/sockbuf.c +++ b/libraries/liblber/sockbuf.c @@ -871,3 +871,106 @@ Sockbuf_IO ber_sockbuf_io_debug = { sb_debug_write, /* sbi_write */ NULL /* sbi_close */ }; + +#ifdef LDAP_CONNECTIONLESS + +/* + * Support for UDP (CLDAP) + * + * All I/O at this level must be atomic. For ease of use, the sb_readahead + * must be used above this module. All data reads and writes are prefixed + * with a sockaddr containing the address of the remote entity. Upper levels + * must read and write this sockaddr before doing the usual ber_printf/scanf + * operations on LDAP messages. + */ + +static int +sb_dgram_setup( Sockbuf_IO_Desc *sbiod, void *arg ) +{ + assert( sbiod != NULL); + assert( SOCKBUF_VALID( sbiod->sbiod_sb ) ); + + if ( arg != NULL ) + sbiod->sbiod_sb->sb_fd = *((int *)arg); + return 0; +} + +static ber_slen_t +sb_dgram_read( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len ) +{ + ber_slen_t rc; + socklen_t addrlen; + struct sockaddr *src; + + assert( sbiod != NULL ); + assert( SOCKBUF_VALID( sbiod->sbiod_sb ) ); + assert( buf != NULL ); + + addrlen = sizeof( struct sockaddr ); + src = buf; + buf += addrlen; + rc = recvfrom( sbiod->sbiod_sb->sb_fd, buf, len, 0, src, + &addrlen ); + + return rc > 0 ? rc+sizeof(struct sockaddr) : rc; +} + +static ber_slen_t +sb_dgram_write( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len ) +{ + ber_slen_t rc; + struct sockaddr *dst; + + assert( sbiod != NULL ); + assert( SOCKBUF_VALID( sbiod->sbiod_sb ) ); + assert( buf != NULL ); + + dst = buf; + buf += sizeof( struct sockaddr ); + len -= sizeof( struct sockaddr ); + + rc = sendto( sbiod->sbiod_sb->sb_fd, buf, len, 0, dst, + sizeof( struct sockaddr ) ); + + if ( rc < 0 ) + return -1; + + /* fake error if write was not atomic */ + if (rc < len) { +# ifdef EMSGSIZE + errno = EMSGSIZE; +# endif + return -1; + } + rc = len + sizeof(struct sockaddr); + return rc; +} + +static int +sb_dgram_close( Sockbuf_IO_Desc *sbiod ) +{ + assert( sbiod != NULL ); + assert( SOCKBUF_VALID( sbiod->sbiod_sb ) ); + + tcp_close( sbiod->sbiod_sb->sb_fd ); + return 0; +} + +static int +sb_dgram_ctrl( Sockbuf_IO_Desc *sbiod, int opt, void *arg ) +{ + /* This is an end IO descriptor */ + return 0; +} + +Sockbuf_IO ber_sockbuf_io_udp = +{ + sb_dgram_setup, /* sbi_setup */ + NULL, /* sbi_remove */ + sb_dgram_ctrl, /* sbi_ctrl */ + sb_dgram_read, /* sbi_read */ + sb_dgram_write, /* sbi_write */ + sb_dgram_close /* sbi_close */ +}; + +#endif /* LDAP_CONNECTIONLESS */ diff --git a/libraries/libldap/abandon.c b/libraries/libldap/abandon.c index 8220e47a99..0bcae1ff95 100644 --- a/libraries/libldap/abandon.c +++ b/libraries/libldap/abandon.c @@ -147,10 +147,25 @@ do_abandon( ld->ld_errno = LDAP_NO_MEMORY; } else { - /* create a message to send */ - err = ber_printf( ber, "{iti", /* '}' */ +#ifdef LDAP_CONNECTIONLESS + if ( LDAP_IS_UDP(ld) ) { + err = ber_write( ber, ld->ld_options.ldo_peer, + sizeof(struct sockaddr), 0); + if (err == sizeof(struct sockaddr)) { + char *dn = ld->ld_options.ldo_cldapdn; + if (!dn) dn = ""; + err = ber_printf( ber, "{isti", /* '}' */ + ++ld->ld_msgid, dn, + LDAP_REQ_ABANDON, msgid ); + } + } else +#endif + { + /* create a message to send */ + err = ber_printf( ber, "{iti", /* '}' */ ++ld->ld_msgid, - LDAP_REQ_ABANDON, msgid ); + LDAP_REQ_ABANDON, msgid ); + } if( err == -1 ) { /* encoding error */ diff --git a/libraries/libldap/init.c b/libraries/libldap/init.c index e9c290c4d9..54c6ba5a4d 100644 --- a/libraries/libldap/init.c +++ b/libraries/libldap/init.c @@ -396,6 +396,11 @@ void ldap_int_initialize_global_options( struct ldapoptions *gopts, int *dbglvl LDAP_BOOL_SET(gopts, LDAP_BOOL_REFERRALS); +#ifdef LDAP_CONNECTIONLESS + gopts->ldo_peer = NULL; + gopts->ldo_cldapdn = NULL; +#endif + #ifdef HAVE_CYRUS_SASL gopts->ldo_def_sasl_mech = NULL; gopts->ldo_def_sasl_realm = NULL; diff --git a/libraries/libldap/ldap-int.h b/libraries/libldap/ldap-int.h index b90b6ab322..fffaa610ec 100644 --- a/libraries/libldap/ldap-int.h +++ b/libraries/libldap/ldap-int.h @@ -68,6 +68,10 @@ LDAP_BEGIN_DECL #define LDAPS_URL_PREFIX_LEN (sizeof(LDAPS_URL_PREFIX)-1) #define LDAPI_URL_PREFIX "ldapi://" #define LDAPI_URL_PREFIX_LEN (sizeof(LDAPI_URL_PREFIX)-1) +#ifdef LDAP_CONNECTIONLESS +#define LDAPC_URL_PREFIX "cldap://" +#define LDAPC_URL_PREFIX_LEN (sizeof(LDAPC_URL_PREFIX)-1) +#endif #define LDAP_URL_URLCOLON "URL:" #define LDAP_URL_URLCOLON_LEN (sizeof(LDAP_URL_URLCOLON)-1) @@ -114,6 +118,12 @@ struct ldapoptions { #define LDAP_UNINITIALIZED 0x0 #define LDAP_INITIALIZED 0x1 #define LDAP_VALID_SESSION 0x2 +#ifdef LDAP_CONNECTIONLESS +#define LDAP_UDP_SESSION 0x4 +#define LDAP_IS_UDP(ld) (ld->ld_options.ldo_valid & LDAP_UDP_SESSION) + void* ldo_peer; /* struct sockaddr* */ + char* ldo_cldapdn; +#endif int ldo_debug; /* per API call timeout */ @@ -291,7 +301,7 @@ struct ldap { LDAPConn *ld_conns; /* list of server connections */ void *ld_selectinfo; /* platform specifics for select */ }; -#define LDAP_VALID(ld) ( (ld)->ld_valid == LDAP_VALID_SESSION ) +#define LDAP_VALID(ld) ( (ld)->ld_valid & LDAP_VALID_SESSION ) #ifdef LDAP_R_COMPILE #include diff --git a/libraries/libldap/open.c b/libraries/libldap/open.c index fcfd80ddaa..fe69cdf246 100644 --- a/libraries/libldap/open.c +++ b/libraries/libldap/open.c @@ -236,6 +236,8 @@ ldap_initialize( LDAP **ldp, LDAP_CONST char *url ) ldap_ld_free(ld, 1, NULL, NULL); return rc; } + if (ldap_is_ldapc_url(url)) + ld->ld_options.ldo_valid |= LDAP_UDP_SESSION; } *ldp = ld; @@ -255,12 +257,12 @@ ldap_int_open_connection( int sasl_ssf = 0; #endif char *host; - int port; + int port, proto; long addr; Debug( LDAP_DEBUG_TRACE, "ldap_int_open_connection\n", 0, 0, 0 ); - switch ( ldap_pvt_url_scheme2proto( srv->lud_scheme ) ) { + switch ( proto = ldap_pvt_url_scheme2proto( srv->lud_scheme ) ) { case LDAP_PROTO_TCP: port = htons( (short) srv->lud_port ); @@ -272,8 +274,8 @@ ldap_int_open_connection( host = srv->lud_host; } - rc = ldap_connect_to_host( ld, conn->lconn_sb, 0, - host, addr, port, async ); + rc = ldap_connect_to_host( ld, conn->lconn_sb, + proto, host, addr, port, async ); if ( rc == -1 ) return rc; @@ -288,13 +290,36 @@ ldap_int_open_connection( sasl_host = ldap_host_connected_to( conn->lconn_sb ); #endif break; +#ifdef LDAP_CONNECTIONLESS + case LDAP_PROTO_UDP: + port = htons( (short) srv->lud_port ); + + addr = 0; + if ( srv->lud_host == NULL || *srv->lud_host == 0 ) { + host = NULL; + addr = htonl( INADDR_LOOPBACK ); + } else { + host = srv->lud_host; + } + ld->ld_options.ldo_valid |= LDAP_UDP_SESSION; + rc = ldap_connect_to_host( ld, conn->lconn_sb, + proto, host, addr, port, async ); + + if ( rc == -1 ) return rc; +#ifdef LDAP_DEBUG + ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_debug, + LBER_SBIOD_LEVEL_PROVIDER, (void *)"udp_" ); +#endif + ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_udp, + LBER_SBIOD_LEVEL_PROVIDER, NULL ); + break; +#endif case LDAP_PROTO_IPC: #ifdef LDAP_PF_LOCAL /* only IPC mechanism supported is PF_LOCAL (PF_UNIX) */ rc = ldap_connect_to_path( ld, conn->lconn_sb, srv->lud_host, async ); if ( rc == -1 ) return rc; - #ifdef LDAP_DEBUG ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_debug, LBER_SBIOD_LEVEL_PROVIDER, (void *)"ipc_" ); @@ -321,6 +346,11 @@ ldap_int_open_connection( INT_MAX, (void *)"ldap_" ); #endif +#ifdef LDAP_CONNECTIONLESS + if( proto == LDAP_PROTO_UDP ) + return 0; +#endif + #ifdef HAVE_CYRUS_SASL /* establish Cyrus SASL context prior to starting TLS so that SASL EXTERNAL might be used */ diff --git a/libraries/libldap/os-ip.c b/libraries/libldap/os-ip.c index 79bd49af9f..b2c44c2f07 100644 --- a/libraries/libldap/os-ip.c +++ b/libraries/libldap/os-ip.c @@ -198,6 +198,18 @@ ldap_pvt_connect(LDAP *ld, ber_socket_t s, fd_set efds; #endif +#ifdef LDAP_CONNECTIONLESS + /* We could do a connect() but that would interfere with + * attempts to poll a broadcast address + */ + if (LDAP_IS_UDP(ld)) { + if (ld->ld_options.ldo_peer) + ldap_memfree(ld->ld_options.ldo_peer); + ld->ld_options.ldo_peer=ldap_memalloc(sizeof(struct sockaddr)); + AC_MEMCPY(ld->ld_options.ldo_peer,sin,sizeof(struct sockaddr)); + return ( 0 ); + } +#endif if ( (opt_tv = ld->ld_options.ldo_tm_net) != NULL ) { tv.tv_usec = opt_tv->tv_usec; tv.tv_sec = opt_tv->tv_sec; @@ -293,9 +305,18 @@ ldap_connect_to_host(LDAP *ld, Sockbuf *sb, int rc, i, use_hp = 0; struct hostent *hp = NULL; char *ha_buf=NULL, *p, *q; + int socktype; osip_debug(ld, "ldap_connect_to_host: %s\n",host,0,0); + switch(proto) { + case LDAP_PROTO_TCP: socktype = SOCK_STREAM; break; + case LDAP_PROTO_UDP: socktype = SOCK_DGRAM; break; + default: osip_debug(ld, "ldap_connect_to_host: unknown proto: %d\n", + proto, 0, 0); + return -1; + } + if (host != NULL) { #if defined( HAVE_GETADDRINFO ) && defined( HAVE_INET_NTOP ) char serv[7]; @@ -304,7 +325,7 @@ ldap_connect_to_host(LDAP *ld, Sockbuf *sb, memset( &hints, '\0', sizeof(hints) ); hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; + hints.ai_socktype = socktype; snprintf(serv, sizeof serv, "%d", ntohs(port)); if ( err = getaddrinfo(host, serv, &hints, &res) ) { @@ -316,7 +337,7 @@ ldap_connect_to_host(LDAP *ld, Sockbuf *sb, rc = -1; do { /* we assume AF_x and PF_x are equal for all x */ - s = ldap_int_socket( ld, sai->ai_family, SOCK_STREAM ); + s = ldap_int_socket( ld, sai->ai_family, socktype ); if ( s == AC_SOCKET_INVALID ) { continue; } @@ -384,7 +405,7 @@ ldap_connect_to_host(LDAP *ld, Sockbuf *sb, rc = s = -1; for ( i = 0; !use_hp || (hp->h_addr_list[i] != 0); ++i, rc = -1 ) { - s = ldap_int_socket( ld, PF_INET, SOCK_STREAM ); + s = ldap_int_socket( ld, PF_INET, socktype ); if ( s == AC_SOCKET_INVALID ) { /* use_hp ? continue : break; */ break; diff --git a/libraries/libldap/request.c b/libraries/libldap/request.c index 576383fb10..a909f4c4f7 100644 --- a/libraries/libldap/request.c +++ b/libraries/libldap/request.c @@ -107,6 +107,18 @@ ldap_send_initial_request( servers = NULL; } +#ifdef LDAP_CONNECTIONLESS + if (LDAP_IS_UDP(ld)) { + if (msgtype == LDAP_REQ_BIND) { + if (ld->ld_options.ldo_cldapdn) + ldap_memfree(ld->ld_options.ldo_cldapdn); + ld->ld_options.ldo_cldapdn = ldap_strdup(dn); + return 0; + } + if (msgtype != LDAP_REQ_ABANDON && msgtype != LDAP_REQ_SEARCH) + return LDAP_PARAM_ERROR; + } +#endif rc = ldap_send_server_request( ld, ber, ld->ld_msgid, NULL, servers, NULL, NULL ); if (servers) diff --git a/libraries/libldap/result.c b/libraries/libldap/result.c index d8c0a6c47a..eb52071ae0 100644 --- a/libraries/libldap/result.c +++ b/libraries/libldap/result.c @@ -372,6 +372,12 @@ try_read1msg( /* get the next message */ errno = 0; +#ifdef LDAP_CONNECTIONLESS + if ( LDAP_IS_UDP(ld) ) { + struct sockaddr from; + ber_int_sb_read(sb, &from, sizeof(struct sockaddr)); + } +#endif if ( (tag = ber_get_next( sb, &len, ber )) != LDAP_TAG_MESSAGE ) { if ( tag == LBER_DEFAULT) { @@ -419,7 +425,14 @@ try_read1msg( ber_free( ber, 1 ); return( -2 ); /* continue looking */ } - +#ifdef LDAP_CONNECTIONLESS + if (LDAP_IS_UDP(ld)) { + char *blank; + ber_scanf(ber, "a{", &blank); + if (blank) + ber_memfree(blank); + } +#endif /* the message type */ if ( (tag = ber_peek_tag( ber, &len )) == LBER_ERROR ) { ld->ld_errno = LDAP_DECODING_ERROR; diff --git a/libraries/libldap/sasl.c b/libraries/libldap/sasl.c index 2d47636b7c..a435b2b2d1 100644 --- a/libraries/libldap/sasl.c +++ b/libraries/libldap/sasl.c @@ -423,7 +423,16 @@ ldap_sasl_interactive_bind_s( #if defined( LDAP_R_COMPILE ) && defined( HAVE_CYRUS_SASL ) ldap_pvt_thread_mutex_lock( &ldap_int_sasl_mutex ); #endif - +#ifdef LDAP_CONNECTIONLESS + if( LDAP_IS_UDP(ld) ) { + /* Just force it to simple bind, silly to make the user + * ask all the time. No, we don't ever actually bind, but I'll + * let the final bind handler take care of saving the cdn. + */ + rc = ldap_simple_bind(ld, dn, NULL); + return rc < 0 ? rc : 0; + } else +#endif if( mechs == NULL || *mechs == '\0' ) { char *smechs; diff --git a/libraries/libldap/search.c b/libraries/libldap/search.c index 5adb5b8ed2..cfcfd8cf30 100644 --- a/libraries/libldap/search.c +++ b/libraries/libldap/search.c @@ -297,11 +297,28 @@ ldap_build_search_req( } } - err = ber_printf( ber, "{it{seeiib", ++ld->ld_msgid, +#ifdef LDAP_CONNECTIONLESS + if ( LDAP_IS_UDP(ld) ) { + err = ber_write( ber, ld->ld_options.ldo_peer, + sizeof(struct sockaddr), 0); + if (err == sizeof(struct sockaddr)) { + char *dn = ld->ld_options.ldo_cldapdn; + if (!dn) dn = ""; + err = ber_printf( ber, "{ist{seeiib", ++ld->ld_msgid, dn, + LDAP_REQ_SEARCH, base, (ber_int_t) scope, ld->ld_deref, + (sizelimit < 0) ? ld->ld_sizelimit : sizelimit, + (timelimit < 0) ? ld->ld_timelimit : timelimit, + attrsonly ); + } + } else +#endif + { + err = ber_printf( ber, "{it{seeiib", ++ld->ld_msgid, LDAP_REQ_SEARCH, base, (ber_int_t) scope, ld->ld_deref, (sizelimit < 0) ? ld->ld_sizelimit : sizelimit, (timelimit < 0) ? ld->ld_timelimit : timelimit, attrsonly ); + } if ( err == -1 ) { ld->ld_errno = LDAP_ENCODING_ERROR; diff --git a/libraries/libldap/unbind.c b/libraries/libldap/unbind.c index 7009a052eb..b138161a41 100644 --- a/libraries/libldap/unbind.c +++ b/libraries/libldap/unbind.c @@ -158,6 +158,10 @@ ldap_send_unbind( Debug( LDAP_DEBUG_TRACE, "ldap_send_unbind\n", 0, 0, 0 ); +#ifdef LDAP_CONNECTIONLESS + if (LDAP_IS_UDP(ld)) + return LDAP_SUCCESS; +#endif /* create a message to send */ if ( (ber = ldap_alloc_ber_with_options( ld )) == NULL ) { return( ld->ld_errno ); diff --git a/libraries/libldap/url.c b/libraries/libldap/url.c index 6fe98adafb..c58ba59f7f 100644 --- a/libraries/libldap/url.c +++ b/libraries/libldap/url.c @@ -60,6 +60,11 @@ int ldap_pvt_url_scheme2proto( const char *scheme ) if( strcmp("ldaps", scheme) == 0 ) { return LDAP_PROTO_TCP; } +#ifdef LDAP_CONNECTIONLESS + if( strcmp("cldap", scheme) == 0 ) { + return LDAP_PROTO_UDP; + } +#endif return -1; } @@ -126,6 +131,25 @@ ldap_is_ldapi_url( LDAP_CONST char *url ) return strcmp(scheme, "ldapi") == 0; } +#ifdef LDAP_CONNECTIONLESS +int +ldap_is_ldapc_url( LDAP_CONST char *url ) +{ + int enclosed; + const char * scheme; + + if( url == NULL ) { + return 0; + } + + if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) { + return 0; + } + + return strcmp(scheme, "cldap") == 0; +} +#endif + static const char* skip_url_prefix( const char *url, @@ -181,6 +205,16 @@ skip_url_prefix( return( p ); } +#ifdef LDAP_CONNECTIONLESS + /* check for "cldap://" prefix */ + if ( strncasecmp( p, LDAPC_URL_PREFIX, LDAPC_URL_PREFIX_LEN ) == 0 ) { + /* skip over "cldap://" prefix and return success */ + p += LDAPC_URL_PREFIX_LEN; + *scheme = "cldap"; + return( p ); + } +#endif + return( NULL ); } diff --git a/servers/slapd/connection.c b/servers/slapd/connection.c index 439c6fdd1c..dc417a4b48 100644 --- a/servers/slapd/connection.c +++ b/servers/slapd/connection.c @@ -317,7 +317,7 @@ long connection_init( const char* dnsname, const char* peername, const char* sockname, - int use_tls, + int tls_udp_option, slap_ssf_t ssf, const char *authid ) { @@ -331,7 +331,7 @@ long connection_init( assert( sockname != NULL ); #ifndef HAVE_TLS - assert( !use_tls ); + assert( tls_udp_option != 1 ); #endif if( s == AC_SOCKET_INVALID ) { @@ -474,12 +474,27 @@ long connection_init( c->c_activitytime = c->c_starttime = slap_get_time(); +#ifdef LDAP_CONNECTIONLESS + c->c_is_udp = 0; + if (tls_udp_option == 2) + { + c->c_is_udp = 1; +#ifdef LDAP_DEBUG + ber_sockbuf_add_io( c->c_sb, &ber_sockbuf_io_debug, + LBER_SBIOD_LEVEL_PROVIDER, (void*)"udp_" ); +#endif + ber_sockbuf_add_io( c->c_sb, &ber_sockbuf_io_udp, + LBER_SBIOD_LEVEL_PROVIDER, (void *)&s ); + } else +#endif + { #ifdef LDAP_DEBUG ber_sockbuf_add_io( c->c_sb, &ber_sockbuf_io_debug, LBER_SBIOD_LEVEL_PROVIDER, (void*)"tcp_" ); #endif ber_sockbuf_add_io( c->c_sb, &ber_sockbuf_io_tcp, LBER_SBIOD_LEVEL_PROVIDER, (void *)&s ); + } ber_sockbuf_add_io( c->c_sb, &ber_sockbuf_io_readahead, LBER_SBIOD_LEVEL_PROVIDER, NULL ); @@ -511,7 +526,7 @@ long connection_init( c->c_tls_ssf = 0; #ifdef HAVE_TLS - if ( use_tls ) { + if ( tls_udp_option == 1 ) { c->c_is_tls = 1; c->c_needs_tls_accept = 1; } else { @@ -1160,6 +1175,10 @@ connection_input( ber_len_t len; ber_int_t msgid; BerElement *ber; +#ifdef LDAP_CONNECTIONLESS + Sockaddr peeraddr; + char *cdn = NULL; +#endif if ( conn->c_currentber == NULL && (conn->c_currentber = ber_alloc()) == NULL ) { @@ -1174,6 +1193,21 @@ connection_input( errno = 0; +#ifdef LDAP_CONNECTIONLESS + if (conn->c_is_udp) + { + char peername[sizeof("IP=255.255.255.255:65336")]; + len = ber_int_sb_read(conn->c_sb, &peeraddr, + sizeof(struct sockaddr)); + sprintf( peername, "IP=%s:%d", + inet_ntoa( peeraddr.sa_in_addr.sin_addr ), + (unsigned) ntohs( peeraddr.sa_in_addr.sin_port ) ); + Statslog( LDAP_DEBUG_STATS, + "conn=%ld UDP request from %s (%s) accepted.\n", + conn->c_connid, peername, + conn->c_sock_name, 0, 0 ); + } +#endif tag = ber_get_next( conn->c_sb, &len, conn->c_currentber ); if ( tag != LDAP_TAG_MESSAGE ) { int err = errno; @@ -1216,6 +1250,11 @@ connection_input( ber_free( ber, 1 ); return -1; } +#ifdef LDAP_CONNECTIONLESS + if (conn->c_is_udp) { + tag = ber_get_stringa( ber, &cdn ); + } +#endif if ( (tag = ber_peek_tag( ber, &len )) == LBER_ERROR ) { /* log, close and send error */ @@ -1232,6 +1271,22 @@ connection_input( return -1; } +#ifdef LDAP_CONNECTIONLESS + if (conn->c_is_udp && (tag != LDAP_REQ_ABANDON && + tag != LDAP_REQ_SEARCH)) + { +#ifdef NEW_LOGGING + LDAP_LOG(( "connection", LDAP_LEVEL_ERR, + "connection_input: conn %d invalid req for UDP 0x%lx.\n", + conn->c_connid, tag )); +#else + Debug( LDAP_DEBUG_ANY, "invalid req for UDP 0x%lx\n", tag, 0, + 0 ); +#endif + ber_free( ber, 1 ); + return 0; + } +#endif if(tag == LDAP_REQ_BIND) { /* immediately abandon all exiting operations upon BIND */ connection_abandon( conn ); @@ -1239,6 +1294,10 @@ connection_input( op = slap_op_alloc( ber, msgid, tag, conn->c_n_ops_received++ ); +#ifdef LDAP_CONNECTIONLESS + op->o_peeraddr = peeraddr; + op->o_dn = cdn; +#endif if ( conn->c_conn_state == SLAP_C_BINDING || conn->c_conn_state == SLAP_C_CLOSING ) { @@ -1366,8 +1425,10 @@ static int connection_op_activate( Connection *conn, Operation *op ) arg->co_conn = conn; arg->co_op = op; - arg->co_op->o_authz = conn->c_authz; - arg->co_op->o_dn = ch_strdup( conn->c_dn != NULL ? conn->c_dn : "" ); + if (!arg->co_op->o_dn) { + arg->co_op->o_authz = conn->c_authz; + arg->co_op->o_dn = ch_strdup( conn->c_dn != NULL ? conn->c_dn : "" ); + } arg->co_op->o_ndn = ch_strdup( arg->co_op->o_dn ); (void) dn_normalize( arg->co_op->o_ndn ); arg->co_op->o_authtype = conn->c_authtype; diff --git a/servers/slapd/daemon.c b/servers/slapd/daemon.c index 57c3a89d50..461e949c18 100644 --- a/servers/slapd/daemon.c +++ b/servers/slapd/daemon.c @@ -35,22 +35,14 @@ int deny_severity = LOG_NOTICE; time_t starttime; ber_socket_t dtblsize; -typedef union slap_sockaddr { - struct sockaddr sa_addr; - struct sockaddr_in sa_in_addr; -#ifdef LDAP_PF_INET6 - struct sockaddr_in6 sa_in6_addr; -#endif -#ifdef LDAP_PF_LOCAL - struct sockaddr_un sa_un_addr; -#endif -} Sockaddr; - typedef struct slap_listener { char* sl_url; char* sl_name; #ifdef HAVE_TLS int sl_is_tls; +#endif +#ifdef LDAP_CONNECTIONLESS + int sl_is_udp; /* UDP listener is also data port */ #endif ber_socket_t sl_sd; Sockaddr sl_sa; @@ -498,6 +490,7 @@ static Listener * slap_open_listener( unsigned short port; int err, addrlen; struct sockaddr **sal, **psal; + int socktype = SOCK_STREAM; /* default to COTS */ rc = ldap_url_parse( url, &lud ); @@ -543,7 +536,8 @@ static Listener * slap_open_listener( port = (unsigned short) lud->lud_port; - if ( ldap_pvt_url_scheme2proto(lud->lud_scheme) == LDAP_PROTO_IPC ) { + tmp = ldap_pvt_url_scheme2proto(lud->lud_scheme); + if ( tmp == LDAP_PROTO_IPC ) { #ifdef LDAP_PF_LOCAL if ( lud->lud_host == NULL || lud->lud_host[0] == '\0' ) { err = slap_get_listener_addresses(LDAPI_SOCK, 0, &sal); @@ -564,6 +558,10 @@ static Listener * slap_open_listener( return NULL; #endif } else { +#ifdef LDAP_CONNECTIONLESS + if ( tmp == LDAP_PROTO_UDP ) + l.sl_is_udp = 1; +#endif if( lud->lud_host == NULL || lud->lud_host[0] == '\0' || strcmp(lud->lud_host, "*") == 0 ) { @@ -593,7 +591,11 @@ static Listener * slap_open_listener( sal++; continue; } - l.sl_sd = socket( (*sal)->sa_family, SOCK_STREAM, 0); +#ifdef LDAP_CONNECTIONLESS + if (l.sl_is_udp) + socktype = SOCK_DGRAM; +#endif + l.sl_sd = socket( (*sal)->sa_family, socktype, 0); if ( l.sl_sd == AC_SOCKET_INVALID ) { int err = sock_errno(); #ifdef NEW_LOGGING @@ -931,6 +933,17 @@ slapd_daemon_task( for ( l = 0; slap_listeners[l] != NULL; l++ ) { if ( slap_listeners[l]->sl_sd == AC_SOCKET_INVALID ) continue; +#ifdef LDAP_CONNECTIONLESS + /* Since this is connectionless, the data port is the + * listening port. The listen() and accept() calls + * are unnecessary. + */ + if ( slap_listeners[l]->sl_is_udp ) + { + slapd_add( slap_listeners[l]->sl_sd ); + continue; + } +#endif if ( listen( slap_listeners[l]->sl_sd, SLAPD_LISTEN ) == -1 ) { int err = sock_errno(); @@ -1146,6 +1159,26 @@ slapd_daemon_task( if ( !FD_ISSET( slap_listeners[l]->sl_sd, &readfds ) ) continue; +#ifdef LDAP_CONNECTIONLESS + if ( slap_listeners[l]->sl_is_udp ) + { + /* The first time we receive a query, we set this + * up as a "connection". It remains open for the life + * of the slapd. + */ + if ( slap_listeners[l]->sl_is_udp < 2 ) + { + id = connection_init( + slap_listeners[l]->sl_sd, + slap_listeners[l]->sl_url, "", "", + slap_listeners[l]->sl_name, + 2, ssf, authid ); + slap_listeners[l]->sl_is_udp++; + } + continue; + } +#endif + s = accept( slap_listeners[l]->sl_sd, (struct sockaddr *) &from, &len ); if ( s == AC_SOCKET_INVALID ) { @@ -1404,6 +1437,13 @@ slapd_daemon_task( for ( l = 0; slap_listeners[l] != NULL; l++ ) { if ( i == slap_listeners[l]->sl_sd ) { +#ifdef LDAP_CONNECTIONLESS + /* The listener is the data port. Don't + * skip it. + */ + if (slap_listeners[l]->sl_is_udp) + continue; +#endif is_listener = 1; break; } @@ -1453,6 +1493,10 @@ slapd_daemon_task( for ( l = 0; slap_listeners[l] != NULL; l++ ) { if ( i == slap_listeners[l]->sl_sd ) { +#ifdef LDAP_CONNECTIONLESS + if (slap_listeners[l]->sl_is_udp) + continue; +#endif is_listener = 1; break; } @@ -1501,6 +1545,10 @@ slapd_daemon_task( for ( l = 0; slap_listeners[l] != NULL; l++ ) { if ( rd == slap_listeners[l]->sl_sd ) { +#ifdef LDAP_CONNECTIONLESS + if (slap_listeners[l]->sl_is_udp) + continue; +#endif is_listener = 1; break; } diff --git a/servers/slapd/result.c b/servers/slapd/result.c index 0736484e85..a4890e8fc6 100644 --- a/servers/slapd/result.c +++ b/servers/slapd/result.c @@ -309,10 +309,32 @@ send_ldap_response( return; } - rc = ber_printf( ber, "{it{ess", +#ifdef LDAP_CONNECTIONLESS + if (conn->c_is_udp) { + rc = ber_write(ber, (char *)&op->o_peeraddr, sizeof(struct sockaddr), 0); + if (rc != sizeof(struct sockaddr)) { +#ifdef NEW_LOGGING + LDAP_LOG(( "operation", LDAP_LEVEL_ERR, + "send_ldap_response: conn %d ber_write failed\n", + conn ? conn->c_connid : 0 )); +#else + Debug( LDAP_DEBUG_ANY, "ber_write failed\n", 0, 0, 0 ); +#endif + ber_free(ber, 1); + return; + } + rc = ber_printf( ber, "{is{t{ess", + msgid, "", tag, err, + matched == NULL ? "" : matched, + text == NULL ? "" : text ); + } else +#endif + { + rc = ber_printf( ber, "{it{ess", msgid, tag, err, matched == NULL ? "" : matched, text == NULL ? "" : text ); + } if( rc != -1 ) { if ( ref != NULL ) { @@ -342,6 +364,11 @@ send_ldap_response( if( rc != -1 ) { rc = ber_printf( ber, "N}N}" ); } +#ifdef LDAP_CONNECTIONLESS + if( conn->c_is_udp && rc != -1 ) { + rc = ber_printf( ber, "N}" ); + } +#endif if ( rc == -1 ) { #ifdef NEW_LOGGING @@ -731,8 +758,28 @@ send_search_entry( goto error_return; } - rc = ber_printf( ber, "{it{s{" /*}}}*/, op->o_msgid, +#ifdef LDAP_CONNECTIONLESS + if (conn->c_is_udp) { + rc = ber_write(ber, (char *)&op->o_peeraddr, sizeof(struct sockaddr), 0); + if (rc != sizeof(struct sockaddr)) { +#ifdef NEW_LOGGING + LDAP_LOG(( "operation", LDAP_LEVEL_ERR, + "send_search_entry: conn %d ber_printf failed\n", + conn ? conn->c_connid : 0 )); +#else + Debug( LDAP_DEBUG_ANY, "ber_printf failed\n", 0, 0, 0 ); +#endif + ber_free(ber, 1); + return; + } + rc = ber_printf( ber, "{is{t{s{", + op->o_msgid, "", LDAP_RES_SEARCH_ENTRY, e->e_dn ); + } else +#endif + { + rc = ber_printf( ber, "{it{s{" /*}}}*/, op->o_msgid, LDAP_RES_SEARCH_ENTRY, e->e_dn ); + } if ( rc == -1 ) { #ifdef NEW_LOGGING @@ -974,6 +1021,10 @@ send_search_entry( rc = ber_printf( ber, /*{{{*/ "}N}N}" ); +#ifdef LDAP_CONNECTIONLESS + if (conn->c_is_udp && rc != -1) + rc = ber_printf( ber, "}" ); +#endif if ( rc == -1 ) { #ifdef NEW_LOGGING LDAP_LOG(( "operation", LDAP_LEVEL_ERR, diff --git a/servers/slapd/slap.h b/servers/slapd/slap.h index 36071a11d8..2e7d74a8d9 100644 --- a/servers/slapd/slap.h +++ b/servers/slapd/slap.h @@ -218,6 +218,17 @@ typedef struct slap_ssf_set { #define SLAP_SCHERR_NOT_SUPPORTED 15 #define SLAP_SCHERR_BAD_DESCR 16 +typedef union slap_sockaddr { + struct sockaddr sa_addr; + struct sockaddr_in sa_in_addr; +#ifdef LDAP_PF_INET6 + struct sockaddr_in6 sa_in6_addr; +#endif +#ifdef LDAP_PF_LOCAL + struct sockaddr_un sa_un_addr; +#endif +} Sockaddr; + typedef struct slap_oid_macro { struct berval som_oid; char **som_names; @@ -1127,6 +1138,9 @@ struct slap_backend_info { typedef struct slap_op { ber_int_t o_opid; /* id of this operation */ ber_int_t o_msgid; /* msgid of the request */ +#ifdef LDAP_CONNECTIONLESS + Sockaddr o_peeraddr; /* UDP peer address */ +#endif ldap_pvt_thread_t o_tid; /* thread handling this op */ @@ -1194,6 +1208,9 @@ typedef struct slap_conn { BerElement *c_currentber; /* ber we're attempting to read */ int c_writewaiter; /* true if writer is waiting */ +#ifdef LDAP_CONNECTIONLESS + int c_is_udp; /* true if this is (C)LDAP over UDP */ +#endif #ifdef HAVE_TLS int c_is_tls; /* true if this LDAP over raw TLS */ int c_needs_tls_accept; /* true if SSL_accept should be called */