#include "pch.h" #include "store.hpp" #define CF_COMPRESSED 0x1 #define TLS // __declspec( thread ) #if defined(_WIN64) && defined(_M_IA64) #pragma section(".base", long, read, write) extern "C" __declspec(allocate(".base")) extern IMAGE_DOS_HEADER __ImageBase; #else extern "C" extern IMAGE_DOS_HEADER __ImageBase; #endif HINSTANCE ghSymSrv = (HINSTANCE)&__ImageBase; UINT_PTR goptions = SSRVOPT_DWORD; DWORD gptype = SSRVOPT_DWORD; PSYMBOLSERVERCALLBACKPROC gcallback = NULL; ULONG64 gcontext = 0; HWND ghwndParent = (HWND)0; char gproxy[MAX_PATH + 1] = ""; int gdbgout = -1; char gdstore[MAX_PATH + 1] = ""; void PrepOutputString( char *in, char *out, int len ) { int i; *out = 0; for (i = 0; *in && i < len; i++, in++, out++) { if (*in == '\b') break; *out = *in; } *out = 0; } VOID OutputDbgString( char *sz ) { char sztxt[3000]; PrepOutputString(sz, sztxt, 3000); if (*sztxt) OutputDebugString(sztxt); } __inline BOOL DoCallback( DWORD action, ULONG64 data ) { return gcallback(action, data, gcontext); } BOOL PostEvent( PIMAGEHLP_CBA_EVENT evt ) { BOOL fdbgout = false; if (!*evt->desc) return true; // write to debug terminal, if called for if (gdbgout == 1) { fdbgout = true; OutputDbgString(evt->desc); } // don't pass info-level messages, unless told to if ((evt->severity <= sevInfo) && !(goptions & SSRVOPT_TRACE)) return true; // If there is no callback function, send to the debug terminal. if (!gcallback) { if (!fdbgout) OutputDbgString(evt->desc); return true; } // Otherwise call the callback function. return DoCallback(SSRVACTION_EVENT, (ULONG64)evt); } BOOL WINAPIV evtprint( DWORD severity, DWORD code, PVOID object, LPSTR format, ... ) { static char buf[1000] = ""; IMAGEHLP_CBA_EVENT evt; va_list args; va_start(args, format); wvsprintf(buf, format, args); va_end(args); if (!*buf) return true; evt.severity = severity; evt.code = code; evt.desc = buf; evt.object = object; return PostEvent(&evt); } int _eprint( LPSTR format, ... ) { static char buf[1000] = ""; va_list args; if (!format || !*format) return 1; if (!(goptions & SSRVOPT_TRACE) && gdbgout != 1) return 1; va_start(args, format); wvsprintf(buf, format, args); va_end(args); if (!evtprint(sevInfo, 0, NULL, buf)) if (gcallback) gcallback(SSRVACTION_TRACE, (ULONG64)buf, gcontext); return 1; } DBGEPRINT geprint = _eprint; int _dprint( LPSTR format, ... ) { static char buf[1000] = "SYMSRV: "; va_list args; if (!format || !*format) return 1; if (!(goptions & SSRVOPT_TRACE) && gdbgout != 1) return 1; va_start(args, format); wvsprintf(buf + 9, format, args); va_end(args); return _eprint(buf); } DBGPRINT gdprint = NULL; // _dprint; // this one is for calling from dload.cpp int __dprint( LPSTR sz ) { static char buf[1000] = "SYMSRV: "; va_list args; if (!sz || !*sz) return 1; CopyStrArray(buf, "SYMSRV: "); CatStrArray(buf, sz); if (gcallback) gcallback(SSRVACTION_TRACE, (ULONG64)buf, gcontext); if (!gcallback || (gdbgout == 1)) OutputDbgString(buf); return 1; } int _querycancel( ) { BOOL rc; BOOL cancel = false; if (!gcallback) return false; rc = gcallback(SSRVACTION_QUERYCANCEL, (ULONG64)&cancel, gcontext); if (rc && cancel) return true; return false; } QUERYCANCEL gquerycancel = _querycancel; BOOL SetError( DWORD err ) { SetLastError(err); return 0; } BOOL copy( IN PCSTR trgsite, IN PCSTR srcsite, IN PCSTR rpath, IN PCSTR file, OUT PSTR trg, // must be at least MAX_PATH elements IN DWORD flags ) { BOOL rc; DWORD type = stUNC; CHAR epath[MAX_PATH + 1]; CHAR srcbuf[MAX_PATH + 1]; CHAR tsite[MAX_PATH + 1]; CHAR ssite[MAX_PATH + 1]; PSTR src; Store *store; DWORD ec; assert(trgsite && srcsite); // use the default downstream store, if specified CopyStrArray(tsite, (*trgsite) ? trgsite : gdstore); CopyStrArray(ssite, srcsite); // get the store type of the target store type = GetStoreType(tsite); switch (type) { case stUNC: break; case stHTTP: case stHTTPS: // Can't use http for the target. // If a source is specifed, then bail. if (*ssite) return SetError(ERROR_INVALID_PARAMETER); // Otherwise, just use the default downstream store for a target. CopyStrArray(ssite, tsite); CopyStrArray(tsite, gdstore); break; case stError: return SetError(ERROR_INVALID_PARAMETER); default: return SetError(ERROR_INVALID_NAME); } // MAYBE PUT A CHECK IN HERE FOR A CAB. LIKE IF THE DIRECTORY IS // ACTUALLY A COMPRESSED FILE AND RETURN stCAB. // generate full target path pathcpy(trg, tsite, rpath, MAX_PATH); pathcat(trg, file, MAX_PATH); // if file exists, return it ec = FileStatus(trg); if (!ec) { return true; } else if (ec == ERROR_NOT_READY) { dprint("%s - drive not ready\n", trg); return false; } if (ReadFilePtr(trg, MAX_PATH)) { ec = FileStatus(trg); if (ec == NO_ERROR) return true; dprint("%s - %s\n", trg, FormatStatus(ec)); return false; } if (!*ssite) { ec = FileStatus(CompressedFileName(trg)); if (ec != NO_ERROR) { // if there is no source to copy from, then error dprint("%s - file not found\n", trg); return SetError(ERROR_FILE_NOT_FOUND); } // There is a compressed file.. // Expand it to the default store. CopyStrArray(ssite, tsite); CopyStrArray(tsite, gdstore); pathcpy(trg, tsite, rpath, MAX_PATH); pathcat(trg, file, MAX_PATH); ec = FileStatus(trg); if (ec == NO_ERROR) return true; } if (goptions & SSRVOPT_SECURE) { dprint("%s - file copy not allowed in secure mode\n", trg); return SetError(ERROR_ACCESS_DENIED); } if (!EnsurePathExists(trg, epath, DIMA(epath))) { dprint("%s - couldn't create target path\n", trg); return SetError(ERROR_PATH_NOT_FOUND); } store = GetStore(ssite); if (!store) return false; rc = store->init(); if (!rc) return false; rc = store->copy(rpath, file, tsite); // test the results and set the return value if (rc && !FileStatus(trg)) return true; UndoPath(trg, epath); return false; } BOOL ping( IN PCSTR trgsite, IN PCSTR srcsite, IN PCSTR rpath, IN PCSTR file, OUT PSTR trg, IN DWORD flags ) { BOOL rc; DWORD type = stUNC; CHAR epath[_MAX_PATH]; CHAR srcbuf[_MAX_PATH]; PSTR src; Store *store; DWORD ec; store = GetStore(srcsite); if (!store) return false; rc = store->init(); if (!rc) return false; rc = store->ping(); return rc; } void CatStrDWORD( IN OUT PSTR sz, IN DWORD value, IN DWORD size ) { CHAR buf[MAX_PATH + 256]; assert(sz); if (!value) return; wsprintf(buf, "%s%x", sz, value); // SECURITY: This will take a 256 digit DWORD. CopyString(sz, buf, size); } void CatStrGUID( IN OUT PSTR sz, IN GUID *guid, IN DWORD size ) { CHAR buf[MAX_PATH + 256]; BYTE byte; int i; assert(sz); if (!guid) return; // append the first DWORD in the pointer wsprintf(buf, "%08X", guid->Data1); CatString(sz, buf, size); // this will catch the passing of a PDWORD and avoid // all the GUID parsing if (!guid->Data2 && !guid->Data3) { for (i = 0, byte = 0; i < 8; i++) { byte |= guid->Data4[i]; if (byte) break; } if (!byte) return; } // go ahead and add the rest of the GUID wsprintf(buf, "%04X", guid->Data2); CatString(sz, buf, size); wsprintf(buf, "%04X", guid->Data3); CatString(sz, buf, size); wsprintf(buf, "%02X", guid->Data4[0]); CatString(sz, buf, size); wsprintf(buf, "%02X", guid->Data4[1]); CatString(sz, buf, size); wsprintf(buf, "%02X", guid->Data4[2]); CatString(sz, buf, size); wsprintf(buf, "%02X", guid->Data4[3]); CatString(sz, buf, size); wsprintf(buf, "%02X", guid->Data4[4]); CatString(sz, buf, size); wsprintf(buf, "%02X", guid->Data4[5]); CatString(sz, buf, size); wsprintf(buf, "%02X", guid->Data4[6]); CatString(sz, buf, size); wsprintf(buf, "%02X", guid->Data4[7]); CatString(sz, buf, size); } void CatStrOldGUID( IN OUT PSTR sz, IN GUID *guid, IN DWORD size ) { CHAR buf[MAX_PATH + 256]; BYTE byte; int i; assert(sz); if (!guid) return; // append the first DWORD in the pointer wsprintf(buf, "%8x", guid->Data1); CatString(sz, buf, size); // this will catch the passing of a PDWORD and avoid // all the GUID parsing if (!guid->Data2 && !guid->Data3) { for (i = 0, byte = 0; i < 8; i++) { byte |= guid->Data4[i]; if (byte) break; } if (!byte) return; } // go ahead and add the rest of the GUID wsprintf(buf, "%4x", guid->Data2); CatString(sz, buf, size); wsprintf(buf, "%4x", guid->Data3); CatString(sz, buf, size); wsprintf(buf, "%2x", guid->Data4[0]); CatString(sz, buf, size); wsprintf(buf, "%2x", guid->Data4[1]); CatString(sz, buf, size); wsprintf(buf, "%2x", guid->Data4[2]); CatString(sz, buf, size); wsprintf(buf, "%2x", guid->Data4[3]); CatString(sz, buf, size); wsprintf(buf, "%2x", guid->Data4[4]); CatString(sz, buf, size); wsprintf(buf, "%2x", guid->Data4[5]); CatString(sz, buf, size); wsprintf(buf, "%2x", guid->Data4[6]); CatString(sz, buf, size); wsprintf(buf, "%2x", guid->Data4[7]); CatString(sz, buf, size); } void CatStrID( IN OUT PSTR sz, PVOID id, DWORD paramtype, DWORD size ) { switch (paramtype) { case SSRVOPT_DWORD: CatStrDWORD(sz, PtrToUlong(id), size); break; case SSRVOPT_DWORDPTR: CatStrDWORD(sz, *(DWORD *)id, size); break; case SSRVOPT_GUIDPTR: CatStrGUID(sz, (GUID *)id, size); break; case SSRVOPT_OLDGUIDPTR: CatStrOldGUID(sz, (GUID *)id, size); break; default: break; } } // for Barb and Greg only. I'm going to get rid of these... void AppendHexStringWithDWORD( IN OUT PSTR sz, IN DWORD value ) { return CatStrDWORD(sz, value, MAX_PATH); } void AppendHexStringWithGUID( IN OUT PSTR sz, IN GUID *guid ) { return CatStrGUID(sz, guid, MAX_PATH); } void AppendHexStringWithOldGUID( IN OUT PSTR sz, IN GUID *guid ) { return CatStrOldGUID(sz, guid, MAX_PATH); } void AppendHexStringWithID( IN OUT PSTR sz, PVOID id, DWORD paramtype ) { return CatStrID(sz, id, paramtype, MAX_PATH); } /* * Given a string, find the next '*' and zero it * out to convert the current token into it's * own string. Return the address of the next character, * if there are any more strings to parse. */ PSTR ExtractToken( PSTR in, PSTR out, size_t size ) { PSTR p = in; *out = 0; if (!in || !*in) return NULL; for (;*p; p++) { if (*p == '*') { *p = 0; p++; break; } } CopyString(out, in, size); return (*p) ? p : NULL; } BOOL BuildRelativePath( OUT LPSTR rpath, IN LPCSTR filename, IN PVOID id, // first number in directory name IN DWORD val2, // second number in directory name IN DWORD val3, // third number in directory name IN DWORD size ) { LPSTR p; assert(rpath); CopyString(rpath, filename, size); EnsureTrailingBackslash(rpath); CatStrID(rpath, id, gptype, size); CatStrDWORD(rpath, val2, size); CatStrDWORD(rpath, val3, size); for (p = rpath + strlen(rpath) - 1; p > rpath; p--) { if (*p == '\\') { dprint("Insufficient information querying for %s\n", filename); SetLastError(ERROR_MORE_DATA); return false; } if (*p != '0') return true; } return true; } BOOL SymbolServerClose() { return true; } BOOL TestParameters( IN PCSTR params, // server and cache path IN PCSTR filename, // name of file to search for IN PVOID id, // first number in directory name IN DWORD val2, // second number in directory name IN DWORD val3, // third number in directory name OUT PSTR path // return validated file path here ) { __try { if (path) *path = 0; } __except(EXCEPTION_EXECUTE_HANDLER) { return SetError(ERROR_INVALID_PARAMETER); } if (!path || !params || !*params || !filename || !*filename || (!id && !val2 && !val3)) return SetError(ERROR_INVALID_PARAMETER); if (strlen(filename) > 100) { dprint("%s - filename cannot exceed 100 characters\n", filename); return SetError(ERROR_INVALID_PARAMETER); } switch (gptype) { case SSRVOPT_GUIDPTR: case SSRVOPT_OLDGUIDPTR: // this test should AV if a valid GUID pointer wasn't passed in __try { GUID *guid = (GUID *)id; BYTE b; b = guid->Data4[8]; } __except(EXCEPTION_EXECUTE_HANDLER) { return SetError(ERROR_INVALID_PARAMETER); } break; case SSRVOPT_DWORDPTR: // this test should AV if a valid DWORD pointer wasn't passed in __try { DWORD dword = *(DWORD *)id; } __except(EXCEPTION_EXECUTE_HANDLER) { return SetError(ERROR_INVALID_PARAMETER); } break; } return true; } BOOL SymbolServer( IN PCSTR params, // server and cache path IN PCSTR filename, // name of file to search for IN PVOID id, // first number in directory name IN DWORD val2, // second number in directory name IN DWORD val3, // third number in directory name OUT PSTR path // return validated file path here ) { CHAR *p; CHAR tdir[MAX_PATH + 1] = ""; CHAR sdir[MAX_PATH + 1] = ""; CHAR sz[MAX_PATH * 2 + 3]; CHAR rpath[MAX_PATH + 1]; BOOL rc; if (!TestParameters(params, filename, id, val2, val3, path)) return false; // test environment if (gdbgout == -1) { if (GetEnvironmentVariable("SYMSRV_DBGOUT", sz, MAX_PATH)) gdbgout = 1; else gdbgout = 0; *sz = 0; } // parse parameters CopyStrArray(sz, params); p = ExtractToken(sz, tdir, DIMA(tdir)); // 1st path is where the symbol should be p = ExtractToken(p, sdir, DIMA(sdir)); // 2nd optional path is the server to copy from // build the relative path to the target symbol file if (!BuildRelativePath(rpath, filename, id, val2, val3, DIMA(rpath))) return false; // if no_copy option is set, just return the path to the target if (goptions & SSRVOPT_NOCOPY) { pathcpy(path, tdir, rpath, MAX_PATH); pathcat(path, filename, MAX_PATH); return true; } // copy from server to specified symbol path rc = copy(tdir, sdir, rpath, filename, path, 0); if (!rc) *path = 0; return rc; } BOOL SymbolServerSetOptions( UINT_PTR options, ULONG64 data ) { DWORD ptype; // set the callback function if (options & SSRVOPT_CALLBACK) { if (data) { goptions |= SSRVOPT_CALLBACK; gcallback = (PSYMBOLSERVERCALLBACKPROC)data; } else { goptions &= ~SSRVOPT_CALLBACK; gcallback = NULL; } } // set the callback context if (options & SSRVOPT_SETCONTEXT) gcontext = data; // when this flags is set, trace output will be delivered if (options & SSRVOPT_TRACE) { if (data) { goptions |= SSRVOPT_TRACE; setdprint(_dprint); } else { goptions &= ~SSRVOPT_TRACE; setdprint(NULL); } } // set the parameter type for the first ID parameter if (options & SSRVOPT_PARAMTYPE) { switch(data) { case SSRVOPT_DWORD: case SSRVOPT_DWORDPTR: case SSRVOPT_GUIDPTR: case SSRVOPT_OLDGUIDPTR: goptions &= ~(SSRVOPT_DWORD | SSRVOPT_DWORDPTR | SSRVOPT_GUIDPTR | SSRVOPT_OLDGUIDPTR); goptions |= data; gptype = (DWORD)data; break; default: SetLastError(ERROR_INVALID_PARAMETER); return false; } } // set the parameter type for the first ID paramter - OLD SYNTAX // the if statements provide and order of precedence ptype = 0; if (options & SSRVOPT_DWORD) ptype = SSRVOPT_DWORD; if (options & SSRVOPT_DWORDPTR) ptype = SSRVOPT_DWORDPTR; if (options & SSRVOPT_GUIDPTR) ptype = SSRVOPT_GUIDPTR; if (options & SSRVOPT_OLDGUIDPTR) ptype = SSRVOPT_OLDGUIDPTR; if (ptype) { goptions &= ~(SSRVOPT_DWORD | SSRVOPT_DWORDPTR | SSRVOPT_GUIDPTR | SSRVOPT_OLDGUIDPTR); if (data) { goptions |= ptype; gptype = ptype; } else if (gptype == ptype) { // when turning off a type, reset it to DWORD goptions |= SSRVOPT_DWORD; gptype = SSRVOPT_DWORD; } } // if this flag is set, no GUI will be displayed if (options & SSRVOPT_UNATTENDED) { if (data) goptions |= SSRVOPT_UNATTENDED; else goptions &= ~SSRVOPT_UNATTENDED; } // when this is set, the existence of the returned file path is not checked if (options & SSRVOPT_NOCOPY) { if (data) goptions |= SSRVOPT_NOCOPY; else goptions &= ~SSRVOPT_NOCOPY; } // this window handle is used as a parent for dialog boxes if (options & SSRVOPT_PARENTWIN) { SetParentWindow((HWND)data); if (data) goptions |= SSRVOPT_PARENTWIN; else goptions &= ~SSRVOPT_PARENTWIN; } // when running in secure mode, we don't copy files to downstream stores if (options & SSRVOPT_SECURE) { if (data) goptions |= SSRVOPT_SECURE; else goptions &= ~SSRVOPT_SECURE; } // set http proxy if (options & SSRVOPT_PROXY) { if (data) { goptions |= SSRVOPT_PROXY; CopyStrArray(gproxy, (char *)data); setproxy(gproxy); } else { goptions &= ~SSRVOPT_PROXY; *gproxy = 0; setproxy(NULL); } } // set default downstream store if (options & SSRVOPT_DOWNSTREAM_STORE) { if (data) { goptions |= SSRVOPT_DOWNSTREAM_STORE; CopyStrArray(gdstore, (char *)data); setdstore(gdstore); } else { goptions &= ~SSRVOPT_DOWNSTREAM_STORE; *gdstore = 0; setdstore(NULL); } } SetStoreOptions(goptions); return true; } UINT_PTR SymbolServerGetOptions( ) { return goptions; } BOOL SymbolServerPing( IN PCSTR params // server and cache path ) { CHAR *p; CHAR sz[MAX_PATH * 2 + 3]; CHAR tdir[MAX_PATH + 1] = ""; CHAR sdir[MAX_PATH + 1] = ""; CHAR rpath[MAX_PATH + 1]; CHAR filename[MAX_PATH + 1]; CHAR path[MAX_PATH + 1]; if (!params || !*params) return SetError(ERROR_INVALID_PARAMETER); // parse parameters // parse parameters CopyStrArray(sz, params); p = ExtractToken(sz, tdir, DIMA(tdir)); // 1st path is where the symbol should be p = ExtractToken(p, sdir, DIMA(sdir)); // 2nd optional path is the server to copy from // copy from server to specified symbol path return ping(tdir, sdir, rpath, filename, path, 0); return true; }