/* * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ /* * mslpd_query.c : Handles service requests coming in to the mini SLPv2 SA. * * Version: 1.14 * Date: 10/05/99 * * Licensee will, at its expense, defend and indemnify Sun Microsystems, * Inc. ("Sun") and its licensors from and against any third party * claims, including costs and reasonable attorneys' fees, and be wholly * responsible for any liabilities arising out of or related to the * Licensee's use of the Software or Modifications. The Software is not * designed or intended for use in on-line control of aircraft, air * traffic, aircraft navigation, or aircraft communications; or in the * design, construction, operation or maintenance of any nuclear facility * and Sun disclaims any express or implied warranty of fitness for such * uses. THE SOFTWARE IS PROVIDED TO LICENSEE "AS IS" AND ALL EXPRESS OR * IMPLIED CONDITION AND WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF * MERCHANTABILITY, FITNESS FOR WARRANTIES, INCLUDING ANY IMPLIED * WARRANTY OF MERCHANTABILITY, FITNESS FOR PARTICULAR PURPOSE OR NON- * INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT WILL SUN BE LIABLE HEREUNDER * FOR ANY DIRECT DAMAGES OR ANY INDIRECT, PUNITIVE, SPECIAL, INCIDENTAL * OR CONSEQUENTIAL DAMAGES OF ANY KIND. * * (c) Sun Microsystems, 1998, All Rights Reserved. * Author: Erik Guttman */ /* Portions Copyright (c) 2002 Apple Computer, Inc. All rights reserved. */ #include #include #include #include #include #include "mslp_sd.h" #include "slp.h" #include "mslp.h" #include "mslp_dat.h" #include "mslpd_store.h" #include "mslpd.h" #include "mslpd_mask.h" #include "mslpd_stack.h" #include "mslpd_parse.h" #include "mslplib.h" /* mslplib defs local to the library */ const CFStringRef kProductBuildVersionSAFE_CFSTR = CFSTR("ProductBuildVersion"); const CFStringRef kProductVersionSAFE_CFSTR = CFSTR("ProductVersion"); /* ------------------------------------------------------------------------- */ typedef enum { BOOL_TOK = TYPE_BOOL, INT_TOK = TYPE_INT, STR_TOK = TYPE_STR, OPQ_TOK = TYPE_OPAQUE, KEY_TOK = TYPE_KEYWORD, INPAREN_TOK = 17, OUTPAREN_TOK = 18, NOT_TOK = 19, OR_TOK = 20, AND_TOK = 21, TAG_TOK = 22, EQ_TOK = 23, LE_TOK = 24, GE_TOK = 25, IS_TOK = 26, TERM_TOK = 27, ERR_TOK = 28, INIT_TOK = 29, APPROX_TOK = 30 /* initially */ } MSLPQToktype; typedef struct mslpqtoken { MSLPQToktype type; union { int i; /* 0 or 1 if BOOL_TOK, errcode if ERR_TOK, int if INT_TOK */ char *pc; /* str if STR_TOK, opaque encoding if OPQ_TOK , op if OP_TOK */ } val; } MSLPQToken; typedef struct ServiceLocationHeader{ char byte1; char byte2; char byte3; char byte4; char byte5; char byte6; char byte7; char byte8; char byte9; char byte10; char byte11; char byte12; char byte13; char byte14; char byte15; char byte16; } ServiceLocationHeader, *ServiceLocationHeaderPtr; #ifdef ENABLE_SLP_LOGGING #define QERR(s,err) { SLP_LOG( SLP_LOG_DROP,(s)); return NULL; } #else #define QERR(s,err) { return NULL; } #endif /* ------------------------------------------------------------------------- */ void CheckPRListAgainstOurKnownDAs( const char* prList ); SLPInternalError CheckIfRequestAlreadyHandled( const char* buffer, int length, char* ourHostIPAddr ); char* MakePluginInfoMessage( SAState* psa, const char* buffer ); short GetSLPHeaderSize( ServiceLocationHeader* header ) ; static SLPInternalError use_mask(SAStore *ps, const char *pcSAScopeList, const char *pcRqstScopeList, const char *pcSrvtype, const char *pcLangTag, const char *pcQuery, Mask **ppMask); static SLPInternalError handle_query(SAStore *ps, const char *pcSASList, const char *pcRqstSList, const char *pcSrvtype, const char *pcLangTag, const char *pcQuery, Mask **ppMask); static void next_token(MSLPQToken tokPrev, MSLPQToken *ptok, const char *pcQuery, int *piIndex); static SLPInternalError handle_term( SAStore *ps, MSLPQState state, Mask *pmUseMask, Mask *pmResultMask, MSLPQToken tagtok, MSLPQToken optok, MSLPQToken valtok); static int op(SAStore *ps,int i,char *pcTag,MSLPQToktype ttOp,MSLPQToken tok); static MSLPQToktype delim2Toktype(char c, const char *pcNext); /* ------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------- */ /* * store_request * * This routine takes a Service Request and returns a buffer with * a reply (or drops the request). If the request is dropped, * the return buffer is set to NULL and the output size is 0. * Otherwise a full reply will be constructed in the output buffer. * * Args: * * psa The SAState, all constants, config and a handle to the store * pslphdr The header of the request - already parsed * pcInBuf The incoming buffer * iInSz The size of the incoming buffer (may be different than hdr 'len' * ppcOutBuf The generated reply buffer will be allocated and passed back * piOutSz The length of the reply buffer will be set and passed back * piGot The number of results obtained. * * Returns: int * * If at all possible, a result (possibly error) is returned. * In the case of a fatal error, -1 is returned and buffers are cleaned up. * * Side Effects: * * The reply buffer is allocated here and must be SLPFreed by the caller. */ int store_request(SAState *psa, SLPBoolean viaTCP, Slphdr *pslphdr, const char *pcInBuf, int iInSz, char **ppcOutBuf, int *piOutSz, int *piGot) { char *pcPRList=NULL, *pcSrvtype = NULL, *pcSList = NULL, *pcPred = NULL; int err; int result = 0; *ppcOutBuf = NULL; /* for now, just drop the request! */ *piOutSz = 0; *piGot = 0; if( psa && pslphdr && pcInBuf && ppcOutBuf && piOutSz ) { if ((err = srvrqst_in(pslphdr,pcInBuf,iInSz,&pcPRList, &pcSrvtype,&pcSList,&pcPred))<0) { #ifdef ENABLE_SLP_LOGGING SLP_LOG( SLP_LOG_DROP,"store_request: drop request due to parse in error" ); #endif } else if (on_PRList(psa,pcPRList)) { #ifdef ENABLE_SLP_LOGGING SLP_LOG( SLP_LOG_DROP,"store_request: drop request which is on my PRList" ); #endif result = -1; } else { /* not on PRList */ if ( !SDstrcasecmp(pcSrvtype,"service:directory-agent") ) { if ( pcPRList && pcPRList[0] != '\0' ) CheckPRListAgainstOurKnownDAs( pcPRList ); #ifdef MAC_OS_X if ( AreWeADirectoryAgent() ) /* if we are running as a DA, then we need to return our DAAdvert */ { #ifdef ENABLE_SLP_LOGGING SLPLOG( SLP_LOG_DA,"handling a service:directory-agent type request" ); #endif err = daadvert_out( psa, viaTCP, pslphdr, ppcOutBuf, piOutSz ); if (err == SLP_OK) *piGot = 1; } else { result = -1; // don't just return, fall out and free our memory! } #else return -1; /* drop DADiscovery requests */ #endif /* MAC_OS_X */ } else if ( !SDstrcasecmp(pcSrvtype,"service:service-agent") ) { #ifdef EXTRA_MSGS SLPLOG(SLP_LOG_SA,"store_request: handling a service:service-agent type request"); /* handle a sa discovery request here */ err = saadvert_out(psa,pslphdr,ppcOutBuf, piOutSz); if (err == SLP_OK) { *piGot = 1; } #else result = -1; /* sa disc is not supported, no result! */ #endif /* EXTRA_MSGS */ } else if ( !SDstrcasecmp(pcSrvtype,"service:service-agent") ) { } else { Mask *pmask; SLPInternalError serr = handle_query(&(psa->store), SLPGetProperty("net.slp.useScopes"), pcSList,pcSrvtype,pslphdr->h_pcLangTag,pcPred, &pmask); /* it is ok to generate a reply if the above had an error - this is how the error result gets propogated back to the requester */ err = srvrply_out(&(psa->store), serr, pmask, pslphdr, ppcOutBuf, piOutSz, piGot); if (err != SLP_OK) result = -1; mask_delete(pmask); } } } else result = SLP_PARAMETER_BAD; if (pcSrvtype) SLPFree((void*)pcSrvtype); if (pcPRList) SLPFree((void*)pcPRList); if (pcSList) SLPFree((void*)pcSList); if (pcPred) SLPFree((void*)pcPred); return result; } SLPInternalError HandlePluginInfoRequest( SAState* psa, const char* buffer, int length, char** reply, int *replySize ) { char* serviceReply = NULL; SLPInternalError error = SLP_OK; // let's check to see if we have already replied to this if ( error == SLP_OK ) error = CheckIfRequestAlreadyHandled( buffer, length, psa->pcSANumAddr ); if ( error == SLP_OK ) serviceReply = MakePluginInfoMessage( psa, buffer ); if ( error == SLP_OK ) { *reply = serviceReply; *replySize = GETLEN( (*reply) ); // Need ()'s around this MACRO!!! } if ( error && error != SLP_REQUEST_ALREADY_HANDLED ) LOG_SLP_ERROR_AND_RETURN( SLP_LOG_DEBUG, "HandlePluginInfoRequest returning error: %d", error ); return error; } char* globalDAList = NULL; char* globalUnknownList = NULL; void CheckPRListAgainstOurKnownDAs( const char* prList ) { #ifdef DEBUG_DA_LIST // this is primarly a dugging tool at the moment, we want to see how many DA's are known by // other people that we don't know about char* pcTemp = NULL; char cDelim; int offset = 0; int result = 0; int i; int listLen; DATable* pdat = GetGlobalDATable(); // ignore what they pass in, only reference the globaly defined table if ( !pdat || !prList || prList[0] == '\0' ) return; if ( globalDAList && globalDAList[0] == '\0' ) { free(globalDAList); globalDAList = NULL; } if ( !globalDAList ) { globalDAList = (char*)malloc( strlen(prList) + 1 ); strcpy( globalDAList, prList ); } else { listLen = strlen(globalDAList); list_merge( prList, &globalDAList, &listLen, 1 ); } LockGlobalDATable(); for (i = 0; i < pdat->iSize; i++) { int pcTempLen; if ( pcTemp ) { pcTempLen = strlen(pcTemp); list_merge( inet_ntoa(pdat->pDAE[i].sin.sin_addr), &pcTemp, &pcTempLen, 0 ); } else { pcTemp = (char*)malloc( strlen(inet_ntoa(pdat->pDAE[i].sin.sin_addr))+1 ); strcpy( pcTemp, inet_ntoa(pdat->pDAE[i].sin.sin_addr) ); } if ( globalDAList && list_intersection( globalDAList, inet_ntoa(pdat->pDAE[i].sin.sin_addr) ) ) { char* oldList = globalDAList; char* newList = list_remove_element( globalDAList, inet_ntoa(pdat->pDAE[i].sin.sin_addr) ); globalDAList = newList; free( oldList ); } } #ifdef ENABLE_SLP_LOGGING if ( globalDAList && globalDAList[0] && pcTemp ) { SLP_LOG( SLP_LOG_DA, "Unknown DA's: %s", globalDAList ); SLP_LOG( SLP_LOG_DA, "Known DA's (%d):%s", pdat->iSize, pcTemp ); } #endif UnlockGlobalDATable(); #endif } SLPInternalError CheckIfRequestAlreadyHandled( const char* buffer, int length, char* ourHostIPAddr ) { const char* curPtr = buffer+GetSLPHeaderSize( (ServiceLocationHeader*)buffer); // point to the length of the prev responders list int preRespondersLength = *(short*)curPtr; SLPInternalError error = SLP_OK; short len; { curPtr += 2; // advance past 2 bit length while ( curPtr < buffer+GetSLPHeaderSize( (ServiceLocationHeader*)buffer)+preRespondersLength ) { len = strlen(ourHostIPAddr); if ( ( strncmp(ourHostIPAddr, curPtr, len) == 0) && ((curPtr[len] == ',') || (curPtr[len] == '\0')) ) { error = SLP_REQUEST_ALREADY_HANDLED; break; } // now advance to the next comma while ( curPtr < buffer+length && *curPtr != ',' ) curPtr++; curPtr++; // advance past comma } } return error; } char* gProductBuildVersion = NULL; char* gProductVersion = NULL; void GetSystemVersion( void ) { size_t xmlLen; CFDataRef xmlRef = NULL; FILE* xmlFP = NULL; CFStringRef errorString = NULL; xmlFP = fopen("/System/Library/CoreServices/SystemVersion.plist","r"); if ( xmlFP ) { char xmlBuffer[4*1024]; xmlLen = fread( xmlBuffer, sizeof(xmlBuffer), 1, xmlFP ); if ( xmlLen || feof(xmlFP) ) xmlRef = CFDataCreate( NULL, (const UInt8*)xmlBuffer, strlen(xmlBuffer) ); fclose( xmlFP ); } if ( xmlRef ) { CFDictionaryRef systemInfoRef = (CFDictionaryRef)CFPropertyListCreateFromXMLData( NULL, xmlRef, kCFPropertyListImmutable, &errorString); if ( systemInfoRef ) { CFStringRef buildVersionRef = (CFStringRef)CFDictionaryGetValue( systemInfoRef, kProductBuildVersionSAFE_CFSTR ); CFStringRef productVersionRef = (CFStringRef)CFDictionaryGetValue( systemInfoRef, kProductVersionSAFE_CFSTR ); char tempBuf[1024]; if ( buildVersionRef && CFGetTypeID(buildVersionRef) == CFStringGetTypeID() ) { CFStringGetCString( buildVersionRef, tempBuf, sizeof(tempBuf), kCFStringEncodingUTF8 ); gProductBuildVersion = (char*)malloc( strlen(tempBuf)+1 ); strcpy( gProductBuildVersion, tempBuf ); } if ( productVersionRef && CFGetTypeID(productVersionRef) == CFStringGetTypeID() ) { CFStringGetCString( productVersionRef, tempBuf, sizeof(tempBuf), kCFStringEncodingUTF8 ); gProductVersion = (char*)malloc( strlen(tempBuf)+1 ); strcpy( gProductVersion, tempBuf ); } CFRelease( systemInfoRef ); } CFRelease( xmlRef ); } if ( !gProductVersion ) gProductVersion = "X"; if ( !gProductBuildVersion ) gProductBuildVersion = "??"; } char* MakePluginInfoMessage( SAState* psa, const char* buffer ) { char siURL[1024]; char* replyPtr; char* newReply = NULL; char* curPtr; short replyLength, newReplyLength, urlEntryLength; // this is a new reply, fill out empty header + room for error code and url entry count replyLength = GetSLPHeaderSize( (ServiceLocationHeader*)buffer)+4; replyPtr = (char*)malloc( replyLength ); SETVER(replyPtr,2); SETFUN(replyPtr, SRVRPLY); SETLEN(replyPtr, replyLength); SETLANG(replyPtr,"en"); SETXID(replyPtr, GETXID(buffer)); // might need to check if this is appropriate for DA *((short*)(replyPtr+GetSLPHeaderSize( (ServiceLocationHeader*)buffer))) = 0; // set the error *((short*)((char*)replyPtr+GetSLPHeaderSize( (ServiceLocationHeader*)buffer)+2)) = 0; // set the entry count to zero replyLength = GETLEN( replyPtr ); if ( !gProductVersion || !gProductBuildVersion ) GetSystemVersion(); sprintf( siURL, "service:x-MacSLPInfo://%s/Version=%s;OSVersion=%s(%s);NumRegServices=%d", psa->pcSANumAddr, SLPD_VERSION, gProductVersion, gProductBuildVersion, psa->store.size ); urlEntryLength = 1 + 2 + 2 + strlen(siURL) + 1; // reserved, lifetime, url len, url, # url authentication blocks newReplyLength = replyLength + urlEntryLength; curPtr = replyPtr + GetSLPHeaderSize( (ServiceLocationHeader*)buffer); *((short*)curPtr) = 0; curPtr += 2; // advance past the error to the urlEntry count; *((short*)curPtr) += 1; // increment the urlEntry count; newReply = (char*)malloc( newReplyLength ); memcpy( newReply, replyPtr, replyLength ); free( replyPtr ); curPtr = newReply+replyLength; // now we should be pointing at the end of old data, append new url entry *curPtr = 0; // zero out the reserved bit curPtr++; *((short*)curPtr) = 0; // set lifetime curPtr += 2; *((short*)curPtr) = strlen(siURL); // set url length curPtr += 2; memcpy( curPtr, siURL, strlen(siURL) ); curPtr += strlen(siURL); *curPtr = 0; // this is for the url auth block (zero of them) replyPtr = newReply; SETLEN( replyPtr, newReplyLength ); return replyPtr; } short GetSLPHeaderSize( ServiceLocationHeader* header ) { // now the length of the header is dependant on the length of the language // tag (which usually will be 2 bytes but it could be longer). short headerSize = sizeof( ServiceLocationHeader ); // always at least this short languageTagLength; if ( header == NULL ) return 0; languageTagLength = *((short*)&(header->byte13)); headerSize += ( languageTagLength - 2 ); // we already assumed 2 bytes, compensate return headerSize; } /* ------------------------------------------------------------------------- */ /* * match_langtag * * Match the languages, ignoring the dialect field * * Returns: * * 0 on no match, 1 on match, SLP_PARSE_ERROR if langtags missing. */ int match_langtag(const char *pc1, const char *pc2) { char c; int i1 = 0, i2 = 0, retval = 0; char *pcNoDialect1 = NULL; char *pcNoDialect2 = NULL; if ( !pc1 || !pc2 ) retval = SLP_PARSE_ERROR; else { pcNoDialect1 = get_next_string("-",pc1,&i1,&c); pcNoDialect2 = get_next_string("-",pc2,&i2,&c); if (!pcNoDialect1 || !pcNoDialect2) retval = SLP_PARSE_ERROR; else if (!SDstrcasecmp(pcNoDialect1,pcNoDialect2)) retval = 1; if (pcNoDialect1) SLPFree((void*)pcNoDialect1); if (pcNoDialect2) SLPFree((void*)pcNoDialect2); } return retval; } /* * match_srvtype * * Match the request by the item in the store. The match may be by * abstract service type. * * Return: * * 0 on no match, 1 on match, SLP_PARSE_ERROR for missing fields. */ int match_srvtype(const char *pcstRqst, const char *pcstStore) { int retval = 0; if (!pcstRqst || !pcstStore) retval = SLP_PARSE_ERROR; else if (!SDstrcasecmp(pcstRqst,pcstStore)) retval = 1; else { int iLen = strlen(pcstRqst); if (!SDstrncasecmp(pcstRqst,pcstStore,iLen) && pcstStore[iLen] == ':') retval = 1; /* abstract service types match */ } return retval; } /* * use_mask * * This routine checks the store to generate a mask which includes only * those services with the right service type, scope list and lang tag. * * ps Pointer to the store. * pcSASList The scope list of the SA. * pcRqstSList The scope list of the request. * pcSrvtype The service type of the request. * pcLangTag The lang tag of the request. * pcQuery The search filter of the request. If it is NULL ignore lang. * ppMask A pointer to the mask which this function will allocate. * * Return: * SLPInternalError. * ppMask is allocated, filled and returned. * * Side effects: * ppMask allocation will have to be SLPFreed by the caller. */ static SLPInternalError use_mask(SAStore *ps, const char *pcSASList, const char *pcRqstSList, const char *pcSrvtype, const char *pcLangTag, const char *pcQuery, Mask **ppMask) { int i, err = 0; #ifdef TRACE_MASK if ( getenv("SLPTRACE") ) printf("trace: mask size = %d. Mask is initially:\n",ps->size); #endif *ppMask = mask_create(ps->size); if (!list_intersection(pcRqstSList, pcSASList)) { #ifdef ENABLE_SLP_LOGGING SLP_LOG( SLP_LOG_DROP, "use_mask returning SLP_SCOPE_NOT_SUPPORTED, requestScope: %s SAScopeList: %s", pcRqstSList?pcRqstSList:"", pcSASList?pcSASList:"" ); #endif return SLP_SCOPE_NOT_SUPPORTED; } for (i = 0; i < ps->size; i++) { mask_set(*ppMask,i,0); if (pcQuery) { if ((err = match_langtag(pcLangTag,ps->lang[i]))==0) { #ifdef ENABLE_SLP_LOGGING SLP_LOG( SLP_LOG_DROP, "use_mask match_langtag no match, pcLangTag: %s ps->lang[%d]: %s", pcLangTag?pcLangTag:"", i, ps->lang[i]?ps->lang[i]:"" ); #endif continue; } } if (err<0 || ((err=match_srvtype(pcSrvtype,ps->srvtype[i]))==0)) { #ifdef ENABLE_SLP_LOGGING SLP_LOG( SLP_LOG_DROP, "use_mask err: %d, or match_srvtype no match, pcSrvtype: %s ps->srvtype[%d]: %s", err, pcSrvtype?pcSrvtype:"", i, ps->srvtype[i]?ps->srvtype[i]:"" ); #endif continue; } /* some services may be registered with a subset of the SA's scopes */ if (!list_intersection(pcRqstSList,ps->scope[i])) { #ifdef ENABLE_SLP_LOGGING SLP_LOG( SLP_LOG_DROP, "use_mask scope: %s doesn't match ps->scope[%d]: %s", pcRqstSList?pcRqstSList:"", i, ps->scope[i]?ps->scope[i]:"" ); #endif continue; } if (err>=0) mask_set(*ppMask,i,1); } #ifdef TRACE_MASK mask_show(*ppMask); #endif if (err >= 0) return SLP_OK; else return SLP_PARSE_ERROR; } /* * on_PRList * * Return: * 0 if not on PRList, 1 if on PRList */ int on_PRList(SAState *psa, char *pcPRList) { char *pcTemp = NULL, cDelim; int offset = 0, result = 0; if ( psa && pcPRList ) { while ( (pcTemp = get_next_string(",",pcPRList,&offset,&cDelim)) != NULL ) { if ( !SDstrcasecmp(pcTemp,psa->pcSAHost) || !SDstrcasecmp(pcTemp,psa->pcSANumAddr) ) { result = 1; break; } SLPFree((void*)pcTemp); pcTemp = NULL; } if (pcTemp) SLPFree((void*)pcTemp); } return result; } /* ------------------------------------------------------------------------- */ /* * * * * * There are two kinds of state, here. One is pushed onto a frame when a * new '(' nesting arrives. These include the transitions below. Note * that transitions to and from TERM do not change the stack state settings. * For this purpose a variable 'state' is kept. It is initialized to the * state of the frame after a push or a pop. It will change in and out of * 'term' till the concluding ')' comes, then a pop will restore the * previous state. * * State graph * * [INIT] -- '(' '&' --> [AND] * -- '(' '|' --> [OR] * -- '(' '!' --> [NOT] * -- '(' tag --> [TERM] * -- ')' --> return result mask, done * [AND] -- '(' '&' push --> [AND] * -- '(' '|' push (invert mask) --> [OR] * -- '(' '!' push --> [NOT] * -- '(' tag --> [TERM] * -- ')' --> pop * [OR] -- '(' '&' push --> [AND] * -- '(' '|' push (invert mask) --> [OR] * -- '(' '!' push --> [NOT] * -- '(' tag --> [TERM] * -- ')' --> pop * [NOT] -- '(' '&' push --> [AND] * -- '(' '|' push (invert mask) --> [OR] * -- '(' '!' push --> [NOT] * -- '(' tag --> [TERM] * -- ')' --> pop (invert mask) * [TERM] -- op ')' --> restore current stack state * -- op val ')' --> restore current stack state * */ static SLPInternalError handle_query(SAStore *ps, const char *pcSAScopeList, const char *pcRqstScopeList, const char *pcSrvtype, const char *pcLangTag, const char *pcQuery, Mask **ppMask) { MSLPQToken tok, prevtok, tagtok; int index = 0; Mask *pmUse; /* the base mask, either returned or in stack */ Mask *pmOnly; /* the mask of the only possible matching services */ Mask *pmTemp; /* used for mask calculations */ SLPInternalError err; MSLPQState state = INIT_STATE; MSLPQStack *pQS; if (pcSAScopeList == NULL) pcSAScopeList = ""; /* guard this parameter */ err = use_mask(ps,pcSAScopeList,pcRqstScopeList, pcSrvtype,pcLangTag,pcQuery,&pmOnly); *ppMask = NULL; /* initialize the returned mask to NULL in case of error */ if (err != SLP_OK) { *ppMask = pmOnly; /* the error came from use_mask. Return '0' mask */ return err; } if (pcQuery[0] == '\0') { /* special case: empty query mask */ *ppMask = pmOnly; return SLP_OK; } pmUse = mask_clone(pmOnly); pQS = stack_init(pmUse); prevtok.type = INIT_TOK; for (next_token(prevtok,&tok,pcQuery,&index) ; (tok.type != TERM_TOK && tok.type != ERR_TOK); next_token(prevtok,&tok,pcQuery,&index)) { switch(state) { case INIT_STATE: /* beginning of parse, INIT_STATE frame was 'pushed' as part of the initialization of the stack */ if (tok.type != INPAREN_TOK) { mask_delete(pmOnly); stack_delete(pQS); LOG_SLP_ERROR_AND_RETURN(SLP_LOG_DROP,"handle_query: no initial '('",SLP_PARSE_ERROR); } prevtok = tok; next_token(prevtok,&tok,pcQuery,&index); /* set the currect state to advance, INIT stack frame already there */ switch(tok.type) { case AND_TOK: state = AND_STATE; stack_push(pQS,mask_clone(stack_curr(pQS)->pmask),AND_STATE); break; case OR_TOK: state = OR_STATE; pmTemp = mask_invert(stack_curr(pQS)->pmask); /* and with the pmOnly mask, so only the allowed services are sought */ stack_push(pQS,mask_and(pmTemp,pmOnly),OR_STATE); mask_delete(pmTemp); break; case NOT_TOK: state = NOT_STATE; stack_push(pQS,mask_clone(stack_curr(pQS)->pmask),NOT_STATE); break; case TAG_TOK: state = TERM_STATE; break; /* do not push */ default: mask_delete(pmOnly); stack_delete(pQS); LOG_SLP_ERROR_AND_RETURN(SLP_LOG_DROP,"handle_query: bad init term:",SLP_PARSE_ERROR); } break; case AND_STATE: /* following a & */ case OR_STATE: /* following a | */ case NOT_STATE: /* following a ! */ if (tok.type == OUTPAREN_TOK) { /* pop */ Mask *pmPopTemp; if (stack_curr(pQS)->state == NOT_STATE) { pmPopTemp = mask_invert(stack_curr(pQS)->pmask); /* and with the pmOnly mask so only allowed services match */ pmTemp = mask_and(pmPopTemp,pmOnly); mask_delete(stack_curr(pQS)->pmask); mask_delete(pmPopTemp); stack_curr(pQS)->pmask = pmTemp; } if (stack_prev(pQS)->state == AND_STATE) { pmTemp = mask_and(stack_prev(pQS)->pmask,stack_curr(pQS)->pmask); } else if (stack_prev(pQS)->state == OR_STATE) { pmTemp = mask_or(stack_prev(pQS)->pmask,stack_curr(pQS)->pmask); } else if (stack_prev(pQS)->state == INIT_STATE) { pmTemp = mask_clone(stack_curr(pQS)->pmask); } else if (stack_prev(pQS)->state == NOT_STATE){ pmTemp = mask_clone(stack_curr(pQS)->pmask); } else { mask_delete(pmOnly); stack_delete(pQS); LOG_SLP_ERROR_AND_RETURN(SLP_LOG_ERR,"handle_query: bad prev state",SLP_PARSE_ERROR); } mask_delete(stack_prev(pQS)->pmask); mask_delete(stack_curr(pQS)->pmask); stack_prev(pQS)->pmask = NULL; stack_curr(pQS)->pmask = NULL; if (stack_prev(pQS)->state == INIT_STATE) { /* done: no more frames to pop */ *ppMask = pmTemp; #ifdef TRACE_MASK printf("at return: \n"); mask_show(*ppMask); #endif mask_delete(pmOnly); stack_delete(pQS); return SLP_OK; } else { /* set the previous frame to have the right updated mask */ stack_prev(pQS)->pmask = pmTemp; if (stack_pop(pQS) == NULL) { SLPLOG(SLP_LOG_ERR,"handle_query: unexpected NULL query stack frame"); } } } else if (tok.type == INPAREN_TOK) { prevtok = tok; next_token(prevtok,&tok,pcQuery,&index); /* push the current stack frame to advance */ switch (tok.type) { case AND_TOK: state = AND_STATE; if (stack_curr(pQS)->state == OR_STATE) { pmTemp = mask_invert(stack_curr(pQS)->pmask); /* and with the pmOnly mask to match only allowed services */ stack_push(pQS,mask_and(pmTemp,pmOnly),AND_STATE); mask_delete(pmTemp); } else { stack_push(pQS,mask_clone(stack_curr(pQS)->pmask),AND_STATE); } break; case OR_TOK: state = OR_STATE; if (stack_curr(pQS)->state != OR_STATE) { pmTemp = mask_invert(stack_curr(pQS)->pmask); stack_push(pQS,mask_and(pmTemp,pmOnly),OR_STATE); mask_delete(pmTemp); } else { stack_push(pQS,mask_clone(stack_curr(pQS)->pmask),OR_STATE); } break; case NOT_TOK: state = NOT_STATE; stack_push(pQS,mask_clone(stack_curr(pQS)->pmask),NOT_STATE); break; case TAG_TOK: state = TERM_STATE; break; /* don't push */ default: mask_delete(pmOnly); stack_delete(pQS); LOG_SLP_ERROR_AND_RETURN(SLP_LOG_DROP,"handle_query: bad init term",SLP_PARSE_ERROR); } } break; case TERM_STATE: /* inside a terminal */ /* prevtok = tag, tok = op, so we must advance */ tagtok = prevtok; prevtok = tok; /* tagtok=tag,prevtok=op,tok=val */ next_token(prevtok,&tok,pcQuery,&index); if (tok.type == ERR_TOK) { mask_delete(pmOnly); stack_delete(pQS); LOG_SLP_ERROR_AND_RETURN(SLP_LOG_DROP,"handle_query: value bad",SLP_PARSE_ERROR); } #ifdef TRACE_MASK printf("before handle query: \n"); mask_show(stack_curr(pQS)->pmask); #endif if ((err = handle_term(ps, stack_curr(pQS)->state, pmUse, stack_curr(pQS)->pmask,tagtok,prevtok,tok)) != SLP_OK) { mask_delete(pmOnly); stack_delete(pQS); LOG_SLP_ERROR_AND_RETURN(SLP_LOG_DROP,"handle_query: processing terminal failed",err); } #ifdef TRACE_MASK printf("after handle query: \n"); mask_show(stack_curr(pQS)->pmask); #endif /* we have already advanced past the outparen. simply reset state to that in the surrounding frame */ state = stack_curr(pQS)->state; if (state == INIT_STATE) { /* special case: ONE TERM predicate */ *ppMask = mask_clone(stack_curr(pQS)->pmask); /* done */ stack_delete(pQS); mask_delete(pmOnly); if (tagtok.val.pc != NULL) SLPFree((void*)tagtok.val.pc); if ((tok.type == STR_TOK || tok.type == OPQ_TOK) && tok.val.pc ) { SLPFree((void*)tok.val.pc); } return SLP_OK; } else { tok.type = OUTPAREN_TOK; /* parsing of value ended at OUTPAREN */ } break; default: stack_delete(pQS); mask_delete(pmOnly); LOG_SLP_ERROR_AND_RETURN(SLP_LOG_DROP, "handle_query: state type?",SLP_INTERNAL_SYSTEM_ERROR); } if (prevtok.type == TAG_TOK && prevtok.val.pc != NULL) SLPFree((void*)prevtok.val.pc); prevtok = tok; } stack_delete(pQS); mask_delete(pmOnly); if (tok.type == ERR_TOK) return (SLPInternalError) tok.val.i; LOG_SLP_ERROR_AND_RETURN(SLP_LOG_DROP,"handle_query: unexpected end of parse",SLP_PARSE_ERROR); } /* ------------------------------------------------------------------------- */ static int skipWild(const char *pcQuery, int iQOffset, const char *pcString, int *piSOffset, int iStrLen) { char cNext = pcQuery[iQOffset]; if (cNext == '*') { return SLP_PARSE_ERROR; } if (cNext == '\0') { return 1; /* final '*' always matches */ } /* skip over a wild card */ while (toupper(pcString[*piSOffset]) != toupper(cNext)) { if (*piSOffset < iStrLen) { (*piSOffset)++; } else { return 0; /* '*' CHAR doesn't match the pcString */ } } return 1; /* we found a the position for the continuing to search */ } /* * isWildMatch * * This match is done in a stateful single pass. The possible grammar is * ['*'] 1*char *['*' 1*char ] ['*'] * * The algorithm: * * If there is a no initial '*' or substring, it is a 'match nothing' * return 0. * * If there is only a '*' it is a presense filter - return 1. * * Pass through the query string, delimited by the '*'. When this * string is exhausted - we had no mismatch - thus we have a match! * * If the substring is NULL and the delimiter is '*' we have an * initial '*'. We do '*' matching - see below: * * '*' matching - when the delimiter is '*': * In this case, the first matching character (after the '*' * in the qs) is found in the searched string. If such a * character doesn't exist, we are done. * * If there is a substring found and the delimiter is NULL - * this is a terminal substring. This means that the terminal * query substring must match the terminal characters of the * searched string. * * If there is a substring and the delimiter is not NULL - * perform a substring match (query substring vs searched string) * * If this is successful, advance the searched string index and * continue with the pass through the query string. Example: * * query string = "xy*yz", searched string = "xyaxyz" * The first query substring will be "xy", matching the first * substring of the searched string. The searched string * index is advanced and the query string '*' is handled * (which will skip over the 'ax' in the searched string). * * If this is unsuccessful, advance the search string index * by one and try again until either there is not enough * room left in the string or there is a success. Examples: * * query string = "*xy*", searched string = "axbxy" * The '*' handling of the initial '*' will skip 'a'. * The query substring 'xy' does not match the searched * string substring 'xb', so the searched string index * is advanced. 'bx' doesn't match either, but 'xy' does. * The query string is advanced to the final '*'. In this * case - it matches the remaining search string (""). * * query string = "*xyz*", searched string = "xyxy" * The '*' handling of the initial '*' has no effect. * The substring 'xyz' is matched to 'xyx' which fails, * then against 'yxy' which also fails. The search aborts * since 'xyz' is length 3 and the index into the searched * string is already 2 of 4. * * returns: * 0 for failure, 1 for success, SLP_PARSE_ERROR for malformed query. */ int isWildMatch(const char *pcQuery, const char *pcString) { int iQOffset = 0; int iSOffset = 0; char *pcSubstring; char cDelim; int iQueryLen = strlen(pcQuery); int iStrLen = strlen(pcString); int retval; int found = 0; int initial_strict = 0; /* if there is an initial non-'*' */ if (iQueryLen == 0) { return SLP_PARSE_ERROR; } if (iQueryLen == 1 && pcQuery[0] == '*') { return 1; /* presense filter - always true */ } if (pcQuery[0] == '*') { /* handle initial '*' */ iQOffset++; /* skip the '*' - required by skipWild */ retval = skipWild(pcQuery, iQOffset, pcString, &iSOffset,iStrLen); if (retval != 1) return retval; } else { initial_strict = 1; } while ((pcSubstring = get_next_string("*",pcQuery,&iQOffset,&cDelim))) { if (pcSubstring != NULL) { found = 0; if (initial_strict) { if (!SDstrncasecmp(pcSubstring,pcString,strlen(pcSubstring))) { iSOffset += strlen(pcSubstring); SLPFree(pcSubstring); initial_strict = 0; continue; } else { SLPFree(pcSubstring); return 0; /* initial substring does not match */ } } while (iSOffset < iStrLen) { int iSLen = strlen(pcSubstring); if (iSLen > (iStrLen-iSOffset)) { SLPFree(pcSubstring); return 0; /* pcSubstring too long for remaining searched string */ } if (cDelim == '\0') { if (!SDstrcasecmp(pcSubstring,&pcString[iSOffset])) { SLPFree(pcSubstring); return 1; /* final string matched */ } } else if (!SDstrncasecmp(pcSubstring,&pcString[iSOffset],iSLen)) { iSOffset += iSLen; /* advance searched string past query substring */ found = 1; break; /* done with the scan through pcString */ } iSOffset++; } SLPFree(pcSubstring); } if (found == 0) return 0; /* could not find substring match */ if(iSOffset > iStrLen) return SLP_PARSE_ERROR; /* assure iSOffset never > iStrLen */ if (cDelim == '*') { retval = skipWild(pcQuery, iQOffset, pcString, &iSOffset,iStrLen); if (retval != 1) return retval; } } return 1; } /* * op * * Actually perform the comparison operation. We will do it liberally - * logging type conflicts, but just returning FALSE not errors. The * absence of a term also indicates 'FALSE' as the result. If ANY value * for an attribute succeeds, the entire request succeeds. * * UNRESOLVED RIGHT NOW: UTF8 comparisons * * ps The store, all service entries * i The index of the specific entry to operate against * pcTag The attribute ID to operate against * ttOp The operation to perform * tok The value to use in the operation * * Return: * * 0 if false, 1 if true, SLPInternalError if an error occurs (negative #) */ static int op(SAStore *ps,int i,char *pcTag,MSLPQToktype ttOp,MSLPQToken tok) { int j,k,cmp; if ( !ps || !pcTag) return SLP_PARSE_ERROR; if(i<0 || i>ps->size) return SLP_PARSE_ERROR; /* make sure index is in range of store */ if (ps->tag[i] == NULL) { return 0; /* services with no attributes can only be matched with NULL queries. A query with any terms will not match as the service has no attributes to operate on */ } for (j = 0; ps->tag[i][j]; j++) { if (!SDstrcasecmp(ps->tag[i][j],pcTag)) { if (ttOp == IS_TOK) return 1; /* just being here satisfies 'presense' */ if (ps->values[i][j].type != tok.type) { #ifdef ENABLE_SLP_LOGGING SLP_LOG( SLP_LOG_DROP, "op: rqst where attr value type != registered attr type"); #endif continue; } for (k = 0; k < ps->values[i][j].numvals; k++) { switch (tok.type) { case OPQ_TOK: case STR_TOK: if (strstr(tok.val.pc,"*") != NULL) { if (ttOp == EQ_TOK) { int result = isWildMatch(tok.val.pc,ps->values[i][j].pval[k].v_pc); if (result == 1) { return 1; } else if (result == 0) { break; } else { LOG_SLP_ERROR_AND_RETURN(SLP_LOG_DROP,"op: illegal query with '*'", (SLPInternalError)result); } } } cmp = SDstrcasecmp(ps->values[i][j].pval[k].v_pc,tok.val.pc); switch (ttOp) { case EQ_TOK: if (cmp == 0) return 1; else break; case LE_TOK: if (cmp <= 0) return 1; else break; case GE_TOK: if (cmp >= 0) return 1; else break; default: break; } break; case INT_TOK: switch (ttOp) { case EQ_TOK: if (ps->values[i][j].pval[k].v_i==tok.val.i) return 1; break; case LE_TOK: if (ps->values[i][j].pval[k].v_i<=tok.val.i) return 1; break; case GE_TOK: if (ps->values[i][j].pval[k].v_i>=tok.val.i) return 1; break; default: break; } break; case BOOL_TOK: if (ttOp == EQ_TOK && tok.val.i == ps->values[i][j].pval[k].v_i) return 1; break; case KEY_TOK: if (ttOp == EQ_TOK && SDstrcasecmp(tok.val.pc,ps->values[i][j].pval[k].v_pc)) return 1; break; default: LOG_SLP_ERROR_AND_RETURN(SLP_LOG_DROP,"op: unknown data token",SLP_PARSE_ERROR); } } /* do any of the values match? */ } /* do the tags and types match the request? */ } /* each attribute */ return 0; } /* * handle term * * ps The store, includes all services and attributes. * state The state (AND, OR, NOT), for optimizations. * pmUseMask The set of possible service entries in reply. * pmResultMask The existing mask to update with terminal results. * tagtok The term's tag (ie. attribute id) * optok The term's operator (ie. <=, etc.) * valtok The term's value (ie. TRUE or 999) * * Result: SLPInternalError * * Side Effects: * */ static SLPInternalError handle_term( SAStore *ps, MSLPQState state, Mask *pmUseMask, Mask *pmResultMask, MSLPQToken tagtok, MSLPQToken optok, MSLPQToken valtok) { MSLPQToktype tval = valtok.type; int i; /* parameter checking */ if(!ps || !pmUseMask || !pmResultMask ) return SLP_PARSE_ERROR; if (tagtok.type != TAG_TOK) { LOG_SLP_ERROR_AND_RETURN(SLP_LOG_DROP,"handle_term: expected tag_tok",SLP_PARSE_ERROR); } if (optok.type != EQ_TOK && optok.type != LE_TOK && optok.type != GE_TOK && optok.type != IS_TOK) { LOG_SLP_ERROR_AND_RETURN(SLP_LOG_DROP,"handle_term: expected op_tok",SLP_PARSE_ERROR); } if (optok.type == IS_TOK && valtok.type != OUTPAREN_TOK) { LOG_SLP_ERROR_AND_RETURN(SLP_LOG_DROP,"handle_term: IS_TOK not followed by ')'", SLP_PARSE_ERROR); } if (optok.type != IS_TOK && tval!= STR_TOK && tval != OPQ_TOK && tval != INT_TOK && tval != BOOL_TOK) { LOG_SLP_ERROR_AND_RETURN(SLP_LOG_DROP,"handle_term: expected val_tok",SLP_PARSE_ERROR); } if (state != AND_STATE && state != OR_STATE && state != NOT_STATE && state != INIT_STATE) { LOG_SLP_ERROR_AND_RETURN(SLP_LOG_DROP,"handle_term: illegal state",SLP_PARSE_ERROR); } mask_reset(pmUseMask); while ((i = mask_next(pmUseMask,1)) != -1) { int iSet = mask_get(pmResultMask,i); int iResult; /* optimization: AND no further if already false, OR no further if true */ if ((!iSet && state == AND_STATE) || (iSet && state == OR_STATE)) continue; if ((iResult = op(ps, i, tagtok.val.pc, optok.type, valtok))<0) LOG_SLP_ERROR_AND_RETURN(SLP_LOG_DROP,"handle_term: op failed",SLP_PARSE_ERROR); mask_set(pmResultMask,i,iResult); } return SLP_OK; } /* ------------------------------------------------------------------------- */ static MSLPQToktype delim2Toktype(char c, const char *pcNext) { switch (c) { case '&': return AND_TOK; case '|': return OR_TOK; case '!': return NOT_TOK; case '=': if (*pcNext != '*') return EQ_TOK; else { /* * Here we have to be careful! * If there's anything nonwhite between the '*' and the end we * have an 'eq' case with a wildcard, not a presense test. */ pcNext++; while (pcNext && isspace(*pcNext) && *pcNext != ')') { pcNext++; } /* If pcNext is now pointing at the end paren, presense */ if (*pcNext == ')') { return IS_TOK; } else { /* otherwise, we have a comparison test */ return EQ_TOK; } } case '~': if (*pcNext == '=') return APPROX_TOK; else return ERR_TOK; case '<': if (*pcNext == '=') return LE_TOK; else return ERR_TOK; case '>': if (*pcNext == '=') return GE_TOK; else return ERR_TOK; default: #ifdef ENABLE_SLP_LOGGING SLP_LOG( SLP_LOG_DEBUG,"delim2Toktype: bad delim - should never get here"); #endif return ERR_TOK; /* should never get here */ } } /* * next_token * * This routine sets the fields in the MSLPQToken, which passes info out * about the stream of tokens. It uses state (previous token) to determine * what sorts of legal 'next tokens' there are. It makes use of the general * mslp utility 'get_next_string' to do the real work. * * tokPrev The previous token is passed in * ptok Will be set to be this token * ptokNext Will be set to be the next token * pcQuery The query string * piIndex The parsing index, increments as the parsing progresses * * Results: None directly - but error codes can be passed out in the ptok * (current token) when the token type is set to ERR_TOK. * * Side Effects: * piIndex is incremented as the query string is parsed through. */ static void next_token(MSLPQToken tokPrev, MSLPQToken *ptok, const char *pcQuery, int *piIndex) { char *pc; char c; MSLPQToktype tt; switch (tokPrev.type) { /* "(" then ( '&' '|' '!' ) */ case INIT_TOK: case AND_TOK: case OR_TOK: case NOT_TOK: { pc = get_next_string("(",pcQuery,piIndex,&c); if (pc != NULL || c != '(') { #ifdef ENABLE_SLP_LOGGING SLP_LOG( SLP_LOG_DROP,"next_token: no leading '('"); #endif ptok->type = ERR_TOK; ptok->val.i = SLP_PARSE_ERROR; } else { ptok->type = INPAREN_TOK; } SLPFree((void*)pc); } break; /* ( '&' '|' '!' ) or then ( <= = ~= >= =* ) Here we get the tag or logical operator. We will to check the next operator, since we get it anyway. Then we back off from it, to allow the TAG_TOK state to handle the operator if it is a leaf. */ case INPAREN_TOK: { pc = get_next_string("&|!=<>~",pcQuery,piIndex,&c); /* be careful not to overrun pcQuery getting next c */ tt = delim2Toktype(c,&pcQuery[*piIndex]); if (tt == AND_TOK || tt == OR_TOK || tt == NOT_TOK) { ptok->type = tt; } else if (pc && (tt==EQ_TOK || tt==LE_TOK || tt==GE_TOK || tt==IS_TOK || tt==APPROX_TOK)) { ptok->type = TAG_TOK; ptok->val.pc = safe_malloc(strlen(pc)+1,pc,strlen(pc)); assert( ptok->val.pc ); /* back up from the operator so it will get parsed next */ *piIndex -= 1; } else { #ifdef ENABLE_SLP_LOGGING SLP_LOG( SLP_LOG_DROP,"next_token: illegal first tok after '('"); #endif ptok->type = ERR_TOK; ptok->val.i = SLP_PARSE_ERROR; } SLPFree((void*)pc); } break; /* '=' then '*' or ('<' '>' '~') then '=' */ case TAG_TOK: { pc = get_next_string("<=>~",pcQuery,piIndex,&c); /* be careful not to overrun pcQuery getting next c */ tt = delim2Toktype(c,&pcQuery[*piIndex]); if (!pc && tt==EQ_TOK ) { ptok->type = tt; } else if (!pc && (tt==LE_TOK || tt==GE_TOK || tt==IS_TOK || tt==APPROX_TOK)) { if (tt==APPROX_TOK) tt = EQ_TOK; /* we handle approx as '=' */ ptok->type = tt; *piIndex += 1; } else { #ifdef ENABLE_SLP_LOGGING SLP_LOG( SLP_LOG_DROP,"next_token: illegal operator after 'tag'"); #endif ptok->type = ERR_TOK; ptok->val.i = SLP_PARSE_ERROR; } SLPFree((void*)pc); } break; /* as per AND_TOK or ')' then '(' or ')' then ')' or ')' then zip or zip */ case OUTPAREN_TOK: { pc = get_next_string("()",pcQuery,piIndex,&c); if (!pc && c=='\0') { ptok->type = TERM_TOK; } else if (!pc && c == ')') { ptok->type = OUTPAREN_TOK; } else if (!pc && c == '(') { ptok->type = INPAREN_TOK; } else { ptok->type = ERR_TOK; ptok->val.i= SLP_PARSE_ERROR; } SLPFree((void*)pc); } break; case IS_TOK: { pc = get_next_string(")",pcQuery,piIndex,&c); if (pc || c != ')') { ptok->type = ERR_TOK; ptok->val.i = SLP_PARSE_ERROR; } else { ptok->type = OUTPAREN_TOK; } SLPFree((void*)pc); } break; /* then ')' */ case EQ_TOK: case LE_TOK: case GE_TOK: { Values v; Val val; int err; v.pval = &val; pc = get_next_string(")",pcQuery,piIndex,&c); /* advance & test */ if (c != ')' || pc == NULL) { ptok->type = ERR_TOK; ptok->val.i = SLP_PARSE_ERROR; } else { char *pcFinger = pc; int offset = 0; err = (SLPInternalError) fill_value(&v,0,pcFinger,&offset); ptok->type = (MSLPQToktype) v.type; if (err) { ptok->type = ERR_TOK; ptok->val.i= err; } else if (v.type == TYPE_BOOL || v.type == TYPE_INT) { ptok->val.i = v.pval->v_i; } else if (v.type == TYPE_OPAQUE) { ptok->val.pc = v.pval->v_pc; } else if (v.type == TYPE_STR) { ptok->val.pc = list_pack(v.pval->v_pc); /* elide spaces */ SLPFree(v.pval->v_pc); } else { ptok->type = ERR_TOK; ptok->val.i= SLP_PARSE_ERROR; } } } SLPFree((void*)pc); break; /* ')' must come next */ case INT_TOK: case STR_TOK: case BOOL_TOK: case OPQ_TOK: { pc = get_next_string(")",pcQuery,piIndex,&c); if (pc || c != ')') { ptok->type = ERR_TOK; ptok->val.i= SLP_PARSE_ERROR; } else { ptok->type = OUTPAREN_TOK; } SLPFree((void*)pc); } break; default: { #ifdef ENABLE_SLP_LOGGING SLP_LOG( SLP_LOG_DROP,"next_tok: unexpected previous token"); #endif ptok->type = ERR_TOK; ptok->val.i= SLP_PARSE_ERROR; } break; } } /* * ------------------------------------------------------------------------ * Below this point is testing code * ------------------------------------------------------------------------ */ #ifdef QUERY_TEST /* try it with escaped characters case insensitivity syntax errors */ void test_wildmatch() { /* those which should match */ if (isWildMatch("*","string") != 1) printf("wm1.0 wild should match\n"); if (isWildMatch("str","str") != 1) printf("wm2.0 unwild should match\n"); if (isWildMatch("*x","x") != 1) printf("wm3.0 '*x' matches x\n"); if (isWildMatch("*x","yx") != 1) printf("wm3.1 '*x' matches yx\n"); if (isWildMatch("*x","xyx") != 1) printf("wm3.2 '*x' matches xyx\n"); if (isWildMatch("a*","a") != 1) printf("wm4.0 'a*' matches a\n"); if (isWildMatch("a*","ab") != 1) printf("wm4.1 'a*' matches ab\n"); if (isWildMatch("a*","aba") != 1) printf("wm4.2 'a*' matches aba\n"); if (isWildMatch("m*m","mm") != 1) printf("wm5.0 'm*m' matches mm\n"); if (isWildMatch("m*m","m x m") != 1) printf("wm5.1 'm*m' matches m x m\n"); if (isWildMatch("m*m","m mvm") != 1) printf("wm5.2 'm*m' matches m mvm\n"); if (isWildMatch("*a*","a") != 1) printf("wm6.0 '*a*' matches a\n"); if (isWildMatch("*a*","bad") != 1) printf("wm6.1 '*a*' matches bad\n"); if (isWildMatch("*a*","foo a") != 1) printf("wm6.2 '*a*' matches foo a\n"); if (isWildMatch("*a*","a foo") != 1) printf("wm6.3 '*a*' matches a foo\n"); if (isWildMatch("*a*","aaa") != 1) printf("wm6.4 '*a*' matches aaa\n"); if (isWildMatch("*a*b","ab")!=1) printf("wm7.0 '*a*b' matches ab \n"); if (isWildMatch("*a*b","xab")!=1) printf("wm7.1 '*a*b' matches xab\n"); if (isWildMatch("*a*b","axb")!=1) printf("wm7.2 '*a*b' matches axb \n"); if (isWildMatch("*a*b","xaxb")!=1) printf("wm7.3 '*a*b' matches xaxb\n"); if (isWildMatch("*a*b","axbxb")!=1) printf("wm7.4 '*a*b' matches axbxb\n"); if (isWildMatch("*a*b","xaxbxb")!=1) printf("wm7.5 '*a*b' matches xaxbxb\n"); if (isWildMatch("c*d*","cd")!=1) printf("wm8.0 'c*d*' matches cd \n"); if (isWildMatch("c*d*","cxd")!=1) printf("wm8.1 'c*d*' matches cxd \n"); if (isWildMatch("c*d*","cxdx")!=1) printf("wm8.2 'c*d*' matches cxdx \n"); if (isWildMatch("c*d*","ccxdd")!=1) printf("wm8.3 'c*d*' matches ccxdd \n"); if (isWildMatch("*e*f*","ef")!=1) printf("wm9.0 '*e*f*' matches ef\n"); if (isWildMatch("*e*f*","xef")!=1) printf("wm9.1 '*e*f*' matches xef\n"); if (isWildMatch("*e*f*","xexf")!=1) printf("wm9.2 '*e*f*' matches xexf\n"); if (isWildMatch("*e*f*","fefx")!=1) printf("wm9.3 '*e*f*' matches fefx\n"); if (isWildMatch("*e*f*","xefx")!=1) printf("wm9.4 '*e*f*' matches xexf\n"); if (isWildMatch("*e*f*","xexfx")!=1) printf("wm9.5 '*e*f*' matches xexfx\n"); if (isWildMatch("*e*f*","exf")!=1) printf("wm9.6 '*e*f*' matches exf\n"); if (isWildMatch("*e*f*","efx")!=1) printf("wm9.7 '*e*f*' matches efx\n"); if (isWildMatch("*e*f*","exfx")!=1) printf("wm9.8 '*e*f*' matches exfx\n"); if (isWildMatch("*e*f*","ff ee ff ee")!=1) printf("wm9.9 '*e*f*' mathches ff ee ff ee\n"); if (isWildMatch("*xy*yz*","axayaxyayza") != 1) printf("wm10.0 complex false start match\n"); /* those which should not match */ if (isWildMatch("xx","xy")!=0) printf("wm11.0 xx doesn't match xy\n"); if (isWildMatch("*x","xy")!=0) printf("wm12.0 *x fails on xy\n"); if (isWildMatch("*x","")!=0) printf("wm12.1 *x fails on \"\"\n"); if (isWildMatch("*x","yy")!=0) printf("wm12.2 *x fails on yy\n"); if (isWildMatch("*x","yxy")!=0) printf("wm12.3 *x fails on yxy\n"); if (isWildMatch("z*","yz")!=0) printf("wm13.0 z* fails on yz\n"); if (isWildMatch("z*","y")!=0) printf("wm13.1 z* fails on y\n"); if (isWildMatch("z*","yzy")!=0) printf("wm13.2 z* fails on yzy\n"); if (isWildMatch("o*o","o")!=0) printf("wm14.0 o*o fails on o\n"); if (isWildMatch("o*o","oop")!=0) printf("wm14.1 o*o fails on oop\n"); if (isWildMatch("o*o","poo")!=0) printf("wm14.2 o*o fails on poo\n"); if (isWildMatch("o*o","opop")!=0) printf("wm14.3 o*o fails on opop\n"); if (isWildMatch("o*o","popo")!=0) printf("wm14.4 o*o fails on popo\n"); if (isWildMatch("*m*","nope")!=0) printf("wm15.0 *m* fails on nope\n"); if (isWildMatch("*m*","")!=0) printf("wm15.1 *m* fails on \"\" \n"); if (isWildMatch("u*v*","vu") == 1) printf("wm16.0 u*v* fails on vu\n"); if (isWildMatch("u*v*","uu") == 1) printf("wm16.1 u*v* fails on uu\n"); if (isWildMatch("u*v*","vv") == 1) printf("wm16.2 u*v* fails on vv\n"); if (isWildMatch("u*v*","") == 1) printf("wm16.3 u*v* fails on \"\"\n"); if (isWildMatch("*f*g","f") == 1) printf("wm17.0 *f*g fails on f\n"); if (isWildMatch("*f*g","g") == 1) printf("wm17.1 *f*g fails on g\n"); if (isWildMatch("*f*g","fgf") == 1) printf("wm17.2 *f*g fails on fgf\n"); if (isWildMatch("*f*g","gf") == 1) printf("wm17.3 *f*g fails on gf\n"); if (isWildMatch("*p*q*","qp") == 1) printf("wm18.0 *p*q* fails on qp\n"); if (isWildMatch("*p*q*","p") == 1) printf("wm18.1 *p*q* fails on p\n"); if (isWildMatch("*p*q*","q") == 1) printf("wm18.2 *p*q* fails on q\n"); if (isWildMatch("*p*q*","qqqp") == 1) printf("wm18.3 *p*q* fails on qqqp\n"); if (isWildMatch("*xy*yz*","axayaxyaz") == 1) printf("wm19.0 complex query almost but not quite matches\n"); /* syntax error */ if (isWildMatch("boo**","booxxx") != SLP_PARSE_ERROR) printf("wm20.0 Warning: double wildcard error not found\n"); } SLPInternalError qtest(SAStore *ps, const char *pcSAScopeList, const char *pcRqstScopeList, const char *pcSrvtype, const char *pcLangTag, const char *pcQuery, Mask **ppMask) { return handle_query(ps,pcSAScopeList, pcRqstScopeList,pcSrvtype,pcLangTag,pcQuery,ppMask); } void test_query_features() { /* test some internal stuff in this file, like onPRList */ /* test PRList */ SAState st; st.pcSAHost = "foo"; st.pcSANumAddr = "128.127.106.34"; /* tpr1.1 NULL prlist */ if (on_PRList(&st, NULL)==1) printf("tpr1.1.1 null PR list should never be 1\n"); if (on_PRList(&st, NULL)!=0) printf("tpr1.1.2 incorrect 'not on list' return value\n"); /* tpr1.2 prlist does not have value */ if (on_PRList(&st, "snort,feng,34.23.23.23") == 1) printf("tpr1.2.1 different PR list should not match\n"); if (on_PRList(&st, "snort,feng,34.23.23.23") != 0) printf("tpr1.2.2 incorrect 'not on list' return value\n"); /* tpr1.3 prlist of 1 has host name */ if (on_PRList(&st, "foo") == 0) printf("tpr1.3.1 host name as only elt of PR list failed to match\n"); if (on_PRList(&st, "foo") != 1) printf("tpr1.3.2 incorrect 'on list' return value\n"); /* tpr1.4 prlist, long, has host name first */ if (on_PRList(&st, "foo,fem,33.5.2.3,snort,gloot,99.33.22.11,geeb,zogo")!= 1) printf("tpr1.4 host name first elt of PR list failed to match\n"); /* tpr1.5 prlist, long, has host name middle */ if (on_PRList(&st, "fem,sno,33.5.2.3,gloot,foo,geeb,99.33.22.11,swinger")!=1) printf("tpr1.5 host name middle elt of PR list failed to match\n"); /* tpr1.6 prlist, long, has host name end */ if (on_PRList(&st, "fem,sno,33.5.2.3,gloot,geeb,zogo,99.33.22.11,foo") != 1) printf("tpr1.6 host name last elt of PR list failed to match\n"); /* tpr1.7 prlist of 1 has numerical address */ if (on_PRList(&st, "128.127.106.34")!=1) printf("tpr1.7 addr as sole elt of PR list failed to match\n"); /* tpr1.8 prlist, long, has numerical address at beginning */ if (on_PRList(&st, "128.127.106.34,foo,fem,33.5.2.3,ot,99.33.22.11,ge,zoo")!= 1) printf("tpr1.8 addr as first elt of PR list failed to match\n"); /* tpr1.9 prlist, long, has numerical address at middle */ if (on_PRList(&st, "fem,sno,33.5.2.3,gloot,128.127.106.34,foo,geeb,99.33.22.11,swinger")!=1) printf("tpr1.9 addr as middle elt of PR list failed to match\n"); /* tpr1.10 prlist, long, has numerical address at end */ if (on_PRList(&st, "fem,sno,33.5.2.3,gloot,geeb,zogo,99.33.22.11,foo,128.127.106.34") != 1) printf("tpr1.10 addr as last elt of PR list failed to match\n"); } #endif