@@ -24,6 +24,11 @@ const (
2424 // ControlTypeSubtreeDelete - https://datatracker.ietf.org/doc/html/draft-armijo-ldap-treedelete-02
2525 ControlTypeSubtreeDelete = "1.2.840.113556.1.4.805"
2626
27+ // ControlTypeServerSideSorting - https://www.ietf.org/rfc/rfc2891.txt
28+ ControlTypeServerSideSorting = "1.2.840.113556.1.4.473"
29+ // ControlTypeServerSideSorting - https://www.ietf.org/rfc/rfc2891.txt
30+ ControlTypeServerSideSortingResult = "1.2.840.113556.1.4.474"
31+
2732 // ControlTypeMicrosoftNotification - https://msdn.microsoft.com/en-us/library/aa366983(v=vs.85).aspx
2833 ControlTypeMicrosoftNotification = "1.2.840.113556.1.4.528"
2934 // ControlTypeMicrosoftShowDeleted - https://msdn.microsoft.com/en-us/library/aa366989(v=vs.85).aspx
@@ -53,18 +58,20 @@ const (
5358
5459// ControlTypeMap maps controls to text descriptions
5560var ControlTypeMap = map [string ]string {
56- ControlTypePaging : "Paging" ,
57- ControlTypeBeheraPasswordPolicy : "Password Policy - Behera Draft" ,
58- ControlTypeManageDsaIT : "Manage DSA IT" ,
59- ControlTypeSubtreeDelete : "Subtree Delete Control" ,
60- ControlTypeMicrosoftNotification : "Change Notification - Microsoft" ,
61- ControlTypeMicrosoftShowDeleted : "Show Deleted Objects - Microsoft" ,
62- ControlTypeMicrosoftServerLinkTTL : "Return TTL-DNs for link values with associated expiry times - Microsoft" ,
63- ControlTypeDirSync : "DirSync" ,
64- ControlTypeSyncRequest : "Sync Request" ,
65- ControlTypeSyncState : "Sync State" ,
66- ControlTypeSyncDone : "Sync Done" ,
67- ControlTypeSyncInfo : "Sync Info" ,
61+ ControlTypePaging : "Paging" ,
62+ ControlTypeBeheraPasswordPolicy : "Password Policy - Behera Draft" ,
63+ ControlTypeManageDsaIT : "Manage DSA IT" ,
64+ ControlTypeSubtreeDelete : "Subtree Delete Control" ,
65+ ControlTypeMicrosoftNotification : "Change Notification - Microsoft" ,
66+ ControlTypeMicrosoftShowDeleted : "Show Deleted Objects - Microsoft" ,
67+ ControlTypeMicrosoftServerLinkTTL : "Return TTL-DNs for link values with associated expiry times - Microsoft" ,
68+ ControlTypeServerSideSorting : "Server Side Sorting Request - LDAP Control Extension for Server Side Sorting of Search Results (RFC2891)" ,
69+ ControlTypeServerSideSortingResult : "Server Side Sorting Results - LDAP Control Extension for Server Side Sorting of Search Results (RFC2891)" ,
70+ ControlTypeDirSync : "DirSync" ,
71+ ControlTypeSyncRequest : "Sync Request" ,
72+ ControlTypeSyncState : "Sync State" ,
73+ ControlTypeSyncDone : "Sync Done" ,
74+ ControlTypeSyncInfo : "Sync Info" ,
6875}
6976
7077// Control defines an interface controls provide to encode and describe themselves
@@ -521,6 +528,10 @@ func DecodeControl(packet *ber.Packet) (Control, error) {
521528 return NewControlMicrosoftServerLinkTTL (), nil
522529 case ControlTypeSubtreeDelete :
523530 return NewControlSubtreeDelete (), nil
531+ case ControlTypeServerSideSorting :
532+ return NewControlServerSideSorting (value )
533+ case ControlTypeServerSideSortingResult :
534+ return NewControlServerSideSortingResult (value )
524535 case ControlTypeDirSync :
525536 value .Description += " (DirSync)"
526537 return NewResponseControlDirSync (value )
@@ -716,6 +727,193 @@ func (c *ControlDirSync) SetCookie(cookie []byte) {
716727 c .Cookie = cookie
717728}
718729
730+ // ControlServerSideSorting
731+
732+ type SortKey struct {
733+ Reverse bool
734+ AttributeType string
735+ MatchingRule string
736+ }
737+
738+ type ControlServerSideSorting struct {
739+ SortKeys []* SortKey
740+ }
741+
742+ func (c * ControlServerSideSorting ) GetControlType () string {
743+ return ControlTypeServerSideSorting
744+ }
745+
746+ func NewControlServerSideSorting (value * ber.Packet ) (* ControlServerSideSorting , error ) {
747+ sortKeys := []* SortKey {}
748+
749+ val := value .Children [1 ].Children
750+
751+ if len (val ) != 1 {
752+ return nil , fmt .Errorf ("no sequence value in packet" )
753+ }
754+
755+ sequences := val [0 ].Children
756+
757+ for i , sequence := range sequences {
758+ sortKey := & SortKey {}
759+
760+ if len (sequence .Children ) < 2 {
761+ return nil , fmt .Errorf ("attributeType or matchingRule is missing from sequence %d" , i )
762+ }
763+
764+ sortKey .AttributeType = sequence .Children [0 ].Value .(string )
765+ sortKey .MatchingRule = sequence .Children [1 ].Value .(string )
766+
767+ if len (sequence .Children ) == 3 {
768+ sortKey .Reverse = sequence .Children [2 ].Value .(bool )
769+ }
770+
771+ sortKeys = append (sortKeys , sortKey )
772+ }
773+
774+ return & ControlServerSideSorting {SortKeys : sortKeys }, nil
775+ }
776+
777+ func NewControlServerSideSortingWithSortKeys (sortKeys []* SortKey ) * ControlServerSideSorting {
778+ return & ControlServerSideSorting {SortKeys : sortKeys }
779+ }
780+
781+ func (c * ControlServerSideSorting ) Encode () * ber.Packet {
782+ packet := ber .Encode (ber .ClassUniversal , ber .TypeConstructed , ber .TagSequence , nil , "Control" )
783+ control := ber .NewString (ber .ClassUniversal , ber .TypePrimitive , ber .TagOctetString , c .GetControlType (), "Control Type" )
784+
785+ value := ber .Encode (ber .ClassUniversal , ber .TypePrimitive , ber .TagOctetString , nil , "Control Value" )
786+ seqs := ber .Encode (ber .ClassUniversal , ber .TypeConstructed , ber .TagSequence , nil , "SortKeyList" )
787+
788+ for _ , f := range c .SortKeys {
789+ seq := ber .Encode (ber .ClassUniversal , ber .TypeConstructed , ber .TagSequence , nil , "" )
790+
791+ seq .AppendChild (
792+ ber .NewString (ber .ClassUniversal , ber .TypePrimitive , ber .TagOctetString , f .AttributeType , "attributeType" ),
793+ )
794+ seq .AppendChild (
795+ ber .NewString (ber .ClassContext , ber .TypePrimitive , 0 , f .MatchingRule , "orderingRule" ),
796+ )
797+ if f .Reverse {
798+ seq .AppendChild (
799+ ber .NewBoolean (ber .ClassContext , ber .TypePrimitive , 1 , f .Reverse , "reverseOrder" ),
800+ )
801+ }
802+
803+ seqs .AppendChild (seq )
804+ }
805+
806+ value .AppendChild (seqs )
807+
808+ packet .AppendChild (control )
809+ packet .AppendChild (value )
810+
811+ return packet
812+ }
813+
814+ func (c * ControlServerSideSorting ) String () string {
815+ return fmt .Sprintf (
816+ "Control Type: %s (%q) Criticality:%t %+v" ,
817+ "Server Side Sorting" ,
818+ c .GetControlType (),
819+ false ,
820+ c .SortKeys ,
821+ )
822+ }
823+
824+ // ControlServerSideSortingResponse
825+
826+ const (
827+ ControlServerSideSortingCodeSuccess ControlServerSideSortingCode = 0
828+ ControlServerSideSortingCodeOperationsError ControlServerSideSortingCode = 1
829+ ControlServerSideSortingCodeTimeLimitExceeded ControlServerSideSortingCode = 2
830+ ControlServerSideSortingCodeStrongAuthRequired ControlServerSideSortingCode = 8
831+ ControlServerSideSortingCodeAdminLimitExceeded ControlServerSideSortingCode = 11
832+ ControlServerSideSortingCodeNoSuchAttribute ControlServerSideSortingCode = 16
833+ ControlServerSideSortingCodeInappropriateMatching ControlServerSideSortingCode = 18
834+ ControlServerSideSortingCodeInsufficientAccessRights ControlServerSideSortingCode = 50
835+ ControlServerSideSortingCodeBusy ControlServerSideSortingCode = 51
836+ ControlServerSideSortingCodeUnwillingToPerform ControlServerSideSortingCode = 53
837+ ControlServerSideSortingCodeOther ControlServerSideSortingCode = 80
838+ )
839+
840+ var ControlServerSideSortingCodes = []ControlServerSideSortingCode {
841+ ControlServerSideSortingCodeSuccess ,
842+ ControlServerSideSortingCodeOperationsError ,
843+ ControlServerSideSortingCodeTimeLimitExceeded ,
844+ ControlServerSideSortingCodeStrongAuthRequired ,
845+ ControlServerSideSortingCodeAdminLimitExceeded ,
846+ ControlServerSideSortingCodeNoSuchAttribute ,
847+ ControlServerSideSortingCodeInappropriateMatching ,
848+ ControlServerSideSortingCodeInsufficientAccessRights ,
849+ ControlServerSideSortingCodeBusy ,
850+ ControlServerSideSortingCodeUnwillingToPerform ,
851+ ControlServerSideSortingCodeOther ,
852+ }
853+
854+ type ControlServerSideSortingCode int64
855+
856+ // Valid test the code contained in the control against the ControlServerSideSortingCodes slice and return an error if the code is unknown.
857+ func (c ControlServerSideSortingCode ) Valid () error {
858+ for _ , validRet := range ControlServerSideSortingCodes {
859+ if c == validRet {
860+ return nil
861+ }
862+ }
863+ return fmt .Errorf ("unknown return code : %d" , c )
864+ }
865+
866+ func NewControlServerSideSortingResult (pkt * ber.Packet ) (* ControlServerSideSortingResult , error ) {
867+ control := & ControlServerSideSortingResult {}
868+
869+ if pkt == nil || len (pkt .Children ) == 0 {
870+ return nil , fmt .Errorf ("bad packet" )
871+ }
872+
873+ codeInt , err := ber .ParseInt64 (pkt .Children [0 ].Data .Bytes ())
874+ if err != nil {
875+ return nil , err
876+ }
877+
878+ code := ControlServerSideSortingCode (codeInt )
879+ if err := code .Valid (); err != nil {
880+ return nil , err
881+ }
882+
883+ return control , nil
884+ }
885+
886+ type ControlServerSideSortingResult struct {
887+ Criticality bool
888+
889+ Result ControlServerSideSortingCode
890+
891+ // Not populated for now. I can't get openldap to send me this value, so I think this is specific to other directory server
892+ // AttributeType string
893+ }
894+
895+ func (control * ControlServerSideSortingResult ) GetControlType () string {
896+ return ControlTypeServerSideSortingResult
897+ }
898+
899+ func (c * ControlServerSideSortingResult ) Encode () * ber.Packet {
900+ packet := ber .Encode (ber .ClassUniversal , ber .TypeConstructed , ber .TagSequence , nil , "SortResult sequence" )
901+ sortResult := ber .NewInteger (ber .ClassUniversal , ber .TypePrimitive , ber .TagEnumerated , int64 (c .Result ), "SortResult" )
902+ packet .AppendChild (sortResult )
903+
904+ return packet
905+ }
906+
907+ func (c * ControlServerSideSortingResult ) String () string {
908+ return fmt .Sprintf (
909+ "Control Type: %s (%q) Criticality:%t ResultCode:%+v" ,
910+ "Server Side Sorting Result" ,
911+ c .GetControlType (),
912+ c .Criticality ,
913+ c .Result ,
914+ )
915+ }
916+
719917// Mode for ControlTypeSyncRequest
720918type ControlSyncRequestMode int64
721919
@@ -752,9 +950,12 @@ func (c *ControlSyncRequest) GetControlType() string {
752950func (c * ControlSyncRequest ) Encode () * ber.Packet {
753951 _mode := int64 (c .Mode )
754952 mode := ber .NewInteger (ber .ClassUniversal , ber .TypePrimitive , ber .TagEnumerated , _mode , "Mode" )
755- cookie := ber .Encode (ber .ClassUniversal , ber .TypePrimitive , ber .TagOctetString , nil , "Cookie" )
756- cookie .Value = c .Cookie
757- cookie .Data .Write (c .Cookie )
953+ var cookie * ber.Packet
954+ if len (c .Cookie ) > 0 {
955+ cookie = ber .Encode (ber .ClassUniversal , ber .TypePrimitive , ber .TagOctetString , nil , "Cookie" )
956+ cookie .Value = c .Cookie
957+ cookie .Data .Write (c .Cookie )
958+ }
758959 reloadHint := ber .NewBoolean (ber .ClassUniversal , ber .TypePrimitive , ber .TagBoolean , c .ReloadHint , "Reload Hint" )
759960
760961 packet := ber .Encode (ber .ClassUniversal , ber .TypeConstructed , ber .TagSequence , nil , "Control" )
@@ -764,7 +965,9 @@ func (c *ControlSyncRequest) Encode() *ber.Packet {
764965 val := ber .Encode (ber .ClassUniversal , ber .TypePrimitive , ber .TagOctetString , nil , "Control Value (Sync Request)" )
765966 seq := ber .Encode (ber .ClassUniversal , ber .TypeConstructed , ber .TagSequence , nil , "Sync Request Value" )
766967 seq .AppendChild (mode )
767- seq .AppendChild (cookie )
968+ if cookie != nil {
969+ seq .AppendChild (cookie )
970+ }
768971 seq .AppendChild (reloadHint )
769972 val .AppendChild (seq )
770973
0 commit comments