blob: 0bf1437ff68ca35a042a52c45db537a171918f13 [file] [log] [blame]
/* **********************************************************
* Copyright (c) 2019 Google, Inc. All rights reserved.
* Copyright (c) 2005-2006 VMware, Inc. All rights reserved.
* **********************************************************/
/*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of VMware, Inc. nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
#include "share.h"
#include "parser.h"
#include <stdio.h>
#ifndef UNIT_TEST
char *
next_token_sep(char *start, SIZE_T *len, char sep)
{
char *curtok = start;
char seps[16];
_snprintf(seps, 16, "%c\r\n", sep);
DO_ASSERT(start != NULL);
DO_ASSERT(len != NULL);
*len = 0;
if (start == NULL)
return NULL;
/* special case: '=' followed by a newline */
if (start[0] == sep && (start[1] == '\r' || start[1] == '\n')) {
curtok += 1;
return curtok;
}
/* determine the next token */
while (curtok[0] != '\0' && *len == 0) {
*len = strcspn(curtok, seps);
if (*len == 0)
curtok = curtok + strspn(curtok, seps);
}
return curtok;
}
char *
next_token(char *start, SIZE_T *len)
{
return next_token_sep(start, len, '=');
}
/* assumes that start points directly after the start_delimeter
* returns the size of the message block, or -1 on error */
char *
get_message_block_size(char *start, WCHAR *end_delimiter_w, SIZE_T *size)
{
char *endptr;
char end_delimiter[MAX_PATH];
DO_ASSERT(start != NULL);
DO_ASSERT(end_delimiter_w != NULL);
DO_ASSERT(size != NULL);
_snprintf(end_delimiter, MAX_PATH, "%S", end_delimiter_w);
endptr = strstr(start, end_delimiter);
if (endptr == NULL) {
DO_DEBUG(DL_WARN, printf("No %s end delimiter!\n", end_delimiter););
*size = 0xffffffff;
return NULL;
} else {
DO_DEBUG(DL_VERB,
printf("Block size %zd for %s:\n", endptr - start, end_delimiter););
*size = endptr - start;
return endptr + strlen(end_delimiter);
}
}
/* takes an optional separator, by default '=' */
char *
parse_line_sep(char *start, char sep, BOOL *done, WCHAR *param, WCHAR *value,
SIZE_T maxchars)
{
char *curtok;
SIZE_T toklen = 0;
const WCHAR *prefix;
DO_ASSERT(start != NULL);
DO_ASSERT(done != NULL);
DO_ASSERT(param != NULL);
DO_ASSERT(maxchars > 0);
*done = FALSE;
param[0] = L'\0';
value[0] = L'\0';
curtok = start;
curtok = next_token_sep(start, &toklen, sep);
DO_DEBUG(DL_FINEST, printf("curtok (tl=%zu):\n%s", toklen, curtok););
if (toklen == 0 && curtok[0] == '\0') {
*done = TRUE;
goto parse_line_out;
}
_snwprintf(param, MIN(toklen, maxchars - 1), L"%S", curtok);
param[MIN(toklen, maxchars - 1)] = L'\0';
DO_DEBUG(DL_FINEST,
printf("curtok: %s, nc=%d (%d or %d?)\n", curtok, curtok[toklen + 1], '\r',
'\n'););
/* handle the empty option properly */
if (curtok[toklen] == '\r' || curtok[toklen] == '\n') {
value[0] = L'\0';
} else {
curtok = next_token_sep(curtok + toklen, &toklen, sep);
if (toklen == 0 && curtok[0] == '\0') {
*done = TRUE;
goto parse_line_out;
}
DO_DEBUG(DL_FINEST, printf("vt(%zu): %s\n", toklen, curtok););
/* all values that start with \\ have DR_HOME prepended */
if ('\\' == curtok[0])
prefix = get_dynamorio_home();
else
prefix = L"";
_snwprintf(value, MIN(toklen + wcslen(prefix), maxchars - 1), L"%s%S", prefix,
curtok);
value[MIN(toklen + wcslen(prefix), maxchars - 1)] = L'\0';
}
parse_line_out:
DO_DEBUG(DL_FINEST, printf("parsed line: %S(%zu)=%S\n", param, toklen, value););
return curtok + toklen;
}
/* first argument is where to start searching from.
* returns a value which is the right place to start searching
* from for the next token.
* a line is either a single token, or an NVP (separated by =). */
char *
parse_line(char *start, BOOL *done, WCHAR *param, WCHAR *value, SIZE_T maxchars)
{
return parse_line_sep(start, '=', done, param, value, maxchars);
}
void
msg_append(char *msg_buffer, SIZE_T maxchars, WCHAR *data, SIZE_T *accumlen)
{
DO_ASSERT(accumlen != NULL);
if (data == NULL)
return;
if (msg_buffer != NULL && strlen(msg_buffer) + wcslen(data) < maxchars) {
SIZE_T oldsz = strlen(msg_buffer);
DO_DEBUG(DL_FINEST,
printf("msg_buffer: %s, os=%zu, data=%S\n", msg_buffer, oldsz, data););
_snprintf(msg_buffer + oldsz, maxchars - oldsz, "%S", data);
msg_buffer[oldsz + wcslen(data)] = '\0';
DO_DEBUG(DL_FINEST, printf("msg_buffer: %s\n", msg_buffer););
DO_ASSERT(strlen(msg_buffer) == oldsz + wcslen(data));
}
*accumlen += wcslen(data);
return;
}
/* ok this is annoying.
*
* apparently there's a bug in MS's CRT that causes \r\n to get
* converted to \r\r\n.
*
* see the unit tests below for an example of this bug.
*
* i guess we're ok with \r\r\n since notepad handles it fine. that's
* preferable to just \n which notepad can't handle.
*/
void
msg_append_nvp(char *msg_buffer, SIZE_T maxchars, SIZE_T *accumlen, WCHAR *name,
WCHAR *value)
{
/* exclude installation-specific parameters */
if (0 == wcscmp(name, L_DYNAMORIO_VAR_HOME) ||
0 == wcscmp(name, L_DYNAMORIO_VAR_LOGDIR))
return;
msg_append(msg_buffer, maxchars, name, accumlen);
msg_append(msg_buffer, maxchars, L_EQUALS, accumlen);
/* relativize any paths to DRHOME */
if (0 == wcsncmp(value, get_dynamorio_home(), wcslen(get_dynamorio_home())))
msg_append(msg_buffer, maxchars, value + wcslen(get_dynamorio_home()), accumlen);
else
msg_append(msg_buffer, maxchars, value, accumlen);
msg_append(msg_buffer, maxchars, L_NEWLINE, accumlen);
}
#else // ifdef UNIT_TEST
int
main()
{
char *testline = "GLOBAL_PROTECT=1\r\nBEGIN_BLOCK\r\nAPP_NAME=inetinfo."
"exe\r\nDYNAMORIO_OPTIONS=\r\nFOO=\\bar.dll\r\n";
WCHAR *sample = L"sample.mfp";
SIZE_T len;
DWORD res;
char *policy;
set_debuglevel(DL_INFO);
set_abortlevel(DL_WARN);
/* load the sample policy file for testing */
res = read_file_contents(sample, NULL, 0, &len);
DO_ASSERT(res == ERROR_MORE_DATA);
DO_ASSERT(len > 1000);
policy = (char *)malloc(len);
res = read_file_contents(sample, policy, len, NULL);
DO_ASSERT(res == ERROR_SUCCESS);
/* next_token tests */
{
char *tok, *line, *ptr;
UINT len;
line = testline;
tok = next_token(line, &len);
DO_ASSERT(line == tok);
DO_ASSERT(len == strlen("GLOBAL_PROTECT"));
ptr = line + len;
tok = next_token(ptr, &len);
DO_ASSERT(ptr + 1 == tok);
DO_ASSERT(len == 1);
ptr = tok + len;
tok = next_token(ptr, &len);
DO_ASSERT(0 == strncmp(tok, "BEGIN_BLOCK", strlen("BEGIN_BLOCK")));
DO_ASSERT(len == strlen("BEGIN_BLOCK"));
ptr = tok + len;
tok = next_token(ptr, &len);
DO_ASSERT(0 == strncmp(tok, "APP_NAME", strlen("APP_NAME")));
DO_ASSERT(len == strlen("APP_NAME"));
ptr = tok + len;
tok = next_token(ptr, &len);
DO_ASSERT(0 == strncmp(tok, "inetinfo.exe", strlen("inetinfo.exe")));
DO_ASSERT(len == strlen("inetinfo.exe"));
ptr = tok + len;
tok = next_token(ptr, &len);
DO_ASSERT(0 == strncmp(tok, "DYNAMORIO_OPTIONS", strlen("DYNAMORIO_OPTIONS")));
DO_ASSERT(len == strlen("DYNAMORIO_OPTIONS"));
ptr = tok + len;
tok = next_token(ptr, &len);
DO_ASSERT(len == 0);
ptr = tok + len;
tok = next_token(ptr, &len);
DO_ASSERT(0 == strncmp(tok, "FOO", strlen("FOO")));
DO_ASSERT(len == strlen("FOO"));
ptr = tok + len;
tok = next_token(ptr, &len);
DO_ASSERT(len == strlen("\\bar.dll"));
/* FIXME: long string / overrun tets.. */
}
/* get_message_block_size tests */
{
char *line, *ptr;
UINT len;
line = "a b\r\nfoo bar=blah\r\nEND_TEST_BLOCK\r\n";
ptr = get_message_block_size(line, L"END_TEST_BLOCK", &len);
DO_ASSERT(len == 19);
DO_ASSERT(ptr == line + strlen(line) - 2);
}
/* parse_line tests */
{
WCHAR param[MAX_PATH], value[MAX_PATH];
BOOL done;
char *ptr, *line = testline;
ptr = parse_line(line, &done, param, value, MAX_PATH);
DO_ASSERT(!done);
DO_ASSERT_WSTR_EQ(param, L"GLOBAL_PROTECT");
DO_ASSERT_WSTR_EQ(value, L"1");
ptr = parse_line(ptr, &done, param, value, MAX_PATH);
DO_ASSERT(!done);
DO_ASSERT_WSTR_EQ(param, L"BEGIN_BLOCK");
DO_ASSERT_WSTR_EQ(value, L"");
ptr = parse_line(ptr, &done, param, value, MAX_PATH);
DO_ASSERT(!done);
DO_ASSERT_WSTR_EQ(param, L"APP_NAME");
DO_ASSERT_WSTR_EQ(value, L"inetinfo.exe");
ptr = parse_line(ptr, &done, param, value, MAX_PATH);
DO_ASSERT(!done);
DO_ASSERT_WSTR_EQ(param, L"DYNAMORIO_OPTIONS");
DO_ASSERT_WSTR_EQ(value, L"");
ptr = parse_line(ptr, &done, param, value, MAX_PATH);
DO_ASSERT(!done);
DO_ASSERT_WSTR_EQ(param, L"FOO");
DO_ASSERT(NULL != wcsstr(value, L"bar"));
DO_ASSERT(NULL != wcsstr(value, get_dynamorio_home()));
}
/* msg_append tests */
{
char buf[MAX_PATH];
UINT loc;
buf[0] = '\0';
loc = 0;
msg_append(buf, 0, L"GLOBAL", &loc);
DO_ASSERT(0 == strlen(buf));
DO_ASSERT(6 == loc);
loc = 0;
msg_append(buf, MAX_PATH, L"GLOBAL", &loc);
DO_ASSERT(0 == strcmp(buf, "GLOBAL"));
DO_ASSERT(6 == loc);
loc = 6;
msg_append(buf, 0, L"_PROTECT", &loc);
DO_ASSERT(0 == strcmp(buf, "GLOBAL"));
DO_ASSERT(14 == loc);
loc = 6;
msg_append(buf, 6, L"_PROTECT", &loc);
DO_ASSERT(0 == strcmp(buf, "GLOBAL"));
DO_ASSERT(14 == loc);
loc = 6;
msg_append(buf, MAX_PATH, L"_PROTECT", &loc);
DO_ASSERT(0 == strcmp(buf, "GLOBAL_PROTECT"));
DO_ASSERT(14 == loc);
}
/* MSVCRT \r\r\n bug */
{
WCHAR *outfn = L"testcr.txt";
char *str = "line\r\n";
char buf[MAX_PATH];
res = write_file_contents(outfn, str, TRUE);
DO_ASSERT(res == ERROR_SUCCESS);
res = read_file_contents(outfn, buf, MAX_PATH, NULL);
DO_ASSERT(res == ERROR_SUCCESS);
DO_ASSERT(0 == strcmp(buf, str));
/* this passes: but check out the file in emacs!
* apparently the bug works both ways. */
}
printf("All Test Passed\n");
return 0;
}
#endif