/* search.c - NWDSSearch implementation Copyright (C) 1999, 2000 Petr Vandrovec This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Revision history: 1.00 2000, February 6 Petr Vandrovec Initial release. 1.01 2000, February 7 Petr Vandrovec NWDSExtSyncSearch added. 1.02 2000, August 25 Petr Vandrovec Removed DS_RESOLVE_WALK_TREE from resolve calls. 1.03 2001, June 13 Petr Vandrovec Fixed internal bug assertion when invalid DN passed to NWDSSearch. */ #include "config.h" #include #include #include #include #include "ncplib_i.h" #include "nwnet_i.h" static NWDSCCODE __NWDSSearchV3( NWCONN_HANDLE conn, nuint32 nameFormat, nuint32* lowlevelIH, NWObjectID objectID, nuint32 scope, nuint32 unk1, nuint32 infoType, nuint32 dsiFlags, nuint32 allAttrs, Buf_T* attr, Buf_T* filter, NWObjectCount* countObjectsSearched, Buf_T* objectInfo) { NWDSCCODE err; NW_FRAGMENT rq_frag[3]; nuint8 rq_b[36]; static const nuint8 zero4[4] = { 0, 0, 0, 0}; NW_FRAGMENT rp_frag[2]; nuint8 rp_b[8]; DSET_LH(rq_b, 0, 3); DSET_LH(rq_b, 4, nameFormat); DSET_LH(rq_b, 8, *lowlevelIH); DSET_HL(rq_b, 12, objectID); DSET_LH(rq_b, 16, scope); DSET_LH(rq_b, 20, unk1); DSET_LH(rq_b, 24, infoType); DSET_LH(rq_b, 28, dsiFlags); DSET_LH(rq_b, 32, allAttrs); rq_frag[0].fragAddr.ro = rq_b; rq_frag[0].fragSize = 36; if (!allAttrs && attr) { size_t len; rq_frag[1].fragAddr.ro = NWDSBufRetrieve(attr, &len); rq_frag[1].fragSize = ROUNDPKT(len); } else { rq_frag[1].fragAddr.ro = zero4; rq_frag[1].fragSize = 4; } rq_frag[2].fragAddr.ro = NWDSBufRetrieve(filter, &rq_frag[2].fragSize); rp_frag[0].fragAddr.rw = rp_b; rp_frag[0].fragSize = 8; rp_frag[1].fragAddr.rw = NWDSBufPutPtrLen(objectInfo, &rp_frag[1].fragSize); err = NWCFragmentRequest(conn, DSV_SEARCH, 3, rq_frag, 2, rp_frag, NULL); if (!err) { *lowlevelIH = DVAL_LH(rp_b, 0); if (countObjectsSearched) { *countObjectsSearched = DVAL_LH(rp_b, 4); } NWDSBufPutSkip(objectInfo, rp_frag[1].fragSize); } return err; } static NWDSCCODE __NWDSSearchV4( NWCONN_HANDLE conn, nuint32 nameFormat, nuint32* lowlevelIH, NWObjectID objectID, nuint32 scope, nuint32 unk1, nuint32 infoType, nuint32 dsiFlags, const TimeStamp_T* timeStamp, nuint32 allAttrs, Buf_T* attr, Buf_T* filter, NWObjectCount* countObjectsSearched, Buf_T* objectInfo) { NWDSCCODE err; nuint8 rq_b[44]; NW_FRAGMENT rq_frag[3]; static const nuint8 zero4[4] = {0, 0, 0, 0}; nuint8 rp_b[8]; NW_FRAGMENT rp_frag[2]; DSET_LH(rq_b, 0, 4); DSET_LH(rq_b, 4, nameFormat); DSET_LH(rq_b, 8, *lowlevelIH); DSET_HL(rq_b, 12, objectID); DSET_LH(rq_b, 16, scope); DSET_LH(rq_b, 20, unk1); DSET_LH(rq_b, 24, infoType); DSET_LH(rq_b, 28, dsiFlags); DSET_LH(rq_b, 32, timeStamp->wholeSeconds); WSET_LH(rq_b, 36, timeStamp->replicaNum); WSET_LH(rq_b, 38, timeStamp->eventID); DSET_LH(rq_b, 40, allAttrs); rq_frag[0].fragAddr.ro = rq_b; rq_frag[0].fragSize = 44; if (!allAttrs && attr) { size_t len; rq_frag[1].fragAddr.ro = NWDSBufRetrieve(attr, &len); rq_frag[1].fragSize = ROUNDPKT(len); } else { rq_frag[1].fragAddr.ro = zero4; rq_frag[1].fragSize = 4; } rq_frag[2].fragAddr.ro = NWDSBufRetrieve(filter, &rq_frag[2].fragSize); rp_frag[0].fragAddr.rw = rp_b; rp_frag[0].fragSize = 8; rp_frag[1].fragAddr.rw = NWDSBufPutPtrLen(objectInfo, &rp_frag[1].fragSize); err = NWCFragmentRequest(conn, DSV_SEARCH, 3, rq_frag, 2, rp_frag, NULL); if (!err) { *lowlevelIH = DVAL_LH(rp_b, 0); if (countObjectsSearched) { *countObjectsSearched = DVAL_LH(rp_b, 4); } NWDSBufPutSkip(objectInfo, rp_frag[1].fragSize); } return err; } struct search_referrals { struct search_referrals* next; nuint32 referrals; unsigned char data[0]; }; struct SearchIH { u_int16_t iteration; nuint8* entrystart; struct search_referrals* namePtr; }; static void __NWDSAbortSearchHandle(struct wrappedIterationHandle* ih) { struct SearchIH* searchIH; searchIH = (struct SearchIH*)ih->data; if (searchIH) { struct search_referrals* ptr; ptr = searchIH->namePtr; while (ptr) { struct search_referrals* tmp; tmp = ptr; ptr = ptr->next; free(tmp); } free(searchIH); ih->data = NULL; } } static NWDSCCODE UpdateSearchHandle(struct wrappedIterationHandle** pSearchIH, Buf_T* buffer, NWCONN_HANDLE conn, NWObjectID objectID, nuint32 lowlevelIH) { struct wrappedIterationHandle* searchIH; nuint32 len; nuint32 referrals; void* data; struct search_referrals** end; struct search_referrals* namePtr; NWDSCCODE err; /* length */ err = NWDSBufGetLE32(buffer, &len); if (err) return err; if (len < 4) return ERR_INVALID_SERVER_RESPONSE; err = NWDSBufGetLE32(buffer, &referrals); if (err) return err; data = NWDSBufGetPtr(buffer, len - 4); if (!data) return ERR_BUFFER_EMPTY; searchIH = *pSearchIH; if (!searchIH) { struct SearchIH* sih; sih = malloc(sizeof(*sih)); if (!sih) return ERR_NOT_ENOUGH_MEMORY; searchIH = __NWDSIHInit(conn, lowlevelIH, DSV_SEARCH); if (!searchIH) { free(sih); return ERR_NOT_ENOUGH_MEMORY; } searchIH->abort = __NWDSAbortSearchHandle; searchIH->objectID = objectID; sih->namePtr = NULL; sih->entrystart = NULL; sih->iteration = 0; searchIH->data = sih; } if (referrals) { struct SearchIH* sih; sih = searchIH->data; end = &sih->namePtr; while ((namePtr = *end) != NULL) end = &namePtr->next; namePtr = malloc(sizeof(*namePtr) + len); if (!namePtr) { __NWDSIHAbort(searchIH); return ERR_NOT_ENOUGH_MEMORY; } *end = namePtr; namePtr->next = NULL; namePtr->referrals = referrals; memcpy(namePtr->data, data, len - 4); } *pSearchIH = searchIH; return 0; } static inline void NextNamePtr(struct SearchIH* searchIH) { struct search_referrals* currName; currName = searchIH->namePtr; if (--currName->referrals) { /* skip entry: 32bit info + name */ searchIH->entrystart += 4; searchIH->entrystart += ROUNDPKT(4 + DVAL_LH(searchIH->entrystart, 0)); } else { struct search_referrals* nextName; nextName = currName->next; searchIH->namePtr = nextName; searchIH->entrystart = NULL; free(currName); } } static NWDSCCODE GetCurrentName(struct SearchIH* searchIH, u_int32_t* objInfo, unicode** objName, size_t* objNameLen) { nuint8* curPos; curPos = searchIH->entrystart; if (!curPos) { struct search_referrals* namePtr; namePtr = searchIH->namePtr; if (!namePtr) return ERR_BUFFER_EMPTY; curPos = namePtr->data; searchIH->entrystart = curPos; } *objInfo = DVAL_LH(curPos, 0); /* FIXME: Check buffer overflow */ *objNameLen = DVAL_LH(curPos, 4); *objName = (unicode*)(curPos + 8); NextNamePtr(searchIH); return 0; } static NWDSCCODE __SearchProtocol( NWDSContextHandle ctx, NWCONN_HANDLE conn, NWObjectID objectID, nint scope, Buf_T* filter, const TimeStamp_T* timeStamp, nuint infoType, nuint allAttrs, Buf_T* attrNames, nuint32* lowlevelIH, UNUSED( NWObjectCount countObjectsToSearch), NWObjectCount* countObjectsSearched, Buf_T* objectInfo, nuint32 nameFormat) { nuint32 dsiFlags; nuint32 dckFlags; NWDSCCODE err; if (!filter) return ERR_NULL_POINTER; err = NWDSGetContext(ctx, DCK_FLAGS, &dckFlags); if (err) return err; dsiFlags = ctx->dck.dsi_flags; if (dckFlags & DCV_DEREF_BASE_CLASS) dsiFlags |= DSI_DEREFERENCE_BASE_CLASS; NWDSBufStartPut(objectInfo, DSV_SEARCH); NWDSBufSetDSIFlags(objectInfo, dsiFlags); if (allAttrs) attrNames = NULL; if (timeStamp) { err = __NWDSSearchV4(conn, nameFormat, lowlevelIH, objectID, scope, 0, infoType, dsiFlags, timeStamp, allAttrs, attrNames, filter, countObjectsSearched, objectInfo); } else { err = __NWDSSearchV3(conn, nameFormat, lowlevelIH, objectID, scope, 0, infoType, dsiFlags, allAttrs, attrNames, filter, countObjectsSearched, objectInfo); } NWDSBufFinishPut(objectInfo); /* Now we skip infoType value returned by server. Maybe we should check it for being equal with passed infoType? */ NWDSBufGetSkip(objectInfo, 4); return err; } static NWDSCCODE UpdateServerInSearchHandle(struct wrappedIterationHandle* searchIH, NWCONN_HANDLE conn, NWObjectID objectID) { searchIH->objectID = objectID; __NWDSIHSetConn(searchIH, conn); return 0; } NWDSCCODE NWDSExtSyncSearch( NWDSContextHandle ctx, const NWDSChar* baseObjectName, nint scope, nuint searchAliases, Buf_T* filter, const TimeStamp_T* timeStamp, nuint infoType, nuint allAttrs, Buf_T* attrNames, nuint32* iterHandle, NWObjectCount countObjectsToSearch, NWObjectCount* countObjectsSearched, Buf_T* objectInfo) { NWCONN_HANDLE conn; NWObjectID objectID; nuint32 dckFlags; nuint32 nameForm; struct wrappedIterationHandle* currIH; NWDSCCODE err; wchar_t* revobjname = NULL; size_t revobjnamelen = 0; nuint32 lowlevelIH; nuint32 le32; void* bufptr; err = NWDSGetContext(ctx, DCK_FLAGS, &dckFlags); if (err) return err; nameForm = ctx->dck.name_form; if (dckFlags & DCV_TYPELESS_NAMES) nameForm |= 0x00000001; if (searchAliases) nameForm |= 0x00010000; if (*iterHandle == NO_MORE_ITERATIONS) { currIH = NULL; err = NWDSResolveName2(ctx, baseObjectName, DS_RESOLVE_READABLE, &conn, &objectID); if (err) goto abandon_nc; lowlevelIH = NO_MORE_ITERATIONS; } else { struct SearchIH* sih; currIH = __NWDSIHLookup(*iterHandle, DSV_SEARCH); if (!currIH) return ERR_INVALID_HANDLE; sih = currIH->data; if (currIH->conn) { conn = currIH->conn; ncp_conn_use(conn); objectID = currIH->objectID; lowlevelIH = currIH->iterHandle; } else { sih->iteration++; while (1) { unicode* objName; size_t objNameLen; nuint32 isReal; err = GetCurrentName(sih, &isReal, &objName, &objNameLen); if (err) { err = 0; goto abandon_nc; } if (searchAliases && !isReal) { wchar_t wname[MAX_DN_CHARS+1]; err = NWDSPtrDN(objName, objNameLen, wname, sizeof(wname)); if (err) goto abandon_nc; wcsrev(wname); if (!revobjname) { revobjname = malloc(MAX_DN_BYTES); if (!revobjname) { err = ERR_NOT_ENOUGH_MEMORY; goto abandon_nc; } /* we should return empty string for [root] */ err = NWDSGetCanonicalizedName(ctx, baseObjectName, revobjname); if (err) { goto abandon_nc; } wcsrev(revobjname); revobjnamelen = wcslen(revobjname); /* append dot at the end... */ revobjname[revobjnamelen++] = '.'; } if (!wcsncasecmp(revobjname, wname, revobjnamelen)) continue; } err = __NWDSResolveName2u(ctx, objName, DS_RESOLVE_READABLE, &conn, &objectID); if (err) continue; lowlevelIH = NO_MORE_ITERATIONS; err = UpdateServerInSearchHandle(currIH, conn, objectID); if (err) goto abandon; break; } } /* check for DS_SEARCH_PARTITION? */ if ((scope == DS_SEARCH_SUBORDINATES) && (sih->iteration)) scope = DS_SEARCH_ENTRY; } err = __SearchProtocol(ctx, conn, objectID, scope, filter, timeStamp, infoType, allAttrs, attrNames, &lowlevelIH, countObjectsToSearch, countObjectsSearched, objectInfo, nameForm); if (err) goto abandon; bufptr = NWDSBufTell(objectInfo); err = NWDSBufSkipBuffer(objectInfo); if (err) goto abandon; /* retrieve external references count */ err = NWDSBufPeekLE32(objectInfo, 4, &le32); if (err) goto abandon; if (le32 || (lowlevelIH != NO_MORE_ITERATIONS)) { err = UpdateSearchHandle(&currIH, objectInfo, conn, objectID, lowlevelIH); if (err) goto abandon; } /* Return back to objects buffer */ NWDSBufSeek(objectInfo, bufptr); /* Retrieve objects buffer length */ err = NWDSBufGetLE32(objectInfo, &le32); if (err) goto abandon; /* And modify object end so that we cannot overrun */ objectInfo->dataend = objectInfo->curPos + le32; if (!currIH) { *iterHandle = NO_MORE_ITERATIONS; } else if (lowlevelIH == NO_MORE_ITERATIONS) { struct SearchIH* sih = currIH->data; if (!sih->namePtr) { *iterHandle = NO_MORE_ITERATIONS; __NWDSIHAbort(currIH); } else { __NWDSIHSetConn(currIH, NULL); __NWDSIHPut(currIH, iterHandle); } } else { currIH->iterHandle = lowlevelIH; __NWDSIHPut(currIH, iterHandle); } NWCCCloseConn(conn); goto quit; abandon:; NWCCCloseConn(conn); abandon_nc:; if (currIH) { __NWDSIHUpdate(currIH, err, lowlevelIH, iterHandle); } else { *iterHandle = NO_MORE_ITERATIONS; } /* Rewind buffer */ NWDSBufSeek(objectInfo, objectInfo->data); /* and put zero objects in */ NWDSBufPutLE32(objectInfo, 0); NWDSBufFinishPut(objectInfo); quit:; if (revobjname) free(revobjname); return err; } NWDSCCODE NWDSSearch( NWDSContextHandle ctx, const NWDSChar* baseObjectName, nint scope, nuint searchAliases, Buf_T* filter, nuint infoType, nuint allAttrs, Buf_T* attrNames, nuint32* iterHandle, NWObjectCount countObjectsToSearch, NWObjectCount* countObjectsSearched, Buf_T* objectInfo) { return NWDSExtSyncSearch(ctx, baseObjectName, scope, searchAliases, filter, NULL, infoType, allAttrs, attrNames, iterHandle, countObjectsToSearch, countObjectsSearched, objectInfo); }