Hi all
I've been looking at the Notice of disconnection handling in OpenLDAP and there is a thing that is confusing me a bit.
in result.c, the function try_read1msg reads data from socket and creates a new message from that. If we receive an unsolicited message, e.g, notice of disconnection, then the msgId will be zero, and we will go through the following code(taken from /* $OpenLDAP: /libraries/libldap/result.c,v 1.144 2006/12/17 21:04:25 ando Exp $ */):
/* message id */ if ( ber_get_int( ber, &id ) == LBER_ERROR ) { ber_free( ber, 1 ); ld->ld_errno = LDAP_DECODING_ERROR; return( -1 ); }
/* if it's been abandoned, toss it */ if ( ldap_abandoned( ld, id, &idx ) ) {
<REMOVED since the msgId is not abandoned>
retry_ber: ber_free( ber, 1 ); if ( ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) { goto retry; } --> return( LDAP_MSG_X_KEEP_LOOKING ); /* continue looking */ }
lr = ldap_find_request_by_msgid( ld, id ); if ( lr == NULL ) { const char *msg = "unknown";
/* the message type */ tag = ber_peek_tag( ber, &len ); switch ( tag ) { case LBER_ERROR: break;
default: msg = ldap_int_msgtype2str( tag ); break; }
Debug( LDAP_DEBUG_ANY, "no request for response on ld %p msgid %ld message type %s (tossing)\n", (void *)ld, (long)id, msg );
goto retry_ber; }
Assuming there is no more data to be read from the socket we will return in the line marked with the arrow '-->' since ldap_find_request_by_msgid() returns NULL.
What confuses me is that later on in try_read1msg we get to (after constructing the message:
/* is this the one we're looking for? */ if ( msgid == LDAP_RES_ANY || id == msgid ) { if ( all == LDAP_MSG_ONE || ( newmsg->lm_msgtype != LDAP_RES_SEARCH_RESULT && newmsg->lm_msgtype != LDAP_RES_SEARCH_ENTRY && newmsg->lm_msgtype != LDAP_RES_SEARCH_REFERENCE ) ) { *result = newmsg; ld->ld_errno = LDAP_SUCCESS; return( tag );
} else if ( newmsg->lm_msgtype == LDAP_RES_SEARCH_RESULT) { foundit = 1; /* return the chain later */ } }
Do we have a test case testing notice of disconnection? I tried to grep the 'tests' directory for 'notice', 'disconnection' and 'unsolicited' but nothing came up.
Perhaps I'm misunderstanding something here, so if I am I would very much appreciate a hint.
Happy newyear everyone,
Lars Hesel Christensen
-- Lars Hesel Christensen Ericsson Denmark A/S, Telebit lars.hesel.xx.christensen@ericsson.com
Lars Hesel Christensen XX (AH/LMD) wrote:
Hi all
I've been looking at the Notice of disconnection handling in OpenLDAP and there is a thing that is confusing me a bit.
in result.c, the function try_read1msg reads data from socket and creates a new message from that. If we receive an unsolicited message, e.g, notice of disconnection, then the msgId will be zero, and we will go through the following code(taken from /* $OpenLDAP: /libraries/libldap/result.c,v 1.144 2006/12/17 21:04:25 ando Exp $ */):
/* message id */ if ( ber_get_int( ber, &id ) == LBER_ERROR ) { ber_free( ber, 1 ); ld->ld_errno = LDAP_DECODING_ERROR; return( -1 ); }
/* if it's been abandoned, toss it */ if ( ldap_abandoned( ld, id, &idx ) ) {
<REMOVED since the msgId is not abandoned>
retry_ber: ber_free( ber, 1 ); if ( ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) { goto retry; } --> return( LDAP_MSG_X_KEEP_LOOKING ); /* continue looking */ }
lr = ldap_find_request_by_msgid( ld, id ); if ( lr == NULL ) { const char *msg = "unknown";
/* the message type */ tag = ber_peek_tag( ber, &len ); switch ( tag ) { case LBER_ERROR: break; default: msg = ldap_int_msgtype2str( tag ); break; } Debug( LDAP_DEBUG_ANY, "no request for response on ld %p msgid %ld
message type %s (tossing)\n", (void *)ld, (long)id, msg );
goto retry_ber;
}
Assuming there is no more data to be read from the socket we will return in the line marked with the arrow '-->' since ldap_find_request_by_msgid() returns NULL.
What confuses me is that later on in try_read1msg we get to (after constructing the message:
/* is this the one we're looking for? */ if ( msgid == LDAP_RES_ANY || id == msgid ) { if ( all == LDAP_MSG_ONE || ( newmsg->lm_msgtype != LDAP_RES_SEARCH_RESULT && newmsg->lm_msgtype != LDAP_RES_SEARCH_ENTRY && newmsg->lm_msgtype != LDAP_RES_SEARCH_REFERENCE ) ) { *result = newmsg; ld->ld_errno = LDAP_SUCCESS; return( tag );
} else if ( newmsg->lm_msgtype ==
LDAP_RES_SEARCH_RESULT) { foundit = 1; /* return the chain later */ } }
Do we have a test case testing notice of disconnection? I tried to grep the 'tests' directory for 'notice', 'disconnection' and 'unsolicited' but nothing came up.
Perhaps I'm misunderstanding something here, so if I am I would very much appreciate a hint.
Lars,
AFAIK there's no handling, in OpenLDAP's code, of "Notice of Disconnect". Your analysis is correct, the message will be ignored since no request can be found for it. Note that there's no means, right now, to test this condition within OpenLDAP since its server side implementation never returns that message. The point, at the client library side, is: how should this be handled? I mean: if the caller requests "msgid == LDAP_RES_ANY", then the message can be returned, and that's it; otherwise, any unsolicited message should not be queued, but either dealt with by the library, if known, or ignored. This because, in principle, multiple unsolicited messages could be returned, and they would share the same msgid (0).
In the case of notice of disconnect, the library could determine it should no longer expect any message from the server and, as soon as the client tries to submit a new request, or asks for response to a pending request, it should return something like LDAP_UNAVAILABLE or a (yet to be defined) specific return code.
I think we should discuss details of how this is supposed to be handled by the client library, since RFC 4511 seems to give implementors a lot of freedom.
p.
Ing. Pierangelo Masarati OpenLDAP Core Team
SysNet s.n.c. Via Dossi, 8 - 27100 Pavia - ITALIA http://www.sys-net.it ------------------------------------------ Office: +39.02.23998309 Mobile: +39.333.4963172 Email: pierangelo.masarati@sys-net.it ------------------------------------------
Pierangelo Masarati wrote:
Lars Hesel Christensen XX (AH/LMD) wrote:
Do we have a test case testing notice of disconnection? I tried to grep the 'tests' directory for 'notice', 'disconnection' and 'unsolicited' but nothing came up.
Perhaps I'm misunderstanding something here, so if I am I would very much appreciate a hint.
Lars,
AFAIK there's no handling, in OpenLDAP's code, of "Notice of Disconnect". Your analysis is correct, the message will be ignored since no request can be found for it. Note that there's no means, right now, to test this condition within OpenLDAP since its server side implementation never returns that message.
Well, the server will send it for unrecognized request types, as well as incorrectly formatted Controls attached to requests. Of course it's still true that we don't have any client tools that generate these erroneous messages. You can in fact see the server send Disconnect messages while processing the PROTOS or PROTOVER test suites, for example. But in each of these cases, the Disconnect is sent because of an incoming request, so it's not the completely spontaneous event that "unsolicited" implies. Certainly we don't have any detection of an invalidated security layer, which could prompt a LDAP_STRONG_AUTH_REQUIRED error code.
The point, at the client library side, is: how should this be handled? I mean: if the caller requests "msgid == LDAP_RES_ANY", then the message can be returned, and that's it; otherwise, any unsolicited message should not be queued, but either dealt with by the library, if known, or ignored. This because, in principle, multiple unsolicited messages could be returned, and they would share the same msgid (0).
In the case of notice of disconnect, the library could determine it should no longer expect any message from the server and, as soon as the client tries to submit a new request, or asks for response to a pending request, it should return something like LDAP_UNAVAILABLE or a (yet to be defined) specific return code.
I guess the main point of the message is to let the client know that the connection has disappeared because of a server-side decision and not because of a network failure. So it would seem we should define a new return code for this purpose. Meanwhile, if the disconnect carries the PROTOCOL_ERROR or STRONG_AUTH_REQUIRED results, it may be sufficient to just remember them and return them on subsequent client calls.
I think we should discuss details of how this is supposed to be handled by the client library, since RFC 4511 seems to give implementors a lot of freedom.
-----Original Message----- From: Pierangelo Masarati [mailto:ando@sys-net.it]
Lars Hesel Christensen XX (AH/LMD) wrote:
Hi all
I've been looking at the Notice of disconnection handling
in OpenLDAP
and there is a thing that is confusing me a bit.
in result.c, the function try_read1msg reads data from socket and creates a new message from that. If we receive an
unsolicited message,
e.g, notice of disconnection, then the msgId will be zero,
and we will
go through the following code(taken from /* $OpenLDAP: /libraries/libldap/result.c,v 1.144 2006/12/17 21:04:25
ando Exp $ */):
/* message id */ if ( ber_get_int( ber, &id ) == LBER_ERROR ) { ber_free( ber, 1 ); ld->ld_errno = LDAP_DECODING_ERROR; return( -1 ); }
/* if it's been abandoned, toss it */ if ( ldap_abandoned( ld, id, &idx ) ) {
<REMOVED since the msgId is not abandoned>
retry_ber: ber_free( ber, 1 ); if ( ber_sockbuf_ctrl( lc->lconn_sb,
LBER_SB_OPT_DATA_READY, NULL )
) { goto retry; } --> return( LDAP_MSG_X_KEEP_LOOKING ); /* continue looking */ }
lr = ldap_find_request_by_msgid( ld, id ); if ( lr == NULL ) { const char *msg = "unknown";
/* the message type */ tag = ber_peek_tag( ber, &len ); switch ( tag ) { case LBER_ERROR: break; default: msg = ldap_int_msgtype2str( tag ); break; } Debug( LDAP_DEBUG_ANY, "no request for response on ld %p msgid
%ld message type %s
(tossing)\n", (void *)ld, (long)id, msg );
goto retry_ber;
}
Assuming there is no more data to be read from the socket we will return in the line marked with the arrow '-->' since ldap_find_request_by_msgid() returns NULL.
What confuses me is that later on in try_read1msg we get to (after constructing the message:
/* is this the one we're looking for? */ if ( msgid == LDAP_RES_ANY || id == msgid ) { if ( all == LDAP_MSG_ONE || ( newmsg->lm_msgtype != LDAP_RES_SEARCH_RESULT && newmsg->lm_msgtype != LDAP_RES_SEARCH_ENTRY && newmsg->lm_msgtype != LDAP_RES_SEARCH_REFERENCE ) ) { *result = newmsg; ld->ld_errno = LDAP_SUCCESS; return( tag );
} else if ( newmsg->lm_msgtype ==
LDAP_RES_SEARCH_RESULT) { foundit = 1; /* return the chain later */ } }
Do we have a test case testing notice of disconnection? I tried to grep the 'tests' directory for 'notice', 'disconnection'
and 'unsolicited'
but nothing came up.
Perhaps I'm misunderstanding something here, so if I am I
would very
much appreciate a hint.
Lars,
AFAIK there's no handling, in OpenLDAP's code, of "Notice of Disconnect". Your analysis is correct, the message will be ignored since no request can be found for it. Note that there's no means, right now, to test this condition within OpenLDAP since its server side implementation never returns that message. The point, at the client library side, is: how should this be handled? I mean: if the caller requests "msgid == LDAP_RES_ANY", then the message can be returned, and that's it; otherwise, any unsolicited message should not be queued, but either dealt with by the library, if known, or ignored. This because, in principle, multiple unsolicited messages could be returned, and they would share the same msgid (0).
In the case of notice of disconnect, the library could determine it should no longer expect any message from the server and, as soon as the client tries to submit a new request, or asks for response to a pending request, it should return something like LDAP_UNAVAILABLE or a (yet to be defined) specific return code.
I think we should discuss details of how this is supposed to be handled by the client library, since RFC 4511 seems to give implementors a lot of freedom.
p.
Ok, what really confused me when looking at this is the fact that the comments for ldap_result (as well as the man pages) claims that we will receive unsolicited messages when calling ldap_result with LDAP_RES_ANY or LDAP_RES_UNSOLICITED.
I'm not sure about how to handle unsolicited messages other than notice of disconnect, but as it is, when the user can just ignore unsolicited messages, I suppose it would be a good thing if the library itself could deal with them and dispose of them. I think this is what is intended when reading the section about unsolicited messages in the RFC. At least it seems to me that it would be bad/confusing if some unsolicited messages are handled by the application and some are handled by the library.
I think it would not be to hard to create a mock-up server that can accept a new session and then send a notice of disconnection to the client. That would be a simple way to test that client-side code/handling of notice of disconnection - whatever the outcome of the above discussion might be :)
Best regards,
Lars
-- Lars Hesel Christensen Ericsson Denmark A/S, Telebit lars.hesel.xx.christensen@ericsson.com
Lars Hesel Christensen XX (AH/LMD) wrote:
I think it would not be to hard to create a mock-up server that can accept a new session and then send a notice of disconnection to the client. That would be a simple way to test that client-side code/handling of notice of disconnection - whatever the outcome of the above discussion might be :)
Ah, actually I think you can use the retcode overlay to produce a disconnect result.
Howard Chu wrote:
Ah, actually I think you can use the retcode overlay to produce a disconnect result.
Not sure: the notice of disconnect is a specific response message which has 0 msgid, while slapo-retcode can return arbitrary response codes within a response message that's generated in response to a specific request, using the frontend's response calls, and thus with the request's msgid. I don't think there's any chance to cleanly return a response with a msgid of 0, unless the frontend's code is hacked.
p.
Ing. Pierangelo Masarati OpenLDAP Core Team
SysNet s.n.c. Via Dossi, 8 - 27100 Pavia - ITALIA http://www.sys-net.it ------------------------------------------ Office: +39.02.23998309 Mobile: +39.333.4963172 Email: pierangelo.masarati@sys-net.it ------------------------------------------
Pierangelo Masarati wrote:
Howard Chu wrote:
Ah, actually I think you can use the retcode overlay to produce a disconnect result.
Not sure: the notice of disconnect is a specific response message which has 0 msgid, while slapo-retcode can return arbitrary response codes within a response message that's generated in response to a specific request, using the frontend's response calls, and thus with the request's msgid. I don't think there's any chance to cleanly return a response with a msgid of 0, unless the frontend's code is hacked.
OK, never mind. I was thinking that if you configured the overlay to return a SLAPD_DISCONNECT code it would send a response to the actual request and then the frontend would send a separate Disconnect message. However, this will fail with an assert() because you can't send SLAPD_DISCONNECT thru send_ldap_result().
Howard Chu wrote:
Pierangelo Masarati wrote:
Howard Chu wrote:
Ah, actually I think you can use the retcode overlay to produce a disconnect result.
Not sure: the notice of disconnect is a specific response message which has 0 msgid, while slapo-retcode can return arbitrary response codes within a response message that's generated in response to a specific request, using the frontend's response calls, and thus with the request's msgid. I don't think there's any chance to cleanly return a response with a msgid of 0, unless the frontend's code is hacked.
OK, never mind. I was thinking that if you configured the overlay to return a SLAPD_DISCONNECT code it would send a response to the actual request and then the frontend would send a separate Disconnect message. However, this will fail with an assert() because you can't send SLAPD_DISCONNECT thru send_ldap_result().
Should work fine in HEAD retcode.c 1.25.
Simply returning SLAPD_DISCONNECT to the frontend is enough to cause it to send the disconnect message to the client.
Lars Hesel Christensen XX (AH/LMD) wrote:
Ok, what really confused me when looking at this is the fact that the comments for ldap_result (as well as the man pages) claims that we will receive unsolicited messages when calling ldap_result with LDAP_RES_ANY or LDAP_RES_UNSOLICITED.
I'm not sure about how to handle unsolicited messages other than notice of disconnect, but as it is, when the user can just ignore unsolicited messages, I suppose it would be a good thing if the library itself could deal with them and dispose of them. I think this is what is intended when reading the section about unsolicited messages in the RFC. At least it seems to me that it would be bad/confusing if some unsolicited messages are handled by the application and some are handled by the library.
I think it would not be to hard to create a mock-up server that can accept a new session and then send a notice of disconnection to the client. That would be a simple way to test that client-side code/handling of notice of disconnection - whatever the outcome of the above discussion might be :)
Lars,
right now HEAD code should allow you to experiment with (almost arbitrary) unsolicited responses.
The client library right now returns correct unsolicited messages (i.e. extended response messages with msgid == 0) if requested (i.e. if the msgid parameter to ldap_result(3) is either set to LDAP_RES_ANY or LDAP_RES_UNSOLICITED); otherwise, it directly handles the RFC 4511 "Notice of Disconnection" message.
The server allows you to craft a special overlay (slapo-retcode(5)) which can: - return regular response messagess with msgid == 0 - return extended response messages with a given OID and optionally some extended response data - disconnect abruptly without notice.
I haven't tested it with in-directory data yet, although the code should be present. I've only tested it with retcode-item configuration directives like
retcode-item "cn=unsolicited" 0x00 unsolicited="0" retcode-item "cn=unsolicited-extended" 0x00 unsolicited="1.3.6.1.4.1.1466.20036" retcode-item "cn=disconnect" 0x00 flags=disconnect
See test038 for an example that sets up the slapo-retcode(5) overlay. To trigger the abnormal behavior, you can search the database using the "offending" entry as search base, although any operation affecting that DN should be fine.
Cheers, p.
Ing. Pierangelo Masarati OpenLDAP Core Team
SysNet s.n.c. Via Dossi, 8 - 27100 Pavia - ITALIA http://www.sys-net.it ------------------------------------------ Office: +39.02.23998309 Mobile: +39.333.4963172 Email: pierangelo.masarati@sys-net.it ------------------------------------------
Hi Pierangelo
-----Original Message----- From: Pierangelo Masarati [mailto:ando@sys-net.it]
Lars,
right now HEAD code should allow you to experiment with (almost arbitrary) unsolicited responses.
The client library right now returns correct unsolicited messages (i.e. extended response messages with msgid == 0) if requested (i.e. if the msgid parameter to ldap_result(3) is either set to LDAP_RES_ANY or LDAP_RES_UNSOLICITED); otherwise, it directly handles the RFC 4511 "Notice of Disconnection" message.
Thanks, this seems like a good solution. It certainly will suffice for my needs and the code corresponds better with comments and manual pages.
The server allows you to craft a special overlay (slapo-retcode(5)) which can:
- return regular response messagess with msgid == 0
- return extended response messages with a given OID and
optionally some extended response data
- disconnect abruptly without notice.
I haven't tested it with in-directory data yet, although the code should be present. I've only tested it with retcode-item configuration directives like
retcode-item "cn=unsolicited" 0x00 unsolicited="0" retcode-item "cn=unsolicited-extended" 0x00 unsolicited="1.3.6.1.4.1.1466.20036" retcode-item "cn=disconnect" 0x00 flags=disconnect
See test038 for an example that sets up the slapo-retcode(5) overlay. To trigger the abnormal behavior, you can search the database using the "offending" entry as search base, although any operation affecting that DN should be fine.
Sounds great! I will try this out as soon as time permits!
Best regards, Lars -- Lars Hesel Christensen Ericsson Denmark A/S, Telebit lars.hesel.xx.christensen@ericsson.com