commit 46c36b11dac8092fb79671955439f33a67ce9f54 Author: Eclypsed Date: Sun May 31 14:34:00 2026 -0400 Removed submodules diff --git a/lab1-ptrs/.gitignore b/lab1-ptrs/.gitignore new file mode 100644 index 0000000..a44fc93 --- /dev/null +++ b/lab1-ptrs/.gitignore @@ -0,0 +1,22 @@ +# Ignore all compiled files regardless of location +*.o +tests/public.o +tests/testsuite.o + +# But not the private.o +!tests/private.o + +# Ignore executables for this project +ptrs +testsuite + +# Ignore test outputs +tests/ckstyle +tests/itests.txt +tests/outputs +tests/style.txt +tests/valgrind +tests/utests.txt + +**/.nfs* +**/.vscode diff --git a/lab1-ptrs/Makefile b/lab1-ptrs/Makefile new file mode 100644 index 0000000..4ed1c9e --- /dev/null +++ b/lab1-ptrs/Makefile @@ -0,0 +1,51 @@ +# +# Simple Makefile +# Mike Lam, James Madison University, August 2016 +# +# This makefile builds a simple application that contains a main module +# (specified by the EXE variable) and a predefined list of additional modules +# (specified by the MODS variable). If there are any external library +# dependencies (e.g., the math library, "-lm"), list them in the LIBS variable. +# If there are any precompiled object files, list them in the OBJS variable. +# +# By default, this makefile will build the project with debugging symbols and +# without optimization. To change this, edit or remove the "-g" and "-O0" +# options in CFLAGS and LDFLAGS accordingly. +# +# By default, this makefile build the application using the GNU C compiler, +# adhering to the C99 standard with all warnings enabled. + + +# application-specific settings and run target + +EXE=ptrs +MODS=movies.o +OBJS= +LIBS= + +default: $(EXE) + +test: $(EXE) + make -C tests test + +# compiler/linker settings + +CC=gcc +CFLAGS=-g -O0 -Wall -Werror --std=c99 +LDFLAGS=-g -O0 + + +# build targets + +$(EXE): main.o $(MODS) $(OBJS) + $(CC) $(LDFLAGS) -o $(EXE) $^ $(LIBS) + +%.o: %.c + $(CC) -c $(CFLAGS) $< + +clean: + rm -f $(EXE) main.o $(MODS) + make -C tests clean + +.PHONY: default clean + diff --git a/lab1-ptrs/main.c b/lab1-ptrs/main.c new file mode 100644 index 0000000..c592c27 --- /dev/null +++ b/lab1-ptrs/main.c @@ -0,0 +1,80 @@ +#include +#include +#include +#include + +#include "movies.h" + +int cmdline (int, char **, bool *); + +void +usage (void) +{ + printf ("Usage: ptrs [-hm]\n"); + printf (" Options are:\n"); + printf (" -m Parse the CSV data as a movie\n"); + printf (" -h Print this help message\n"); + printf ("Only one option is allowable\n"); +} + +int +main (int argc, char **argv) +{ + bool run_movie = false; + + if (cmdline (argc, argv, &run_movie) != 0) + return EXIT_FAILURE; + + if (!run_movie) + { + usage (); + return EXIT_FAILURE; + } + + // Split the following comma-separated value (CSV) data into fields + char *data = "The Shawshank Redemption,1994,Drama"; + movie_t movie = split_data (data); + printf ("Title: %s\n", movie.title); + printf ("Year: %d\n", movie.year); + printf ("Genre: %s\n", movie.genre); + + char *rebuilt = merge_data (movie); + printf ("%s\n", rebuilt); + + free (rebuilt); + free (movie.title); + free (movie.genre); + + return EXIT_SUCCESS; +} + +/* DO NOT MODIFY THIS FUNCTION. Parses the command line arguments to get + the signal numbers to override, the number of overridden signals, and + the order of signals to raise in the full implementation. */ +int +cmdline (int argc, char **argv, bool *movies) +{ + int option; + + if ((option = getopt (argc, argv, "mh")) == -1) + { + usage (); + return -1; + } + + switch (option) + { + case 'h': + *movies = false; + return -1; + case 'm': + *movies = true; + break; + default: + printf ("Invalid command-line option: %c\n", optopt); + usage (); + return -1; + } + + return 0; +} diff --git a/lab1-ptrs/movies.c b/lab1-ptrs/movies.c new file mode 100644 index 0000000..3a14bb5 --- /dev/null +++ b/lab1-ptrs/movies.c @@ -0,0 +1,97 @@ +#define _POSIX_C_SOURCE 200809L // needed for strdup extension + +#include +#include +#include + +#include "movies.h" + +/* Given a string (possibly read-only) of IMDB movie data + (formatted as "Title,Year,Genre"), create a movie_t object + based on the appropriate fields. */ +movie_t +split_data (char *csv) +{ + movie_t movie; + char *temp = strdup (csv); + char *token = strtok (temp, ","); + movie.title = strdup (token); // make a copy the first token as the title + + char *year_str = strtok (NULL, ","); + char *endptr; + movie.year = strtol (year_str, &endptr, 10); + + char *genre = strtok (NULL, ","); + movie.genre = strdup (genre); + + free (temp); + + return movie; +} + +/* Build a dynamically allocated string from a movie_t object as follows: + "Title [Year] - Genre" + */ +char * +merge_data (movie_t movie) +{ + // There are a few ways to merge strings in C. One approach is to start with + // a base string on the heap and repeatedly grow and append the string. In + // pseudocode, it might look like this: + // + // str = strdup ("Hello"); + // str = realloc (str, ...length...); // grow + // strncat (str, " World", ...length...); // concatenate + // str = realloc (str, ...length...); // grow + // strncat (str, " Again", ...length...); // concatenate + // + // This is the C equivalent of something like this in Java: + // + // str = "Hello"; + // str += " World"; + // str += " Again"; + // + // Another (generally more efficient) way to do this is to use snprintf(), + // for formatting a string. If you are not familiar with snprintf(), you + // should be familiar with its cousin, printf(). Consider the following + // line of code, which prints three string variables to the screen: + // + // printf ("%s %s %s\n", str1, str2, str3); + // + // You do the exact same thing with snprintf(), but add two variables at + // the beginning: the destination buffer (i.e., where do you want the + // final string in memory) and the length: + // + // snprintf (buffer, length, "%s %s %s\n", str1, str2, str3); + // + // The original strings str1, str2, and str3 are unmodified, because their + // contents are copied into the buffer. snprintf() also has a great built-in + // feature that strncat() doesn't: it works with more than just strings! + // + // int num = 42; + // char *str = "My favorite number is "; + // snprintf (message, length, "%s%d\n", str, num); + // + // Just remember that the message/buffer variable must be a pointer to a + // writable portion of memory where you have allocated enough space to store + // the combined string. + + // HINT: When doing string manipulation, you should use a variable to keep + // explicit track of the string length. C string lengths are not inherently + // stored anywhere. + size_t len = 1; // always keep track of the null byte + + // +10 byte space for yeaar, spaces, brackets, and dash + len += strlen (movie.title) + 10 + strlen (movie.genre); + + // Use one of the two techniques above to allocated enough space for the + // merged string. You should NOT rely on any length unless its exact value + // is known. For instance, you can assume that " [Year] - " adds 10 bytes, + // but you cannot assume anything about the length of the genre or title. + // As such, you need to compute those lengths using strlen(). Do not forget + // to account for the null byte. + char *result = calloc (len, sizeof (char)); + snprintf (result, len, "%s [%d] - %s", movie.title, movie.year, movie.genre); + + return result; +} diff --git a/lab1-ptrs/movies.h b/lab1-ptrs/movies.h new file mode 100644 index 0000000..7e289ef --- /dev/null +++ b/lab1-ptrs/movies.h @@ -0,0 +1,16 @@ +#ifndef __CS361_MOVIES_H__ +#define __CS361_MOVIES_H__ + +#include + +typedef struct movie +{ + char *title; + int year; + char *genre; +} movie_t; + +movie_t split_data (char *); +char *merge_data (movie_t); + +#endif diff --git a/lab1-ptrs/tests/Makefile b/lab1-ptrs/tests/Makefile new file mode 100644 index 0000000..b4bda4d --- /dev/null +++ b/lab1-ptrs/tests/Makefile @@ -0,0 +1,89 @@ +# +# Simple Test Makefile +# Mike Lam, James Madison University, August 2016 +# +# This version of the Makefile includes support for building a test suite. The +# recommended framework is Check (http://check.sourceforge.net/). To build and +# run the test suite, execute the "test" target. The test suite must be located +# in a module called "testsuite". The MODS, LIBS, and OBJS variables work as +# they do in the main Makefile. +# +# To change the default build target (which executes when you just type +# "make"), change the right-hand side of the definition of the "default" +# target. +# +# By default, this makefile will build the project with debugging symbols and +# without optimization. To change this, edit or remove the "-g" and "-O0" +# options in CFLAGS and LDFLAGS accordingly. +# +# By default, this makefile build the application using the GNU C compiler, +# adhering to the C99 standard with all warnings enabled. + + +# application-specific settings and run target + +EXE=../ptrs +TEST=testsuite +MODS=public.o +OBJS=../movies.o +LIBS= + +UTESTOUT=utests.txt +ITESTOUT=itests.txt +SCHECKOUT=style.txt + +default: $(TEST) + +$(EXE): + make -C ../ + +test: utest itest style + @echo "========================================" + +utest: $(EXE) $(TEST) + @echo "========================================" + @echo " UNIT TESTS" + @./$(TEST) 2>/dev/null >$(UTESTOUT) + @cat $(UTESTOUT) | sed -n -e '/Checks/,$$p' | sed -e 's/^private.*:[EF]://g' + +itest: $(EXE) + @echo "========================================" + @echo " INTEGRATION TESTS" + @./integration.sh | tee $(ITESTOUT) + +style: $(EXE) + @echo "========================================" + @echo " CODING STYLE CHECK" + @./style.sh 2>/dev/null >$(SCHECKOUT) + @cat $(SCHECKOUT) + +# compiler/linker settings + +CC=gcc +CFLAGS=-g -O0 -Wall --std=c99 -pedantic +LDFLAGS=-g -O0 + +#CFLAGS+=-I/opt/local/include -Wno-gnu-zero-variadic-macro-arguments +CFLAGS+=-Wno-gnu-zero-variadic-macro-arguments +#LDFLAGS+=-L/opt/local/lib +LDFLAGS= +LIBS+=-lcheck -lm -lpthread + +ifeq ($(shell uname -s),Linux) + LIBS+=-lrt -lsubunit +endif + + +# build targets + +$(TEST): $(TEST).o $(MODS) $(OBJS) + $(CC) $(LDFLAGS) -o $(TEST) $^ $(LIBS) + +%.o: %.c + $(CC) -c $(CFLAGS) $< + +clean: + rm -rf $(TEST) $(TEST).o $(MODS) $(UTESTOUT) $(ITESTOUT) $(SCHECKOUT) outputs valgrind ckstyle + +.PHONY: default clean test unittest inttest + diff --git a/lab1-ptrs/tests/expected/.INTEG_movies.sha b/lab1-ptrs/tests/expected/.INTEG_movies.sha new file mode 100644 index 0000000..0f71a4d --- /dev/null +++ b/lab1-ptrs/tests/expected/.INTEG_movies.sha @@ -0,0 +1 @@ +514af63cc957d92dab366709ad95b76ad030e9d9 diff --git a/lab1-ptrs/tests/expected/.INTEG_movies_inc.sha b/lab1-ptrs/tests/expected/.INTEG_movies_inc.sha new file mode 100644 index 0000000..0f71a4d --- /dev/null +++ b/lab1-ptrs/tests/expected/.INTEG_movies_inc.sha @@ -0,0 +1 @@ +514af63cc957d92dab366709ad95b76ad030e9d9 diff --git a/lab1-ptrs/tests/expected/INTEG_movies.txt b/lab1-ptrs/tests/expected/INTEG_movies.txt new file mode 100644 index 0000000..69a154c --- /dev/null +++ b/lab1-ptrs/tests/expected/INTEG_movies.txt @@ -0,0 +1,4 @@ +Title: The Shawshank Redemption +Year: 1994 +Genre: Drama +The Shawshank Redemption [1994] - Drama diff --git a/lab1-ptrs/tests/expected/INTEG_movies_inc.txt b/lab1-ptrs/tests/expected/INTEG_movies_inc.txt new file mode 100644 index 0000000..69a154c --- /dev/null +++ b/lab1-ptrs/tests/expected/INTEG_movies_inc.txt @@ -0,0 +1,4 @@ +Title: The Shawshank Redemption +Year: 1994 +Genre: Drama +The Shawshank Redemption [1994] - Drama diff --git a/lab1-ptrs/tests/integration.sh b/lab1-ptrs/tests/integration.sh new file mode 100755 index 0000000..de719a9 --- /dev/null +++ b/lab1-ptrs/tests/integration.sh @@ -0,0 +1,75 @@ +#!/bin/bash + +EXE="../ptrs" + +function run_test { + + # parameters + TAG=$1 + ARGS=$2 + + # file paths + OUTPUT=outputs/$TAG.txt + DIFF=outputs/$TAG.diff + EXPECT=expected/$TAG.txt + VALGRND=valgrind/$TAG.txt + + # print tag format + PTAG=$(printf '%-30s' "$TAG") + + # check for expected text that needs to be fixed + if [ ! -z "$(egrep "<<<<.*>>>>" "$EXPECT")" ] ; then + echo "$PTAG FAIL ($EXPECT not correct)" + return + fi + + # compare the file + SHAOUT=$(shasum "$EXPECT" | awk '{print $1}') + SHAEXP=$(cat "expected/.$TAG.sha") + if [ "$SHAOUT" != "$SHAEXP" ] ; then + echo "$PTAG FAIL ($EXPECT not correct)" + return + fi + + # run test and compare output to the expected version + $EXE "$ARGS" 2>/dev/null >"$OUTPUT" + diff -u "$OUTPUT" "$EXPECT" >"$DIFF" + if [ -s "$DIFF" ]; then + + # try alternative solution (if it exists) + EXPECT=expected/$TAG-2.txt + if [ -e "$EXPECT" ]; then + diff -u "$OUTPUT" "$EXPECT" >"$DIFF" + if [ -s "$DIFF" ]; then + echo "$PTAG FAIL (see $DIFF for details)" + else + echo "$PTAG pass" + fi + else + echo "$PTAG FAIL (see $DIFF for details)" + fi + else + echo "$PTAG pass" + fi + + # run valgrind + valgrind $EXE $ARGS &>$VALGRND +} + +# initialize output folders +mkdir -p outputs +mkdir -p valgrind +rm -f outputs/* valgrind/* + +# run individual tests +source itests.include + +# check for memory leaks +LEAK=`cat valgrind/*.txt | grep 'definitely lost' | grep -v ' 0 bytes in 0 blocks'` +if [ -z "$LEAK" ]; then + echo "No memory leak found." +else + echo "Memory leak(s) found. See files listed below for details." + grep 'definitely lost' valgrind/*.txt | grep -v ' 0 bytes in 0 blocks' | sed -e 's/:.*$//g' | sed -e 's/^/ - /g' +fi + diff --git a/lab1-ptrs/tests/itests.include b/lab1-ptrs/tests/itests.include new file mode 100644 index 0000000..dfaf482 --- /dev/null +++ b/lab1-ptrs/tests/itests.include @@ -0,0 +1,7 @@ +# list of integration tests +# format: run_test +# used as the root for all filenames (i.e., "expected/$TAG.txt") +# command-line arguments to test + +run_test INTEG_movies "-m" +run_test INTEG_movies_inc "-m" diff --git a/lab1-ptrs/tests/public.c b/lab1-ptrs/tests/public.c new file mode 100644 index 0000000..b517d80 --- /dev/null +++ b/lab1-ptrs/tests/public.c @@ -0,0 +1,95 @@ +#include +#include +#include + +#include + +#include "../movies.h" + +// Test that split_data gets the title copy +START_TEST (UNIT_split_title) +{ + char *csv = "Casablanca,1942,Romance"; + movie_t movie = split_data (csv); + ck_assert_str_eq (csv, "Casablanca,1942,Romance"); + ck_assert_str_eq (movie.title, "Casablanca"); +} +END_TEST + +// Test that split_data parses numeric values +START_TEST (UNIT_split_nums) +{ + char *csv = "Casablanca,1942,Romance"; + movie_t movie = split_data (csv); + ck_assert_str_eq (csv, "Casablanca,1942,Romance"); + ck_assert_int_eq (movie.year, 1942); +} +END_TEST + +// Test that split_data gets the genre copy +START_TEST (UNIT_split_genre) +{ + char *csv = "Casablanca,1942,Romance"; + movie_t movie = split_data (csv); + ck_assert_str_eq (csv, "Casablanca,1942,Romance"); + ck_assert_str_eq (movie.genre, "Romance"); +} +END_TEST + +// Test that split_data operates correctly on stack-based strings +START_TEST (UNIT_split_stack) +{ + + char csv[] = "Casablanca,1942,Romance"; + movie_t movie = split_data (csv); + ck_assert_str_eq (movie.title, "Casablanca"); + ck_assert_int_eq (movie.year, 1942); + ck_assert_str_eq (movie.genre, "Romance"); + ck_assert_str_eq (csv, "Casablanca,1942,Romance"); +} +END_TEST + +// Test that merge_data combines the fields into a string +START_TEST (UNIT_merge_movie) +{ + movie_t movie; + movie.title = "The Lord of the Rings: The Fellowship of the Ring"; + movie.year = 2001; + movie.genre = "Fantasy"; + char *result = merge_data (movie); + ck_assert_str_eq (result, "The Lord of the Rings: The Fellowship of the Ring [2001] - Fantasy"); + free (result); +} +END_TEST + + +// Test that merge_data combines the fields from the heap into a string +START_TEST (UNIT_merge_heap) +{ + movie_t *movie = calloc (1, sizeof (movie_t)); + movie->title = "The Lord of the Rings: The Fellowship of the Ring"; + movie->year = 2001; + movie->genre = "Fantasy"; + char *result = merge_data (*movie); + ck_assert_str_eq (result, "The Lord of the Rings: The Fellowship of the Ring [2001] - Fantasy"); + free (result); + result = NULL; + ck_assert_str_eq (movie->title, "The Lord of the Rings: The Fellowship of the Ring"); + ck_assert_int_eq (movie->year, 2001); + ck_assert_str_eq (movie->genre, "Fantasy"); + free (movie); +} +END_TEST + +void public_tests (Suite *s) +{ + TCase *tc_public = tcase_create ("Public"); + tcase_add_test (tc_public, UNIT_split_title); + tcase_add_test (tc_public, UNIT_split_nums); + tcase_add_test (tc_public, UNIT_split_genre); + tcase_add_test (tc_public, UNIT_split_stack); + tcase_add_test (tc_public, UNIT_merge_movie); + tcase_add_test (tc_public, UNIT_merge_heap); + suite_add_tcase (s, tc_public); +} + diff --git a/lab1-ptrs/tests/style.sh b/lab1-ptrs/tests/style.sh new file mode 100755 index 0000000..ba2210a --- /dev/null +++ b/lab1-ptrs/tests/style.sh @@ -0,0 +1,53 @@ +#!/bin/bash + +STYLE="gnu" + +IGNORE=() +FAIL=0 + +function comp_file { + + SRC=$1 + SRC_NAME=$2 + + # file paths + FORMAT=ckstyle/${SRC_NAME}.$STYLE + DIFF=ckstyle/${SRC_NAME}.diff + + # run clang-format and compare results + clang-format --style=$STYLE $source > $FORMAT + diff -u $SRC $FORMAT >$DIFF + + PTAG=$(printf '%-30s' "$SRC_NAME") + if [ -s $DIFF ]; then + echo "$PTAG FAIL (see $DIFF for details)" + FAIL=1 + else + echo "$PTAG pass" + rm $DIFF + fi + rm $FORMAT +} + +mkdir -p ckstyle +rm -f ckstyle/* + +for source in $(ls ../*.c ../*.h) ; do + SKIP=0 + src=$(basename $source) + for ignore in ${IGNORE[*]} ; do + if [ "$src" = "$ignore" ] ; then + SKIP=1 + fi + done + if [ $SKIP = 0 ] ; then + comp_file $source $src + fi +done + +if [ $FAIL != 0 ] ; then + echo "Code that does not adhere to GNU standards will not be accepted." + echo "You must fix these files before submission." +else + echo "Code correctly adheres to required style." +fi diff --git a/lab1-ptrs/tests/testsuite.c b/lab1-ptrs/tests/testsuite.c new file mode 100644 index 0000000..a938490 --- /dev/null +++ b/lab1-ptrs/tests/testsuite.c @@ -0,0 +1,36 @@ +#include +#include +#include +#include +#include +#include + +#include + +extern void public_tests (Suite *s); +void run_testsuite (void); + +Suite * +test_suite (void) +{ + Suite *s = suite_create ("Default"); + public_tests (s); + return s; +} + +void +run_testsuite (void) +{ + Suite *s = test_suite (); + SRunner *sr = srunner_create (s); + srunner_run_all (sr, CK_NORMAL); + srunner_free (sr); +} + +int +main (void) +{ + srand((unsigned)time(NULL)); + run_testsuite (); + return EXIT_SUCCESS; +} diff --git a/lab2-cmd/.gitignore b/lab2-cmd/.gitignore new file mode 100644 index 0000000..d70f6f2 --- /dev/null +++ b/lab2-cmd/.gitignore @@ -0,0 +1,20 @@ +# Ignore all compiled files regardless of location +**/*.o + +# But not the private.o +!tests/private.o + +# Ignore executables for this project +shell +testsuite + +# Ignore test outputs +tests/ckstyle +tests/itests.txt +tests/outputs +tests/style.txt +tests/valgrind +tests/utests.txt + +**/.nfs* +**/.vscode diff --git a/lab2-cmd/Makefile b/lab2-cmd/Makefile new file mode 100644 index 0000000..3998d5a --- /dev/null +++ b/lab2-cmd/Makefile @@ -0,0 +1,51 @@ +# +# Simple Makefile +# Mike Lam, James Madison University, August 2016 +# +# This makefile builds a simple application that contains a main module +# (specified by the EXE variable) and a predefined list of additional modules +# (specified by the MODS variable). If there are any external library +# dependencies (e.g., the math library, "-lm"), list them in the LIBS variable. +# If there are any precompiled object files, list them in the OBJS variable. +# +# By default, this makefile will build the project with debugging symbols and +# without optimization. To change this, edit or remove the "-g" and "-O0" +# options in CFLAGS and LDFLAGS accordingly. +# +# By default, this makefile build the application using the GNU C compiler, +# adhering to the C99 standard with all warnings enabled. + + +# application-specific settings and run target + +EXE=shell +MODS=model.o effects.o +OBJS= +LIBS= + +default: $(EXE) + +test: $(EXE) + make -C tests test + +# compiler/linker settings + +CC=gcc +CFLAGS=-g -O0 -Wall -Werror --std=c99 -pedantic +LDFLAGS=-g -O0 + + +# build targets + +$(EXE): main.o $(MODS) $(OBJS) + $(CC) $(LDFLAGS) -o $(EXE) $^ $(LIBS) + +%.o: %.c + $(CC) -c $(CFLAGS) $< + +clean: + rm -f $(EXE) main.o $(MODS) + make -C tests clean + +.PHONY: default clean + diff --git a/lab2-cmd/effects.c b/lab2-cmd/effects.c new file mode 100644 index 0000000..e0947ed --- /dev/null +++ b/lab2-cmd/effects.c @@ -0,0 +1,88 @@ +#define _POSIX_C_SOURCE 200809L // needed for strdup extension + +#include +#include +#include +#include +#include +#include + +#include "effects.h" +#include "model.h" + +/* Executed when starting to process a new command line. The fsm_t + should have been updated to include a pointer to the current token. + For instance, if the command line was "ls -l data NL", the fsm_t + has a field that points to "ls". */ +void +start_command (fsm_t *cmdmodel) +{ + printf ("Starting new command: %s\n", cmdmodel->current_token); + // TODO: Copy the current token to store it in the FSM's command + // field. Next, create the FSM's args array (length MAX_ARGUMENTS) + // set the current token as args[0], and initialize nargs to be + // the number of arguments (1 at this point). + // Allocate args array (NULL-initialized) if not already + cmdmodel->args = calloc (MAX_ARGUMENTS, sizeof (char *)); + cmdmodel->command = cmdmodel->current_token; + cmdmodel->args[0] = cmdmodel->current_token; + cmdmodel->nargs = 1; +} + +/* Executed when processing a token after the command name. For instance, + if the command line was "ls -l data NL", this function will be called + when the current token is "-l" and again when it is "data". */ +void +append (fsm_t *cmdmodel) +{ + if (cmdmodel->nargs >= MAX_ARGUMENTS) + return; + + printf ("Appending %s to the argument list\n", cmdmodel->current_token); + assert (cmdmodel->args != NULL); + + // TODO: Store the current token into the args array and increment nargs + cmdmodel->args[cmdmodel->nargs++] = cmdmodel->current_token; +} + +/* Executed when either a NL or | (pipe) is encountered. For instance, if + the command line is "ls -l data NL", the current token will be "NL"; also, + the FSM's args array should be complete, containing "ls", "-l", and "data", + followed by several NULL pointers. */ +void +execute (fsm_t *cmdmodel) +{ + assert (cmdmodel->args != NULL); + + // TODO: Print out the argument list similar to the format shown and free + // the args array. + printf ("Execute %s with arguments { ", cmdmodel->args[0]); + for (uint8_t i = 0; i < cmdmodel->nargs; i++) + { + printf ("%s, ", cmdmodel->args[i]); + } + printf ("(null) }\n"); +} + +// No changes are needed to the effects below + +void +link_commands (fsm_t *cmdmodel) +{ + printf ("Set up pipe\n"); + execute (cmdmodel); +} + +void +error_pipe (fsm_t *cmdmodel) +{ + printf ("ERROR: Received token %s while in state %s\n", + cmdmodel->current_token, state_name (cmdmodel->state)); +} + +void +error_newline (fsm_t *cmdmodel) +{ + printf ("ERROR: Received token %s while in state %s\n", + cmdmodel->current_token, state_name (cmdmodel->state)); +} diff --git a/lab2-cmd/effects.h b/lab2-cmd/effects.h new file mode 100644 index 0000000..18ca2e8 --- /dev/null +++ b/lab2-cmd/effects.h @@ -0,0 +1,13 @@ +#ifndef __effects_h__ +#define __effects_h__ + +#include "model.h" + +void start_command (fsm_t *); +void append (fsm_t *); +void execute (fsm_t *); +void link_commands (fsm_t *); +void error_pipe (fsm_t *); +void error_newline (fsm_t *); + +#endif diff --git a/lab2-cmd/main.c b/lab2-cmd/main.c new file mode 100644 index 0000000..437fb40 --- /dev/null +++ b/lab2-cmd/main.c @@ -0,0 +1,61 @@ +/* + * CS 361: Template lab driver + * + * Name: + */ + +#include +#include +#include +#include +#include +#include + +#include "model.h" + +void +usage (void) +{ + printf ("shell, a command line state model simulator\n\n"); + printf ("usage: shell cmd [arg ...] [ | cmd [arg ...] ... ]\n"); + printf ("Each cmd can contain an argument list of variable length\n"); + printf ("Each cmd after the first must be preceded by a pipe | character\n"); +} + +int +main (int argc, char **argv) +{ + // With no arguments, show usage and exit + if (argc != 2) + { + usage (); + return EXIT_FAILURE; + } + + // Declare and initialize a FSM + fsm_t *cmdmodel = cmdline_init (); + if (cmdmodel == NULL) + return EXIT_FAILURE; + + // TODO: Change this to split the string into tokens, where + // each token is an event that needs to be handled. After + // looking up the event number, store the token in the FSM + // and call handle_event(). + char *token = strtok (argv[1], " "); + while (token != NULL) + { + cmdmodel->current_token = token; + event_t event = lookup (token); + if (!handle_event (cmdmodel, event)) + break; + + token = strtok (NULL, " "); + } + + // Free remaining allocated data + if (cmdmodel->args != NULL) + free (cmdmodel->args); + free (cmdmodel); + + return EXIT_SUCCESS; +} diff --git a/lab2-cmd/model.c b/lab2-cmd/model.c new file mode 100644 index 0000000..0840814 --- /dev/null +++ b/lab2-cmd/model.c @@ -0,0 +1,130 @@ +#include +#include +#include +#include +#include + +#include "effects.h" +#include "model.h" + +// TODO: Complete this table mapping state/events to the target state. Each +// row corresponds to the current state. Each column within that row indicates +// the next state for a given event. If there is no transition defined, that +// entry should be NST ("no state"). +static state_t const _transitions[NUM_STATES][NUM_EVENTS] + = { { Command, Term, NST }, + { Arguments, Make_Pipe, Term }, + { Arguments, Make_Pipe, Term }, + { Command, Term, Term }, + { NST, NST, NST } }; + +// TODO: Create a table mapping states/events to the effect functions. If +// there is no valid transition, the entry here would be NULL because actions +// are function pointers. +static action_t const _effects[NUM_STATES][NUM_EVENTS] + = { { start_command, error_pipe, NULL }, + { append, link_commands, execute }, + { append, link_commands, execute }, + { start_command, error_pipe, error_newline }, + { NULL, NULL, NULL } }; + +/* Create an instance of an FSM and initialize its fields as appropriate. + Some fields are common to most FSMs (such as an initial state or a + pointer to a transition function). Other fields will be specific to + this fsm_t declaration. Return NULL if any part of the initialization + fails. */ +fsm_t * +cmdline_init (void) +{ + fsm_t *fsm = calloc (1, sizeof (fsm_t)); + fsm->state = Init; + fsm->transition = transition; + + return fsm; +} + +// TODO: Create a transition function that is specific to this type +// of FSM. This function needs to take an fsm_t* and an event, returning +// both the new state and the effect to perform (the latter is returned +// using a call-by-reference parameter. This function should NOT contain +// any "if" types of statements based on the state or event; it should +// simply lookup these values in the tables defined above. +state_t +transition (struct fsm *fsm, event_t event, action_t *effect) +{ + assert (fsm->state < NST); + assert (event < NIL); + + *effect = _effects[fsm->state][event]; + return _transitions[fsm->state][event]; +} + +/* Helper function for providing a printable string name for an event */ +const char * +event_name (event_t evt) +{ + assert (evt <= NIL); + + // Event names for printing out + const char *names[] = { "TOKEN", "PIPE", "NEWLINE", "NIL" }; + return names[evt]; +} + +/* Helper function for providing a printable string name for an state */ +const char * +state_name (state_t st) +{ + assert (st <= NST); + + // State names for printing out + const char *names[] + = { "Init", "Command", "Arguments", "Make_Pipe", "Term", "NST" }; + return names[st]; +} + +/* Generic front-end for handling events. Should do nothing more + than calling the FSM's transition function, performing an effect + (if appropriate) and updating the state. Return false if the new + state is the terminal state. */ +bool +handle_event (fsm_t *fsm, event_t event) +{ + assert (fsm != NULL); + // TODO: Look up the current state/event combination in the + // transition table. Print the following line for debugging + // purposes just for this lab. This should be printed even + // if there is no transition. + action_t effect; + state_t new_state = fsm->transition (fsm, event, &effect); + printf ("[%s.%s -> %s]\n", state_name (fsm->state), event_name (event), + state_name (new_state)); + + // TODO: If the state/event combination is valid, execute + // the transition and effect function (if there is one). + // If the next state is Term (terminated), return false. + // Otherwise return true. + if (new_state != NST) + { + if (effect != NULL) + { + effect (fsm); + } + + fsm->state = new_state; + } + + return fsm->state != Term; +} + +/* Given a string, return the event type. Do not modify this function. */ +event_t +lookup (char *token) +{ + if (!strcmp (token, "|")) + return PIPE; + + if (!strcmp (token, "NL")) + return NEWLINE; + + return TOKEN; +} diff --git a/lab2-cmd/model.h b/lab2-cmd/model.h new file mode 100644 index 0000000..cb2a7ee --- /dev/null +++ b/lab2-cmd/model.h @@ -0,0 +1,75 @@ +#ifndef __model_h__ +#define __model_h__ + +#include +#include +#include +#include + +// Generic definitions for any type of statemodel + +// States and events should just be integers +typedef int state_t; +typedef int event_t; + +// Needed for circular typedef. This lets action_t use fsm_t in its parameter +// list, while the struct fsm can use action_t as a field. +typedef struct fsm fsm_t; + +// All entry, exit, and effect instances use the action type +typedef void (*action_t) (fsm_t *); + +// Each FSM instance contains a current state +struct fsm +{ + state_t state; // current state + + // pointer to the FSM's transition function + state_t (*transition) (struct fsm *, event_t, action_t *); + + // Additional data fields specific to this FSM + char *command; // the name of the command to run + size_t nargs; // the number of command-line arguments + char **args; // the command-line arguments + char *current_token; // current token being processed +}; + +// Generic entry point for handling events +bool handle_event (fsm_t *, event_t); + +// Additional definitions specific to an FSM for command line processing + +#define MAX_ARGUMENTS 10 + +// Events +typedef enum +{ + TOKEN, // normal command-line token + PIPE, // vertical bar character + NEWLINE, // newline at the end of the command + NIL // invalid non-event +} cmdevt_t; +#define NUM_EVENTS NIL + +// States +typedef enum +{ + Init, // initial state + Command, // establishing the command name + Arguments, // building the argument list + Make_Pipe, // linking the commands together for a pipe + Term, // terminal state (execute program or error) + NST // invalid non-state +} cmdst_t; +#define NUM_STATES NST + +// Helper functions +fsm_t *cmdline_init (void); // initialize the FSM +state_t transition (struct fsm *fsm, event_t event, action_t *effect); +event_t lookup (char *); // convert an event string to its numeric value + +// Translate event/state numbers to their string equivalent +const char *event_name (event_t); +const char *state_name (state_t); + +#endif diff --git a/lab2-cmd/tests/Makefile b/lab2-cmd/tests/Makefile new file mode 100644 index 0000000..0f5df03 --- /dev/null +++ b/lab2-cmd/tests/Makefile @@ -0,0 +1,88 @@ +# +# Simple Test Makefile +# Mike Lam, James Madison University, August 2016 +# +# This version of the Makefile includes support for building a test suite. The +# recommended framework is Check (http://check.sourceforge.net/). To build and +# run the test suite, execute the "test" target. The test suite must be located +# in a module called "testsuite". The MODS, LIBS, and OBJS variables work as +# they do in the main Makefile. +# +# To change the default build target (which executes when you just type +# "make"), change the right-hand side of the definition of the "default" +# target. +# +# By default, this makefile will build the project with debugging symbols and +# without optimization. To change this, edit or remove the "-g" and "-O0" +# options in CFLAGS and LDFLAGS accordingly. +# +# By default, this makefile build the application using the GNU C compiler, +# adhering to the C99 standard with all warnings enabled. + + +# application-specific settings and run target + +EXE=../fsm +TEST=testsuite +MODS=public.o +OBJS=../model.o ../effects.o private.o +LIBS= + +UTESTOUT=utests.txt +ITESTOUT=itests.txt +SCHECKOUT=style.txt + +default: $(TEST) + +$(EXE): + make -C ../ + +test: utest itest style + @echo "========================================" + +utest: $(EXE) $(TEST) + @echo "========================================" + @echo " UNIT TESTS" + @./utests.sh | tee $(UTESTOUT) + +itest: $(EXE) + @echo "========================================" + @echo " INTEGRATION TESTS" + @./integration.sh | tee $(ITESTOUT) + +style: $(EXE) + @echo "========================================" + @echo " CODING STYLE CHECK" + @./style.sh 2>/dev/null >$(SCHECKOUT) + @cat $(SCHECKOUT) + +# compiler/linker settings + +CC=gcc +CFLAGS=-g -O0 -Wall --std=c99 +LDFLAGS=-g -O0 + +#CFLAGS+=-I/opt/local/include -Wno-gnu-zero-variadic-macro-arguments +#CFLAGS+=-Wno-gnu-zero-variadic-macro-arguments +#LDFLAGS+=-L/opt/local/lib +#LDFLAGS= +LIBS+=-lcheck -lm -lpthread + +ifeq ($(shell uname -s),Linux) + LIBS+=-lrt -lsubunit +endif + + +# build targets + +$(TEST): $(TEST).o $(MODS) $(OBJS) + $(CC) $(LDFLAGS) -o $(TEST) $^ $(LIBS) + +%.o: %.c + $(CC) -c $(CFLAGS) $< + +clean: + rm -rf $(TEST) $(TEST).o $(MODS) $(UTESTOUT) $(ITESTOUT) $(SCHECKOUT) outputs valgrind ckstyle + +.PHONY: default clean test unittest inttest + diff --git a/lab2-cmd/tests/expected/.INTEG_command.sha b/lab2-cmd/tests/expected/.INTEG_command.sha new file mode 100644 index 0000000..2e74f11 --- /dev/null +++ b/lab2-cmd/tests/expected/.INTEG_command.sha @@ -0,0 +1 @@ +015ebe95460b1d6fbf79df280ec3ae5fab362ae8 diff --git a/lab2-cmd/tests/expected/.INTEG_execute.sha b/lab2-cmd/tests/expected/.INTEG_execute.sha new file mode 100644 index 0000000..32a3cce --- /dev/null +++ b/lab2-cmd/tests/expected/.INTEG_execute.sha @@ -0,0 +1 @@ +42ae8f1bdf52b57ad234791690bf611379fc8249 diff --git a/lab2-cmd/tests/expected/.INTEG_execute_flags.sha b/lab2-cmd/tests/expected/.INTEG_execute_flags.sha new file mode 100644 index 0000000..40e3ea6 --- /dev/null +++ b/lab2-cmd/tests/expected/.INTEG_execute_flags.sha @@ -0,0 +1 @@ +ebc846cda2557a05e691ef4d7dc7de4ee312cbd1 diff --git a/lab2-cmd/tests/expected/.INTEG_execute_name.sha b/lab2-cmd/tests/expected/.INTEG_execute_name.sha new file mode 100644 index 0000000..b1e3ee8 --- /dev/null +++ b/lab2-cmd/tests/expected/.INTEG_execute_name.sha @@ -0,0 +1 @@ +2fd3754d04e83c387269e26d81186b290d299f82 diff --git a/lab2-cmd/tests/expected/.INTEG_pipe.sha b/lab2-cmd/tests/expected/.INTEG_pipe.sha new file mode 100644 index 0000000..7bfe9bc --- /dev/null +++ b/lab2-cmd/tests/expected/.INTEG_pipe.sha @@ -0,0 +1 @@ +17611b03d098cb2d095ddac6dc39c08f66514f4e diff --git a/lab2-cmd/tests/expected/.INTEG_pipe_flags.sha b/lab2-cmd/tests/expected/.INTEG_pipe_flags.sha new file mode 100644 index 0000000..02017ee --- /dev/null +++ b/lab2-cmd/tests/expected/.INTEG_pipe_flags.sha @@ -0,0 +1 @@ +10b932ebcb7ca5600c9b81652547ea3721854698 diff --git a/lab2-cmd/tests/expected/INTEG_command.txt b/lab2-cmd/tests/expected/INTEG_command.txt new file mode 100644 index 0000000..756ac9a --- /dev/null +++ b/lab2-cmd/tests/expected/INTEG_command.txt @@ -0,0 +1,2 @@ +[Init.TOKEN -> Command] +Starting new command: ls diff --git a/lab2-cmd/tests/expected/INTEG_execute.txt b/lab2-cmd/tests/expected/INTEG_execute.txt new file mode 100644 index 0000000..d0bba3a --- /dev/null +++ b/lab2-cmd/tests/expected/INTEG_execute.txt @@ -0,0 +1,4 @@ +[Init.TOKEN -> Command] +Starting new command: ls +[Command.NEWLINE -> Term] +Execute ls with arguments { ls, (null) } diff --git a/lab2-cmd/tests/expected/INTEG_execute_flags.txt b/lab2-cmd/tests/expected/INTEG_execute_flags.txt new file mode 100644 index 0000000..294ffe9 --- /dev/null +++ b/lab2-cmd/tests/expected/INTEG_execute_flags.txt @@ -0,0 +1,8 @@ +[Init.TOKEN -> Command] +Starting new command: ls +[Command.TOKEN -> Arguments] +Appending -a to the argument list +[Arguments.TOKEN -> Arguments] +Appending -l to the argument list +[Arguments.NEWLINE -> Term] +Execute ls with arguments { ls, -a, -l, (null) } diff --git a/lab2-cmd/tests/expected/INTEG_execute_name.txt b/lab2-cmd/tests/expected/INTEG_execute_name.txt new file mode 100644 index 0000000..1dc586e --- /dev/null +++ b/lab2-cmd/tests/expected/INTEG_execute_name.txt @@ -0,0 +1,6 @@ +[Init.TOKEN -> Command] +Starting new command: ls +[Command.TOKEN -> Arguments] +Appending data to the argument list +[Arguments.NEWLINE -> Term] +Execute ls with arguments { ls, data, (null) } diff --git a/lab2-cmd/tests/expected/INTEG_pipe.txt b/lab2-cmd/tests/expected/INTEG_pipe.txt new file mode 100644 index 0000000..71e3b1c --- /dev/null +++ b/lab2-cmd/tests/expected/INTEG_pipe.txt @@ -0,0 +1,9 @@ +[Init.TOKEN -> Command] +Starting new command: ls +[Command.PIPE -> Make_Pipe] +Set up pipe +Execute ls with arguments { ls, (null) } +[Make_Pipe.TOKEN -> Command] +Starting new command: head +[Command.NEWLINE -> Term] +Execute head with arguments { head, (null) } diff --git a/lab2-cmd/tests/expected/INTEG_pipe_flags.txt b/lab2-cmd/tests/expected/INTEG_pipe_flags.txt new file mode 100644 index 0000000..8ac5f3d --- /dev/null +++ b/lab2-cmd/tests/expected/INTEG_pipe_flags.txt @@ -0,0 +1,15 @@ +[Init.TOKEN -> Command] +Starting new command: ls +[Command.TOKEN -> Arguments] +Appending -l to the argument list +[Arguments.PIPE -> Make_Pipe] +Set up pipe +Execute ls with arguments { ls, -l, (null) } +[Make_Pipe.TOKEN -> Command] +Starting new command: head +[Command.TOKEN -> Arguments] +Appending -n to the argument list +[Arguments.TOKEN -> Arguments] +Appending 1 to the argument list +[Arguments.NEWLINE -> Term] +Execute head with arguments { head, -n, 1, (null) } diff --git a/lab2-cmd/tests/integration.sh b/lab2-cmd/tests/integration.sh new file mode 100755 index 0000000..777b769 --- /dev/null +++ b/lab2-cmd/tests/integration.sh @@ -0,0 +1,75 @@ +#!/bin/bash + +EXE="../shell" + +function run_test { + + # parameters + TAG=$1 + ARGS=$2 + + # file paths + OUTPUT=outputs/$TAG.txt + DIFF=outputs/$TAG.diff + EXPECT=expected/$TAG.txt + VALGRND=valgrind/$TAG.txt + + # print tag format + PTAG=$(printf '%-30s' "$TAG") + + # check for expected text that needs to be fixed + if [ ! -z "$(egrep "<<<<.*>>>>" "$EXPECT")" ] ; then + echo "$PTAG FAIL ($EXPECT not correct)" + return + fi + + # compare the file + SHAOUT=$(shasum "$EXPECT" | awk '{print $1}') + SHAEXP=$(cat "expected/.$TAG.sha") + if [ "$SHAOUT" != "$SHAEXP" ] ; then + echo "$PTAG FAIL ($EXPECT not correct)" + return + fi + + # run test and compare output to the expected version + $EXE "$ARGS" 2>/dev/null >"$OUTPUT" + diff -u "$OUTPUT" "$EXPECT" >"$DIFF" + if [ -s "$DIFF" ]; then + + # try alternative solution (if it exists) + EXPECT=expected/$TAG-2.txt + if [ -e "$EXPECT" ]; then + diff -u "$OUTPUT" "$EXPECT" >"$DIFF" + if [ -s "$DIFF" ]; then + echo "$PTAG FAIL (see $DIFF for details)" + else + echo "$PTAG pass" + fi + else + echo "$PTAG FAIL (see $DIFF for details)" + fi + else + echo "$PTAG pass" + fi + + # run valgrind + valgrind $EXE $ARGS &>$VALGRND +} + +# initialize output folders +mkdir -p outputs +mkdir -p valgrind +rm -f outputs/* valgrind/* + +# run individual tests +source itests.include + +# check for memory leaks +LEAK=`cat valgrind/*.txt | grep 'definitely lost' | grep -v ' 0 bytes in 0 blocks'` +if [ -z "$LEAK" ]; then + echo "No memory leak found." +else + echo "Memory leak(s) found. See files listed below for details." + grep 'definitely lost' valgrind/*.txt | grep -v ' 0 bytes in 0 blocks' | sed -e 's/:.*$//g' | sed -e 's/^/ - /g' +fi + diff --git a/lab2-cmd/tests/itests.include b/lab2-cmd/tests/itests.include new file mode 100644 index 0000000..2c9c862 --- /dev/null +++ b/lab2-cmd/tests/itests.include @@ -0,0 +1,11 @@ +# list of integration tests +# format: run_test +# used as the root for all filenames (i.e., "expected/$TAG.txt") +# command-line arguments to test + +run_test INTEG_command "ls" +run_test INTEG_execute "ls NL" +run_test INTEG_execute_name "ls data NL" +run_test INTEG_execute_flags "ls -a -l NL" +run_test INTEG_pipe "ls | head NL" +run_test INTEG_pipe_flags "ls -l | head -n 1 NL" diff --git a/lab2-cmd/tests/private.o b/lab2-cmd/tests/private.o new file mode 100644 index 0000000..9f5baf0 Binary files /dev/null and b/lab2-cmd/tests/private.o differ diff --git a/lab2-cmd/tests/public.c b/lab2-cmd/tests/public.c new file mode 100644 index 0000000..9e92131 --- /dev/null +++ b/lab2-cmd/tests/public.c @@ -0,0 +1,53 @@ +#include +#include +#include +#include + +#include + +#include "../effects.h" +#include "../model.h" + +/* nothing but starting a new cmdline */ +START_TEST (UNIT_new_cmdline) +{ + printf ("\n======================================\n"); + printf ("UNIT TEST: new_cmdline\n\n"); + + fsm_t *fsm = cmdline_init (); + assert (fsm != NULL); + + handle_event (fsm, TOKEN); + ck_assert_int_eq (fsm->state, Command); + + free (fsm); +} +END_TEST + +/* a single cmdline from NEW to TRM */ +START_TEST (UNIT_exec_cmd) +{ + printf ("\n======================================\n"); + printf ("UNIT TEST: exec_cmd\n\n"); + + fsm_t *fsm = cmdline_init (); + assert (fsm != NULL); + + handle_event (fsm, TOKEN); + ck_assert_int_eq (fsm->state, Command); + + handle_event (fsm, NEWLINE); + ck_assert_int_eq (fsm->state, Term); + + free (fsm); +} +END_TEST + +void public_tests (Suite *s) +{ + TCase *tc_public = tcase_create ("Public"); + tcase_add_test (tc_public, UNIT_new_cmdline); + tcase_add_test (tc_public, UNIT_exec_cmd); + suite_add_tcase (s, tc_public); +} + diff --git a/lab2-cmd/tests/style.sh b/lab2-cmd/tests/style.sh new file mode 100755 index 0000000..ba2210a --- /dev/null +++ b/lab2-cmd/tests/style.sh @@ -0,0 +1,53 @@ +#!/bin/bash + +STYLE="gnu" + +IGNORE=() +FAIL=0 + +function comp_file { + + SRC=$1 + SRC_NAME=$2 + + # file paths + FORMAT=ckstyle/${SRC_NAME}.$STYLE + DIFF=ckstyle/${SRC_NAME}.diff + + # run clang-format and compare results + clang-format --style=$STYLE $source > $FORMAT + diff -u $SRC $FORMAT >$DIFF + + PTAG=$(printf '%-30s' "$SRC_NAME") + if [ -s $DIFF ]; then + echo "$PTAG FAIL (see $DIFF for details)" + FAIL=1 + else + echo "$PTAG pass" + rm $DIFF + fi + rm $FORMAT +} + +mkdir -p ckstyle +rm -f ckstyle/* + +for source in $(ls ../*.c ../*.h) ; do + SKIP=0 + src=$(basename $source) + for ignore in ${IGNORE[*]} ; do + if [ "$src" = "$ignore" ] ; then + SKIP=1 + fi + done + if [ $SKIP = 0 ] ; then + comp_file $source $src + fi +done + +if [ $FAIL != 0 ] ; then + echo "Code that does not adhere to GNU standards will not be accepted." + echo "You must fix these files before submission." +else + echo "Code correctly adheres to required style." +fi diff --git a/lab2-cmd/tests/testsuite.c b/lab2-cmd/tests/testsuite.c new file mode 100644 index 0000000..145faa1 --- /dev/null +++ b/lab2-cmd/tests/testsuite.c @@ -0,0 +1,34 @@ +#include +#include +#include +#include +#include +#include + +#include + +extern void public_tests (Suite *s); +extern void private_tests (Suite *s); + +Suite * test_suite (void) +{ + Suite *s = suite_create ("Default"); + public_tests (s); + private_tests (s); + return s; +} + +void run_testsuite () +{ + Suite *s = test_suite (); + SRunner *sr = srunner_create (s); + srunner_run_all (sr, CK_NORMAL); + srunner_free (sr); +} + +int main (void) +{ + srand((unsigned)time(NULL)); + run_testsuite (); + return EXIT_SUCCESS; +} diff --git a/lab2-cmd/tests/utests.sh b/lab2-cmd/tests/utests.sh new file mode 100755 index 0000000..3f133d6 --- /dev/null +++ b/lab2-cmd/tests/utests.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +OUTPUT=utests.output + +./testsuite > "$OUTPUT" 2>/dev/null + +if [ ! -s "$OUTPUT" ] ; then + echo "UNIT FAIL (testsuite produced no output)" + rm -f "$OUTPUT" + exit 1 +fi + +cat "$OUTPUT" | awk '/Failures/,0' + +percent=$(cat "$OUTPUT" | grep Failures | cut -d':' -f1) +rm -f "$OUTPUT" +[ "$percent" = "100%" ] && exit 0 + +echo "(run ./testsuite in tests directory for more information)" +exit 1 + diff --git a/lab3-proc/.gitignore b/lab3-proc/.gitignore new file mode 100644 index 0000000..783d568 --- /dev/null +++ b/lab3-proc/.gitignore @@ -0,0 +1,20 @@ +# Ignore all compiled files regardless of location +**/*.o + +# But not the private.o +!tests/private.o + +# Ignore executables for this project +procs +testsuite + +# Ignore test outputs +tests/ckstyle +tests/itests.txt +tests/outputs +tests/style.txt +tests/valgrind +tests/utests.txt + +**/.nfs* +**/.vscode diff --git a/lab3-proc/Makefile b/lab3-proc/Makefile new file mode 100644 index 0000000..5560846 --- /dev/null +++ b/lab3-proc/Makefile @@ -0,0 +1,51 @@ +# +# Simple Makefile +# Mike Lam, James Madison University, August 2016 +# +# This makefile builds a simple application that contains a main module +# (specified by the EXE variable) and a predefined list of additional modules +# (specified by the MODS variable). If there are any external library +# dependencies (e.g., the math library, "-lm"), list them in the LIBS variable. +# If there are any precompiled object files, list them in the OBJS variable. +# +# By default, this makefile will build the project with debugging symbols and +# without optimization. To change this, edit or remove the "-g" and "-O0" +# options in CFLAGS and LDFLAGS accordingly. +# +# By default, this makefile build the application using the GNU C compiler, +# adhering to the C99 standard with all warnings enabled. + + +# application-specific settings and run target + +EXE=procs +MODS=signals.o child.o +OBJS= +LIBS= + +default: $(EXE) + +test: $(EXE) + make -C tests test + +# compiler/linker settings + +CC=gcc +CFLAGS=-g -O0 -Wall -Werror --std=c99 -pedantic -D_POSIX_SOURCE +LDFLAGS=-g -O0 -pthread + + +# build targets + +$(EXE): main.o $(MODS) $(OBJS) + $(CC) $(LDFLAGS) -o $(EXE) $^ $(LIBS) + +%.o: %.c + $(CC) -c $(CFLAGS) $< + +clean: + rm -f $(EXE) main.o $(MODS) + make -C tests clean + +.PHONY: default clean + diff --git a/lab3-proc/child.c b/lab3-proc/child.c new file mode 100644 index 0000000..2d329c8 --- /dev/null +++ b/lab3-proc/child.c @@ -0,0 +1,39 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "child.h" + +pid_t +run_child (int rc) +{ + char *str = "I am the parent"; + + // TODO: Add code here to create a child process that prints + // "I am the child" and exits with return code passed as the rc + // parameter to this function. The parent must wait for the child + // to finish before printing the child's exit status (consult the + // textbook for this as needed) before printing "I am the parent". + // The parent should return the child's PID from this function. + int status = 0; + pid_t pid = fork (); + if (pid == 0) + { + printf ("I am the child\n"); + exit (rc); + } + else + { + waitpid (pid, &status, 0); + } + + printf ("Child exited with status %d\n", WEXITSTATUS (status)); + + printf ("%s\n", str); + return pid; +} diff --git a/lab3-proc/child.h b/lab3-proc/child.h new file mode 100644 index 0000000..fc7023a --- /dev/null +++ b/lab3-proc/child.h @@ -0,0 +1,6 @@ +#ifndef __CS361_CHILD_H__ +#define __CS361_CHILD_H__ + +pid_t run_child (int); + +#endif diff --git a/lab3-proc/main.c b/lab3-proc/main.c new file mode 100644 index 0000000..dc431a3 --- /dev/null +++ b/lab3-proc/main.c @@ -0,0 +1,87 @@ +/* + * CS 361: Process management lab + * + * Name: + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "child.h" +#include "signals.h" + +#define SIGNAL_MAX 20 + +int cmdline (int, char **, int *, char **); + +void +usage (void) +{ + printf ("Usage: procs [option]\n"); + printf (" Options are:\n"); + printf (" -e N Create a child that exits with status N\n"); + printf (" -s N Override signal number N and run signal handler\n"); + printf ("\n Default is to create a child process with normal exit\n"); +} + +int +main (int argc, char **argv) +{ + char *signal_override = NULL; + int rc = 42; + if (cmdline (argc, argv, &rc, &signal_override) < 0) + { + usage (); + return EXIT_FAILURE; + } + + // Set up a timeout to kill the process after 5 seconds + struct itimerval timeout; + memset (&timeout, 0, sizeof (timeout)); + timeout.it_value.tv_sec = 5; + timeout.it_value.tv_usec = 0; + setitimer (ITIMER_REAL, &timeout, NULL); + + if (signal_override == NULL) + run_child (rc); + else + override (signal_override); + + return EXIT_SUCCESS; +} + +/* DO NOT MODIFY THIS FUNCTION. Parses the command line arguments to get + the portion of the lab to run. */ +int +cmdline (int argc, char **argv, int *rc, char **sig) +{ + int option; + + while ((option = getopt (argc, argv, "e:s:")) != -1) + { + switch (option) + { + case 'e': + *rc = (int)strtol (optarg, NULL, 10); + break; + case 's': + *sig = optarg; + break; + default: + return -1; + } + } + + return 0; +} diff --git a/lab3-proc/signals.c b/lab3-proc/signals.c new file mode 100644 index 0000000..b7870e3 --- /dev/null +++ b/lab3-proc/signals.c @@ -0,0 +1,110 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "signals.h" + +static void catch_loop (int); + +/* Convert the short signal name (NNN part of SIGNNN) to its number. + Consult /usr/include/asm/signal.h for the number mappings. For + this lab, hard-code the numbers; do not use the numeric constants. + Return -1 if the name isn't one you need to look up. */ +int +signal_lookup (char *name) +{ + // TODO: Look up the numbers for INT, SEGV, KILL, and TSTP + if (strcmp (name, "INT") == 0) + return 2; + if (strcmp (name, "SEGV") == 0) + return 11; + if (strcmp (name, "KILL") == 0) + return 9; + if (strcmp (name, "TSTP") == 0) + return 20; + return -1; +} + +// Entry point for overriding a signal handler in this lab. +void +override (char *signal) +{ + int sig = signal_lookup (signal); + if (sig == SIGKILL) + printf ("Received bad signal name: %s\n", signal); + else + catch_loop (sig); +} + +// Generic signal handler. This function doesn't use the signal number +// in the provided distribution. +void +sig_handler (int signum __attribute__ ((unused))) +{ + // TODO: Complete this signal handler so that it writes the message + // "Breaking out with signal %d\n" with the signal number that was + // passed. Note that you cannot use printf() in a signal handler. + // You must use write(). After writing the message, exit with return + // code 37. + // + // HINT: If you fail integration tests and get a message that the + // binary files are different, you may be writing an extra byte (the + // null byte). You can make sure you avoid this by using strlen() + // instead of trying to hard-code a length. + char buf[128]; + snprintf (buf, 128, "Breaking out with signal %d\n", signum); + write (STDOUT_FILENO, buf, strlen (buf)); + exit (37); +} + +void +catch_loop (int signum) +{ + // sigaction(signum) + // TODO: Override the signal handler for the signal number passed. + // Then create a child that runs the following code: + // prctl (PR_SET_PDEATHSIG, SIGKILL); + // printf ("Child has started\n"); + // fflush (stdout); + // while (1) ; + + struct sigaction sa = { 0 }; + sa.sa_handler = sig_handler; + + sigaction (signum, &sa, NULL); + + pid_t pid = fork (); + + if (pid == 0) + { + prctl (PR_SET_PDEATHSIG, SIGKILL); + printf ("Child has started\n"); + fflush (stdout); + while (1) + pause (); + } + + // HINT: You will NOT call sig_handler() directly here. Instead, + // you must use sigaction() to modify the signal's interrupt handler + // so that sig_handler() will be called later when the signal occurs. + + // After starting the child, the parent will call sleep (1) to wait + // for 1 second before sending the signal to the child. The parent + // must wait for the child to run (and exit), then print + // "Child was stopped with status %d\n" with the child process's exit + // status code, retrieved with waitpid(). Note that you will need to + // call WEXITSTATUS() on the retrieved status code. + + sleep (1); + + int status = 0; + kill (pid, signum); + waitpid (pid, &status, 0); + printf ("Child was stopped with status %d\n", WEXITSTATUS (status)); +} diff --git a/lab3-proc/signals.h b/lab3-proc/signals.h new file mode 100644 index 0000000..c6fc1a2 --- /dev/null +++ b/lab3-proc/signals.h @@ -0,0 +1,7 @@ +#ifndef __CS361_SIGNALS_H__ +#define __CS361_SIGNALS_H__ + +void override (char *); +int signal_lookup (char *); + +#endif diff --git a/lab3-proc/tests/Makefile b/lab3-proc/tests/Makefile new file mode 100644 index 0000000..ab16746 --- /dev/null +++ b/lab3-proc/tests/Makefile @@ -0,0 +1,87 @@ +# +# Simple Test Makefile +# Mike Lam, James Madison University, August 2016 +# +# This version of the Makefile includes support for building a test suite. The +# recommended framework is Check (http://check.sourceforge.net/). To build and +# run the test suite, execute the "test" target. The test suite must be located +# in a module called "testsuite". The MODS, LIBS, and OBJS variables work as +# they do in the main Makefile. +# +# To change the default build target (which executes when you just type +# "make"), change the right-hand side of the definition of the "default" +# target. +# +# By default, this makefile will build the project with debugging symbols and +# without optimization. To change this, edit or remove the "-g" and "-O0" +# options in CFLAGS and LDFLAGS accordingly. +# +# By default, this makefile build the application using the GNU C compiler, +# adhering to the C99 standard with all warnings enabled. + + +# application-specific settings and run target + +EXE=../sigs +TEST=testsuite +MODS=public.o +OBJS=../signals.o ../child.o +LIBS= + +UTESTOUT=utests.txt +ITESTOUT=itests.txt +SCHECKOUT=style.txt + +default: $(TEST) + +$(EXE): + make -C ../ + +test: utest itest style + @echo "========================================" + +utest: $(EXE) $(TEST) + @echo "========================================" + @echo " UNIT TESTS" + @./$(TEST) 2>/dev/null >$(UTESTOUT) + @cat $(UTESTOUT) | sed -n -e '/Checks/,$$p' | sed -e 's/^private.*:[EF]://g' + +itest: $(EXE) + @echo "========================================" + @echo " INTEGRATION TESTS" + @./integration.sh | tee $(ITESTOUT) + +style: $(EXE) + @echo "========================================" + @echo " CODING STYLE CHECK" + @./style.sh 2>/dev/null >$(SCHECKOUT) + @cat $(SCHECKOUT) + +# compiler/linker settings + +CC=gcc +CFLAGS=-g -O0 -Wall --std=c99 -pedantic +LDFLAGS=-g -O0 + +CFLAGS+=-I/opt/local/include -Wno-gnu-zero-variadic-macro-arguments +LDFLAGS+=-L/opt/local/lib +LIBS+=-lcheck -lm -lpthread + +ifeq ($(shell uname -s),Linux) + LIBS+=-lrt -lsubunit +endif + + +# build targets + +$(TEST): $(TEST).o $(MODS) $(OBJS) + $(CC) $(LDFLAGS) -o $(TEST) $^ $(LIBS) + +%.o: %.c + $(CC) -c $(CFLAGS) $< + +clean: + rm -rf $(TEST) $(TEST).o $(MODS) $(UTESTOUT) $(ITESTOUT) $(SCHECKOUT) outputs valgrind + +.PHONY: default clean test unittest inttest + diff --git a/lab3-proc/tests/expected/.INTEG_child.sha b/lab3-proc/tests/expected/.INTEG_child.sha new file mode 100644 index 0000000..d768e3a --- /dev/null +++ b/lab3-proc/tests/expected/.INTEG_child.sha @@ -0,0 +1 @@ +6037b06b79a54f57617de151d505444f78a23489 diff --git a/lab3-proc/tests/expected/.INTEG_kill.sha b/lab3-proc/tests/expected/.INTEG_kill.sha new file mode 100644 index 0000000..0dae746 --- /dev/null +++ b/lab3-proc/tests/expected/.INTEG_kill.sha @@ -0,0 +1 @@ +ebef9658a6b46bd9cfcfeaf732a6d2444fe0b98a diff --git a/lab3-proc/tests/expected/.INTEG_segfault.sha b/lab3-proc/tests/expected/.INTEG_segfault.sha new file mode 100644 index 0000000..5f51de9 --- /dev/null +++ b/lab3-proc/tests/expected/.INTEG_segfault.sha @@ -0,0 +1 @@ +145cbab85e3834cf39635245272d5f80e6bd66f2 diff --git a/lab3-proc/tests/expected/.INTEG_sigint.sha b/lab3-proc/tests/expected/.INTEG_sigint.sha new file mode 100644 index 0000000..1568243 --- /dev/null +++ b/lab3-proc/tests/expected/.INTEG_sigint.sha @@ -0,0 +1 @@ +b4ec94fbc4feb8f7ce089341bd6c51e775fce9e1 diff --git a/lab3-proc/tests/expected/.INTEG_status.sha b/lab3-proc/tests/expected/.INTEG_status.sha new file mode 100644 index 0000000..8f94e2d --- /dev/null +++ b/lab3-proc/tests/expected/.INTEG_status.sha @@ -0,0 +1 @@ +fb52530bd8b6756b4acec57a42391f8b7c9c0e83 diff --git a/lab3-proc/tests/expected/INTEG_child.txt b/lab3-proc/tests/expected/INTEG_child.txt new file mode 100644 index 0000000..b2d306f --- /dev/null +++ b/lab3-proc/tests/expected/INTEG_child.txt @@ -0,0 +1,3 @@ +I am the child +Child exited with status 42 +I am the parent diff --git a/lab3-proc/tests/expected/INTEG_kill.txt b/lab3-proc/tests/expected/INTEG_kill.txt new file mode 100644 index 0000000..f110c52 --- /dev/null +++ b/lab3-proc/tests/expected/INTEG_kill.txt @@ -0,0 +1 @@ +Received bad signal name: KILL diff --git a/lab3-proc/tests/expected/INTEG_segfault.txt b/lab3-proc/tests/expected/INTEG_segfault.txt new file mode 100644 index 0000000..97847e2 --- /dev/null +++ b/lab3-proc/tests/expected/INTEG_segfault.txt @@ -0,0 +1,3 @@ +Child has started +Breaking out with signal 11 +Child was stopped with status 37 diff --git a/lab3-proc/tests/expected/INTEG_sigint.txt b/lab3-proc/tests/expected/INTEG_sigint.txt new file mode 100644 index 0000000..5c96cd8 --- /dev/null +++ b/lab3-proc/tests/expected/INTEG_sigint.txt @@ -0,0 +1,3 @@ +Child has started +Breaking out with signal 2 +Child was stopped with status 37 diff --git a/lab3-proc/tests/expected/INTEG_status.txt b/lab3-proc/tests/expected/INTEG_status.txt new file mode 100644 index 0000000..99187e4 --- /dev/null +++ b/lab3-proc/tests/expected/INTEG_status.txt @@ -0,0 +1,3 @@ +I am the child +Child exited with status 54 +I am the parent diff --git a/lab3-proc/tests/integration.sh b/lab3-proc/tests/integration.sh new file mode 100755 index 0000000..beb5b79 --- /dev/null +++ b/lab3-proc/tests/integration.sh @@ -0,0 +1,75 @@ +#!/bin/bash + +EXE="../procs" + +function run_test { + + # parameters + TAG=$1 + ARGS=$2 + + # file paths + OUTPUT=outputs/$TAG.txt + DIFF=outputs/$TAG.diff + EXPECT=expected/$TAG.txt + VALGRND=valgrind/$TAG.txt + + # print tag format + PTAG=$(printf '%-30s' "$TAG") + + # check for expected text that needs to be fixed + if [ ! -z "$(egrep "<<<<.*>>>>" "$EXPECT")" ] ; then + echo "$PTAG FAIL ($EXPECT not correct)" + return + fi + + # compare the file + SHAOUT=$(shasum "$EXPECT" | awk '{print $1}') + SHAEXP=$(cat "expected/.$TAG.sha") + if [ "$SHAOUT" != "$SHAEXP" ] ; then + echo "$PTAG FAIL (expected file is not correct)" + return + fi + + # run test and compare output to the expected version + $EXE $ARGS 2>/dev/null >"$OUTPUT" + diff -u "$OUTPUT" "$EXPECT" >"$DIFF" + if [ -s "$DIFF" ]; then + + # try alternative solution (if it exists) + EXPECT=expected/$TAG-2.txt + if [ -e "$EXPECT" ]; then + diff -u "$OUTPUT" "$EXPECT" >"$DIFF" + if [ -s "$DIFF" ]; then + echo "$PTAG FAIL (see $DIFF for details)" + else + echo "$PTAG pass" + fi + else + echo "$PTAG FAIL (see $DIFF for details)" + fi + else + echo "$PTAG pass" + fi + + # run valgrind + valgrind $EXE $ARGS &>$VALGRND +} + +# initialize output folders +mkdir -p outputs +mkdir -p valgrind +rm -f outputs/* valgrind/* + +# run individual tests +source itests.include + +# check for memory leaks +LEAK=`cat valgrind/*.txt | grep 'definitely lost' | grep -v ' 0 bytes in 0 blocks'` +if [ -z "$LEAK" ]; then + echo "No memory leak found." +else + echo "Memory leak(s) found. See files listed below for details." + grep 'definitely lost' valgrind/*.txt | grep -v ' 0 bytes in 0 blocks' | sed -e 's/:.*$//g' | sed -e 's/^/ - /g' +fi + diff --git a/lab3-proc/tests/itests.include b/lab3-proc/tests/itests.include new file mode 100644 index 0000000..0838538 --- /dev/null +++ b/lab3-proc/tests/itests.include @@ -0,0 +1,10 @@ +# list of integration tests +# format: run_test +# used as the root for all filenames (i.e., "expected/$TAG.txt") +# command-line arguments to test + +run_test INTEG_child "" +run_test INTEG_status "-e 54" +run_test INTEG_sigint "-s INT" +run_test INTEG_segfault "-s SEGV" +run_test INTEG_kill "-s KILL" diff --git a/lab3-proc/tests/private.o b/lab3-proc/tests/private.o new file mode 100644 index 0000000..20f56ee Binary files /dev/null and b/lab3-proc/tests/private.o differ diff --git a/lab3-proc/tests/public.c b/lab3-proc/tests/public.c new file mode 100644 index 0000000..a5ea1f6 --- /dev/null +++ b/lab3-proc/tests/public.c @@ -0,0 +1,46 @@ +#include +#include +#include +#include + +#include + +#include "../signals.h" + +START_TEST (UNIT_good_signals) +{ + printf ("\n======================================\n"); + printf ("UNIT good signals\n"); + printf ("This test checks for signal name lookups\n"); + char *names[] = { "INT", "KILL", "SEGV", "TSTP" }; + int signals[] = { SIGINT, SIGKILL, SIGSEGV, SIGTSTP }; + for (int i = 0; i < 4; i++) + { + int sig = signal_lookup (names[i]); + ck_assert_int_eq (signals[i], sig); + } +} +END_TEST + +START_TEST (UNIT_bad_signals) +{ + printf ("\n======================================\n"); + printf ("UNIT bad signals\n"); + printf ("This test checks for other signal name lookups\n"); + char *names[] = { "HUP", "ALRM", "ILL", "KIL" }; + for (int i = 0; i < 4; i++) + { + int sig = signal_lookup (names[i]); + ck_assert_int_eq (sig, -1); + } +} +END_TEST + +void public_tests (Suite *s) +{ + TCase *tc_public = tcase_create ("Public"); + tcase_add_test (tc_public, UNIT_good_signals); + tcase_add_test (tc_public, UNIT_bad_signals); + suite_add_tcase (s, tc_public); +} + diff --git a/lab3-proc/tests/style.sh b/lab3-proc/tests/style.sh new file mode 100755 index 0000000..ba2210a --- /dev/null +++ b/lab3-proc/tests/style.sh @@ -0,0 +1,53 @@ +#!/bin/bash + +STYLE="gnu" + +IGNORE=() +FAIL=0 + +function comp_file { + + SRC=$1 + SRC_NAME=$2 + + # file paths + FORMAT=ckstyle/${SRC_NAME}.$STYLE + DIFF=ckstyle/${SRC_NAME}.diff + + # run clang-format and compare results + clang-format --style=$STYLE $source > $FORMAT + diff -u $SRC $FORMAT >$DIFF + + PTAG=$(printf '%-30s' "$SRC_NAME") + if [ -s $DIFF ]; then + echo "$PTAG FAIL (see $DIFF for details)" + FAIL=1 + else + echo "$PTAG pass" + rm $DIFF + fi + rm $FORMAT +} + +mkdir -p ckstyle +rm -f ckstyle/* + +for source in $(ls ../*.c ../*.h) ; do + SKIP=0 + src=$(basename $source) + for ignore in ${IGNORE[*]} ; do + if [ "$src" = "$ignore" ] ; then + SKIP=1 + fi + done + if [ $SKIP = 0 ] ; then + comp_file $source $src + fi +done + +if [ $FAIL != 0 ] ; then + echo "Code that does not adhere to GNU standards will not be accepted." + echo "You must fix these files before submission." +else + echo "Code correctly adheres to required style." +fi diff --git a/lab3-proc/tests/testsuite.c b/lab3-proc/tests/testsuite.c new file mode 100644 index 0000000..3aa216b --- /dev/null +++ b/lab3-proc/tests/testsuite.c @@ -0,0 +1,34 @@ +#include +#include +#include +#include +#include +#include + +#include + +extern void public_tests (Suite *s); +//extern void private_tests (Suite *s); + +Suite * test_suite (void) +{ + Suite *s = suite_create ("Default"); + public_tests (s); + //private_tests (s); + return s; +} + +void run_testsuite () +{ + Suite *s = test_suite (); + SRunner *sr = srunner_create (s); + srunner_run_all (sr, CK_NORMAL); + srunner_free (sr); +} + +int main (void) +{ + srand((unsigned)time(NULL)); + run_testsuite (); + return EXIT_SUCCESS; +} diff --git a/lab4-ipc/.gitignore b/lab4-ipc/.gitignore new file mode 100644 index 0000000..bc55d8b --- /dev/null +++ b/lab4-ipc/.gitignore @@ -0,0 +1,20 @@ +# Ignore all compiled files regardless of location +**/*.o + +# But not the private.o +!tests/private.o + +# Ignore executables for this project +ipc +testsuite + +# Ignore test outputs +tests/ckstyle +tests/itests.txt +tests/outputs +tests/style.txt +tests/valgrind +tests/utests.txt + +**/.nfs* +**/.vscode diff --git a/lab4-ipc/Makefile b/lab4-ipc/Makefile new file mode 100644 index 0000000..84bb14d --- /dev/null +++ b/lab4-ipc/Makefile @@ -0,0 +1,52 @@ +# +# Simple Makefile +# Mike Lam, James Madison University, August 2016 +# +# This makefile builds a simple application that contains a main module +# (specified by the EXE variable) and a predefined list of additional modules +# (specified by the MODS variable). If there are any external library +# dependencies (e.g., the math library, "-lm"), list them in the LIBS variable. +# If there are any precompiled object files, list them in the OBJS variable. +# +# By default, this makefile will build the project with debugging symbols and +# without optimization. To change this, edit or remove the "-g" and "-O0" +# options in CFLAGS and LDFLAGS accordingly. +# +# By default, this makefile build the application using the GNU C compiler, +# adhering to the C99 standard with all warnings enabled. + + +# application-specific settings and run target + +EXE=ipc +MODS=pipe.o +OBJS= +LIBS=-lm + +default: $(EXE) + +test: $(EXE) + make -C tests test + +# compiler/linker settings + +CC=gcc +#CFLAGS=-g -O0 -Wall -Werror --std=gnu99 -pedantic +CFLAGS=-g -O0 -Wall -Werror --std=gnu99 -pedantic +LDFLAGS=-g -O0 -pthread + + +# build targets + +$(EXE): main.o $(MODS) $(OBJS) + $(CC) $(LDFLAGS) -o $(EXE) $^ $(LIBS) + +%.o: %.c + $(CC) -c $(CFLAGS) $< + +clean: + rm -f $(EXE) main.o $(MODS) + make -C tests clean + +.PHONY: default clean + diff --git a/lab4-ipc/data/f1.txt b/lab4-ipc/data/f1.txt new file mode 100644 index 0000000..ce01362 --- /dev/null +++ b/lab4-ipc/data/f1.txt @@ -0,0 +1 @@ +hello diff --git a/lab4-ipc/data/f2.txt b/lab4-ipc/data/f2.txt new file mode 100644 index 0000000..dd7e1c6 --- /dev/null +++ b/lab4-ipc/data/f2.txt @@ -0,0 +1 @@ +goodbye diff --git a/lab4-ipc/data/f3.txt b/lab4-ipc/data/f3.txt new file mode 100644 index 0000000..72704b2 --- /dev/null +++ b/lab4-ipc/data/f3.txt @@ -0,0 +1 @@ +alohomora diff --git a/lab4-ipc/data/index b/lab4-ipc/data/index new file mode 100644 index 0000000..51aad9d Binary files /dev/null and b/lab4-ipc/data/index differ diff --git a/lab4-ipc/main.c b/lab4-ipc/main.c new file mode 100644 index 0000000..be9d471 --- /dev/null +++ b/lab4-ipc/main.c @@ -0,0 +1,87 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pipe.h" + +int cmdline (int, char **, bool *, char **, char **, size_t *); + +void +usage (void) +{ + printf ("Usage: ipc [options] EXEC ARG\n"); + printf (" Send ARG through a pipe to child process that runs EXEC.\n"); + printf (" Option is:\n"); + printf (" -s Use posix_spawn() instead of fork() and exec().\n"); +} + +int +main (int argc, char **argv) +{ + bool use_spawn = false; + char *exec = NULL; + char *fname = NULL; + size_t lineno = -1; + + if (cmdline (argc, argv, &use_spawn, &exec, &fname, &lineno) < 0) + { + usage (); + return EXIT_FAILURE; + } + + // Use fork/exec or spawn to run the executable exec + char *sum = NULL; + if (!use_spawn) + sum = get_result (exec, fname); + else + sum = spawn_result (exec, fname); + + if (sum == NULL) + return EXIT_FAILURE; + + printf ("%s(%s) = '%s'\n", exec, fname, sum); + + // If you get an error here, it's because sum is not dynamically + // allocated correctly. + free (sum); + return EXIT_SUCCESS; +} + +/* Helper for parsing the command line arguments. Sets spawn, + filename, and lineno as call-by-reference parameters. Return 0 + on success, -1 otherwise. */ +int +cmdline (int argc, char **argv, bool *spawn, char **exec, char **filename, + size_t *lineno) +{ + int option; + + while ((option = getopt (argc, argv, "sh")) != -1) + { + switch (option) + { + case 's': + *spawn = true; + break; + case 'h': + return -1; + break; + default: + return -1; + } + } + + if (optind + 2 != argc) + return -1; + + *exec = argv[optind]; + *filename = argv[optind + 1]; + + return 0; +} diff --git a/lab4-ipc/pipe.c b/lab4-ipc/pipe.c new file mode 100644 index 0000000..6b1c8a9 --- /dev/null +++ b/lab4-ipc/pipe.c @@ -0,0 +1,118 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "pipe.h" + +/* Creates a child process. If this fails, close the pipe and return -1. + If the child is created, use it to run the executable program on the + filename passed. Send the result back through the pipe. The parent process + should just return the child's PID. Sample call: + + int fd[2]; + pipe (fd); + pid_t child_pid = create_child (exec, fd, "foo.txt"); + */ +pid_t +create_child (char *const exec, int *pipe, char *const filename) +{ + char *args[3] = { exec, filename, NULL }; + pid_t pid = fork (); + + if (pid == 0) + { + close (pipe[0]); + dup2 (pipe[1], STDOUT_FILENO); + close (pipe[1]); + execvp (exec, args); + exit (-1); + } + + close (pipe[1]); + return pid; +} + +/* Runs the executable program on the input filename. Start by creating a + pipe and calling create_child() to create the child process. If that + fails, return NULL. Otherwise, implement the parent here so that it + reads the result of the process from the pipe and returns the result + without a newline. The returned string must be a dynamically allocated + copy; you can use the constant BUFFER_LENGTH for its size. Be sure to + close the ends of the pipe at appropriate times. Sample call: + + char *sum = get_result ("cksum", "data/f1.txt"); + // sum is "3015617425 6 data/f1.txt" [without the newline] + */ +char * +get_result (char *const exec, char *const filename) +{ + int fd[2]; + if (pipe (fd) == -1) + { + return NULL; + } + + pid_t pid = create_child (exec, fd, filename); + + char *buffer = calloc (BUFFER_LENGTH, sizeof (char)); + ssize_t n = read (fd[0], buffer, BUFFER_LENGTH); + buffer[n - 1] = '\0'; + + close (fd[0]); + waitpid (pid, NULL, 0); + + return buffer; +} + +/* Re-implementation of the create_child() in pipe.c. Instead of + using the calls to pipe/fork/dup2/exec, combine the latter three + into a call to posix_spawn(). Sample call: + + char *sum = spawn_result ("cksum", "data/f1.txt"); + // sum is "3015617425 6 data/f1.txt\n" [with the newline] + */ +char * +spawn_result (char *const exec, char *const filename) +{ + // Instead of using fork() and exec(), use the posix_spawn functions. + // Note that you will need to create a pipe, initialize a + // posix_spawn_file_actions_t instance, and add close and dup2 actions + // to it so that the child process writes its STDOUT to the pipe. + // After spawning the new process, make sure to call + // posix_spawn_file_actions_destroy() on the actions to prevent + // memory leaks. To avoid looking up the executable in the $PATH, + // you can use posix_spawnp(). + + // Parent code: read the value back from the pipe into a dynamically + // allocated buffer. Wait for the child to exit, then return the + // buffer. Make sure to close the ends of the pipe in appropriate + // places. + int fd[2]; + pipe (fd); + + posix_spawn_file_actions_t actions; + posix_spawn_file_actions_init (&actions); + posix_spawn_file_actions_addclose (&actions, fd[0]); + posix_spawn_file_actions_adddup2 (&actions, fd[1], STDOUT_FILENO); + posix_spawn_file_actions_addclose (&actions, fd[1]); + + pid_t pid = -1; + char *args[3] = { exec, filename, NULL }; + + posix_spawnp (&pid, exec, &actions, NULL, args, NULL); + posix_spawn_file_actions_destroy (&actions); + close (fd[1]); + + char *buffer = calloc (BUFFER_LENGTH, sizeof (char)); + ssize_t n = read (fd[0], buffer, BUFFER_LENGTH); + buffer[n - 1] = '\0'; + + close (fd[0]); + waitpid (pid, NULL, 0); + + return buffer; +} diff --git a/lab4-ipc/pipe.h b/lab4-ipc/pipe.h new file mode 100644 index 0000000..81bc545 --- /dev/null +++ b/lab4-ipc/pipe.h @@ -0,0 +1,12 @@ +#ifndef __pipe_h__ +#define __pipe_h__ + +#include + +#define BUFFER_LENGTH 100 + +pid_t create_child (char *const, int *, char *const); +char *get_result (char *const, char *const); +char *spawn_result (char *const, char *const); + +#endif diff --git a/lab4-ipc/tests/Makefile b/lab4-ipc/tests/Makefile new file mode 100644 index 0000000..b830cde --- /dev/null +++ b/lab4-ipc/tests/Makefile @@ -0,0 +1,86 @@ +# +# Simple Test Makefile +# Mike Lam, James Madison University, August 2016 +# +# This version of the Makefile includes support for building a test suite. The +# recommended framework is Check (http://check.sourceforge.net/). To build and +# run the test suite, execute the "test" target. The test suite must be located +# in a module called "testsuite". The MODS, LIBS, and OBJS variables work as +# they do in the main Makefile. +# +# To change the default build target (which executes when you just type +# "make"), change the right-hand side of the definition of the "default" +# target. +# +# By default, this makefile will build the project with debugging symbols and +# without optimization. To change this, edit or remove the "-g" and "-O0" +# options in CFLAGS and LDFLAGS accordingly. +# +# By default, this makefile build the application using the GNU C compiler, +# adhering to the C99 standard with all warnings enabled. + + +# application-specific settings and run target + +EXE=../ipc +TEST=testsuite +MODS=public.o +OBJS=../pipe.o private.o +LIBS= + +UTESTOUT=utests.txt +ITESTOUT=itests.txt +SCHECKOUT=style.txt + +default: $(TEST) + +$(EXE): + make -C ../ + +test: utest itest style + @echo "========================================" + +utest: $(EXE) $(TEST) + @echo "========================================" + @echo " UNIT TESTS" + @./$(TEST) 2>/dev/null >$(UTESTOUT) + @cat $(UTESTOUT) | sed -n -e '/Checks/,$$p' | sed -e 's/^private.*:[EF]://g' + +itest: $(EXE) + @echo "========================================" + @echo " INTEGRATION TESTS" + @./integration.sh | tee $(ITESTOUT) + +style: $(EXE) + @echo "========================================" + @echo " CODING STYLE CHECK" + @./style.sh 2>/dev/null >$(SCHECKOUT) + @cat $(SCHECKOUT) + +# compiler/linker settings + +CC=gcc +CFLAGS=-g -O0 -Wall --std=gnu99 -pedantic +LDFLAGS=-g -O0 + +CFLAGS= +LIBS+=-lcheck -lm -lpthread + +ifeq ($(shell uname -s),Linux) + LIBS+=-lrt -lsubunit +endif + + +# build targets + +$(TEST): $(TEST).o $(MODS) $(OBJS) + $(CC) $(LDFLAGS) -o $(TEST) $^ $(LIBS) + +%.o: %.c + $(CC) -c $(CFLAGS) $< + +clean: + rm -rf $(TEST) $(TEST).o $(MODS) $(UTESTOUT) $(ITESTOUT) $(SCHECKOUT) outputs valgrind ckstyle + +.PHONY: default clean test unittest inttest + diff --git a/lab4-ipc/tests/data/f1.txt b/lab4-ipc/tests/data/f1.txt new file mode 100644 index 0000000..ce01362 --- /dev/null +++ b/lab4-ipc/tests/data/f1.txt @@ -0,0 +1 @@ +hello diff --git a/lab4-ipc/tests/data/f2.txt b/lab4-ipc/tests/data/f2.txt new file mode 100644 index 0000000..dd7e1c6 --- /dev/null +++ b/lab4-ipc/tests/data/f2.txt @@ -0,0 +1 @@ +goodbye diff --git a/lab4-ipc/tests/data/f3.txt b/lab4-ipc/tests/data/f3.txt new file mode 100644 index 0000000..72704b2 --- /dev/null +++ b/lab4-ipc/tests/data/f3.txt @@ -0,0 +1 @@ +alohomora diff --git a/lab4-ipc/tests/data/index b/lab4-ipc/tests/data/index new file mode 100644 index 0000000..51aad9d Binary files /dev/null and b/lab4-ipc/tests/data/index differ diff --git a/lab4-ipc/tests/expected/.INTEG_fork.sha b/lab4-ipc/tests/expected/.INTEG_fork.sha new file mode 100644 index 0000000..124407d --- /dev/null +++ b/lab4-ipc/tests/expected/.INTEG_fork.sha @@ -0,0 +1 @@ +85cdeaba8a3d4b24f3471d8c91724de8fc9eb6de diff --git a/lab4-ipc/tests/expected/.INTEG_fork_no_exec.sha b/lab4-ipc/tests/expected/.INTEG_fork_no_exec.sha new file mode 100644 index 0000000..5949756 --- /dev/null +++ b/lab4-ipc/tests/expected/.INTEG_fork_no_exec.sha @@ -0,0 +1 @@ +e69f8afeb4d967606c11ee01002df44e6c01b910 diff --git a/lab4-ipc/tests/expected/.INTEG_fork_no_file.sha b/lab4-ipc/tests/expected/.INTEG_fork_no_file.sha new file mode 100644 index 0000000..1d96cd1 --- /dev/null +++ b/lab4-ipc/tests/expected/.INTEG_fork_no_file.sha @@ -0,0 +1 @@ +22cdb52c3ef40e84d4213809c7dcd1747efe8ff0 diff --git a/lab4-ipc/tests/expected/.INTEG_spawn.sha b/lab4-ipc/tests/expected/.INTEG_spawn.sha new file mode 100644 index 0000000..7a9e87b --- /dev/null +++ b/lab4-ipc/tests/expected/.INTEG_spawn.sha @@ -0,0 +1 @@ +3881b07e1676a7fbe53089dfe7b3add8ffed7fb0 diff --git a/lab4-ipc/tests/expected/.INTEG_spawn_no_exec.sha b/lab4-ipc/tests/expected/.INTEG_spawn_no_exec.sha new file mode 100644 index 0000000..5949756 --- /dev/null +++ b/lab4-ipc/tests/expected/.INTEG_spawn_no_exec.sha @@ -0,0 +1 @@ +e69f8afeb4d967606c11ee01002df44e6c01b910 diff --git a/lab4-ipc/tests/expected/.INTEG_spawn_no_file.sha b/lab4-ipc/tests/expected/.INTEG_spawn_no_file.sha new file mode 100644 index 0000000..cc16d5d --- /dev/null +++ b/lab4-ipc/tests/expected/.INTEG_spawn_no_file.sha @@ -0,0 +1 @@ +e609d97c46fa6bb297cb26adc575edf33978ca5a diff --git a/lab4-ipc/tests/expected/INTEG_fork.txt b/lab4-ipc/tests/expected/INTEG_fork.txt new file mode 100644 index 0000000..e4d62af --- /dev/null +++ b/lab4-ipc/tests/expected/INTEG_fork.txt @@ -0,0 +1 @@ +cksum(../data/f1.txt) = '3015617425 6 ../data/f1.txt' diff --git a/lab4-ipc/tests/expected/INTEG_fork_no_exec.txt b/lab4-ipc/tests/expected/INTEG_fork_no_exec.txt new file mode 100644 index 0000000..1059168 --- /dev/null +++ b/lab4-ipc/tests/expected/INTEG_fork_no_exec.txt @@ -0,0 +1 @@ +asldkfja(../data/f1.txt) = '' diff --git a/lab4-ipc/tests/expected/INTEG_fork_no_file.txt b/lab4-ipc/tests/expected/INTEG_fork_no_file.txt new file mode 100644 index 0000000..3bed00a --- /dev/null +++ b/lab4-ipc/tests/expected/INTEG_fork_no_file.txt @@ -0,0 +1 @@ +cksum(../data/f4.txt) = '' diff --git a/lab4-ipc/tests/expected/INTEG_spawn.txt b/lab4-ipc/tests/expected/INTEG_spawn.txt new file mode 100644 index 0000000..ce89512 --- /dev/null +++ b/lab4-ipc/tests/expected/INTEG_spawn.txt @@ -0,0 +1 @@ +shasum(../data/f2.txt) = 'e7d9b82b45d5833c9dada13f2379e7b66c823434 ../data/f2.txt' diff --git a/lab4-ipc/tests/expected/INTEG_spawn_no_exec.txt b/lab4-ipc/tests/expected/INTEG_spawn_no_exec.txt new file mode 100644 index 0000000..1059168 --- /dev/null +++ b/lab4-ipc/tests/expected/INTEG_spawn_no_exec.txt @@ -0,0 +1 @@ +asldkfja(../data/f1.txt) = '' diff --git a/lab4-ipc/tests/expected/INTEG_spawn_no_file.txt b/lab4-ipc/tests/expected/INTEG_spawn_no_file.txt new file mode 100644 index 0000000..14b9b61 --- /dev/null +++ b/lab4-ipc/tests/expected/INTEG_spawn_no_file.txt @@ -0,0 +1 @@ +shasum(../data/f5.txt) = '' diff --git a/lab4-ipc/tests/integration.sh b/lab4-ipc/tests/integration.sh new file mode 100755 index 0000000..e959edb --- /dev/null +++ b/lab4-ipc/tests/integration.sh @@ -0,0 +1,75 @@ +#!/bin/bash + +EXE="../ipc" + +function run_test { + + # parameters + TAG=$1 + ARGS=$2 + + # file paths + OUTPUT=outputs/$TAG.txt + DIFF=outputs/$TAG.diff + EXPECT=expected/$TAG.txt + VALGRND=valgrind/$TAG.txt + + # print tag format + PTAG=$(printf '%-30s' "$TAG") + + # check for expected text that needs to be fixed + if [ ! -z "$(egrep "<<<<.*>>>>" "$EXPECT")" ] ; then + echo "$PTAG FAIL ($EXPECT not correct)" + return + fi + + # compare the file + SHAOUT=$(shasum "$EXPECT" | awk '{print $1}') + SHAEXP=$(cat "expected/.$TAG.sha") + if [ "$SHAOUT" != "$SHAEXP" ] ; then + echo "$PTAG FAIL (expected file is not correct)" + return + fi + + # run test and compare output to the expected version + $EXE $ARGS 2>/dev/null >"$OUTPUT" + diff -u "$OUTPUT" "$EXPECT" >"$DIFF" + if [ -s "$DIFF" ]; then + + # try alternative solution (if it exists) + EXPECT=expected/$TAG-2.txt + if [ -e "$EXPECT" ]; then + diff -u "$OUTPUT" "$EXPECT" >"$DIFF" + if [ -s "$DIFF" ]; then + echo "$PTAG FAIL (see $DIFF for details)" + else + echo "$PTAG pass" + fi + else + echo "$PTAG FAIL (see $DIFF for details)" + fi + else + echo "$PTAG pass" + fi + + # run valgrind + valgrind $EXE $ARGS &>$VALGRND +} + +# initialize output folders +mkdir -p outputs +mkdir -p valgrind +rm -f outputs/* valgrind/* + +# run individual tests +source itests.include + +# check for memory leaks +LEAK=`cat valgrind/*.txt | grep 'definitely lost' | grep -v ' 0 bytes in 0 blocks'` +if [ -z "$LEAK" ]; then + echo "No memory leak found." +else + echo "Memory leak(s) found. See files listed below for details." + grep 'definitely lost' valgrind/*.txt | grep -v ' 0 bytes in 0 blocks' | sed -e 's/:.*$//g' | sed -e 's/^/ - /g' +fi + diff --git a/lab4-ipc/tests/itests.include b/lab4-ipc/tests/itests.include new file mode 100644 index 0000000..8a1dc5b --- /dev/null +++ b/lab4-ipc/tests/itests.include @@ -0,0 +1,11 @@ +# list of integration tests +# format: run_test +# used as the root for all filenames (i.e., "expected/$TAG.txt") +# command-line arguments to test + +run_test INTEG_fork "cksum ../data/f1.txt" +run_test INTEG_fork_no_exec "asldkfja ../data/f1.txt" +run_test INTEG_fork_no_file "cksum ../data/f4.txt" +run_test INTEG_spawn "-s shasum ../data/f2.txt" +run_test INTEG_spawn_no_exec "-s asldkfja ../data/f1.txt" +run_test INTEG_spawn_no_file "-s shasum ../data/f5.txt" diff --git a/lab4-ipc/tests/private.o b/lab4-ipc/tests/private.o new file mode 100644 index 0000000..89b106d Binary files /dev/null and b/lab4-ipc/tests/private.o differ diff --git a/lab4-ipc/tests/public.c b/lab4-ipc/tests/public.c new file mode 100644 index 0000000..a5eaadb --- /dev/null +++ b/lab4-ipc/tests/public.c @@ -0,0 +1,35 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../pipe.h" + +// Change this to true if you want to inspect the files produced in +// tests/inputs for debugging +#define DEBUG false + +START_TEST (UNIT_result) +{ + printf ("\n======================================\n"); + printf ("UNIT_result\n"); + printf ("Gets cksum of data/f1.txt\n"); + + char *result = get_result ("cksum", "data/f1.txt"); + ck_assert_str_eq (result, "3015617425 6 data/f1.txt"); +} +END_TEST + +void public_tests (Suite *s) +{ + TCase *tc_public = tcase_create ("Public"); + tcase_add_test (tc_public, UNIT_result); + suite_add_tcase (s, tc_public); +} + diff --git a/lab4-ipc/tests/style.sh b/lab4-ipc/tests/style.sh new file mode 100755 index 0000000..ba2210a --- /dev/null +++ b/lab4-ipc/tests/style.sh @@ -0,0 +1,53 @@ +#!/bin/bash + +STYLE="gnu" + +IGNORE=() +FAIL=0 + +function comp_file { + + SRC=$1 + SRC_NAME=$2 + + # file paths + FORMAT=ckstyle/${SRC_NAME}.$STYLE + DIFF=ckstyle/${SRC_NAME}.diff + + # run clang-format and compare results + clang-format --style=$STYLE $source > $FORMAT + diff -u $SRC $FORMAT >$DIFF + + PTAG=$(printf '%-30s' "$SRC_NAME") + if [ -s $DIFF ]; then + echo "$PTAG FAIL (see $DIFF for details)" + FAIL=1 + else + echo "$PTAG pass" + rm $DIFF + fi + rm $FORMAT +} + +mkdir -p ckstyle +rm -f ckstyle/* + +for source in $(ls ../*.c ../*.h) ; do + SKIP=0 + src=$(basename $source) + for ignore in ${IGNORE[*]} ; do + if [ "$src" = "$ignore" ] ; then + SKIP=1 + fi + done + if [ $SKIP = 0 ] ; then + comp_file $source $src + fi +done + +if [ $FAIL != 0 ] ; then + echo "Code that does not adhere to GNU standards will not be accepted." + echo "You must fix these files before submission." +else + echo "Code correctly adheres to required style." +fi diff --git a/lab4-ipc/tests/testsuite.c b/lab4-ipc/tests/testsuite.c new file mode 100644 index 0000000..145faa1 --- /dev/null +++ b/lab4-ipc/tests/testsuite.c @@ -0,0 +1,34 @@ +#include +#include +#include +#include +#include +#include + +#include + +extern void public_tests (Suite *s); +extern void private_tests (Suite *s); + +Suite * test_suite (void) +{ + Suite *s = suite_create ("Default"); + public_tests (s); + private_tests (s); + return s; +} + +void run_testsuite () +{ + Suite *s = test_suite (); + SRunner *sr = srunner_create (s); + srunner_run_all (sr, CK_NORMAL); + srunner_free (sr); +} + +int main (void) +{ + srand((unsigned)time(NULL)); + run_testsuite (); + return EXIT_SUCCESS; +} diff --git a/lab5-net/.gitignore b/lab5-net/.gitignore new file mode 100644 index 0000000..53df104 --- /dev/null +++ b/lab5-net/.gitignore @@ -0,0 +1,20 @@ +# Ignore all compiled files regardless of location +**/*.o + +# But not the private.o +!tests/private.o + +# Ignore executables for this project +net +testsuite + +# Ignore test outputs +tests/ckstyle +tests/itests.txt +tests/outputs +tests/style.txt +tests/valgrind +tests/utests.txt + +**/.nfs* +**/.vscode diff --git a/lab5-net/Makefile b/lab5-net/Makefile new file mode 100644 index 0000000..5fcb4b4 --- /dev/null +++ b/lab5-net/Makefile @@ -0,0 +1,52 @@ +# +# Simple Makefile +# Mike Lam, James Madison University, August 2016 +# +# This makefile builds a simple application that contains a main module +# (specified by the EXE variable) and a predefined list of additional modules +# (specified by the MODS variable). If there are any external library +# dependencies (e.g., the math library, "-lm"), list them in the LIBS variable. +# If there are any precompiled object files, list them in the OBJS variable. +# +# By default, this makefile will build the project with debugging symbols and +# without optimization. To change this, edit or remove the "-g" and "-O0" +# options in CFLAGS and LDFLAGS accordingly. +# +# By default, this makefile build the application using the GNU C compiler, +# adhering to the C99 standard with all warnings enabled. + + +# application-specific settings and run target + +EXE=net +MODS=client.o +OBJS= +LIBS=-lm + +default: $(EXE) + +test: $(EXE) + make -C tests test + +# compiler/linker settings + +CC=gcc +#CFLAGS=-g -O0 -Wall --std=gnu99 -pedantic +CFLAGS=-g -O0 -Wall --std=gnu99 -pedantic +LDFLAGS=-g -O0 -pthread + + +# build targets + +$(EXE): main.o $(MODS) $(OBJS) + $(CC) $(LDFLAGS) -o $(EXE) $^ $(LIBS) + +%.o: %.c + $(CC) -c $(CFLAGS) $< + +clean: + rm -f $(EXE) main.o $(MODS) + make -C tests clean + +.PHONY: default clean + diff --git a/lab5-net/client.c b/lab5-net/client.c new file mode 100644 index 0000000..5ab2d1a --- /dev/null +++ b/lab5-net/client.c @@ -0,0 +1,108 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "client.h" + +#define BUFFER_MAX_SIZE 4096 + +/* Given a struct with server information, return a dynamically allocated + string of the IP address in dotted decimal or colon hexadecimal notation. + Consult Chapter 4, as well as /usr/include/netdb.h and + /usr/include/arpa/inet.h as needed. Use inet_ntop() to get the formatted + string based on the address's ai_addr field. + */ +char * +addr_string (struct addrinfo *server) +{ + // Return safely in case server is NULL: + if (server == NULL) + return strdup ("no address information"); + + size_t addrlen + = server->ai_family == AF_INET6 ? INET6_ADDRSTRLEN : INET_ADDRSTRLEN; + char *buffer = calloc (addrlen, sizeof (char)); + + void *addr; + if (server->ai_family == AF_INET) + addr = &((struct sockaddr_in *)server->ai_addr)->sin_addr; + else + addr = &((struct sockaddr_in6 *)server->ai_addr)->sin6_addr; + + inet_ntop (server->ai_family, addr, buffer, addrlen); + return buffer; +} + +/* Given the server address info, return a dynamically allocated string with + its transport-layer protocol, IP version, and IP address. For instance, + querying jmu.edu over TCP/IPv4 should return: + "TCP IPv4 134.126.10.50" + Use addr_string() to get the formatted address string, concatenate it to + the string to return, then free the result from addr_string(). If the + passed server parameter is NULL, use strdup() to return a dynamically + allocated copy of "no address information". + + NOTE: When distinguishing TCP and UDP, you should be checking the + server->ai_socktype field, not server->ai_protocol. The test cases do not + set the server->ai_protocol, which is sometimes not used in practice. + */ +char * +serv_string (struct addrinfo *server) +{ + // Return safely in case server is NULL: + if (server == NULL) + return strdup ("no address information"); + + size_t addrlen + = server->ai_family == AF_INET6 ? INET6_ADDRSTRLEN : INET_ADDRSTRLEN; + + char *buffer = calloc (10 + addrlen, sizeof (char)); + + char *proto = server->ai_socktype == SOCK_STREAM ? "TCP" : "UDP"; + char *ipver = server->ai_family == AF_INET ? "IPv4" : "IPv6"; + + char *addr = addr_string (server); + + snprintf (buffer, 10 + addrlen, "%s %s %s", proto, ipver, addr); + + free (addr); + + return buffer; +} + +/* Given a hostname string, use getaddrinfo() to query DNS for the specified + protocol parameters. Boolean values indicate whether or not to use IPv6 + (as opposed to IPv4) or TCP (as opposed to UDP). For this lab, the protocol + will only be "http", though it could be others in a more complete + implementation. Return the pointer to the linked list of server results. + */ +struct addrinfo * +get_server_list (const char *hostname, const char *proto, bool tcp, bool ipv6) +{ + struct addrinfo hints = { 0 }; + struct addrinfo *server_list = NULL; + + hints.ai_family = ipv6 ? AF_INET6 : AF_INET; + + if (tcp) + { + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + } + else + { + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = IPPROTO_UDP; + } + + getaddrinfo (hostname, proto, &hints, &server_list); + + return server_list; +} diff --git a/lab5-net/client.h b/lab5-net/client.h new file mode 100644 index 0000000..82b3582 --- /dev/null +++ b/lab5-net/client.h @@ -0,0 +1,12 @@ +#ifndef __client_h__ +#define __client_h__ + +#include +#include + +char *addr_string (struct addrinfo *); +char *serv_string (struct addrinfo *); +struct addrinfo *get_server_list (const char *, const char *, bool, bool); +char *web (const char *, char *, const char *, char **, bool); + +#endif diff --git a/lab5-net/main.c b/lab5-net/main.c new file mode 100644 index 0000000..6a5bc69 --- /dev/null +++ b/lab5-net/main.c @@ -0,0 +1,106 @@ +/* + * CS 361: Template project driver + * + * Name: + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "client.h" + +int cmdline (int, char **, char **, bool *, short int *); + +void +usage (void) +{ + printf ("Usage: net [options] domain\n"); + printf (" Options are:\n"); + printf (" -6 Use IPv6 addresses\n"); + printf (" -p P Lookup address for application protocol P\n"); +} + +int +main (int argc, char **argv) +{ + char *protocol = "http"; + bool ipv6 = false; + short int port = 0; + if (cmdline (argc, argv, &protocol, &ipv6, &port) < 0) + { + usage (); + return EXIT_FAILURE; + } + char *domain = argv[optind]; + + // MINIMUM requirements: + // Get the server list for the specified domain and protocol + // Given this command-line: + // ./net -p http www.jmu.edu + // Print the following output: + // www.jmu.edu http: TCP IPv4 134.126.10.50 + // The requested transport-layer protocol should be based on the + // requested application-layer protocol. TCP is default, but use + // UDP for protocols "53", "67", "dns", or "dhcp". + + bool tcp = true; + char *udpProtocols[] = { "53", "67", "dns", "dhcp" }; + + for (size_t i = 0; i < 4; i++) + { + if (strcmp (protocol, udpProtocols[i]) == 0) + { + tcp = false; + break; + } + } + + struct addrinfo *server = get_server_list (domain, protocol, tcp, ipv6); + char *server_string = serv_string (server); + + printf ("%s %s: %s\n", domain, protocol, server_string); + free (server); + free (server_string); + + return EXIT_SUCCESS; +} + +/* DO NOT MODIFY THIS FUNCTION */ + +int +cmdline (int argc, char **argv, char **protocol, bool *ipv6, short int *port) +{ + int option; + + while ((option = getopt (argc, argv, "p:6sh")) != -1) + { + switch (option) + { + // Change this to merge -p and -o as same flag + case 'p': + *protocol = optarg; + break; + case '6': + *ipv6 = true; + break; + case 'h': + return -1; + break; + default: + return -1; + } + } + + if (optind != argc - 1) + return -1; + + return 0; +} diff --git a/lab5-net/tests/Makefile b/lab5-net/tests/Makefile new file mode 100644 index 0000000..f4577ab --- /dev/null +++ b/lab5-net/tests/Makefile @@ -0,0 +1,86 @@ +# +# Simple Test Makefile +# Mike Lam, James Madison University, August 2016 +# +# This version of the Makefile includes support for building a test suite. The +# recommended framework is Check (http://check.sourceforge.net/). To build and +# run the test suite, execute the "test" target. The test suite must be located +# in a module called "testsuite". The MODS, LIBS, and OBJS variables work as +# they do in the main Makefile. +# +# To change the default build target (which executes when you just type +# "make"), change the right-hand side of the definition of the "default" +# target. +# +# By default, this makefile will build the project with debugging symbols and +# without optimization. To change this, edit or remove the "-g" and "-O0" +# options in CFLAGS and LDFLAGS accordingly. +# +# By default, this makefile build the application using the GNU C compiler, +# adhering to the C99 standard with all warnings enabled. + + +# application-specific settings and run target + +EXE=../net +TEST=testsuite +MODS=public.o +OBJS=../client.o private.o +LIBS= + +UTESTOUT=utests.txt +ITESTOUT=itests.txt +SCHECKOUT=style.txt + +default: $(TEST) + +$(EXE): + make -C ../ + +test: utest itest style + @echo "========================================" + +utest: $(EXE) $(TEST) + @echo "========================================" + @echo " UNIT TESTS" + @./$(TEST) 2>/dev/null >$(UTESTOUT) + @cat $(UTESTOUT) | sed -n -e '/Checks/,$$p' | sed -e 's/^private.*:[EF]://g' + +itest: $(EXE) + @echo "========================================" + @echo " INTEGRATION TESTS" + @./integration.sh | tee $(ITESTOUT) + +style: $(EXE) + @echo "========================================" + @echo " CODING STYLE CHECK" + @./style.sh 2>/dev/null >$(SCHECKOUT) + @cat $(SCHECKOUT) + +# compiler/linker settings + +CC=gcc +CFLAGS=-g -O0 -Wall --std=gnu99 -pedantic +LDFLAGS=-g -O0 + +CFLAGS= +LIBS+=-lcheck -lm -lpthread + +ifeq ($(shell uname -s),Linux) + LIBS+=-lrt -lsubunit +endif + + +# build targets + +$(TEST): $(TEST).o $(MODS) $(OBJS) + $(CC) $(LDFLAGS) -o $(TEST) $^ $(LIBS) + +%.o: %.c + $(CC) -c $(CFLAGS) $< + +clean: + rm -rf $(TEST) $(TEST).o $(MODS) $(UTESTOUT) $(ITESTOUT) $(SCHECKOUT) outputs valgrind ckstyle + +.PHONY: default clean test unittest inttest + diff --git a/lab5-net/tests/expected/.INTEG_53_ns3.sha b/lab5-net/tests/expected/.INTEG_53_ns3.sha new file mode 100644 index 0000000..a7574b9 --- /dev/null +++ b/lab5-net/tests/expected/.INTEG_53_ns3.sha @@ -0,0 +1 @@ +6aa4050ebd7cf3e2973480a61c8f3e10abae0b8b diff --git a/lab5-net/tests/expected/.INTEG_53_ns3_ipv6.sha b/lab5-net/tests/expected/.INTEG_53_ns3_ipv6.sha new file mode 100644 index 0000000..3689671 --- /dev/null +++ b/lab5-net/tests/expected/.INTEG_53_ns3_ipv6.sha @@ -0,0 +1 @@ +1954727e95f02e160a1ca5747b42de74ba7a73bb diff --git a/lab5-net/tests/expected/.INTEG_localhost_ipv4.sha b/lab5-net/tests/expected/.INTEG_localhost_ipv4.sha new file mode 100644 index 0000000..9075df2 --- /dev/null +++ b/lab5-net/tests/expected/.INTEG_localhost_ipv4.sha @@ -0,0 +1 @@ +9e5b32bfdef634ce9687b592ef6ab66dd2359d3e diff --git a/lab5-net/tests/expected/.INTEG_localhost_ipv6.sha b/lab5-net/tests/expected/.INTEG_localhost_ipv6.sha new file mode 100644 index 0000000..6eedaa4 --- /dev/null +++ b/lab5-net/tests/expected/.INTEG_localhost_ipv6.sha @@ -0,0 +1 @@ +16f74b0708234d24ac814851071de4a4f8d3893a diff --git a/lab5-net/tests/expected/.INTEG_w3_ipv4.sha b/lab5-net/tests/expected/.INTEG_w3_ipv4.sha new file mode 100644 index 0000000..2284927 --- /dev/null +++ b/lab5-net/tests/expected/.INTEG_w3_ipv4.sha @@ -0,0 +1 @@ +c0a296ad55ce3db887a3fbfa529ec4b3dc407e4a diff --git a/lab5-net/tests/expected/.INTEG_w3_ipv6.sha b/lab5-net/tests/expected/.INTEG_w3_ipv6.sha new file mode 100644 index 0000000..dc0cf3a --- /dev/null +++ b/lab5-net/tests/expected/.INTEG_w3_ipv6.sha @@ -0,0 +1 @@ +fb304af0dc2a1fd9029541d8e653995755bc8b2c diff --git a/lab5-net/tests/expected/INTEG_53_ns3.txt b/lab5-net/tests/expected/INTEG_53_ns3.txt new file mode 100644 index 0000000..4948f60 --- /dev/null +++ b/lab5-net/tests/expected/INTEG_53_ns3.txt @@ -0,0 +1 @@ +ns3.jmu.edu 53: UDP IPv4 134.126.115.46 diff --git a/lab5-net/tests/expected/INTEG_53_ns3_ipv6-2.txt b/lab5-net/tests/expected/INTEG_53_ns3_ipv6-2.txt new file mode 100644 index 0000000..b654fd1 --- /dev/null +++ b/lab5-net/tests/expected/INTEG_53_ns3_ipv6-2.txt @@ -0,0 +1 @@ +ns3.jmu.edu 53: UDP IPv6 ::ffff:134.126.115.46 diff --git a/lab5-net/tests/expected/INTEG_53_ns3_ipv6.txt b/lab5-net/tests/expected/INTEG_53_ns3_ipv6.txt new file mode 100644 index 0000000..48af529 --- /dev/null +++ b/lab5-net/tests/expected/INTEG_53_ns3_ipv6.txt @@ -0,0 +1 @@ +ns3.jmu.edu 53: no address information diff --git a/lab5-net/tests/expected/INTEG_localhost_ipv4.txt b/lab5-net/tests/expected/INTEG_localhost_ipv4.txt new file mode 100644 index 0000000..5f23b5b --- /dev/null +++ b/lab5-net/tests/expected/INTEG_localhost_ipv4.txt @@ -0,0 +1 @@ +localhost http: TCP IPv4 127.0.0.1 diff --git a/lab5-net/tests/expected/INTEG_localhost_ipv6.txt b/lab5-net/tests/expected/INTEG_localhost_ipv6.txt new file mode 100644 index 0000000..46915f2 --- /dev/null +++ b/lab5-net/tests/expected/INTEG_localhost_ipv6.txt @@ -0,0 +1 @@ +localhost http: TCP IPv6 ::1 diff --git a/lab5-net/tests/expected/INTEG_w3_ipv4.txt b/lab5-net/tests/expected/INTEG_w3_ipv4.txt new file mode 100644 index 0000000..ab67f7e --- /dev/null +++ b/lab5-net/tests/expected/INTEG_w3_ipv4.txt @@ -0,0 +1 @@ +w3.cs.jmu.edu http: TCP IPv4 134.126.20.33 diff --git a/lab5-net/tests/expected/INTEG_w3_ipv6-2.txt b/lab5-net/tests/expected/INTEG_w3_ipv6-2.txt new file mode 100644 index 0000000..78985cc --- /dev/null +++ b/lab5-net/tests/expected/INTEG_w3_ipv6-2.txt @@ -0,0 +1 @@ +w3.cs.jmu.edu http: TCP IPv6 ::ffff:134.126.20.33 diff --git a/lab5-net/tests/expected/INTEG_w3_ipv6.txt b/lab5-net/tests/expected/INTEG_w3_ipv6.txt new file mode 100644 index 0000000..50b786f --- /dev/null +++ b/lab5-net/tests/expected/INTEG_w3_ipv6.txt @@ -0,0 +1 @@ +w3.cs.jmu.edu http: no address information diff --git a/lab5-net/tests/integration.sh b/lab5-net/tests/integration.sh new file mode 100755 index 0000000..43ad640 --- /dev/null +++ b/lab5-net/tests/integration.sh @@ -0,0 +1,75 @@ +#!/bin/bash + +EXE="../net" + +function run_test { + + # parameters + TAG=$1 + ARGS=$2 + + # file paths + OUTPUT=outputs/$TAG.txt + DIFF=outputs/$TAG.diff + EXPECT=expected/$TAG.txt + VALGRND=valgrind/$TAG.txt + + # print tag format + PTAG=$(printf '%-30s' "$TAG") + + # check for expected text that needs to be fixed + if [ ! -z "$(egrep "<<<<.*>>>>" "$EXPECT")" ] ; then + echo "$PTAG FAIL ($EXPECT not correct)" + return + fi + + # compare the file + SHAOUT=$(shasum "$EXPECT" | awk '{print $1}') + SHAEXP=$(cat "expected/.$TAG.sha") + if [ "$SHAOUT" != "$SHAEXP" ] ; then + echo "$PTAG FAIL (expected file is not correct)" + return + fi + + # run test and compare output to the expected version + $EXE $ARGS 2>/dev/null >"$OUTPUT" + diff -u "$OUTPUT" "$EXPECT" >"$DIFF" + if [ -s "$DIFF" ]; then + + # try alternative solution (if it exists) + EXPECT=expected/$TAG-2.txt + if [ -e "$EXPECT" ]; then + diff -u "$OUTPUT" "$EXPECT" >"$DIFF" + if [ -s "$DIFF" ]; then + echo "$PTAG FAIL (see $DIFF for details)" + else + echo "$PTAG pass" + fi + else + echo "$PTAG FAIL (see $DIFF for details)" + fi + else + echo "$PTAG pass" + fi + + # run valgrind + valgrind $EXE $ARGS &>$VALGRND +} + +# initialize output folders +mkdir -p outputs +mkdir -p valgrind +rm -f outputs/* valgrind/* + +# run individual tests +source itests.include + +# check for memory leaks +LEAK=`cat valgrind/*.txt | grep 'definitely lost' | grep -v ' 0 bytes in 0 blocks'` +if [ -z "$LEAK" ]; then + echo "No memory leak found." +else + echo "Memory leak(s) found. See files listed below for details." + grep 'definitely lost' valgrind/*.txt | grep -v ' 0 bytes in 0 blocks' | sed -e 's/:.*$//g' | sed -e 's/^/ - /g' +fi + diff --git a/lab5-net/tests/itests.include b/lab5-net/tests/itests.include new file mode 100644 index 0000000..446ee74 --- /dev/null +++ b/lab5-net/tests/itests.include @@ -0,0 +1,14 @@ +# list of integration tests +# format: run_test +# used as the root for all filenames (i.e., "expected/$TAG.txt") +# command-line arguments to test + +run_test INTEG_localhost_ipv4 "localhost" +run_test INTEG_localhost_ipv6 "-6 localhost" +run_test INTEG_w3_ipv6 "-6 w3.cs.jmu.edu" +run_test INTEG_w3_ipv4 "w3.cs.jmu.edu" +run_test INTEG_53_ns3 "-p 53 ns3.jmu.edu" +run_test INTEG_53_ns3_ipv6 "-6 -p 53 ns3.jmu.edu" +#run_test EXTRA_youtube_H "-w / youtube.com" +#run_test EXTRA_w3_index_H "-w /index.html w3.cs.jmu.edu" +#run_test EXTRA_nope_H "-w /nope.html example.com" diff --git a/lab5-net/tests/private.o b/lab5-net/tests/private.o new file mode 100644 index 0000000..cb4d6f8 Binary files /dev/null and b/lab5-net/tests/private.o differ diff --git a/lab5-net/tests/public.c b/lab5-net/tests/public.c new file mode 100644 index 0000000..30cc5bf --- /dev/null +++ b/lab5-net/tests/public.c @@ -0,0 +1,100 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../client.h" + +START_TEST (UNIT_addr_string_localhost_v4) +{ + printf ("======================================\n"); + printf ("PUBLIC\n"); + printf (" UNIT addr string localhost v4\n"); + printf (" Convert 32-bit integer address for localhost\n"); + printf (" into dotted-decimal 127.0.0.1 string format.\n\n"); + + struct sockaddr_in address; + memset (&address, 0, sizeof (address)); + address.sin_addr.s_addr = htonl (0x7f000001); + + struct addrinfo server; + server.ai_family = AF_INET; + server.ai_addr = (struct sockaddr *) &address; + + char *ipv4 = addr_string (&server); + printf (" IPv4 address for localhost is '%s'\n", ipv4); + printf ("\n"); + + ck_assert_str_eq (ipv4, "127.0.0.1"); + free (ipv4); + +} +END_TEST + +START_TEST (UNIT_get_server_list_localhost) +{ + printf ("======================================\n"); + printf ("PUBLIC\n"); + printf (" UNIT get server list localhost\n"); + printf (" Use getaddrinfo() to gather information about localhost\n"); + printf (" for the HTTP protocol with IPv4. Requires network access\n"); + printf (" to get information from DNS.\n\n"); + + struct addrinfo *server = + get_server_list ("localhost", "http", true, false); + struct sockaddr_in *addr4 = (struct sockaddr_in *) server->ai_addr; + uint32_t address = ntohl (addr4->sin_addr.s_addr); + + printf (" IPv4 address for localhost is 0x%08" PRIx32 "\n", address); + printf ("\n"); + ck_assert_int_eq (address, 0x7f000001); + + freeaddrinfo (server); +} +END_TEST + +START_TEST (UNIT_serv_string_localhost) +{ + printf ("======================================\n"); + printf ("PUBLIC\n"); + printf (" UNIT serv string localhost\n"); + printf (" Converts a struct addrinfo into a string that contains\n"); + printf (" the transport-layer protocol, IP version, and IP address\n"); + printf (" in the appropriate format.\n\n"); + + struct sockaddr_in address; + memset (&address, 0, sizeof (address)); + address.sin_addr.s_addr = htonl (0x7f000001); + + struct addrinfo server; + server.ai_family = AF_INET; + server.ai_addr = (struct sockaddr *) &address; + server.ai_socktype = SOCK_STREAM; + + char *serv_info = serv_string (&server); + printf (" Server information for localhost: '%s'\n", serv_info); + printf ("\n"); + + ck_assert_str_eq (serv_info, "TCP IPv4 127.0.0.1"); + free (serv_info); +} +END_TEST + +void public_tests (Suite *s) +{ + TCase *tc_public = tcase_create ("Public"); + tcase_add_test (tc_public, UNIT_addr_string_localhost_v4); + tcase_add_test (tc_public, UNIT_get_server_list_localhost); + tcase_add_test (tc_public, UNIT_serv_string_localhost); + suite_add_tcase (s, tc_public); +} + diff --git a/lab5-net/tests/style.sh b/lab5-net/tests/style.sh new file mode 100755 index 0000000..ba2210a --- /dev/null +++ b/lab5-net/tests/style.sh @@ -0,0 +1,53 @@ +#!/bin/bash + +STYLE="gnu" + +IGNORE=() +FAIL=0 + +function comp_file { + + SRC=$1 + SRC_NAME=$2 + + # file paths + FORMAT=ckstyle/${SRC_NAME}.$STYLE + DIFF=ckstyle/${SRC_NAME}.diff + + # run clang-format and compare results + clang-format --style=$STYLE $source > $FORMAT + diff -u $SRC $FORMAT >$DIFF + + PTAG=$(printf '%-30s' "$SRC_NAME") + if [ -s $DIFF ]; then + echo "$PTAG FAIL (see $DIFF for details)" + FAIL=1 + else + echo "$PTAG pass" + rm $DIFF + fi + rm $FORMAT +} + +mkdir -p ckstyle +rm -f ckstyle/* + +for source in $(ls ../*.c ../*.h) ; do + SKIP=0 + src=$(basename $source) + for ignore in ${IGNORE[*]} ; do + if [ "$src" = "$ignore" ] ; then + SKIP=1 + fi + done + if [ $SKIP = 0 ] ; then + comp_file $source $src + fi +done + +if [ $FAIL != 0 ] ; then + echo "Code that does not adhere to GNU standards will not be accepted." + echo "You must fix these files before submission." +else + echo "Code correctly adheres to required style." +fi diff --git a/lab5-net/tests/testsuite.c b/lab5-net/tests/testsuite.c new file mode 100644 index 0000000..145faa1 --- /dev/null +++ b/lab5-net/tests/testsuite.c @@ -0,0 +1,34 @@ +#include +#include +#include +#include +#include +#include + +#include + +extern void public_tests (Suite *s); +extern void private_tests (Suite *s); + +Suite * test_suite (void) +{ + Suite *s = suite_create ("Default"); + public_tests (s); + private_tests (s); + return s; +} + +void run_testsuite () +{ + Suite *s = test_suite (); + SRunner *sr = srunner_create (s); + srunner_run_all (sr, CK_NORMAL); + srunner_free (sr); +} + +int main (void) +{ + srand((unsigned)time(NULL)); + run_testsuite (); + return EXIT_SUCCESS; +} diff --git a/lab7-threads/.gitignore b/lab7-threads/.gitignore new file mode 100644 index 0000000..51c74a7 --- /dev/null +++ b/lab7-threads/.gitignore @@ -0,0 +1,24 @@ +# Ignore all compiled files regardless of location +discrete.o +main.o +pthread.o +tests/public.o +tests/testsuite.o + +# But not the private.o +!tests/private.o + +# Ignore executables for this project +threads +testsuite + +# Ignore test outputs +tests/ckstyle +tests/itests.txt +tests/outputs +tests/style.txt +tests/valgrind +tests/utests.txt + +**/.nfs* +**/.vscode diff --git a/lab7-threads/Makefile b/lab7-threads/Makefile new file mode 100644 index 0000000..12efc5d --- /dev/null +++ b/lab7-threads/Makefile @@ -0,0 +1,52 @@ +# +# Simple Makefile +# Mike Lam, James Madison University, August 2016 +# +# This makefile builds a simple application that contains a main module +# (specified by the EXE variable) and a predefined list of additional modules +# (specified by the MODS variable). If there are any external library +# dependencies (e.g., the math library, "-lm"), list them in the LIBS variable. +# If there are any precompiled object files, list them in the OBJS variable. +# +# By default, this makefile will build the project with debugging symbols and +# without optimization. To change this, edit or remove the "-g" and "-O0" +# options in CFLAGS and LDFLAGS accordingly. +# +# By default, this makefile build the application using the GNU C compiler, +# adhering to the C99 standard with all warnings enabled. + + +# application-specific settings and run target + +EXE=threads +MODS=pthread.o discrete.o +OBJS= +LIBS=-lm + +default: $(EXE) + +test: $(EXE) + make -C tests test + +# compiler/linker settings + +CC=gcc +#CFLAGS=-g -O0 -Wall --std=gnu99 -pedantic +CFLAGS=-g -O0 -Wall --std=gnu99 -pedantic +LDFLAGS=-g -O0 -pthread + + +# build targets + +$(EXE): main.o $(MODS) $(OBJS) + $(CC) $(LDFLAGS) -o $(EXE) $^ $(LIBS) + +%.o: %.c + $(CC) -c $(CFLAGS) $< + +clean: + rm -f $(EXE) main.o $(MODS) + make -C tests clean + +.PHONY: default clean + diff --git a/lab7-threads/discrete.c b/lab7-threads/discrete.c new file mode 100644 index 0000000..49818cf --- /dev/null +++ b/lab7-threads/discrete.c @@ -0,0 +1,53 @@ +#include +#include +#include +#include + +#include "discrete.h" + +/* Find a generator for Z_prime* */ +uint64_t +find_generator (uint64_t prime) +{ + uint64_t g = prime/2; // candidate generator + uint64_t n; // the nth power of g + uint64_t product = g; // product = g^n mod p + bool searching = true; // still searching for g + + while (searching) + { + product = g; + searching = false; // optimistically guess g is good + // stop at prime - 2, because g^(prime-1) = 1 for Z_p + for (n = 1; n < prime - 2; n++) + { + product = (product * g) % prime; + if (product == 1) + { + // FAIL: g is not a generator + g++; + if (g == prime) return 0; // should never get here + searching = true; + n = prime - 2; + } + } + } + + return g; +} + +/* Given g^n mod p, g, and p, compute n */ +uint64_t +discrete_log (uint64_t g2n, uint64_t g, uint64_t p) +{ + uint64_t value = g; + uint64_t n = 1; + while (value != g2n) + { + value = (value * g) % p; + n++; + } + + return n; +} + diff --git a/lab7-threads/discrete.h b/lab7-threads/discrete.h new file mode 100644 index 0000000..595be99 --- /dev/null +++ b/lab7-threads/discrete.h @@ -0,0 +1,9 @@ +#ifndef __discrete_h__ +#define __discrete_h__ + +#include + +uint64_t find_generator (uint64_t); +uint64_t discrete_log (uint64_t, uint64_t, uint64_t); + +#endif diff --git a/lab7-threads/main.c b/lab7-threads/main.c new file mode 100644 index 0000000..9b8e98d --- /dev/null +++ b/lab7-threads/main.c @@ -0,0 +1,200 @@ +/* + * CS 361: Template project driver + * + * Name: Nicholas Tamassia + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "discrete.h" +#include "pthread.h" + +int cmdline (int, char **, size_t *, uint64_t **, uint64_t **, uint64_t **, + size_t *); + +void +usage (void) +{ + printf ("Usage: threads [options] (g:p:g2n ... )\n"); + printf (" Required arguments must be an array of g:p:g2n values\n"); + printf (" Options are:\n"); + printf (" -n N Create N concurrent threads\n"); + printf (" g:p:g2n is a colon-delimited list of 3 uint64_t values\n"); + printf (" g A group generator\n"); + printf (" p A prime number\n"); + printf (" g2n g^n mod p for some unknown n\n"); +} + +int +main (int argc, char **argv) +{ + uint64_t *primes = NULL; + uint64_t *gens = NULL; + uint64_t *powers = NULL; + size_t num_threads = 1; + size_t num_args = 0; + + if (cmdline (argc, argv, &num_args, &primes, &gens, &powers, &num_threads) + < 0) + { + usage (); + return EXIT_FAILURE; + } + + if (num_args < 1 || num_args > 20) + { + printf ("There must be between 1 and 20 arguments\n"); + return EXIT_FAILURE; + } + + // Unithreaded version to serve as a template + if (num_threads == 1) + { + for (int i = 0; i < num_args; i++) + { + uint64_t result = discrete_log (powers[i], gens[i], primes[i]); + printf ("Given g = %" PRId64 ", p = %" PRId64 ", g2n = %" PRId64 + " => n = %" PRId64 "\n", + gens[i], primes[i], powers[i], result); + } + } + else + { + uint64_t *logs = calloc (num_args, sizeof (uint64_t)); + if (logs == NULL) + { + fprintf (stderr, "Failed to allocate logs array\n"); + return EXIT_FAILURE; + } + + // Adjust number of threads if there are fewer arguments than threads + if (num_threads > num_args) + num_threads = num_args; + + size_t base + = (num_args + num_threads - 1) / num_threads; // Ceiling division + size_t rem = num_args % num_threads; + + pthread_t *threads = calloc (num_threads, sizeof (pthread_t)); + struct time_args *targs + = calloc (num_threads, sizeof (struct time_args)); + if (threads == NULL || targs == NULL) + { + free (logs); + free (threads); + free (targs); + fprintf (stderr, "Failed to allocate thread structures\n"); + return EXIT_FAILURE; + } + + size_t created = 0; + for (size_t t = 0; t < num_threads; t++) + { + size_t start = t * base; + size_t count = (t + 1 == num_threads && rem > 0) ? rem : base; + + targs[created].start_index = start; + targs[created].number = count; + targs[created].generators = gens; + targs[created].primes = primes; + targs[created].mod_powers = powers; + targs[created].results = logs; + + if (pthread_create (&threads[created], NULL, time_log_thread, + (void *)&targs[created]) + != 0) + { + for (size_t j = 0; j < created; j++) + pthread_join (threads[j], NULL); + free (logs); + free (threads); + free (targs); + fprintf (stderr, "Failed to create worker thread\n"); + return EXIT_FAILURE; + } + + created++; + } + + for (size_t j = 0; j < created; j++) + pthread_join (threads[j], NULL); + + for (size_t i = 0; i < num_args; i++) + { + printf ("Given g = %lu, p = %lu, g2n = %lu => n = %lu\n", gens[i], + primes[i], powers[i], logs[i]); + } + + free (logs); + free (threads); + free (targs); + } + + if (primes != NULL) + free (primes); + if (gens != NULL) + free (gens); + if (powers != NULL) + free (powers); + + return EXIT_SUCCESS; +} + +/***************************************************************************** + ****************** DO NOT MODIFY FUNCTIONS IN THIS SECTION ****************** + *****************************************************************************/ + +int +cmdline (int argc, char **argv, size_t *length, uint64_t **primes, + uint64_t **gens, uint64_t **powers, size_t *num_threads) +{ + int option; + + while ((option = getopt (argc, argv, "n:h")) != -1) + { + switch (option) + { + case 'n': + *num_threads = (size_t)strtol (optarg, NULL, 10); + break; + case 'h': + return -1; + break; + default: + return -1; + } + } + + if (optind == argc) + return -1; + *length = (size_t)(argc - optind); + *primes = calloc (*length, sizeof (uint64_t)); + + size_t index; + // These must both have g:p:g2n values + *gens = calloc (*length, sizeof (uint64_t)); + *powers = calloc (*length, sizeof (uint64_t)); + for (index = optind; index < argc; index++) + { + char *token = strtok (argv[index], ":"); + uint64_t value = strtol (token, NULL, 10); + (*gens)[index - optind] = value; + token = strtok (NULL, ":"); + assert (token != NULL); + value = strtol (token, NULL, 10); + (*primes)[index - optind] = value; + token = strtok (NULL, ":"); + assert (token != NULL); + value = strtol (token, NULL, 10); + (*powers)[index - optind] = value; + } + + return 0; +} diff --git a/lab7-threads/private.h b/lab7-threads/private.h new file mode 100644 index 0000000..87cc60b --- /dev/null +++ b/lab7-threads/private.h @@ -0,0 +1,6 @@ +#ifndef __private_h__ +#define __private_h__ + +void * generator (void *); + +#endif diff --git a/lab7-threads/pthread.c b/lab7-threads/pthread.c new file mode 100644 index 0000000..83980ac --- /dev/null +++ b/lab7-threads/pthread.c @@ -0,0 +1,152 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "discrete.h" +#include "private.h" +#include "pthread.h" + +/* Create a thread that will run generator(prime). Use pthread_join() to + retrieve the result from the thread and return that value. */ +uint64_t +find_gen (uint64_t prime) +{ + pthread_t thread; + int rc = pthread_create (&thread, NULL, generator, (void *)prime); + if (rc != 0) + { + return 0; + } + + void *result = NULL; + rc = pthread_join (thread, &result); + if (rc != 0) + { + return 0; + } + + return (uint64_t)result; +} + +/* Helper function that is a wrapper for calling find_generator() in a + separate thread. Do not modify. */ +void * +generator (void *args) +{ + uint64_t prime = (uint64_t)args; + uint64_t gen = find_generator (prime); + pthread_exit ((void *)gen); +} + +/* Create multiple threads, each of which makes a single call to generator(). + Return a dynamically allocated array structured like the following: + results[0] = generator (0); + results[1] = generator (1); + ... + The arguments are the number of threads to create and the array of primes + to distribute between the threads (one prime per thread). */ +uint64_t * +find_gens (size_t num_threads, uint64_t *primes) +{ + if (num_threads == 0 || primes == NULL) + return NULL; + + pthread_t *threads = calloc (num_threads, sizeof (pthread_t)); + uint64_t *results = calloc (num_threads, sizeof (uint64_t)); + + for (size_t i = 0; i < num_threads; i++) + { + int rc + = pthread_create (&threads[i], NULL, generator, (void *)primes[i]); + if (rc != 0) + { + for (size_t j = 0; j < i; j++) + { + pthread_join (threads[j], NULL); + } + free (threads); + free (results); + return NULL; + } + } + + for (size_t i = 0; i < num_threads; i++) + { + void *retval = NULL; + int rc = pthread_join (threads[i], &retval); + if (rc != 0) + { + results[i] = 0; + } + else + { + results[i] = (uint64_t)retval; + } + } + + free (threads); + return results; +} + +/* Helper function to calculate the difference between a start and end time. */ +double +time_diff (struct timeval start, struct timeval end) +{ + double ending = end.tv_sec + (end.tv_usec * 0.000001); + double starting = start.tv_sec + (start.tv_usec * 0.000001); + return ending - starting; +} + +/* Calculate the discrete logarithm for several values. That is, given the + values g, p, and g^n mod p, determine n. (Note that this is an intentionally + SLOW operation!) The parameters are stored in three global arrays: + gens[] - this contains the generator values (g) + primes[] - this contains the prime numbers (p) + mod_powers[] - this contains the values g^n mod p for unknown n values + In pseudocode, you should be doing: + for i in start_index .. end_index-1 + logs [i] = discrete_log (parameters[i]) + The return value is the real time it takes to compute these values. Use + the C standard library function gettimeofday() to get a start and end time, + then use the helper function time_diff() to compute the difference. + This function will be called by time_log_thread() below. */ +double +time_log (size_t start_index, size_t end_index, uint64_t *gens, + uint64_t *primes, uint64_t *mod_powers, uint64_t *logs) +{ + struct timeval start, end; + gettimeofday (&start, NULL); + + for (size_t i = start_index; i < end_index; i++) + { + logs[i] = discrete_log (mod_powers[i], gens[i], primes[i]); + } + + gettimeofday (&end, NULL); + return time_diff (start, end); +} + +/* Wrapper function to call time_log() from within a thread. Given the + arguments passed, store the return value from time_log() into the + time_taken field. */ +void * +time_log_thread (void *_args) +{ + struct time_args *args = (struct time_args *)_args; + if (args == NULL) + pthread_exit (NULL); + + size_t start = args->start_index; + size_t end = start + args->number; + + args->time_taken = time_log (start, end, args->generators, args->primes, + args->mod_powers, args->results); + + pthread_exit (NULL); +} diff --git a/lab7-threads/pthread.h b/lab7-threads/pthread.h new file mode 100644 index 0000000..e262f43 --- /dev/null +++ b/lab7-threads/pthread.h @@ -0,0 +1,25 @@ +#ifndef __pthread_h__ +#define __pthread_h__ + +#include +#include +#include + +uint64_t find_gen (uint64_t); +uint64_t * find_gens (size_t, uint64_t *); + +// struct used for passing arguments to time_log_thread () +struct time_args { + size_t start_index; // starting index in the array of 20 items + size_t number; // how many items to compute + double time_taken; // how long total to compute all of the items + uint64_t * generators; // pointer to array of group generators + uint64_t * primes; // pointer to array of primes to use + uint64_t * mod_powers; // pointer to array of g^n mod p values + uint64_t * results; // pointer to array to capture the results +}; + +double time_diff (struct timeval, struct timeval); +void * time_log_thread (void *); + +#endif diff --git a/lab7-threads/tests/Makefile b/lab7-threads/tests/Makefile new file mode 100644 index 0000000..2af6694 --- /dev/null +++ b/lab7-threads/tests/Makefile @@ -0,0 +1,86 @@ +# +# Simple Test Makefile +# Mike Lam, James Madison University, August 2016 +# +# This version of the Makefile includes support for building a test suite. The +# recommended framework is Check (http://check.sourceforge.net/). To build and +# run the test suite, execute the "test" target. The test suite must be located +# in a module called "testsuite". The MODS, LIBS, and OBJS variables work as +# they do in the main Makefile. +# +# To change the default build target (which executes when you just type +# "make"), change the right-hand side of the definition of the "default" +# target. +# +# By default, this makefile will build the project with debugging symbols and +# without optimization. To change this, edit or remove the "-g" and "-O0" +# options in CFLAGS and LDFLAGS accordingly. +# +# By default, this makefile build the application using the GNU C compiler, +# adhering to the C99 standard with all warnings enabled. + + +# application-specific settings and run target + +EXE=../threads +TEST=testsuite +MODS=public.o +OBJS=../pthread.o ../discrete.o +LIBS= + +UTESTOUT=utests.txt +ITESTOUT=itests.txt + +default: $(TEST) + +$(EXE): + make -C ../ + +test: utest itest + @echo "========================================" + +utest: $(EXE) $(TEST) + @echo "========================================" + @echo " UNIT TESTS" + @echo "----------------------------------------" + @echo "Have patience:" + @echo "This should take several seconds" + @echo "You can see how it works by running:" + @echo " ./tests/testsuite" + @echo "----------------------------------------" + @./$(TEST) 2>/dev/null >$(UTESTOUT) + @cat $(UTESTOUT) | sed -n -e '/Checks/,$$p' | sed -e 's/^private.*:[EF]://g' + +itest: $(EXE) + @echo "========================================" + @echo " INTEGRATION TESTS" + @./integration.sh | tee $(ITESTOUT) + + +# compiler/linker settings + +CC=gcc +CFLAGS=-g -O0 -Wall --std=gnu99 -pedantic +LDFLAGS=-g -O0 + +CFLAGS= +LIBS+=-lcheck -lm -lpthread + +ifeq ($(shell uname -s),Linux) + LIBS+=-lrt -lsubunit +endif + + +# build targets + +$(TEST): $(TEST).o $(MODS) $(OBJS) + $(CC) $(LDFLAGS) -o $(TEST) $^ $(LIBS) + +%.o: %.c + $(CC) -c $(CFLAGS) $< + +clean: + rm -rf $(TEST) $(TEST).o $(MODS) $(UTESTOUT) $(ITESTOUT) outputs valgrind + +.PHONY: default clean test unittest inttest + diff --git a/lab7-threads/tests/expected/INTEG_logs_large.txt b/lab7-threads/tests/expected/INTEG_logs_large.txt new file mode 100644 index 0000000..3380a03 --- /dev/null +++ b/lab7-threads/tests/expected/INTEG_logs_large.txt @@ -0,0 +1,4 @@ +Given g = 43013398, p = 86026793, g2n = 64520094 => n = 62484235 +Given g = 35739627, p = 71479241, g2n = 53609430 => n = 37573249 +Given g = 47594171, p = 95188343, g2n = 71391255 => n = 83365257 +Given g = 32133930, p = 64267859, g2n = 48200892 => n = 42413257 diff --git a/lab7-threads/tests/expected/INTEG_logs_large_thr.txt b/lab7-threads/tests/expected/INTEG_logs_large_thr.txt new file mode 100644 index 0000000..3380a03 --- /dev/null +++ b/lab7-threads/tests/expected/INTEG_logs_large_thr.txt @@ -0,0 +1,4 @@ +Given g = 43013398, p = 86026793, g2n = 64520094 => n = 62484235 +Given g = 35739627, p = 71479241, g2n = 53609430 => n = 37573249 +Given g = 47594171, p = 95188343, g2n = 71391255 => n = 83365257 +Given g = 32133930, p = 64267859, g2n = 48200892 => n = 42413257 diff --git a/lab7-threads/tests/expected/INTEG_logs_medium.txt b/lab7-threads/tests/expected/INTEG_logs_medium.txt new file mode 100644 index 0000000..d6a0116 --- /dev/null +++ b/lab7-threads/tests/expected/INTEG_logs_medium.txt @@ -0,0 +1,9 @@ +Given g = 1374234, p = 2748467, g2n = 2061348 => n = 1014681 +Given g = 3290982, p = 6581963, g2n = 4936470 => n = 1229705 +Given g = 1449191, p = 2898383, g2n = 2173785 => n = 2576065 +Given g = 2511322, p = 5022643, g2n = 3766980 => n = 4460251 +Given g = 1523458, p = 3046903, g2n = 2285175 => n = 285081 +Given g = 935190, p = 1870369, g2n = 1402776 => n = 593354 +Given g = 1598454, p = 3196909, g2n = 2397681 => n = 1024170 +Given g = 2051286, p = 4102573, g2n = 3076929 => n = 2765678 +Given g = 1748890, p = 3497779, g2n = 2623332 => n = 1722817 diff --git a/lab7-threads/tests/expected/INTEG_logs_medium_thr.txt b/lab7-threads/tests/expected/INTEG_logs_medium_thr.txt new file mode 100644 index 0000000..d6a0116 --- /dev/null +++ b/lab7-threads/tests/expected/INTEG_logs_medium_thr.txt @@ -0,0 +1,9 @@ +Given g = 1374234, p = 2748467, g2n = 2061348 => n = 1014681 +Given g = 3290982, p = 6581963, g2n = 4936470 => n = 1229705 +Given g = 1449191, p = 2898383, g2n = 2173785 => n = 2576065 +Given g = 2511322, p = 5022643, g2n = 3766980 => n = 4460251 +Given g = 1523458, p = 3046903, g2n = 2285175 => n = 285081 +Given g = 935190, p = 1870369, g2n = 1402776 => n = 593354 +Given g = 1598454, p = 3196909, g2n = 2397681 => n = 1024170 +Given g = 2051286, p = 4102573, g2n = 3076929 => n = 2765678 +Given g = 1748890, p = 3497779, g2n = 2623332 => n = 1722817 diff --git a/lab7-threads/tests/expected/INTEG_logs_small.txt b/lab7-threads/tests/expected/INTEG_logs_small.txt new file mode 100644 index 0000000..a5327a8 --- /dev/null +++ b/lab7-threads/tests/expected/INTEG_logs_small.txt @@ -0,0 +1,12 @@ +Given g = 131, p = 263, g2n = 195 => n = 229 +Given g = 227, p = 439, g2n = 327 => n = 429 +Given g = 262, p = 523, g2n = 390 => n = 191 +Given g = 414, p = 829, g2n = 521 => n = 757 +Given g = 442, p = 883, g2n = 660 => n = 723 +Given g = 474, p = 947, g2n = 708 => n = 539 +Given g = 511, p = 1009, g2n = 756 => n = 974 +Given g = 526, p = 1049, g2n = 786 => n = 681 +Given g = 398, p = 797, g2n = 597 => n = 235 +Given g = 274, p = 547, g2n = 408 => n = 509 +Given g = 606, p = 1213, g2n = 909 => n = 892 +Given g = 11, p = 23, g2n = 15 => n = 19 diff --git a/lab7-threads/tests/expected/INTEG_logs_small_thr.txt b/lab7-threads/tests/expected/INTEG_logs_small_thr.txt new file mode 100644 index 0000000..a5327a8 --- /dev/null +++ b/lab7-threads/tests/expected/INTEG_logs_small_thr.txt @@ -0,0 +1,12 @@ +Given g = 131, p = 263, g2n = 195 => n = 229 +Given g = 227, p = 439, g2n = 327 => n = 429 +Given g = 262, p = 523, g2n = 390 => n = 191 +Given g = 414, p = 829, g2n = 521 => n = 757 +Given g = 442, p = 883, g2n = 660 => n = 723 +Given g = 474, p = 947, g2n = 708 => n = 539 +Given g = 511, p = 1009, g2n = 756 => n = 974 +Given g = 526, p = 1049, g2n = 786 => n = 681 +Given g = 398, p = 797, g2n = 597 => n = 235 +Given g = 274, p = 547, g2n = 408 => n = 509 +Given g = 606, p = 1213, g2n = 909 => n = 892 +Given g = 11, p = 23, g2n = 15 => n = 19 diff --git a/lab7-threads/tests/integration.sh b/lab7-threads/tests/integration.sh new file mode 100755 index 0000000..002eecb --- /dev/null +++ b/lab7-threads/tests/integration.sh @@ -0,0 +1,59 @@ +#!/bin/bash + +EXE="../threads" + +function run_test { + + # parameters + TAG=$1 + ARGS=$2 + + # file paths + OUTPUT=outputs/$TAG.txt + DIFF=outputs/$TAG.diff + EXPECT=expected/$TAG.txt + VALGRND=valgrind/$TAG.txt + + # run test and compare output to the expected version + $EXE $ARGS 2>/dev/null >"$OUTPUT" + diff -u "$OUTPUT" "$EXPECT" >"$DIFF" + PTAG=$(printf '%-30s' "$TAG") + if [ -s "$DIFF" ]; then + + # try alternative solution (if it exists) + EXPECT=expected/$TAG-2.txt + if [ -e "$EXPECT" ]; then + diff -u "$OUTPUT" "$EXPECT" >"$DIFF" + if [ -s "$DIFF" ]; then + echo "$PTAG FAIL (see $DIFF for details)" + else + echo "$PTAG pass" + fi + else + echo "$PTAG FAIL (see $DIFF for details)" + fi + else + echo "$PTAG pass" + fi + + # run valgrind +#valgrind $EXE $ARGS &>$VALGRND +} + +# initialize output folders +mkdir -p outputs +mkdir -p valgrind +rm -f outputs/* valgrind/* + +# run individual tests +source itests.include + +# check for memory leaks +#LEAK=`cat valgrind/*.txt | grep 'definitely lost' | grep -v ' 0 bytes in 0 blocks'` +#if [ -z "$LEAK" ]; then +# echo "No memory leak found." +#else +# echo "Memory leak(s) found. See files listed below for details." +# grep 'definitely lost' valgrind/*.txt | grep -v ' 0 bytes in 0 blocks' | sed -e 's/:.*$//g' | sed -e 's/^/ - /g' +#fi + diff --git a/lab7-threads/tests/itests.include b/lab7-threads/tests/itests.include new file mode 100644 index 0000000..0aa7120 --- /dev/null +++ b/lab7-threads/tests/itests.include @@ -0,0 +1,11 @@ +# list of integration tests +# format: run_test +# used as the root for all filenames (i.e., "expected/$TAG.txt") +# command-line arguments to test + +run_test INTEG_logs_small "131:263:195 227:439:327 262:523:390 414:829:521 442:883:660 474:947:708 511:1009:756 526:1049:786 398:797:597 274:547:408 606:1213:909 11:23:15" +run_test INTEG_logs_small_thr "-n 6 131:263:195 227:439:327 262:523:390 414:829:521 442:883:660 474:947:708 511:1009:756 526:1049:786 398:797:597 274:547:408 606:1213:909 11:23:15" +run_test INTEG_logs_medium "1374234:2748467:2061348 3290982:6581963:4936470 1449191:2898383:2173785 2511322:5022643:3766980 1523458:3046903:2285175 935190:1870369:1402776 1598454:3196909:2397681 2051286:4102573:3076929 1748890:3497779:2623332" +run_test INTEG_logs_medium_thr "-n 3 1374234:2748467:2061348 3290982:6581963:4936470 1449191:2898383:2173785 2511322:5022643:3766980 1523458:3046903:2285175 935190:1870369:1402776 1598454:3196909:2397681 2051286:4102573:3076929 1748890:3497779:2623332" +run_test INTEG_logs_large "43013398:86026793:64520094 35739627:71479241:53609430 47594171:95188343:71391255 32133930:64267859:48200892" +run_test INTEG_logs_large_thr "-n 4 43013398:86026793:64520094 35739627:71479241:53609430 47594171:95188343:71391255 32133930:64267859:48200892" diff --git a/lab7-threads/tests/public.c b/lab7-threads/tests/public.c new file mode 100644 index 0000000..9eef6cd --- /dev/null +++ b/lab7-threads/tests/public.c @@ -0,0 +1,217 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../discrete.h" +#include "../pthread.h" + +uint64_t large_primes[] = { + 22181233, 30722903, 39410039, 48210479, 23878483, 32452597, 41161361, + 49978519, 25582069, 34185953, 42918971, 51754777, 27290279, 35925737, + 44679787, 53532901, 29005421, 37666553, 46439677, 55316903 +}; + +uint64_t large_gens[] = { + 11090619, 15361451, 19705019, 24105239, 11939242, 16226298, 20580682, + 24989259, 12791034, 17092978, 21459486, 25877391, 13645139, 17962870, + 22339897, 26766452, 14502710, 18833278, 23219838, 27658451 +}; + +uint64_t mod_powers[] = { + 10722318, 10718795, 16846904, 28514080, 13198105, 21563282, 16451148, + 31188765, 8174568, 24132030, 5838284, 8726777, 10697873, 30365043, + 40656885, 6262555, 301068, 34096955, 29442656, 36415956 +}; + +// Move these to tests/private.c +// These are p / 4 * 3 +uint64_t expected[] = { + 16635924, 23042175, 29557527, 36157857, 17908860, 24339447, 30871020, + 37483887, 19186551, 25639464, 32189226, 38816082, 20467707, 26944302, + 33509838, 40149675, 21754065, 28249914, 34829757, 41487675 +}; + + +START_TEST (MIN_find_one_generator) +{ + printf ("======================================\n"); + printf ("PUBLIC\n"); + printf (" MIN find one generator\n"); + printf (" Calls find generator(%" PRId64 ") in a separate thread\n", large_primes[10]); + printf (" Should compute %" PRId64 "\n\n", large_gens[10]); + + uint64_t single = find_gen (large_primes[10]); + ck_assert_int_eq (single, large_gens[10]); +} +END_TEST + +START_TEST (MIN_find_two_generators) +{ + printf ("======================================\n"); + printf ("PUBLIC\n"); + printf (" MIN find two generators\n"); + printf (" Calls find_generator(%" PRId64 ") in a separate thread\n", large_primes[0]); + printf (" Should compute %" PRId64 "\n", large_gens[0]); + printf (" Calls find_generator(%" PRId64 ") in a separate thread\n", large_primes[1]); + printf (" Should compute %" PRId64 "\n\n", large_gens[1]); + + uint64_t * pair = find_gens (2, large_primes); + ck_assert (pair != NULL); + ck_assert_int_eq (pair[0], large_gens[0]); + ck_assert_int_eq (pair[1], large_gens[1]); + free (pair); +} +END_TEST + +START_TEST (MIN_find_5_generators) +{ + printf ("======================================\n"); + printf ("PRIVATE\n"); + printf (" MIN find one generator\n"); + printf (" Calls find generator() for the following values:\n "); + + size_t i; + for (i = 0; i < 5; i++) + printf (" %" PRId64, large_primes[i]); + printf ("\n"); + uint64_t * gens = find_gens (5, large_primes); + + printf (" Computed the following generators:\n "); + for (i = 0; i < 5; i++) + printf (" %" PRId64, gens[i]); + printf ("\n"); + + printf (" Expected the following generators:\n "); + for (i = 0; i < 5; i++) + printf (" %" PRId64, large_gens[i]); + printf ("\n\n"); + + ck_assert (gens != NULL); + for (i = 0; i < 5; i++) + ck_assert_int_eq (gens[i], large_gens[i]); + +} +END_TEST + +START_TEST (MIN_last_5_discrete_logs) +{ + printf ("======================================\n"); + printf ("PUBLIC\n"); + printf (" MIN last 5 discrete logs\n"); + printf (" Uses threads to compute the discrete logarithms for:\n"); + size_t i; + for (i = 0; i < 5; i++) + { + printf (" g = %8"PRId64 "; ", large_gens[15 + i]); + printf (" p = %"PRId64 "; ", large_primes[15 + i]); + printf (" g^n = %"PRId64 "\n", mod_powers[15 + i]); + } + + struct time_args args; + memset (&args, 0, sizeof (args)); + args.results = calloc (20, sizeof (uint64_t)); + args.start_index = 15; + args.number = 5; + args.generators = large_gens; + args.primes = large_primes; + args.mod_powers = mod_powers; + + pthread_t thread; + pthread_create (&thread, NULL, time_log_thread, (void *) &args); + pthread_join (thread, NULL); + printf (" Thread took %lf seconds\n", args.time_taken); + for (i = 0; i < 5; i++) + printf (" Computed n = %"PRId64 " [expected %"PRId64"]\n", + args.results[15 + i], expected[15 + i]); + printf ("\n"); + for (i = 0; i < 5; i++) + ck_assert_int_eq (args.results[15 + i], expected[15 + i]); + free (args.results); + +} +END_TEST + +START_TEST (MIN_parallel_discrete_logs) +{ + printf ("======================================\n"); + printf ("PUBLIC\n"); + printf (" MIN parallel discrete logs\n"); + printf (" Uses 4 threads to compute the discrete logarithms for:\n"); + size_t i; + for (i = 0; i < 20; i++) + { + printf (" g = %8"PRId64 "; ", large_gens[15 + i]); + printf (" p = %"PRId64 "; ", large_primes[15 + i]); + printf (" g^n = %"PRId64 "\n", mod_powers[15 + i]); + } + + struct timeval start, end; + gettimeofday (&start, NULL); + for (i = 0; i < 20; i++) + discrete_log (mod_powers[i], large_gens[i], large_primes[i]); + gettimeofday (&end, NULL); + double sequential_time = time_diff (start, end); + printf (" Sequential real-time took %lf seconds\n", sequential_time); + + uint64_t * logs = calloc (20, sizeof (uint64_t)); + + gettimeofday (&start, NULL); + pthread_t thread[4]; + struct time_args args[4]; + for (i = 0; i < 4; i++) + { + memset (&args[i], 0, sizeof (args[i])); + args[i].results = logs; + args[i].start_index = i * 5; + args[i].number = 5; + args[i].generators = large_gens; + args[i].primes = large_primes; + args[i].mod_powers = mod_powers; + pthread_create (&thread[i], NULL, time_log_thread, (void *) &args[i]); + } + + for (i = 0; i < 4; i++) + pthread_join (thread[i], NULL); + gettimeofday (&end, NULL); + + double parallel_time = time_diff (start, end); + printf (" Parallel real-time took %lf seconds\n", parallel_time); + for (i = 0; i < 4; i++) + printf (" Thread %zd took %lf seconds\n", i, args[i].time_taken); + + size_t j; + for (i = 0; i < 4; i++) + { + for (j = 0; j < 5; j++) + printf (" Thread %zd computed n = %"PRId64 " [expected %"PRId64"]\n", + i, logs[i * 4 + j], expected[i * 4 + j]); + } + printf ("\n"); + for (i = 0; i < 20; i++) + ck_assert_int_eq (logs[i], expected[i]); + free (logs); + + + ck_assert (parallel_time * 2 < sequential_time); + ck_assert (parallel_time * 4 > sequential_time); + +} +END_TEST + +void public_tests (Suite *s) +{ + TCase *tc_public = tcase_create ("Public"); + tcase_set_timeout (tc_public, 20.0); + tcase_add_test (tc_public, MIN_find_one_generator); + tcase_add_test (tc_public, MIN_find_two_generators); + tcase_add_test (tc_public, MIN_last_5_discrete_logs); + tcase_add_test (tc_public, MIN_parallel_discrete_logs); + suite_add_tcase (s, tc_public); +} + diff --git a/lab7-threads/tests/testsuite.c b/lab7-threads/tests/testsuite.c new file mode 100644 index 0000000..79db20b --- /dev/null +++ b/lab7-threads/tests/testsuite.c @@ -0,0 +1,32 @@ +#include +#include +#include +#include +#include +#include + +#include + +extern void public_tests (Suite *s); + +Suite * test_suite (void) +{ + Suite *s = suite_create ("Default"); + public_tests (s); + return s; +} + +void run_testsuite () +{ + Suite *s = test_suite (); + SRunner *sr = srunner_create (s); + srunner_run_all (sr, CK_NORMAL); + srunner_free (sr); +} + +int main (void) +{ + srand((unsigned)time(NULL)); + run_testsuite (); + return EXIT_SUCCESS; +} diff --git a/lab8-synch/.gitignore b/lab8-synch/.gitignore new file mode 100644 index 0000000..793f13b --- /dev/null +++ b/lab8-synch/.gitignore @@ -0,0 +1,24 @@ +# Ignore all compiled files regardless of location +main.o +mutex.o +pingpong.o +tests/public.o +tests/testsuite.o + +# But not the private.o +!tests/private.o + +# Ignore executables for this project +synch +testsuite + +# Ignore test outputs +tests/ckstyle +tests/itests.txt +tests/outputs +tests/style.txt +tests/valgrind +tests/utests.txt + +**/.nfs* +**/.vscode diff --git a/lab8-synch/Makefile b/lab8-synch/Makefile new file mode 100644 index 0000000..b9883d2 --- /dev/null +++ b/lab8-synch/Makefile @@ -0,0 +1,52 @@ +# +# Simple Makefile +# Mike Lam, James Madison University, August 2016 +# +# This makefile builds a simple application that contains a main module +# (specified by the EXE variable) and a predefined list of additional modules +# (specified by the MODS variable). If there are any external library +# dependencies (e.g., the math library, "-lm"), list them in the LIBS variable. +# If there are any precompiled object files, list them in the OBJS variable. +# +# By default, this makefile will build the project with debugging symbols and +# without optimization. To change this, edit or remove the "-g" and "-O0" +# options in CFLAGS and LDFLAGS accordingly. +# +# By default, this makefile build the application using the GNU C compiler, +# adhering to the C99 standard with all warnings enabled. + + +# application-specific settings and run target + +EXE=synch +MODS=mutex.o pingpong.o +OBJS= +LIBS=-lm + +default: $(EXE) + +test: $(EXE) + make -C tests test + +# compiler/linker settings + +CC=gcc +#CFLAGS=-g -O0 -Wall --std=gnu99 -pedantic +CFLAGS=-g -O0 -Wall --std=gnu99 -pedantic +LDFLAGS=-g -O0 -pthread + + +# build targets + +$(EXE): main.o $(MODS) $(OBJS) + $(CC) $(LDFLAGS) -o $(EXE) $^ $(LIBS) + +%.o: %.c + $(CC) -c $(CFLAGS) $< + +clean: + rm -f $(EXE) main.o $(MODS) + make -C tests clean + +.PHONY: default clean + diff --git a/lab8-synch/main.c b/lab8-synch/main.c new file mode 100644 index 0000000..7640b2d --- /dev/null +++ b/lab8-synch/main.c @@ -0,0 +1,93 @@ +/* + * CS 361: Template project driver + * + * Name: Nicholas Tamassia + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mutex.h" +#include "pingpong.h" + +int cmdline (int, char**, bool*, bool*, size_t *); + +void +usage (void) +{ + printf ("Usage: synch option [option...]\n"); + printf (" At least one argument must be passed\n"); + printf (" Options are:\n"); + printf (" -m Execute the runner() threads to demonstrate locks\n"); + printf (" -p N Run pingong() with N iterations to demonstrate " + "semaphore-based timing\n"); +} + +int +main (int argc, char **argv) +{ + bool run_mutex = false; + bool run_pingpong = false; + size_t count = 0; + if (cmdline (argc, argv, &run_mutex, &run_pingpong, &count) < 0) + { + usage (); + return EXIT_FAILURE; + } + + // Use mutexes for mutual exclusion + if (run_mutex) + { + printf ("Running mutual exclusion test\n"); + int64_t shared = run (); + printf ("Value of shared: %" PRId64 "\n", shared); + } + + // Use semaphores to demonstrate signaling + if (run_pingpong) + { + printf ("Running %zd iteration(s) of ping-pong\n", count); + ping (&count); + printf ("There should be 0 iterations left: %zd\n", count); + } + + pthread_exit (NULL); +} + +/***************************************************************************** + ****************** DO NOT MODIFY FUNCTIONS IN THIS SECTION ****************** + *****************************************************************************/ + +int +cmdline (int argc, char **argv, bool *mutex, bool *pingpong, size_t *count) +{ + int option; + + while ((option = getopt (argc, argv, "mp:h")) != -1) + { + switch (option) + { + case 'm': *mutex = true; + break; + case 'p': *pingpong = true; + *count = strtol (optarg, NULL, 10); + break; + case 'h': return -1; + break; + default: return -1; + } + } + + if (!*mutex && !*pingpong) + { + printf ("You must pass at least one argument\n"); + return -1; + } + return 0; +} diff --git a/lab8-synch/mutex.c b/lab8-synch/mutex.c new file mode 100644 index 0000000..e3183cc --- /dev/null +++ b/lab8-synch/mutex.c @@ -0,0 +1,89 @@ +#include +#include +#include +#include +#include +#include +#include + +// Use the struct here for passing arguments to the runner() threads. +// All instances of this struct should point to the same pthread_mutex_t +// and shared variables. +typedef struct args { + pthread_mutex_t *lock; + int64_t *shared; +} arg_t; + +/* Function to run in concurrent threads. The argument passed should be a + pointer to a pthread_mutex_t to be used for mutual exclusion. Within the + nested for-loop structure, use the mutex to protect the the increments + and decrements of the shared variable. To get the timing right for the + unit tests, experiment with placing the lock/unlock calls in different + places (e.g., around both for-loops, inside the outer one, inside the + inner one, etc.) */ +void * +runner (void *arg) +{ + // Retrieve the shared pointer and mutex from the passed struct. + arg_t *args = (arg_t *) arg; + pthread_mutex_t *lock = args->lock; + int64_t *shared = args->shared; + + for (int j = 0; j < 1000000; j++) + { + pthread_mutex_lock (lock); + for (int i = 0; i < 100; i++) + { + *shared += 1; + *shared -= 1; + } + pthread_mutex_unlock (lock); + } + + pthread_exit (NULL); +} + +/* Simple fork-join routine that creates two threads running the runner() + function above. Pass the lock and a pointer to the shared variable to + both threads. When both threads complete, return the shared variable, + which should have a value of 0. + */ +int64_t +run (void) +{ + int64_t shared = 0; + + pthread_mutex_t lock; + if (pthread_mutex_init (&lock, NULL) != 0) + { + fprintf (stderr, "Failed to initialize mutex\n"); + return shared; + } + + arg_t args; + args.lock = &lock; + args.shared = &shared; + + pthread_t t1, t2; + if (pthread_create (&t1, NULL, runner, &args) != 0) + { + fprintf (stderr, "Failed to create thread 1\n"); + pthread_mutex_destroy (&lock); + return shared; + } + if (pthread_create (&t2, NULL, runner, &args) != 0) + { + fprintf (stderr, "Failed to create thread 2\n"); + pthread_cancel (t1); + pthread_join (t1, NULL); + pthread_mutex_destroy (&lock); + return shared; + } + + pthread_join (t1, NULL); + pthread_join (t2, NULL); + + pthread_mutex_destroy (&lock); + + return shared; +} diff --git a/lab8-synch/mutex.h b/lab8-synch/mutex.h new file mode 100644 index 0000000..e882619 --- /dev/null +++ b/lab8-synch/mutex.h @@ -0,0 +1,10 @@ +#ifndef __mutex_h__ +#define __mutex_h__ + +#include +#include + +void * runner (void *); +int64_t run (void); + +#endif diff --git a/lab8-synch/pingpong.c b/lab8-synch/pingpong.c new file mode 100644 index 0000000..6e29653 --- /dev/null +++ b/lab8-synch/pingpong.c @@ -0,0 +1,94 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pingpong.h" + +typedef struct pparg { + sem_t *sem_ping; // Tell ping to wake up + sem_t *sem_pong; // Tell pong to wake up + size_t *shared; +} ppargs_t; + +void * +pong (void * _args) +{ + ppargs_t *args = (ppargs_t *) _args; + + while (1) + { + sem_wait (args->sem_pong); + + printf ("PONG!\n"); + + if (*(args->shared) == 0) + { + break; + } + else + { + sem_post (args->sem_ping); + } + } + + printf ("PONG: Game over\n"); + pthread_exit (NULL); +} + +void +ping (size_t *count) +{ + pthread_t pong_thread; + sem_t sem_ping; + sem_t sem_pong; + + if (sem_init (&sem_ping, 0, 1) != 0) + { + perror ("sem_init sem_ping"); + return; + } + if (sem_init (&sem_pong, 0, 0) != 0) + { + perror ("sem_init sem_pong"); + sem_destroy (&sem_ping); + return; + } + + ppargs_t args; + args.sem_ping = &sem_ping; + args.sem_pong = &sem_pong; + args.shared = count; + + if (pthread_create (&pong_thread, NULL, pong, (void *)&args) != 0) + { + perror ("pthread_create"); + sem_destroy (&sem_ping); + sem_destroy (&sem_pong); + return; + } + + while (1) + { + sem_wait (&sem_ping); + + (*count)--; + printf ("PING!\n"); + + sem_post (&sem_pong); + + if (*count == 0) + break; + } + + // After PONG prints game over and exits, do the same for PING + pthread_join (pong_thread, NULL); + printf ("PING: Game over\n"); + + sem_destroy (&sem_ping); + sem_destroy (&sem_pong); +} diff --git a/lab8-synch/pingpong.h b/lab8-synch/pingpong.h new file mode 100644 index 0000000..6a86ff9 --- /dev/null +++ b/lab8-synch/pingpong.h @@ -0,0 +1,6 @@ +#ifndef __CS361_PINGPONG__ +#define __CS361_PINGPONG__ + +void ping (size_t *); + +#endif diff --git a/lab8-synch/tests/Makefile b/lab8-synch/tests/Makefile new file mode 100644 index 0000000..54a4bf8 --- /dev/null +++ b/lab8-synch/tests/Makefile @@ -0,0 +1,88 @@ +# +# Simple Test Makefile +# Mike Lam, James Madison University, August 2016 +# +# This version of the Makefile includes support for building a test suite. The +# recommended framework is Check (http://check.sourceforge.net/). To build and +# run the test suite, execute the "test" target. The test suite must be located +# in a module called "testsuite". The MODS, LIBS, and OBJS variables work as +# they do in the main Makefile. +# +# To change the default build target (which executes when you just type +# "make"), change the right-hand side of the definition of the "default" +# target. +# +# By default, this makefile will build the project with debugging symbols and +# without optimization. To change this, edit or remove the "-g" and "-O0" +# options in CFLAGS and LDFLAGS accordingly. +# +# By default, this makefile build the application using the GNU C compiler, +# adhering to the C99 standard with all warnings enabled. + + +# application-specific settings and run target + +EXE=../synch +TEST=testsuite +MODS=public.o +OBJS=../mutex.o ../pingpong.o +LIBS= + +UTESTOUT=utests.txt +ITESTOUT=itests.txt + +default: $(TEST) + +$(EXE): + make -C ../ + +test: utest itest + @echo "========================================" + +utest: $(EXE) $(TEST) + @echo "==============================================" + @echo " UNIT TESTS" + @echo "This should take about 5 seconds" + @echo "If it is much slower or faster, your calls to" + @echo "pthread_mutex_lock and pthread_mutex_unlock" + @echo "are probably in the wrong place." + @echo "" + @echo "You can see how it works by running:" + @echo " ./tests/testsuite" + @echo "----------------------------------------------" + @./$(TEST) 2>/dev/null >$(UTESTOUT) + @cat $(UTESTOUT) | sed -n -e '/Checks/,$$p' | sed -e 's/^private.*:[EF]://g' + +itest: $(EXE) + @echo "==============================================" + @echo " INTEGRATION TESTS" + @./integration.sh | tee $(ITESTOUT) + + +# compiler/linker settings + +CC=gcc +CFLAGS=-g -O0 -Wall --std=gnu99 -pedantic +LDFLAGS=-g -O0 + +CFLAGS= +LIBS+=-lcheck -lm -lpthread + +ifeq ($(shell uname -s),Linux) + LIBS+=-lrt -lsubunit +endif + + +# build targets + +$(TEST): $(TEST).o $(MODS) $(OBJS) + $(CC) $(LDFLAGS) -o $(TEST) $^ $(LIBS) + +%.o: %.c + $(CC) -c $(CFLAGS) $< + +clean: + rm -rf $(TEST) $(TEST).o $(MODS) $(UTESTOUT) $(ITESTOUT) outputs valgrind + +.PHONY: default clean test unittest inttest + diff --git a/lab8-synch/tests/expected/.INTEG_run_10.sha b/lab8-synch/tests/expected/.INTEG_run_10.sha new file mode 100644 index 0000000..e69de29 diff --git a/lab8-synch/tests/expected/.INTEG_run_5.sha b/lab8-synch/tests/expected/.INTEG_run_5.sha new file mode 100644 index 0000000..e69de29 diff --git a/lab8-synch/tests/expected/.INTEG_run_mutex.sha b/lab8-synch/tests/expected/.INTEG_run_mutex.sha new file mode 100644 index 0000000..46da628 --- /dev/null +++ b/lab8-synch/tests/expected/.INTEG_run_mutex.sha @@ -0,0 +1 @@ +2c9be6afe105589c6486941c344fa8af47c4f96b diff --git a/lab8-synch/tests/expected/.INTEG_run_pingpong.sha b/lab8-synch/tests/expected/.INTEG_run_pingpong.sha new file mode 100644 index 0000000..51094fc --- /dev/null +++ b/lab8-synch/tests/expected/.INTEG_run_pingpong.sha @@ -0,0 +1 @@ +258a022a43b06ef7cd1187fb3445760809d5af0c diff --git a/lab8-synch/tests/expected/.INTEG_run_pingpong_10.sha b/lab8-synch/tests/expected/.INTEG_run_pingpong_10.sha new file mode 100644 index 0000000..5ea2f64 --- /dev/null +++ b/lab8-synch/tests/expected/.INTEG_run_pingpong_10.sha @@ -0,0 +1 @@ +8f29a21768d1d35aead683f394e6435fcae1edaf diff --git a/lab8-synch/tests/expected/.INTEG_run_pingpong_5.sha b/lab8-synch/tests/expected/.INTEG_run_pingpong_5.sha new file mode 100644 index 0000000..926889d --- /dev/null +++ b/lab8-synch/tests/expected/.INTEG_run_pingpong_5.sha @@ -0,0 +1 @@ +016cd27526965203f7be5709148c6ba67a2a8cec diff --git a/lab8-synch/tests/expected/INTEG_run_mutex.txt b/lab8-synch/tests/expected/INTEG_run_mutex.txt new file mode 100644 index 0000000..bc3530d --- /dev/null +++ b/lab8-synch/tests/expected/INTEG_run_mutex.txt @@ -0,0 +1,2 @@ +Running mutual exclusion test +Value of shared: 0 diff --git a/lab8-synch/tests/expected/INTEG_run_pingpong.txt b/lab8-synch/tests/expected/INTEG_run_pingpong.txt new file mode 100644 index 0000000..b382e04 --- /dev/null +++ b/lab8-synch/tests/expected/INTEG_run_pingpong.txt @@ -0,0 +1,6 @@ +Running 1 iteration(s) of ping-pong +PING! +PONG! +PONG: Game over +PING: Game over +There should be 0 iterations left: 0 diff --git a/lab8-synch/tests/expected/INTEG_run_pingpong_10.txt b/lab8-synch/tests/expected/INTEG_run_pingpong_10.txt new file mode 100644 index 0000000..be28abb --- /dev/null +++ b/lab8-synch/tests/expected/INTEG_run_pingpong_10.txt @@ -0,0 +1,24 @@ +Running 10 iteration(s) of ping-pong +PING! +PONG! +PING! +PONG! +PING! +PONG! +PING! +PONG! +PING! +PONG! +PING! +PONG! +PING! +PONG! +PING! +PONG! +PING! +PONG! +PING! +PONG! +PONG: Game over +PING: Game over +There should be 0 iterations left: 0 diff --git a/lab8-synch/tests/expected/INTEG_run_pingpong_5.txt b/lab8-synch/tests/expected/INTEG_run_pingpong_5.txt new file mode 100644 index 0000000..a669556 --- /dev/null +++ b/lab8-synch/tests/expected/INTEG_run_pingpong_5.txt @@ -0,0 +1,14 @@ +Running 5 iteration(s) of ping-pong +PING! +PONG! +PING! +PONG! +PING! +PONG! +PING! +PONG! +PING! +PONG! +PONG: Game over +PING: Game over +There should be 0 iterations left: 0 diff --git a/lab8-synch/tests/expected/MIN_run_pingpong.txt b/lab8-synch/tests/expected/MIN_run_pingpong.txt new file mode 100644 index 0000000..0d5aeaf --- /dev/null +++ b/lab8-synch/tests/expected/MIN_run_pingpong.txt @@ -0,0 +1,24 @@ +Running a controlled timing exercise +PING! +PONG! +PING! +PONG! +PING! +PONG! +PING! +PONG! +PING! +PONG! +PING! +PONG! +PING! +PONG! +PING! +PONG! +PING! +PONG! +PING! +PONG! +PONG: Game over +PING: Game over +Value of result should be 0: 0 diff --git a/lab8-synch/tests/integration.sh b/lab8-synch/tests/integration.sh new file mode 100755 index 0000000..3138272 --- /dev/null +++ b/lab8-synch/tests/integration.sh @@ -0,0 +1,75 @@ +#!/bin/bash + +EXE="../synch" + +function run_test { + + # parameters + TAG=$1 + ARGS=$2 + + # file paths + OUTPUT=outputs/$TAG.txt + DIFF=outputs/$TAG.diff + EXPECT=expected/$TAG.txt + VALGRND=valgrind/$TAG.txt + + # print tag format + PTAG=$(printf '%-30s' "$TAG") + + # check for expected text that needs to be fixed + if [ ! -z "$(egrep "<<<<.*>>>>" "$EXPECT")" ] ; then + echo "$PTAG FAIL ($EXPECT not correct)" + return + fi + + # compare the file + SHAOUT=$(shasum "$EXPECT" | awk '{print $1}') + SHAEXP=$(cat "expected/.$TAG.sha") + if [ "$SHAOUT" != "$SHAEXP" ] ; then + echo "$PTAG FAIL (expected file is not correct)" + return + fi + + # run test and compare output to the expected version + $EXE $ARGS 2>/dev/null >"$OUTPUT" + diff -u "$OUTPUT" "$EXPECT" >"$DIFF" + if [ -s "$DIFF" ]; then + + # try alternative solution (if it exists) + EXPECT=expected/$TAG-2.txt + if [ -e "$EXPECT" ]; then + diff -u "$OUTPUT" "$EXPECT" >"$DIFF" + if [ -s "$DIFF" ]; then + echo "$PTAG FAIL (see $DIFF for details)" + else + echo "$PTAG pass" + fi + else + echo "$PTAG FAIL (see $DIFF for details)" + fi + else + echo "$PTAG pass" + fi + + # run valgrind + valgrind $EXE $ARGS &>$VALGRND +} + +# initialize output folders +mkdir -p outputs +mkdir -p valgrind +rm -f outputs/* valgrind/* + +# run individual tests +source itests.include + +# check for memory leaks +LEAK=`cat valgrind/*.txt | grep 'definitely lost' | grep -v ' 0 bytes in 0 blocks'` +if [ -z "$LEAK" ]; then + echo "No memory leak found." +else + echo "Memory leak(s) found. See files listed below for details." + grep 'definitely lost' valgrind/*.txt | grep -v ' 0 bytes in 0 blocks' | sed -e 's/:.*$//g' | sed -e 's/^/ - /g' +fi + diff --git a/lab8-synch/tests/itests.include b/lab8-synch/tests/itests.include new file mode 100644 index 0000000..e79a0e8 --- /dev/null +++ b/lab8-synch/tests/itests.include @@ -0,0 +1,9 @@ +# list of integration tests +# format: run_test +# used as the root for all filenames (i.e., "expected/$TAG.txt") +# command-line arguments to test + +run_test INTEG_run_mutex "-m" +run_test INTEG_run_pingpong "-p 1" +run_test INTEG_run_pingpong_5 "-p 5" +run_test INTEG_run_pingpong_10 "-p 10" diff --git a/lab8-synch/tests/public.c b/lab8-synch/tests/public.c new file mode 100644 index 0000000..ec523d9 --- /dev/null +++ b/lab8-synch/tests/public.c @@ -0,0 +1,98 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../mutex.h" + +double time_diff (struct timeval start, struct timeval end) +{ + double ending = end.tv_sec + (end.tv_usec * 0.000001); + double starting = start.tv_sec + (start.tv_usec * 0.000001); + return ending - starting; +} + +START_TEST (MIN_mutex_run_two_threads) +{ + printf ("======================================\n"); + printf ("PUBLIC\n"); + printf (" MIN mutex run two threads\n"); + printf (" Call runner in two threads and ensure result is 0\n"); + + pthread_mutex_t lock; + pthread_mutex_init (&lock, NULL); + int64_t shared = run (); + pthread_mutex_destroy (&lock); + ck_assert_int_eq (shared, 0); + printf ("\n"); +} +END_TEST + +START_TEST (MIN_mutex_run_two_threads_time) +{ + printf ("======================================\n"); + printf ("PUBLIC\n"); + printf (" MIN mutex runner two threads time\n"); + printf (" Call runner in two threads and measure time\n"); + printf (" Multithreaded should take more than 3 times and\n"); + printf (" less than 6 times the unithreaded time.\n"); + + int64_t num = 0; + int64_t *shared = # + + struct timeval start, end; + gettimeofday (&start, NULL); + for (int j = 0; j < 1000000; j++) + { + for (int i = 0; i < 100; i++) + { + *shared += 1; + *shared -= 1; + } + } + gettimeofday (&end, NULL); + double unidiff = time_diff (start, end); + + pthread_mutex_t lock; + pthread_mutex_init (&lock, NULL); + gettimeofday (&start, NULL); + num = run (); + gettimeofday (&end, NULL); + double multidiff = time_diff (start, end); + + printf ("\n Unithreaded time: %f\n Multithreaded time: %f\n\n", + unidiff, multidiff); + + ck_assert_int_eq (num, 0); + ck_assert (unidiff * 6 > multidiff); + ck_assert (unidiff * 2.3 < multidiff); + printf ("\n"); + +} +END_TEST + +/* + 1 - Create a queue of size 1 and put things in one at a time, pausing + for 50 ms in between. + 2 - Create a queue of size 8, fill it, then start the worker. + 3 - Create a queue of size 15, start the listener, then pull one item + at a time. + 4 - Just use fifo_queue for size 10, 20, and 30. +*/ + +void public_tests (Suite *s) +{ + TCase *tc_public = tcase_create ("Public"); + tcase_set_timeout (tc_public, 30.0); + tcase_add_test (tc_public, MIN_mutex_run_two_threads); + tcase_add_test (tc_public, MIN_mutex_run_two_threads_time); + suite_add_tcase (s, tc_public); +} diff --git a/lab8-synch/tests/testsuite.c b/lab8-synch/tests/testsuite.c new file mode 100644 index 0000000..79db20b --- /dev/null +++ b/lab8-synch/tests/testsuite.c @@ -0,0 +1,32 @@ +#include +#include +#include +#include +#include +#include + +#include + +extern void public_tests (Suite *s); + +Suite * test_suite (void) +{ + Suite *s = suite_create ("Default"); + public_tests (s); + return s; +} + +void run_testsuite () +{ + Suite *s = test_suite (); + SRunner *sr = srunner_create (s); + srunner_run_all (sr, CK_NORMAL); + srunner_free (sr); +} + +int main (void) +{ + srand((unsigned)time(NULL)); + run_testsuite (); + return EXIT_SUCCESS; +} diff --git a/p1-sh/.gitignore b/p1-sh/.gitignore new file mode 100644 index 0000000..1a509ab --- /dev/null +++ b/p1-sh/.gitignore @@ -0,0 +1,24 @@ +*.swp +*.swo + +# Ignore all compiled files regardless of location +build/*.o +tests/public.o +tests/testsuite.o + +# Ignore executables for this project +dukesh +testsuite +bin/* + +# Ignore test outputs +tests/ckstyle +tests/itests.txt +tests/outputs +tests/style.txt +tests/valgrind +tests/utests.txt + +# Ignore all vscode stuff and NFS files +**/.nfs* +**/.vscode diff --git a/p1-sh/Makefile b/p1-sh/Makefile new file mode 100644 index 0000000..4b86919 --- /dev/null +++ b/p1-sh/Makefile @@ -0,0 +1,61 @@ +# +# Simple Makefile +# Mike Lam, James Madison University, August 2016 +# +# This makefile builds a simple application that contains a main module +# (specified by the EXE variable) and a predefined list of additional modules +# (specified by the MODS variable). If there are any external library +# dependencies (e.g., the math library, "-lm"), list them in the LIBS variable. +# If there are any precompiled object files, list them in the OBJS variable. +# +# By default, this makefile will build the project with debugging symbols and +# without optimization. To change this, edit or remove the "-g" and "-O0" +# options in CFLAGS and LDFLAGS accordingly. +# +# By default, this makefile build the application using the GNU C compiler, +# adhering to the C99 standard with all warnings enabled. + + +# application-specific settings and run target + +EXE=dukesh +MODS=main.o process.o shell.o builtins.o cmd.o hash.o +OBJS= +LIBS= + +default: build $(EXE) + +build: + mkdir build + +test: build $(EXE) + make -C tests test + +style: $(EXE) + make -C tests style + +# compiler/linker settings + +CC=gcc +CFLAGS=-g -O0 -Wall -Werror -std=c99 -pedantic -D_POSIX_C_SOURCE=200809L +LDFLAGS=-g -O0 + + +# build targets + +BUILD=$(addprefix build/, $(MODS)) + +$(EXE): build/main.o $(BUILD) $(OBJS) + $(CC) $(LDFLAGS) -o $(EXE) $^ $(LIBS) + make -C utils + +build/%.o: src/%.c + $(CC) -c $(CFLAGS) -o $@ $< + +clean: + rm -rf $(EXE) build + make -C utils clean + make -C tests clean + +.PHONY: default clean + diff --git a/p1-sh/PHASE-1.txt b/p1-sh/PHASE-1.txt new file mode 100644 index 0000000..ccc5c1c --- /dev/null +++ b/p1-sh/PHASE-1.txt @@ -0,0 +1,15 @@ +Answer the following questions to describe your code submission. Please keep +all lines to a maximum of 80 characters wide. + +1 - From the main() function, how did you distinguish if the shell was using + a script or an interactive prompt? + + If the -b flag was passed we read in whatever file was passed, printing + an error message if the input file was invalid. Otherwise, use stdin. + + +2 - How does the quit command exit the shell? + + It calls exit(0) + + diff --git a/p1-sh/PHASE-2.txt b/p1-sh/PHASE-2.txt new file mode 100644 index 0000000..775b125 --- /dev/null +++ b/p1-sh/PHASE-2.txt @@ -0,0 +1,26 @@ +Answer the following questions to describe your code submission. Please keep +all lines to a maximum of 80 characters wide. + +1 - When implementing the built-ins, which ones essentially only required + making a call to an existing C function? What function(s)? + + The pwd, cd, and quit are essentially just calls to c functions. + +2 - Briefly describe how you sorted the files in the ls program. If you used + an existing C function to sort, explain the arguments you passed. + + We created a custom, leading dot ignoring, case-insensitive comparison + function that can be used by scandir to sort the files before they are + returned to us. The two arguments we two dirents, the names of which + were compared, and an integer returned to tell scandir which should be + sorted first. + +3 - If you used your lab 2 code to parse the command line, briefly describe + any changes or adaptations you made. If you didn't use lab 2, briefly + describe how you built the array of arguments to pass when executing the + program. + + Most of the code from lab 2 was fairly plug in play. The only adaptation + necessary was turning the parse_buffer and functions process.c to use the + fsm from lab2 instead of just strings. + \ No newline at end of file diff --git a/p1-sh/PHASE-3.txt b/p1-sh/PHASE-3.txt new file mode 100644 index 0000000..44557b2 --- /dev/null +++ b/p1-sh/PHASE-3.txt @@ -0,0 +1,27 @@ +Answer the following questions to describe your code submission. Please keep +all lines to a maximum of 80 characters wide. + +1 - Explain how you are keeping track of the return code for use in echo $?. + + After each process is executed in the shell's main loop, the "?" key is + set to the return code of that process. + +2 - Briefly describe how you are storing or deleting environment variales. + + Environment variables are stored directly in the hashmap. Export sets + the value to the corresponding key in the hashmap. Unset removes the + key from the hasmap. Echo finds the value in the hashmap based on the key. + +3 - Consider the following three command lines: + $ echo ${VAR} + $ echo {VAR} + $ echo ${VAR + Briefly explain how your code processes each. Does your code handle bad + input strings? + + In the first case, the correct environment variable is printed. + In the second case the literal string {VAR} is printed. + In the third case nothig is printed. + In the first two cases the exit code of echo is 0 because they are both + valid inputs to echo, but the third exits with a code 1 because the + syntax is invalid. \ No newline at end of file diff --git a/p1-sh/PHASE-4.txt b/p1-sh/PHASE-4.txt new file mode 100644 index 0000000..604baba --- /dev/null +++ b/p1-sh/PHASE-4.txt @@ -0,0 +1,73 @@ +Answer the following questions to describe your code submission. Please keep +all lines to a maximum of 80 characters wide. + +1 - In pseudo-code, show your general algorithm for setting up two processes + that are connected by a pipe. You need to show all calls to relevant + functions (e.g., pipe(), fork(), etc.) but you do not need to show error + checking or precise syntax. + + pipes[# of processes][2] + for process in processes: + pipe(pipes[process #]) + + for process in processes: + if not first process: + add_dup2(pipes[process #][0], stdin) + + if not last process: + add_dup2(pipes[process #][1], stdout) + + for pipe in pipes: + add_close(pipe[0]) + add_close(pipe[1]) + + if process is util: + posix_spawn(process) + else: + posix_spawnp(process) + + +2 - The project does not ask you to implement pipes with built-ins, but show + a pseudo-code approach to modifying echo for this purpose. For simplicity, + ignore the issues of escape characters and environment variables, and + assume that echo only writes into the pipe. Specifically, what would be + the flow of relevant functions (pipe(), fork(), etc.) for the following + command line: + + $ echo hello world | cut -f2 + + (HINT: Be careful that you do NOT close or redirect the shell's STDOUT, + which would prevent it from displaying any more prompts!) + + pipefd[2] + pipe(fd) + + echo(pipefd[1], "Message") // Modify echo to take in a file descriptor to write to + close(pipefd[1]) + + add_dup2(pipefd[0], stdin) + add_close(pipefd[0]); + + posix_spawnp("cut", argv) + + close(pipefd[0]) + + +3 - The project does not ask you to implement file redirection, but show a + general algorithm in pseudo-code (like above) for the following command + line (note that 2>&1 says to send STDERR to the same file as STDOUT): + + $ ls > data.txt 2>&1 + + fd = open("data.txt") + + add_dup2(fd, stdout) + add_dup2(fd, stderr) + + add_close(fd) + + posix_spawnp("ls", argv) + + close(fd) + + diff --git a/p1-sh/cut_data/data.csv b/p1-sh/cut_data/data.csv new file mode 100644 index 0000000..8c9b2a6 --- /dev/null +++ b/p1-sh/cut_data/data.csv @@ -0,0 +1,2 @@ +hello,world,me +later,gator diff --git a/p1-sh/cut_data/data.spaces b/p1-sh/cut_data/data.spaces new file mode 100644 index 0000000..80ea279 --- /dev/null +++ b/p1-sh/cut_data/data.spaces @@ -0,0 +1,2 @@ +hello world me +later gator diff --git a/p1-sh/data/.hidden.txt b/p1-sh/data/.hidden.txt new file mode 100755 index 0000000..75891bc --- /dev/null +++ b/p1-sh/data/.hidden.txt @@ -0,0 +1 @@ +oops diff --git a/p1-sh/data/FIRST.txt b/p1-sh/data/FIRST.txt new file mode 100644 index 0000000..4856fe2 --- /dev/null +++ b/p1-sh/data/FIRST.txt @@ -0,0 +1 @@ +First diff --git a/p1-sh/data/empty.txt b/p1-sh/data/empty.txt new file mode 100644 index 0000000..e69de29 diff --git a/p1-sh/data/pwd.txt b/p1-sh/data/pwd.txt new file mode 100644 index 0000000..c992476 --- /dev/null +++ b/p1-sh/data/pwd.txt @@ -0,0 +1,2 @@ +pwd +quit diff --git a/p1-sh/data/yat.txt b/p1-sh/data/yat.txt new file mode 100755 index 0000000..7fb00f9 --- /dev/null +++ b/p1-sh/data/yat.txt @@ -0,0 +1 @@ +yet another test file diff --git a/p1-sh/src/builtins.c b/p1-sh/src/builtins.c new file mode 100644 index 0000000..f1d8682 --- /dev/null +++ b/p1-sh/src/builtins.c @@ -0,0 +1,281 @@ +#include +#include +#include +#include +#include +#include + +#include "hash.h" + +// Given a message as input, print it to the screen followed by a +// newline ('\n'). If the message contains the two-byte escape sequence +// "\\n", print a newline '\n' instead. No other escape sequence is +// allowed. If the sequence contains a '$', it must be an environment +// variable or the return code variable ("$?"). Environment variable +// names must be wrapped in curly braces (e.g., ${PATH}). +// +// Returns 0 for success, 1 for errors (invalid escape sequence or no +// curly braces around environment variables). +int +echo (char **args) +{ + // Loop through arguments skipping echo + for (int i = 1; args[i] != NULL; i++) + { + char *msg = args[i]; + + // Loop over the arg string + for (int j = 0; msg[j] != '\0'; j++) + { + // Check for escape key + if (msg[j] == '\\') + { + + if (msg[j + 1] == 'n') + { + putchar ('\n'); + j++; + } + else + { + putchar ('\n'); + return 1; + } + } + else if (msg[j] == '$') + { + if (msg[j + 1] == '?') + { + printf ("%s", hash_find ("?")); + j++; + } + else if (msg[j + 1] == '{') + { + j += 2; + char varname[256]; + int z = 0; + + while (msg[j] != '\0' && msg[j] != '}') + { + if (z >= 255) + return 1; + varname[z++] = msg[j++]; + } + + if (msg[j] != '}') + { + putchar ('\n'); + return 1; + } + + varname[z] = '\0'; + + char *val = hash_find (varname); + if (val != NULL) + { + printf ("%s", val); + } + } + else + { + putchar ('\n'); + return 1; + } + } + else + { + putchar (msg[j]); + } + } + if (args[i + 1] != NULL) + putchar (' '); + } + putchar ('\n'); + return 0; +} + +// Given a key-value pair string (e.g., "alpha=beta"), insert the mapping +// into the global hash table (hash_insert ("alpha", "beta")). +// +// Returns 0 on success, 1 for an invalid pair string (kvpair is NULL or +// there is no '=' in the string). +// +// NOTE: For some strange reason, clang-format (used for checking the style) +// expects export to be initially formatted this way... +int export (char **args) +{ + if (args[1] == NULL) + { + return 1; + } + + char buffer[256]; + snprintf (buffer, sizeof (buffer), "%s", args[1]); + + char *name = strtok (buffer, "="); + char *set = strtok (NULL, "="); + + if (name == NULL || set == NULL) + { + return 1; + } + + hash_insert (name, set); + + return 0; +} + +// Prints the current working directory (see getcwd()). Returns 0. +int +pwd (void) +{ + char cwd[1024]; + + if (getcwd (cwd, sizeof (cwd)) != NULL) + { + printf ("%s\n", cwd); + } + else + { + return 1; + } + return 0; +} + +// Removes a key-value pair from the global hash table. +// Returns 0 on success, 1 if the key does not exist. +int +unset (char **args) +{ + if (args[1] == NULL) + { + return 1; + } + char *value = hash_find (args[1]); + + if (value == NULL) + { + return 1; + } + + hash_remove (args[1]); + return 0; +} + +// Given a string of commands, find their location(s) in the $PATH global +// variable. If the string begins with "-a", print all locations, not just +// the first one. +// +// Returns 0 if at least one location is found, 1 if no commands were +// passed or no locations found. +int +which (char **args) +{ + const char *builtins[] + = { "cd", "echo", "pwd", "which", "export", "unset", "quit", NULL }; + if (args[1] == NULL) + { + return 1; + } + + int print_all = 0; + int index = 1; + + if (strcmp (args[index], "-a") == 0) + { + print_all = 1; + index = 2; + + if (args[index] == NULL) + { + return 1; + } + } + + int found_any = 0; + int if_builtin = 0; + + for (int a = index; args[a] != NULL; a++) + { + char *cmd = args[a]; + + // Check if its a built-in + for (int i = 0; builtins[i] != NULL; i++) + { + if (strcmp (cmd, builtins[i]) == 0) + { + printf ("%s: dukesh built-in command\n", cmd); + found_any = 1; + if_builtin = 1; + } + } + if (if_builtin) + { + continue; + } + + // Check if its an executable + if (strncmp (cmd, "./", 2) == 0) + { + printf ("%s\n", cmd); + found_any = 1; + continue; + } + + // Search through the path + char *path_env = getenv ("PATH"); + + char *path_copy = strdup (path_env); + + char *dir = strtok (path_copy, ":"); + + while (dir != NULL) + { + char fullpath[1024]; + snprintf (fullpath, sizeof (fullpath), "%s/%s", dir, cmd); + + if (access (fullpath, X_OK) == 0) + { + printf ("%s\n", fullpath); + found_any = 1; + if (!print_all) + break; + } + dir = strtok (NULL, ":"); + } + + free (path_copy); + } + + if (found_any) + { + return 0; + } + else + { + return 1; + } +} + +int +cd (char **args) +{ + if (args[1] == NULL) + { + return 1; + } + if (chdir (args[1]) != 0) + { + return 1; + } + return 0; +} + +// Immediately exits the shell +int +quit (void) +{ + printf ("\n"); + exit (0); + return 0; +} diff --git a/p1-sh/src/builtins.h b/p1-sh/src/builtins.h new file mode 100644 index 0000000..feae4f7 --- /dev/null +++ b/p1-sh/src/builtins.h @@ -0,0 +1,14 @@ +#ifndef __cs361_builtins__ +#define __cs361_builtins__ + +#include + +int echo (char **); +int export (char **); +int pwd (void); +int unset (char **); +int which (char **); +int cd (char **); +int quit (void); + +#endif diff --git a/p1-sh/src/cmd.c b/p1-sh/src/cmd.c new file mode 100644 index 0000000..4f8753b --- /dev/null +++ b/p1-sh/src/cmd.c @@ -0,0 +1,202 @@ +#include "cmd.h" +#include +#include +#include +#include +#include +#include + +// Integrate the FSM command-line parser from lab 2 here. Note that the FSM +// effects will be vastly different from that of lab 2. Instead of implementing +// the effects here, this file should focus on the model and the parsing. You +// should modify the model's effects table to call functions in process.c or +// builtins.c as appropriate. (Or you can have a function here to call those +// functions indirectly.) + +static state_t const _transitions[NUM_STATES][NUM_EVENTS] + = { { Command, Term, NST }, + { Arguments, Make_Pipe, Term }, + { Arguments, Make_Pipe, Term }, + { Command, Term, Term }, + { NST, NST, NST } }; + +static action_t const _effects[NUM_STATES][NUM_EVENTS] + = { { start_command, error_pipe, NULL }, + { append, NULL, NULL }, + { append, NULL, NULL }, + { start_command, error_pipe, error_newline }, + { NULL, NULL, NULL } }; + +/* Helper function for providing a printable string name for an event */ +const char * +event_name (event_t evt) +{ + assert (evt <= NIL); + + // Event names for printing out + const char *names[] = { "TOKEN", "PIPE", "NEWLINE", "NIL" }; + return names[evt]; +} + +/* Helper function for providing a printable string name for an state */ +const char * +state_name (state_t st) +{ + assert (st <= NST); + + // State names for printing out + const char *names[] + = { "Init", "Command", "Arguments", "Make_Pipe", "Term", "NST" }; + return names[st]; +} + +/* Executed when starting to process a new command line. The fsm_t + should have been updated to include a pointer to the current token. + For instance, if the command line was "ls -l data NL", the fsm_t + has a field that points to "ls". */ +void +start_command (fsm_t *cmdmodel) +{ + // printf ("Starting new command: %s\n", cmdmodel->current_token); + // TODO: Copy the current token to store it in the FSM's command + // field. Next, create the FSM's args array (length MAX_ARGUMENTS) + // set the current token as args[0], and initialize nargs to be + // the number of arguments (1 at this point). + // Allocate args array (NULL-initialized) if not already + cmdmodel->args = calloc (MAX_ARGUMENTS, sizeof (char *)); + cmdmodel->command = cmdmodel->current_token; + cmdmodel->args[0] = cmdmodel->current_token; + cmdmodel->nargs = 1; +} + +/* Executed when processing a token after the command name. For instance, + if the command line was "ls -l data NL", this function will be called + when the current token is "-l" and again when it is "data". */ +void +append (fsm_t *cmdmodel) +{ + if (cmdmodel->nargs >= MAX_ARGUMENTS) + return; + + // printf ("Appending %s to the argument list\n", cmdmodel->current_token); + assert (cmdmodel->args != NULL); + + // TODO: Store the current token into the args array and increment nargs + cmdmodel->args[cmdmodel->nargs++] = cmdmodel->current_token; +} + +void +error_pipe (fsm_t *cmdmodel) +{ + printf ("ERROR: Received token %s while in state %s\n", + cmdmodel->current_token, state_name (cmdmodel->state)); +} + +void +error_newline (fsm_t *cmdmodel) +{ + printf ("ERROR: Received token %s while in state %s\n", + cmdmodel->current_token, state_name (cmdmodel->state)); +} + +state_t +transition (struct fsm *fsm, event_t event, action_t *effect) +{ + assert (fsm->state < NST); + assert (event < NIL); + + *effect = _effects[fsm->state][event]; + return _transitions[fsm->state][event]; +} + +/* Generic front-end for handling events. Should do nothing more + than calling the FSM's transition function, performing an effect + (if appropriate) and updating the state. Return false if the new + state is the terminal state. */ +bool +handle_event (fsm_t *fsm, event_t event) +{ + assert (fsm != NULL); + // Look up the current state/event combination in the + // transition table. Print the following line for debugging + // purposes just for this lab. This should be printed even + // if there is no transition. + action_t effect; + state_t new_state = fsm->transition (fsm, event, &effect); + // printf("[%s.%s -> %s]\n", state_name(fsm->state), event_name(event), + // state_name(new_state)); + + // If the state/event combination is valid, execute + // the transition and effect function (if there is one). + // If the next state is Term (terminated), return false. + // Otherwise return true. + if (new_state != NST) + { + if (effect != NULL) + { + effect (fsm); + } + + fsm->state = new_state; + } + + return fsm->state != Term; +} + +/* Given a string, return the event type. Do not modify this function. */ +event_t +lookup (char *token) +{ + if (!strcmp (token, "|")) + return PIPE; + + if (!strcmp (token, "NL")) + return NEWLINE; + + return TOKEN; +} + +char *** +split_commands (char **tokens) +{ + size_t ncmds = 0; + char ***cmds = calloc (MAX_ARGUMENTS, sizeof (*cmds)); + + size_t start = 0; + size_t i; + for (i = 0; tokens[i]; i++) + { + if (strcmp (tokens[i], "|") == 0) + { + tokens[i] = NULL; + cmds[ncmds++] = &tokens[start]; + start = i + 1; + } + } + + cmds[ncmds++] = &tokens[start]; + cmds[ncmds] = NULL; + return cmds; +} + +// You should provide some function like this to serve as the interface to +// parsing the command line. This function is just a placeholder and you +// should define your own. +char *** +parse_buffer (char *buffer) +{ + buffer[strcspn (buffer, "\n")] + = '\0'; // Replace newline with null terminator + + char **argv = calloc (MAX_ARGUMENTS, sizeof (*argv)); + + size_t i = 0; + char *token = strtok (buffer, " "); + while (token != NULL) + { + argv[i++] = token; + token = strtok (NULL, " "); + } + + return split_commands (argv); +} diff --git a/p1-sh/src/cmd.h b/p1-sh/src/cmd.h new file mode 100644 index 0000000..98e7209 --- /dev/null +++ b/p1-sh/src/cmd.h @@ -0,0 +1,74 @@ +#include + +#ifndef __cs361_cmd_h__ +#define __cs361_cmd_h__ + +// Generic definitions for any type of statemodel + +// States and events should just be integers +typedef int state_t; +typedef int event_t; + +// Needed for circular typedef. This lets action_t use fsm_t in its parameter +// list, while the struct fsm can use action_t as a field. +typedef struct fsm fsm_t; + +// All entry, exit, and effect instances use the action type +typedef void (*action_t) (fsm_t *); + +// Each FSM instance contains a current state +struct fsm +{ + state_t state; // current state + + // pointer to the FSM's transition function + state_t (*transition) (struct fsm *, event_t, action_t *); + + // Additional data fields specific to this FSM + char *command; // the name of the command to run + size_t nargs; // the number of command-line arguments + char **args; // the command-line arguments + char *current_token; // current token being processed +}; + +#define MAX_ARGUMENTS 32 + +// Events +typedef enum +{ + TOKEN, // normal command-line token + PIPE, // vertical bar character + NEWLINE, // newline at the end of the command + NIL // invalid non-event +} cmdevt_t; +#define NUM_EVENTS NIL + +// States +typedef enum +{ + Init, // initial state + Command, // establishing the command name + Arguments, // building the argument list + Make_Pipe, // linking the commands together for a pipe + Term, // terminal state (execute program or error) + NST // invalid non-state +} cmdst_t; +#define NUM_STATES NST + +state_t transition (struct fsm *fsm, event_t event, action_t *effect); +event_t lookup (char *); // convert an event string to its numeric value + +// Translate event/state numbers to their string equivalent +const char *event_name (event_t); +const char *state_name (state_t); + +void start_command (fsm_t *); +void append (fsm_t *); +void execute (fsm_t *); +void link_commands (fsm_t *); +void error_pipe (fsm_t *); +void error_newline (fsm_t *); + +char ***parse_buffer (char *buffer); + +#endif diff --git a/p1-sh/src/hash.c b/p1-sh/src/hash.c new file mode 100644 index 0000000..c573c0c --- /dev/null +++ b/p1-sh/src/hash.c @@ -0,0 +1,284 @@ +#include +#include +#include +#include +#include +#include + +#include "hash.h" + +static ssize_t find_index (char *); +static unsigned long hash (unsigned char *); +static bool insert_help (char *, char *, bool); +static void rehash (size_t); + +#define MINSIZE 100 +#define PRIME 97 + +typedef struct kvpair +{ + char *key; + char *value; + bool alive; +} kvpair_t; + +static kvpair_t *table = NULL; +static size_t capacity = 0; +static size_t entries = 0; + +void +hash_destroy (void) +{ + if (table == NULL) + return; + + for (size_t i = 0; i < capacity; i++) + { + if (table[i].key != NULL) + { + free (table[i].key); + table[i].key = NULL; + } + if (table[i].value != NULL) + { + free (table[i].value); + table[i].value = NULL; + } + } + free (table); +} + +/* Dumps the table contents to STDOUT (useful for debugging) */ +void +hash_dump (void) +{ + printf ("TABLE:\n"); + for (size_t i = 0; i < capacity; i++) + if (table[i].key != NULL) + printf (" [%zd].%s = %s%s\n", i, table[i].key, table[i].value, + (!table[i].alive ? " [deleted]" : "")); +} + +/* Initializes the hash table to a given size (minimum 100) */ +void +hash_init (size_t size) +{ + if (table != NULL) + free (table); + + // Minimum of 100 entries to start + if (size < MINSIZE) + size = MINSIZE; + + table = calloc (size, sizeof (kvpair_t)); + capacity = size; + entries = 0; +} + +/* Find the value for a given key. Returns NULL if there is no entry for + the given key. */ +char * +hash_find (char *key) +{ + if (table == NULL) // uninitialized table + return NULL; + + ssize_t index = find_index (key); + assert (index >= 0); + + if (table[index].key == NULL) // key not found + return NULL; + + // If .key is not NULL, find_index found a key match + assert (!strcmp (table[index].key, key)); + if (table[index].alive) // not marked for deletion + return table[index].value; + + return NULL; +} + +/* Inserts a new entry into the hash table. If there is already an entry + for the given key, replace the value (freeing the old one). */ +bool +hash_insert (char *key, char *value) +{ + if (table == NULL) // uninitialized table + return false; + + return insert_help (key, value, true); +} + +/* Gets a list of pointers to the keys in the hash table. */ +char ** +hash_keys (void) +{ + if (table == NULL) // uninitialized table + return NULL; + + char **keys = calloc (entries + 1, sizeof (char *)); + size_t next = 0; + for (size_t i = 0; i < capacity; i++) + if (table[i].key != NULL && table[i].alive) + keys[next++] = table[i].key; + return keys; +} + +/* Removes a key-value pair from the hash table. Marks the entry as + deleted. Can be overwritten later. */ +bool +hash_remove (char *key) +{ + if (table == NULL) // uninitialized table + return false; + + ssize_t index = find_index (key); + assert (index >= 0); + + if (table[index].key == NULL) // key not found + return true; + + // If .key is not NULL, find_index found a key match + assert (!strcmp (table[index].key, key)); + table[index].alive = false; + entries--; + + if ((entries < capacity / 4) && (capacity / 2 >= MINSIZE)) + rehash (capacity / 2); + + return true; +} + +/* ********************************************************************** + * Helper functions only below this point * + * ********************************************************************** */ + +static bool +insert_help (char *key, char *value, bool dup) +{ + ssize_t index = find_index (key); + assert (index >= 0); // failed to find an open slot; should never happen + + if (table[index].key == NULL) + { + // New entry for this key. Check if rehashing is needed. + if (entries + 1 > capacity / 2) + { + rehash (capacity * 2); + index = find_index (key); + } + + // Set the entries in the table (duplicating if requested) + if (dup) + { + table[index].key = strdup (key); + table[index].value = strdup (value); + } + else + { + table[index].key = key; + table[index].value = value; + } + entries++; + table[index].alive = true; + + return true; + } + + // If table[index].key is not NULL, it must match the existing key OR + // the existing entry has been deleted. Otherwise, the double hashing + // should have found a different location. If the key does not match + // the current key, replace it. + if (strcmp (table[index].key, key)) + { + assert (!table[index].alive); + free (table[index].key); + if (dup) + table[index].key = strdup (key); + else + table[index].key = key; + } + + // Free the old value and replace it + free (table[index].value); + if (dup) + table[index].value = strdup (value); + else + table[index].value = value; + table[index].alive = true; + + return true; +} + +static ssize_t +find_index (char *str) +{ + if (table == NULL) + return -1; + + unsigned long keyhash = hash ((unsigned char *)str); + // probe is 1 .. PRIME, guaranteed non-zero + unsigned long probe = PRIME - (keyhash % PRIME); + + // Use double hashing to resolve collisions + size_t index = (size_t)(keyhash % capacity); + ssize_t first_open = -1; + for (size_t i = 0; i < capacity; i++) + { + size_t trial = (index + (size_t)(i * probe)) % capacity; + // If the key is NULL, we have not encountered the string. If there + // was an earlier open spot, use that one. Otherwise, use the spot + // with the empty key. + if (table[trial].key == NULL) + { + if (first_open < 0) + return (ssize_t)trial; + else + return first_open; + } + + // Keep track of the first open index. This will be returned if + // the key has not been previously entered and deleted. + if (first_open < 0 && !table[trial].alive) + first_open = trial; + + // If the key has been previously used, re-use this position + if (!strcmp (table[trial].key, str)) + return (ssize_t)trial; + } + + // Due to rehashing, there should always be at least 50% of the table + // entries free. So there should always be a free space. + abort (); +} + +static unsigned long +hash (unsigned char *string) +{ + if (string == NULL) + return 0; + + unsigned long hash = 5381; + + // Derived from djb2 by Dan Bernstein + // hash = hash * 33 + ch + for (unsigned char *ptr = string; *ptr != '\0'; ptr++) + hash = ((hash << 5) + hash) + *ptr; + + return hash; +} + +static void +rehash (size_t newcap) +{ + size_t oldcap = capacity; + capacity = newcap; + kvpair_t *oldtable = table; + table = calloc (capacity, sizeof (kvpair_t)); + entries = 0; + + for (int i = 0; i < oldcap; i++) + { + if (oldtable[i].key != NULL && oldtable[i].alive) + insert_help (oldtable[i].key, oldtable[i].value, false); + } +} diff --git a/p1-sh/src/hash.h b/p1-sh/src/hash.h new file mode 100644 index 0000000..22a1b9c --- /dev/null +++ b/p1-sh/src/hash.h @@ -0,0 +1,15 @@ +#ifndef __cs361_hash__ +#define __cs361_hash__ + +#include + +void hash_dump (void); // for debugging if needed + +void hash_destroy (void); +void hash_init (size_t); +char *hash_find (char *); +bool hash_insert (char *, char *); +char **hash_keys (void); +bool hash_remove (char *); + +#endif diff --git a/p1-sh/src/main.c b/p1-sh/src/main.c new file mode 100644 index 0000000..0ae0063 --- /dev/null +++ b/p1-sh/src/main.c @@ -0,0 +1,65 @@ +#include +#include +#include +#include +#include + +#include "hash.h" +#include "shell.h" + +static bool get_args (int, char **, FILE **); +static void usage (void); + +int +main (int argc, char *argv[]) +{ + FILE *input = stdin; + + if (!get_args (argc, argv, &input)) + { + usage (); + } + + if (input == NULL) + { + printf ("Invalid input file\n"); + return EXIT_FAILURE; + } + + shell (input); + + return EXIT_SUCCESS; +} + +/* Parse the command-line arguments. Sets the client/server variables to + point to a file name (typically in the data/ directory). Can also set + the bot variable if a second file is used to interact with the + client/server. If -d was passed, turn on debugging mode to print + information about state transitions. */ +static bool +get_args (int argc, char **argv, FILE **script) +{ + int ch = 0; + while ((ch = getopt (argc, argv, "b:h")) != -1) + { + switch (ch) + { + case 'b': + *script = fopen (optarg, "r"); + break; + default: + return false; + } + } + return true; +} + +static void +usage (void) +{ + printf ("dukesh, a simple command shell\n"); + printf ("usage: dukesh [-b FILE]\n"); + printf (" -b FILE use FILE as a shell script to execute\n"); + printf ("If no script is passed, then the shell should be interactive,\n"); + printf ("processing one command at a time from STDIN.\n"); +} diff --git a/p1-sh/src/process.c b/p1-sh/src/process.c new file mode 100644 index 0000000..04f59e5 --- /dev/null +++ b/p1-sh/src/process.c @@ -0,0 +1,152 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "builtins.h" +#include "cmd.h" +#include "hash.h" + +// The contents of this file are up to you, but they should be related to +// running separate processes. It is recommended that you have functions +// for: +// - performing a $PATH lookup +// - determining if a command is a built-in or executable +// - running a single command in a second process +// - running a pair of commands that are connected with a pipe + +// You should provide some function like this to serve as the interface to +// parsing the command line. This function is just a placeholder and you +// should define your own. +bool +is_util (char *str) +{ + char *utils[] = { "./bin/cat", "./bin/chmod", "./bin/cut", "./bin/env", + "./bin/head", "./bin/ls", "./bin/repeat" }; + + for (size_t i = 0; i < 7; i++) + { + if (strcmp (str, utils[i]) == 0) + { + return true; + } + } + + return false; +} + +char ** +hash_to_envp () +{ + char **keys = hash_keys (); + + size_t key_count = 0; + while (keys[key_count] != NULL) + key_count++; + + char **envp = calloc (key_count + 1, sizeof (char *)); + + for (size_t i = 0; i < key_count; i++) + { + char *key = keys[i]; // get the key + char *value + = hash_find (key); // entry should be found since this is a key + + size_t len = strlen (key) + strlen (value) + 2; + envp[i] = calloc (1, len); + snprintf (envp[i], len, "%s=%s", key, value); + } + + return envp; +} + +int +run_process (char ***cmds) +{ + if (strcmp (cmds[0][0], "quit") == 0) + return quit (); + if (strcmp (cmds[0][0], "echo") == 0) + return echo (cmds[0]); + if (strcmp (cmds[0][0], "cd") == 0) + return cd (cmds[0]); + if (strcmp (cmds[0][0], "pwd") == 0) + return pwd (); + if (strcmp (cmds[0][0], "which") == 0) + return which (cmds[0]); + if (strcmp (cmds[0][0], "export") == 0) + return export (cmds[0]); + if (strcmp (cmds[0][0], "unset") == 0) + return unset (cmds[0]); + + int ncmds = 0; + while (cmds[ncmds]) + ncmds++; + + int pipes[ncmds - 1][2]; + for (int i = 0; i < ncmds - 1; i++) + { + if (pipe (pipes[i]) < 0) + { + perror ("pipe"); + return -1; + } + } + + pid_t pids[ncmds]; + for (int i = 0; i < ncmds; i++) + { + posix_spawn_file_actions_t actions; + posix_spawn_file_actions_init (&actions); + + if (i > 0) + { + posix_spawn_file_actions_adddup2 (&actions, pipes[i - 1][0], + STDIN_FILENO); + } + if (i < ncmds - 1) + { + posix_spawn_file_actions_adddup2 (&actions, pipes[i][1], + STDOUT_FILENO); + } + + for (int j = 0; j < ncmds - 1; j++) + { + posix_spawn_file_actions_addclose (&actions, pipes[j][0]); + posix_spawn_file_actions_addclose (&actions, pipes[j][1]); + } + + if (is_util (cmds[i][0])) + { + posix_spawn (&pids[i], cmds[i][0], &actions, NULL, cmds[i], + hash_to_envp ()); + } + else + { + posix_spawnp (&pids[i], cmds[i][0], &actions, NULL, cmds[i], + hash_to_envp ()); + } + + posix_spawn_file_actions_destroy (&actions); + } + + // parent closes all pipe fds + for (int i = 0; i < ncmds - 1; i++) + { + close (pipes[i][0]); + close (pipes[i][1]); + } + + // wait for all children + int status; + for (int i = 0; i < ncmds; i++) + { + waitpid (pids[i], &status, 0); + } + + free (cmds); + + return WEXITSTATUS (status); +} diff --git a/p1-sh/src/process.h b/p1-sh/src/process.h new file mode 100644 index 0000000..43e21b6 --- /dev/null +++ b/p1-sh/src/process.h @@ -0,0 +1,25 @@ +#include "cmd.h" +#include + +#ifndef __cs361_process__ +#define __cs361_process__ + +// The contents of this file are up to you, but they should be related to +// running separate processes. It is recommended that you have functions +// for: +// - performing a $PATH lookup +// - determining if a command is a built-in or executable +// - running a single command in a second process +// - running a pair of commands that are connected with a pipe + +// You should provide some function like this to serve as the interface to +// parsing the command line. This function is just a placeholder and you +// should define your own. + +bool is_util (char *str); + +char **hash_to_envp (); + +int run_process (char ***cmd); + +#endif diff --git a/p1-sh/src/shell.c b/p1-sh/src/shell.c new file mode 100644 index 0000000..3402cec --- /dev/null +++ b/p1-sh/src/shell.c @@ -0,0 +1,43 @@ +#include +#include +#include + +#include "builtins.h" +#include "cmd.h" +#include "hash.h" +#include "process.h" + +// No command line can be more than 100 characters +#define MAXLENGTH 100 + +void +shell (FILE *input) +{ + hash_init (100); + hash_insert ("?", "0"); + hash_insert ("PATH", getenv ("PATH")); + char buffer[MAXLENGTH]; + while (1) + { + // Print the cursor and get the next command entered + printf ("$ "); + memset (buffer, 0, sizeof (buffer)); + if (fgets (buffer, MAXLENGTH, input) == NULL) + break; + + if (input != stdin) + printf ("%s", buffer); + + // Keep this here to avoid weird line interleavings + fflush (stdout); + + char ***cmds = parse_buffer (buffer); + int rc = run_process (cmds); + char buffer[20]; + sprintf (buffer, "%d", rc); + char *str = buffer; + hash_insert ("?", str); + } + printf ("\n"); + hash_destroy (); +} diff --git a/p1-sh/src/shell.h b/p1-sh/src/shell.h new file mode 100644 index 0000000..15aae1f --- /dev/null +++ b/p1-sh/src/shell.h @@ -0,0 +1,6 @@ +#ifndef __cs361_shell__ +#define __cs361_shell__ + +void shell (FILE *); + +#endif diff --git a/p1-sh/tests/Makefile b/p1-sh/tests/Makefile new file mode 100644 index 0000000..a6d8d32 --- /dev/null +++ b/p1-sh/tests/Makefile @@ -0,0 +1,89 @@ +# +# Simple Test Makefile +# Mike Lam, James Madison University, August 2016 +# +# This version of the Makefile includes support for building a test suite. The +# recommended framework is Check (http://check.sourceforge.net/). To build and +# run the test suite, execute the "test" target. The test suite must be located +# in a module called "testsuite". The MODS, LIBS, and OBJS variables work as +# they do in the main Makefile. +# +# To change the default build target (which executes when you just type +# "make"), change the right-hand side of the definition of the "default" +# target. +# +# By default, this makefile will build the project with debugging symbols and +# without optimization. To change this, edit or remove the "-g" and "-O0" +# options in CFLAGS and LDFLAGS accordingly. +# +# By default, this makefile build the application using the GNU C compiler, +# adhering to the C99 standard with all warnings enabled. + + +# application-specific settings and run target + +EXE=../dukesh +TEST=testsuite +MODS=public.o +OBJS=../build/process.o ../build/shell.o ../build/builtins.o ../build/cmd.o ../build/hash.o +LIBS= + +UTESTOUT=utests.txt +ITESTOUT=itests.txt +SCHECKOUT=style.txt + +default: $(TEST) + +$(EXE): + make -C ../ + +test: utest itest style + @echo "========================================" + +utest: $(EXE) $(TEST) + @echo "========================================" + @echo " UNIT TESTS" + @./$(TEST) 2>/dev/null >$(UTESTOUT) + @cat $(UTESTOUT) | sed -n -e '/Checks/,$$p' | sed -e 's/^private.*:[EF]://g' + +itest: $(EXE) + @echo "========================================" + @echo " INTEGRATION TESTS" + @./integration.sh | tee $(ITESTOUT) + +style: $(EXE) + @echo "========================================" + @echo " CODING STYLE CHECK" + @./style.sh 2>/dev/null >$(SCHECKOUT) + @cat $(SCHECKOUT) + +# compiler/linker settings + +CC=gcc +CFLAGS=-g -O0 -Wall --std=c99 -pedantic -D_POSIX_C_SOURCE=200809L +LDFLAGS=-g -O0 + +#CFLAGS+=-I/opt/local/include -Wno-gnu-zero-variadic-macro-arguments +CFLAGS+=-Wno-gnu-zero-variadic-macro-arguments +#LDFLAGS+=-L/opt/local/lib +LDFLAGS= +LIBS+=-lcheck -lm -lpthread + +ifeq ($(shell uname -s),Linux) + LIBS+=-lrt -lsubunit +endif + + +# build targets + +$(TEST): $(TEST).o $(MODS) $(OBJS) + $(CC) $(LDFLAGS) -o $(TEST) $^ $(LIBS) + +%.o: %.c + $(CC) -c $(CFLAGS) $< + +clean: + rm -rf $(TEST) $(TEST).o $(MODS) $(UTESTOUT) $(ITESTOUT) $(SCHECKOUT) outputs valgrind ckstyle + +.PHONY: default clean test unittest inttest + diff --git a/p1-sh/tests/bin/cat b/p1-sh/tests/bin/cat new file mode 100755 index 0000000..d33a645 Binary files /dev/null and b/p1-sh/tests/bin/cat differ diff --git a/p1-sh/tests/bin/chmod b/p1-sh/tests/bin/chmod new file mode 100755 index 0000000..6d53336 Binary files /dev/null and b/p1-sh/tests/bin/chmod differ diff --git a/p1-sh/tests/bin/cut b/p1-sh/tests/bin/cut new file mode 100755 index 0000000..f5c941f Binary files /dev/null and b/p1-sh/tests/bin/cut differ diff --git a/p1-sh/tests/bin/env b/p1-sh/tests/bin/env new file mode 100755 index 0000000..abcc417 Binary files /dev/null and b/p1-sh/tests/bin/env differ diff --git a/p1-sh/tests/bin/head b/p1-sh/tests/bin/head new file mode 100755 index 0000000..c2e8803 Binary files /dev/null and b/p1-sh/tests/bin/head differ diff --git a/p1-sh/tests/bin/ls b/p1-sh/tests/bin/ls new file mode 100755 index 0000000..d95e5be Binary files /dev/null and b/p1-sh/tests/bin/ls differ diff --git a/p1-sh/tests/bin/repeat b/p1-sh/tests/bin/repeat new file mode 100755 index 0000000..360cf4c Binary files /dev/null and b/p1-sh/tests/bin/repeat differ diff --git a/p1-sh/tests/cut_data/data.csv b/p1-sh/tests/cut_data/data.csv new file mode 100644 index 0000000..8c9b2a6 --- /dev/null +++ b/p1-sh/tests/cut_data/data.csv @@ -0,0 +1,2 @@ +hello,world,me +later,gator diff --git a/p1-sh/tests/cut_data/data.spaces b/p1-sh/tests/cut_data/data.spaces new file mode 100644 index 0000000..80ea279 --- /dev/null +++ b/p1-sh/tests/cut_data/data.spaces @@ -0,0 +1,2 @@ +hello world me +later gator diff --git a/p1-sh/tests/data/.hidden.txt b/p1-sh/tests/data/.hidden.txt new file mode 100755 index 0000000..75891bc --- /dev/null +++ b/p1-sh/tests/data/.hidden.txt @@ -0,0 +1 @@ +oops diff --git a/p1-sh/tests/data/FIRST.txt b/p1-sh/tests/data/FIRST.txt new file mode 100644 index 0000000..4856fe2 --- /dev/null +++ b/p1-sh/tests/data/FIRST.txt @@ -0,0 +1 @@ +First diff --git a/p1-sh/tests/data/empty.txt b/p1-sh/tests/data/empty.txt new file mode 100644 index 0000000..e69de29 diff --git a/p1-sh/tests/data/pwd.txt b/p1-sh/tests/data/pwd.txt new file mode 100644 index 0000000..c992476 --- /dev/null +++ b/p1-sh/tests/data/pwd.txt @@ -0,0 +1,2 @@ +pwd +quit diff --git a/p1-sh/tests/data/yat.txt b/p1-sh/tests/data/yat.txt new file mode 100755 index 0000000..7fb00f9 --- /dev/null +++ b/p1-sh/tests/data/yat.txt @@ -0,0 +1 @@ +yet another test file diff --git a/p1-sh/tests/expected/A_cat_tail.txt b/p1-sh/tests/expected/A_cat_tail.txt new file mode 100644 index 0000000..01569d9 --- /dev/null +++ b/p1-sh/tests/expected/A_cat_tail.txt @@ -0,0 +1,10 @@ +$ ./bin/cat cut_data/data.csv +hello,world,me +later,gator +$ ./bin/cat cut_data/data.csv | ./bin/cut -d , -f 2 +world +gator +$ ./bin/cat cut_data/data.csv | tail -n 1 +later,gator +$ quit + diff --git a/p1-sh/tests/expected/A_env.txt b/p1-sh/tests/expected/A_env.txt new file mode 100644 index 0000000..3dd5441 --- /dev/null +++ b/p1-sh/tests/expected/A_env.txt @@ -0,0 +1,17 @@ +$ export A=5 +$ ./bin/env ./bin/repeat 1 A +A=5 +$ ./bin/env B=6 C=7 ./bin/repeat 1 A 2 B 3 C +A=5 +B=6 +B=6 +C=7 +C=7 +C=7 +$ ./bin/env C=7 ./bin/repeat 1 A 2 B 3 C | head -n 4 +A=5 +B= +B= +C=7 +$ quit + diff --git a/p1-sh/tests/expected/A_pipe.txt b/p1-sh/tests/expected/A_pipe.txt new file mode 100644 index 0000000..be4e7d2 --- /dev/null +++ b/p1-sh/tests/expected/A_pipe.txt @@ -0,0 +1,17 @@ +$ ./bin/ls -a data | ./bin/head -n 1 +empty.txt +$ ./bin/ls -a data | ./bin/head -n 2000 +empty.txt +FIRST.txt +.hidden.txt +pwd.txt +subdir +yat.txt +$ ./bin/head -n 1 cut_data/data.spaces | ./bin/cut -f 2 +world +$ ./bin/head -n 1 cut_data/data.csv | ./bin/cut -d , -f 1 +hello +$ ./bin/head -n 1 cut_data/data.csv | ./bin/cut -d , -f 3 +me +$ quit + diff --git a/p1-sh/tests/expected/A_which.txt b/p1-sh/tests/expected/A_which.txt new file mode 100644 index 0000000..2417237 --- /dev/null +++ b/p1-sh/tests/expected/A_which.txt @@ -0,0 +1,6 @@ +$ which ./bin/rm +./bin/rm +$ which ./bin/cat +./bin/cat +$ quit + diff --git a/p1-sh/tests/expected/B_env.txt b/p1-sh/tests/expected/B_env.txt new file mode 100644 index 0000000..12fda9e --- /dev/null +++ b/p1-sh/tests/expected/B_env.txt @@ -0,0 +1,18 @@ +$ export A=5 +$ ./bin/repeat 2 A +A=5 +A=5 +$ ./bin/env B=6 ./bin/repeat 1 A +A=5 +$ ./bin/env C=10 ./bin/repeat 2 A 3 B 4 C +A=5 +A=5 +B= +B= +B= +C=10 +C=10 +C=10 +C=10 +$ quit + diff --git a/p1-sh/tests/expected/B_export_unset.txt b/p1-sh/tests/expected/B_export_unset.txt new file mode 100644 index 0000000..4980278 --- /dev/null +++ b/p1-sh/tests/expected/B_export_unset.txt @@ -0,0 +1,9 @@ +$ which export +export: dukesh built-in command +$ echo N=${NUM} +N= +$ export NUM=5 +$ echo N=${NUM} +N=5 +$ quit + diff --git a/p1-sh/tests/expected/B_repeat.txt b/p1-sh/tests/expected/B_repeat.txt new file mode 100644 index 0000000..72843f0 --- /dev/null +++ b/p1-sh/tests/expected/B_repeat.txt @@ -0,0 +1,9 @@ +$ export SHELL=/bin/bash +$ export USER=me +$ ./bin/repeat 1 USER +USER=me +$ ./bin/repeat 2 SHELL +SHELL=/bin/bash +SHELL=/bin/bash +$ quit + diff --git a/p1-sh/tests/expected/B_return_code.txt b/p1-sh/tests/expected/B_return_code.txt new file mode 100644 index 0000000..dd38dc0 --- /dev/null +++ b/p1-sh/tests/expected/B_return_code.txt @@ -0,0 +1,13 @@ +$ ./bin/ls data +empty.txt +FIRST.txt +pwd.txt +subdir +yat.txt +$ echo $? +0 +$ ./bin/ls asldfkjasldfkj +$ echo $? +1 +$ quit + diff --git a/p1-sh/tests/expected/B_setenv.txt b/p1-sh/tests/expected/B_setenv.txt new file mode 100644 index 0000000..c869635 --- /dev/null +++ b/p1-sh/tests/expected/B_setenv.txt @@ -0,0 +1,14 @@ +$ ./bin/env A=5 ./bin/repeat 2 A +A=5 +A=5 +$ ./bin/env A=5 B=6 C=7 ./bin/repeat 1 A 2 B 3 C +A=5 +B=6 +B=6 +C=7 +C=7 +C=7 +$ ./bin/env A=10 ./bin/repeat 1 B +B= +$ quit + diff --git a/p1-sh/tests/expected/C_binaries.txt b/p1-sh/tests/expected/C_binaries.txt new file mode 100644 index 0000000..ce13b63 --- /dev/null +++ b/p1-sh/tests/expected/C_binaries.txt @@ -0,0 +1,14 @@ +$ ./bin/ls data +empty.txt +FIRST.txt +pwd.txt +subdir +yat.txt +$ ./bin/head Makefile +# +# Simple Test Makefile +# Mike Lam, James Madison University, August 2016 +# +# This version of the Makefile includes support for building a test suite. The +$ quit + diff --git a/p1-sh/tests/expected/C_binaries_bad-2.txt b/p1-sh/tests/expected/C_binaries_bad-2.txt new file mode 100644 index 0000000..167e091 --- /dev/null +++ b/p1-sh/tests/expected/C_binaries_bad-2.txt @@ -0,0 +1,4 @@ +$ ./bin/ls -l +$ ./bin/head -c 5 Makefile +$ quit + diff --git a/p1-sh/tests/expected/C_binaries_bad.txt b/p1-sh/tests/expected/C_binaries_bad.txt new file mode 100644 index 0000000..39147d3 --- /dev/null +++ b/p1-sh/tests/expected/C_binaries_bad.txt @@ -0,0 +1,6 @@ +$ ./bin/ls -l +./bin/ls: invalid option -- 'l' +$ ./bin/head -c 5 Makefile +./bin/head: invalid option -- 'c' +$ quit + diff --git a/p1-sh/tests/expected/C_binaries_flags.txt b/p1-sh/tests/expected/C_binaries_flags.txt new file mode 100644 index 0000000..bad3cbb --- /dev/null +++ b/p1-sh/tests/expected/C_binaries_flags.txt @@ -0,0 +1,35 @@ +$ /usr/bin/chmod 751 data/subdir +$ /usr/bin/chmod 640 data/empty.txt +$ /usr/bin/chmod 640 data/FIRST.txt +$ /usr/bin/chmod 400 data/.hidden.txt +$ /usr/bin/chmod 640 data/pwd.txt +$ /usr/bin/chmod 640 data/yat.txt +$ ./bin/ls -a data +empty.txt +FIRST.txt +.hidden.txt +pwd.txt +subdir +yat.txt +$ ./bin/ls -s data +0 empty.txt +6 FIRST.txt +9 pwd.txt +22 yat.txt +$ ./bin/ls -sa data +0 empty.txt +6 FIRST.txt +5 .hidden.txt +9 pwd.txt +22 yat.txt +$ ./bin/ls -ap data +-rw-r----- empty.txt +-rw-r----- FIRST.txt +-r-------- .hidden.txt +-rw-r----- pwd.txt +drwxr-x--x subdir +-rw-r----- yat.txt +$ ./bin/head -n 1 Makefile +# +$ quit + diff --git a/p1-sh/tests/expected/C_cd_pwd.txt b/p1-sh/tests/expected/C_cd_pwd.txt new file mode 100644 index 0000000..96b0e04 --- /dev/null +++ b/p1-sh/tests/expected/C_cd_pwd.txt @@ -0,0 +1,7 @@ +$ cd /usr/bin +$ pwd +/usr/bin +$ which ls +/usr/bin/ls +$ quit + diff --git a/p1-sh/tests/expected/C_chmod.txt b/p1-sh/tests/expected/C_chmod.txt new file mode 100644 index 0000000..ff09e4d --- /dev/null +++ b/p1-sh/tests/expected/C_chmod.txt @@ -0,0 +1,35 @@ +$ ./bin/ls -a data +empty.txt +FIRST.txt +.hidden.txt +pwd.txt +subdir +yat.txt +$ ./bin/ls -s data +0 empty.txt +6 FIRST.txt +9 pwd.txt +22 yat.txt +$ ./bin/ls -sa data +0 empty.txt +6 FIRST.txt +5 .hidden.txt +9 pwd.txt +22 yat.txt +$ ./bin/chmod rw- rw- --- data/empty.txt +$ ./bin/chmod r-- --- --x data/FIRST.txt +$ ./bin/chmod rw- rw- rw- data/pwd.txt +$ ./bin/chmod r-x r-x r-x data/yat.txt +$ ./bin/chmod rwx --- --- data/.hidden.txt +$ ./bin/chmod r-x --x r-- data/subdir +$ ./bin/ls -ap data +-rw-rw---- empty.txt +-r-------x FIRST.txt +-rwx------ .hidden.txt +-rw-rw-rw- pwd.txt +dr-x--xr-- subdir +-r-xr-xr-x yat.txt +$ ./bin/head -n 1 Makefile +# +$ quit + diff --git a/p1-sh/tests/expected/C_cut.txt b/p1-sh/tests/expected/C_cut.txt new file mode 100644 index 0000000..5328e11 --- /dev/null +++ b/p1-sh/tests/expected/C_cut.txt @@ -0,0 +1,17 @@ +$ ./bin/cut cut_data/data.spaces +hello +later +$ ./bin/cut -f 2 cut_data/data.spaces +world +gator +$ ./bin/cut cut_data/data.csv +hello,world,me +later,gator +$ ./bin/cut -d , cut_data/data.csv +hello +later +$ ./bin/cut -d , -f 2 cut_data/data.csv +world +gator +$ quit + diff --git a/p1-sh/tests/expected/C_cut_bad.txt b/p1-sh/tests/expected/C_cut_bad.txt new file mode 100644 index 0000000..92ae22d --- /dev/null +++ b/p1-sh/tests/expected/C_cut_bad.txt @@ -0,0 +1,15 @@ +$ ./bin/cut -f -1 cut_data/data.spaces +cut, splits each line based on a delimiter +usage: cut [FLAG] FILE +FLAG can be: + -d C split each line based on the character C (default ' ') + -f N print the Nth field (1 is first, default 1) +If no FILE specified, read from STDIN +$ ./bin/cut -f 3 cut_data/data.spaces +me + +$ ./bin/cut -d , -f 5 cut_data/data.csv + + +$ quit + diff --git a/p1-sh/tests/expected/C_echo.txt b/p1-sh/tests/expected/C_echo.txt new file mode 100644 index 0000000..3e7f1de --- /dev/null +++ b/p1-sh/tests/expected/C_echo.txt @@ -0,0 +1,7 @@ +$ echo goodbye +goodbye +$ echo hello\nworld +hello +world +$ quit + diff --git a/p1-sh/tests/expected/C_echo_space.txt b/p1-sh/tests/expected/C_echo_space.txt new file mode 100644 index 0000000..fc282e2 --- /dev/null +++ b/p1-sh/tests/expected/C_echo_space.txt @@ -0,0 +1,8 @@ +$ echo this has extra spaces +this has extra spaces +$ echo this\nhas\nnewlines +this +has +newlines +$ quit + diff --git a/p1-sh/tests/expected/C_which.txt b/p1-sh/tests/expected/C_which.txt new file mode 100644 index 0000000..91a8380 --- /dev/null +++ b/p1-sh/tests/expected/C_which.txt @@ -0,0 +1,18 @@ +$ which cd +cd: dukesh built-in command +$ which echo +echo: dukesh built-in command +$ which pwd +pwd: dukesh built-in command +$ which which +which: dukesh built-in command +$ which ./bin/ls +./bin/ls +$ which ./bin/head +./bin/head +$ which ls +/usr/bin/ls +$ which head +/usr/bin/head +$ quit + diff --git a/p1-sh/tests/expected/D_echo.txt b/p1-sh/tests/expected/D_echo.txt new file mode 100644 index 0000000..42f391a --- /dev/null +++ b/p1-sh/tests/expected/D_echo.txt @@ -0,0 +1,6 @@ +$ echo hello +hello +$ echo goodbye +goodbye +$ quit + diff --git a/p1-sh/tests/expected/D_quit.txt b/p1-sh/tests/expected/D_quit.txt new file mode 100644 index 0000000..8891f6b --- /dev/null +++ b/p1-sh/tests/expected/D_quit.txt @@ -0,0 +1,2 @@ +$ quit + diff --git a/p1-sh/tests/integration.sh b/p1-sh/tests/integration.sh new file mode 100755 index 0000000..db16deb --- /dev/null +++ b/p1-sh/tests/integration.sh @@ -0,0 +1,95 @@ +#!/bin/bash + +# Ensure the data directory has 750 permissions +chmod 750 data + +# Must run with env -i so that students cannot "accidentally" use the +# standard shell environment variables +EXE="env -i PATH=/usr/bin:. ../dukesh" + +SKIP_C="" +SKIP_B="" +SKIP_A="" + +function run_test { + + # parameters + TAG=$1 + ARGS=$2 + + PTAG=$(printf '%-30s' "$TAG") + if [[ $(echo $TAG | cut -d'_' -f1) == $SKIP_C || + $(echo $TAG | cut -d'_' -f1) == $SKIP_B || + $(echo $TAG | cut -d'_' -f1) == $SKIP_A ]] ; then + echo "$PTAG SKIPPED (previous phases not complete)" + return + fi + + # file paths + OUTPUT=outputs/$TAG.txt + DIFF=outputs/$TAG.diff + EXPECT=expected/$TAG.txt + VALGRND=valgrind/$TAG.txt + + # run test and compare output to the expected version + $EXE $ARGS 2>/dev/null >"$OUTPUT" + diff -u "$OUTPUT" "$EXPECT" >"$DIFF" + passed=false + EFILES=$(find expected -type f -name "$TAG-*.txt") + if [ ! -s "$DIFF" ]; then + passed=true + elif [ "$EFILES" != "" ]; then + for EF in $EFILES ; do + DF=$(echo $EF | sed 's/^expected/outputs/' | sed 's/txt$/diff/') + diff -u "$OUTPUT" "$EF" >"$DF" + if [ ! -s "$DF" ]; then + passed=true + fi + done + fi + if [[ $passed = true ]] ; then + echo "$PTAG pass" + rm outputs/$TAG*diff + else + echo "$PTAG FAIL - Command line: $EXE $ARGS" + if [ "$EFILES" != "" ] ; then + echo "$(printf '%30s' ' ') *WARNING* This test has more than one possible output file." + echo "$(printf '%30s' ' ') *WARNING* Please inspect each manually for comparison." + else + echo "$(printf '%30s' ' ') See $DIFF for diff with expected output" + fi + echo "" + + if [[ $(echo $PTAG | cut -d'_' -f1) == 'D' ]] ; then + SKIP_C="C" + SKIP_B="B" + SKIP_A="A" + elif [[ $(echo $PTAG | cut -d'_' -f1) == 'C' ]] ; then + SKIP_B="B" + SKIP_A="A" + elif [[ $(echo $PTAG | cut -d'_' -f1) == 'B' ]] ; then + SKIP_A="A" + fi + fi + + # run valgrind + valgrind $EXE $ARGS &>$VALGRND +} + +# initialize output folders +mkdir -p outputs +mkdir -p valgrind +rm -f outputs/* valgrind/* + +# run individual tests +source itests.include + +# check for memory leaks +LEAK=`cat valgrind/*.txt | grep 'definitely lost' | grep -v ' 0 bytes in 0 blocks'` +if [ -z "$LEAK" ]; then + echo "No memory leak found." +else + echo "Memory leak(s) found. See files listed below for details." + grep 'definitely lost' valgrind/*.txt | grep -v ' 0 bytes in 0 blocks' | sed -e 's/:.*$//g' | sed -e 's/^/ - /g' +fi + diff --git a/p1-sh/tests/itests.include b/p1-sh/tests/itests.include new file mode 100644 index 0000000..ba11ecb --- /dev/null +++ b/p1-sh/tests/itests.include @@ -0,0 +1,40 @@ +# list of integration tests +# format: run_test +# used as the root for all filenames (i.e., "expected/$TAG.txt") +# command-line arguments to test + +run_test D_quit "-b scripts/quit.txt" +run_test D_echo "-b scripts/echo_min.txt" + +run_test C_cd_pwd "-b scripts/dirs.txt" +run_test C_echo "-b scripts/echo.txt" +run_test C_echo_space "-b scripts/echo_space.txt" + +run_test C_binaries "-b scripts/bins.txt" + # Your ls implementation must list in alphabetical order +run_test C_binaries_flags "-b scripts/bins_flags.txt" + # Your ls must print directories (. and ..) first, then + # the rest in alphabetical order (ignoring leading . and case) + +run_test C_chmod "-b scripts/bins_chmod.txt" + # Your ls must work correctly before chmod will work + +run_test C_binaries_bad "-b scripts/bins_bad.txt" +run_test C_which "-b scripts/which.txt" +run_test C_cut "-b scripts/cut.txt" +run_test C_cut_bad "-b scripts/cut_bad.txt" + +run_test B_return_code "-b scripts/rc.txt" + # Your ls implementation must list in alphabetical order +run_test B_export_unset "-b scripts/export.txt" + # Previous test exports and uses echo, no binaries +run_test B_repeat "-b scripts/repeat.txt" + # Previous test exports and uses repeat binary +run_test B_setenv "-b scripts/setenv.txt" + # Previous test uses ./bin/env to set env vars +run_test B_env "-b scripts/env.txt" + # Previous combines export and ./bin/env with ./bin/repeat + +run_test A_cat_tail "-b scripts/tail.txt" +run_test A_pipe "-b scripts/pipe.txt" +run_test A_env "-b scripts/env_pipe.txt" diff --git a/p1-sh/tests/public.c b/p1-sh/tests/public.c new file mode 100644 index 0000000..b01c9bd --- /dev/null +++ b/p1-sh/tests/public.c @@ -0,0 +1,114 @@ +#include + +#include "../src/hash.h" + +START_TEST (C_hash_insert) +{ + char *strings[] = { "foo", "bar", "zoo", "yadda", "help", "goo", "hi", "boo", + "nine", "ten" }; + + hash_init (100); + + for (int i = 0; i < 10; i++) + ck_assert (hash_insert (strings[i], strings[(i + 1) % 10])); + + for (int i = 0; i < 10; i++) + { + char *value = hash_find (strings[i]); + ck_assert_str_eq (value, strings[(i + 1) % 10]); + } +} +END_TEST + +START_TEST (C_hash_find_missing) +{ + char *strings[] = { "foo", "bar", "zoo", "yadda", "help", "goo", "hi", "boo", + "nine", "ten" }; + + hash_init (100); + + for (int i = 0; i < 10; i++) + ck_assert (hash_insert (strings[i], strings[(i + 1) % 10])); + + ck_assert (hash_find ("gobble") == NULL); +} +END_TEST + +START_TEST (C_hash_remove) +{ + char *strings[] = { "foo", "bar", "zoo", "yadda", "help", "goo", "hi", "boo", + "nine", "ten" }; + + hash_init (100); + + for (int i = 0; i < 10; i++) + ck_assert (hash_insert (strings[i], strings[(i + 1) % 10])); + + for (int i = 0; i < 5; i++) + ck_assert (hash_remove (strings[i])); + + ck_assert_str_eq (hash_find ("goo"), "hi"); + + for (int i = 5; i < 10; i++) + ck_assert (hash_remove (strings[i])); + + ck_assert (hash_find ("goo") == NULL); +} +END_TEST + +START_TEST (C_hash_replace) +{ + char *strings[] = { "foo", "bar", "zoo", "yadda", "help", "goo", "hi", "boo", + "nine", "ten" }; + + hash_init (100); + + for (int i = 0; i < 10; i++) + ck_assert (hash_insert (strings[i], strings[(i + 1) % 10])); + + ck_assert (hash_insert ("yadda", "again?!")); + ck_assert_str_eq (hash_find ("yadda"), "again?!"); + + ck_assert (hash_insert ("yadda", "really?!")); + ck_assert_str_eq (hash_find ("yadda"), "really?!"); +} +END_TEST + +START_TEST (C_hash_collisions) +{ + char *strings[] = { "foo", "bar", "zoo", "yadda", "help", "goo", "hi", "boo", + "nine", "ten" }; + + hash_init (100); + + for (int i = 0; i < 10; i++) + ck_assert (hash_insert (strings[i], strings[(i + 1) % 10])); + + ck_assert (hash_insert ("bar", "a")); + ck_assert (hash_insert ("help", "b")); + ck_assert_str_eq (hash_find ("bar"), "a"); + ck_assert_str_eq (hash_find ("help"), "b"); + + ck_assert (hash_remove ("bar")); + ck_assert (hash_remove ("help")); + ck_assert (hash_find ("bar") == NULL); + ck_assert (hash_find ("help") == NULL); + + ck_assert (hash_insert ("help", "asldasdffkj")); + ck_assert (hash_insert ("ten", "asldfkj")); + ck_assert_str_eq (hash_find ("help"), "asldasdffkj"); + ck_assert_str_eq (hash_find ("ten"), "asldfkj"); +} +END_TEST + +void +public_tests (Suite *s) +{ + TCase *tc_public = tcase_create ("Public"); + tcase_add_test (tc_public, C_hash_insert); + tcase_add_test (tc_public, C_hash_find_missing); + tcase_add_test (tc_public, C_hash_remove); + tcase_add_test (tc_public, C_hash_replace); + tcase_add_test (tc_public, C_hash_collisions); + suite_add_tcase (s, tc_public); +} diff --git a/p1-sh/tests/scripts/bins.txt b/p1-sh/tests/scripts/bins.txt new file mode 100644 index 0000000..8f4b0ea --- /dev/null +++ b/p1-sh/tests/scripts/bins.txt @@ -0,0 +1,3 @@ +./bin/ls data +./bin/head Makefile +quit diff --git a/p1-sh/tests/scripts/bins_bad.txt b/p1-sh/tests/scripts/bins_bad.txt new file mode 100644 index 0000000..2f8c7d7 --- /dev/null +++ b/p1-sh/tests/scripts/bins_bad.txt @@ -0,0 +1,3 @@ +./bin/ls -l +./bin/head -c 5 Makefile +quit diff --git a/p1-sh/tests/scripts/bins_chmod.txt b/p1-sh/tests/scripts/bins_chmod.txt new file mode 100644 index 0000000..0130ae2 --- /dev/null +++ b/p1-sh/tests/scripts/bins_chmod.txt @@ -0,0 +1,12 @@ +./bin/ls -a data +./bin/ls -s data +./bin/ls -sa data +./bin/chmod rw- rw- --- data/empty.txt +./bin/chmod r-- --- --x data/FIRST.txt +./bin/chmod rw- rw- rw- data/pwd.txt +./bin/chmod r-x r-x r-x data/yat.txt +./bin/chmod rwx --- --- data/.hidden.txt +./bin/chmod r-x --x r-- data/subdir +./bin/ls -ap data +./bin/head -n 1 Makefile +quit diff --git a/p1-sh/tests/scripts/bins_flags.txt b/p1-sh/tests/scripts/bins_flags.txt new file mode 100644 index 0000000..dd52c3c --- /dev/null +++ b/p1-sh/tests/scripts/bins_flags.txt @@ -0,0 +1,12 @@ +/usr/bin/chmod 751 data/subdir +/usr/bin/chmod 640 data/empty.txt +/usr/bin/chmod 640 data/FIRST.txt +/usr/bin/chmod 400 data/.hidden.txt +/usr/bin/chmod 640 data/pwd.txt +/usr/bin/chmod 640 data/yat.txt +./bin/ls -a data +./bin/ls -s data +./bin/ls -sa data +./bin/ls -ap data +./bin/head -n 1 Makefile +quit diff --git a/p1-sh/tests/scripts/cut.txt b/p1-sh/tests/scripts/cut.txt new file mode 100644 index 0000000..85ca653 --- /dev/null +++ b/p1-sh/tests/scripts/cut.txt @@ -0,0 +1,6 @@ +./bin/cut cut_data/data.spaces +./bin/cut -f 2 cut_data/data.spaces +./bin/cut cut_data/data.csv +./bin/cut -d , cut_data/data.csv +./bin/cut -d , -f 2 cut_data/data.csv +quit diff --git a/p1-sh/tests/scripts/cut_bad.txt b/p1-sh/tests/scripts/cut_bad.txt new file mode 100644 index 0000000..aca5514 --- /dev/null +++ b/p1-sh/tests/scripts/cut_bad.txt @@ -0,0 +1,4 @@ +./bin/cut -f -1 cut_data/data.spaces +./bin/cut -f 3 cut_data/data.spaces +./bin/cut -d , -f 5 cut_data/data.csv +quit diff --git a/p1-sh/tests/scripts/dirs.txt b/p1-sh/tests/scripts/dirs.txt new file mode 100644 index 0000000..d95eb4e --- /dev/null +++ b/p1-sh/tests/scripts/dirs.txt @@ -0,0 +1,4 @@ +cd /usr/bin +pwd +which ls +quit diff --git a/p1-sh/tests/scripts/echo.txt b/p1-sh/tests/scripts/echo.txt new file mode 100644 index 0000000..055b554 --- /dev/null +++ b/p1-sh/tests/scripts/echo.txt @@ -0,0 +1,3 @@ +echo goodbye +echo hello\nworld +quit diff --git a/p1-sh/tests/scripts/echo_min.txt b/p1-sh/tests/scripts/echo_min.txt new file mode 100644 index 0000000..11a8cfe --- /dev/null +++ b/p1-sh/tests/scripts/echo_min.txt @@ -0,0 +1,3 @@ +echo hello +echo goodbye +quit diff --git a/p1-sh/tests/scripts/echo_space.txt b/p1-sh/tests/scripts/echo_space.txt new file mode 100644 index 0000000..0c94e67 --- /dev/null +++ b/p1-sh/tests/scripts/echo_space.txt @@ -0,0 +1,3 @@ +echo this has extra spaces +echo this\nhas\nnewlines +quit diff --git a/p1-sh/tests/scripts/env.txt b/p1-sh/tests/scripts/env.txt new file mode 100644 index 0000000..61e5774 --- /dev/null +++ b/p1-sh/tests/scripts/env.txt @@ -0,0 +1,5 @@ +export A=5 +./bin/repeat 2 A +./bin/env B=6 ./bin/repeat 1 A +./bin/env C=10 ./bin/repeat 2 A 3 B 4 C +quit diff --git a/p1-sh/tests/scripts/env_pipe.txt b/p1-sh/tests/scripts/env_pipe.txt new file mode 100644 index 0000000..4a633f9 --- /dev/null +++ b/p1-sh/tests/scripts/env_pipe.txt @@ -0,0 +1,5 @@ +export A=5 +./bin/env ./bin/repeat 1 A +./bin/env B=6 C=7 ./bin/repeat 1 A 2 B 3 C +./bin/env C=7 ./bin/repeat 1 A 2 B 3 C | head -n 4 +quit diff --git a/p1-sh/tests/scripts/export.txt b/p1-sh/tests/scripts/export.txt new file mode 100644 index 0000000..ac49b07 --- /dev/null +++ b/p1-sh/tests/scripts/export.txt @@ -0,0 +1,5 @@ +which export +echo N=${NUM} +export NUM=5 +echo N=${NUM} +quit diff --git a/p1-sh/tests/scripts/pipe.txt b/p1-sh/tests/scripts/pipe.txt new file mode 100644 index 0000000..9d91288 --- /dev/null +++ b/p1-sh/tests/scripts/pipe.txt @@ -0,0 +1,6 @@ +./bin/ls -a data | ./bin/head -n 1 +./bin/ls -a data | ./bin/head -n 2000 +./bin/head -n 1 cut_data/data.spaces | ./bin/cut -f 2 +./bin/head -n 1 cut_data/data.csv | ./bin/cut -d , -f 1 +./bin/head -n 1 cut_data/data.csv | ./bin/cut -d , -f 3 +quit diff --git a/p1-sh/tests/scripts/quit.txt b/p1-sh/tests/scripts/quit.txt new file mode 100644 index 0000000..ff60466 --- /dev/null +++ b/p1-sh/tests/scripts/quit.txt @@ -0,0 +1 @@ +quit diff --git a/p1-sh/tests/scripts/rc.txt b/p1-sh/tests/scripts/rc.txt new file mode 100644 index 0000000..45e31fb --- /dev/null +++ b/p1-sh/tests/scripts/rc.txt @@ -0,0 +1,5 @@ +./bin/ls data +echo $? +./bin/ls asldfkjasldfkj +echo $? +quit diff --git a/p1-sh/tests/scripts/repeat.txt b/p1-sh/tests/scripts/repeat.txt new file mode 100644 index 0000000..85c9b24 --- /dev/null +++ b/p1-sh/tests/scripts/repeat.txt @@ -0,0 +1,5 @@ +export SHELL=/bin/bash +export USER=me +./bin/repeat 1 USER +./bin/repeat 2 SHELL +quit diff --git a/p1-sh/tests/scripts/setenv.txt b/p1-sh/tests/scripts/setenv.txt new file mode 100644 index 0000000..f011f66 --- /dev/null +++ b/p1-sh/tests/scripts/setenv.txt @@ -0,0 +1,4 @@ +./bin/env A=5 ./bin/repeat 2 A +./bin/env A=5 B=6 C=7 ./bin/repeat 1 A 2 B 3 C +./bin/env A=10 ./bin/repeat 1 B +quit diff --git a/p1-sh/tests/scripts/tail.txt b/p1-sh/tests/scripts/tail.txt new file mode 100644 index 0000000..645e89f --- /dev/null +++ b/p1-sh/tests/scripts/tail.txt @@ -0,0 +1,4 @@ +./bin/cat cut_data/data.csv +./bin/cat cut_data/data.csv | ./bin/cut -d , -f 2 +./bin/cat cut_data/data.csv | tail -n 1 +quit diff --git a/p1-sh/tests/scripts/unset.txt b/p1-sh/tests/scripts/unset.txt new file mode 100644 index 0000000..028d04a --- /dev/null +++ b/p1-sh/tests/scripts/unset.txt @@ -0,0 +1,8 @@ +echo N=${NUM} +export NUM=5 +echo N=${NUM} +unset NUM +echo N=${NUM} +export NUM=10 +echo N=${NUM} +quit diff --git a/p1-sh/tests/scripts/which.txt b/p1-sh/tests/scripts/which.txt new file mode 100644 index 0000000..988634d --- /dev/null +++ b/p1-sh/tests/scripts/which.txt @@ -0,0 +1,9 @@ +which cd +which echo +which pwd +which which +which ./bin/ls +which ./bin/head +which ls +which head +quit diff --git a/p1-sh/tests/scripts/which_exec.txt b/p1-sh/tests/scripts/which_exec.txt new file mode 100644 index 0000000..8bbdf60 --- /dev/null +++ b/p1-sh/tests/scripts/which_exec.txt @@ -0,0 +1,3 @@ +which ./bin/rm +which ./bin/cat +quit diff --git a/p1-sh/tests/style.sh b/p1-sh/tests/style.sh new file mode 100755 index 0000000..0243934 --- /dev/null +++ b/p1-sh/tests/style.sh @@ -0,0 +1,53 @@ +#!/bin/bash + +STYLE="gnu" + +IGNORE=() +FAIL=0 + +function comp_file { + + SRC=$1 + SRC_NAME=$2 + + # file paths + FORMAT=ckstyle/${SRC_NAME}.$STYLE + DIFF=ckstyle/${SRC_NAME}.diff + + # run clang-format and compare results + clang-format --style=$STYLE $source > $FORMAT + diff -u $SRC $FORMAT >$DIFF + + PTAG=$(printf '%-30s' "$SRC_NAME") + if [ -s $DIFF ]; then + echo "$PTAG FAIL (see $DIFF for details)" + FAIL=1 + else + echo "$PTAG pass" + rm $DIFF + fi + rm $FORMAT +} + +mkdir -p ckstyle +rm -f ckstyle/* + +for source in $(ls ../src/*.c ../src/*.h) ; do + SKIP=0 + src=$(basename $source) + for ignore in ${IGNORE[*]} ; do + if [ "$src" = "$ignore" ] ; then + SKIP=1 + fi + done + if [ $SKIP = 0 ] ; then + comp_file $source $src + fi +done + +if [ $FAIL != 0 ] ; then + echo "Code that does not adhere to GNU standards will not be accepted." + echo "You must fix these files before submission." +else + echo "Code correctly adheres to required style." +fi diff --git a/p1-sh/tests/testsuite.c b/p1-sh/tests/testsuite.c new file mode 100644 index 0000000..79db20b --- /dev/null +++ b/p1-sh/tests/testsuite.c @@ -0,0 +1,32 @@ +#include +#include +#include +#include +#include +#include + +#include + +extern void public_tests (Suite *s); + +Suite * test_suite (void) +{ + Suite *s = suite_create ("Default"); + public_tests (s); + return s; +} + +void run_testsuite () +{ + Suite *s = test_suite (); + SRunner *sr = srunner_create (s); + srunner_run_all (sr, CK_NORMAL); + srunner_free (sr); +} + +int main (void) +{ + srand((unsigned)time(NULL)); + run_testsuite (); + return EXIT_SUCCESS; +} diff --git a/p1-sh/utils/Makefile b/p1-sh/utils/Makefile new file mode 100644 index 0000000..3b66c73 --- /dev/null +++ b/p1-sh/utils/Makefile @@ -0,0 +1,41 @@ +# +# Simple Makefile +# Mike Lam, James Madison University, August 2016 +# +# This makefile builds a simple application that contains a main module +# (specified by the EXE variable) and a predefined list of additional modules +# (specified by the MODS variable). If there are any external library +# dependencies (e.g., the math library, "-lm"), list them in the LIBS variable. +# If there are any precompiled object files, list them in the OBJS variable. +# +# By default, this makefile will build the project with debugging symbols and +# without optimization. To change this, edit or remove the "-g" and "-O0" +# options in CFLAGS and LDFLAGS accordingly. +# +# By default, this makefile build the application using the GNU C compiler, +# adhering to the C99 standard with all warnings enabled. + +EXES=ls chmod head cut repeat env cat + +# compiler/linker settings + +CC=gcc +CFLAGS=-g -O0 -Wall -Werror -std=c99 -pedantic -D_POSIX_C_SOURCE=200809L +LDFLAGS=-O0 + +# build targets + +all: ../bin $(EXES) + +.c: + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< + mv $@ ../bin + +../bin: + mkdir ../bin + +clean: + rm -rf ../bin + +.PHONY: all clean + diff --git a/p1-sh/utils/cat.c b/p1-sh/utils/cat.c new file mode 100644 index 0000000..94eeee7 --- /dev/null +++ b/p1-sh/utils/cat.c @@ -0,0 +1,41 @@ +#include +#include + +static void usage (void) __attribute__ ((unused)); + +int +main (int argc, char *argv[]) +{ + if (argc < 2) + { + int c; + while ((c = getchar()) != EOF) + { + putchar(c); + } + return EXIT_SUCCESS; + } + + FILE *fp = fopen(argv[1], "r"); + if (fp == NULL) + { + return EXIT_FAILURE; + } + + int c; + while ((c = fgetc(fp)) != EOF) + { + putchar(c); + } + + fclose(fp); + + return EXIT_SUCCESS; +} + +static void +usage (void) +{ + printf ("cat, print the contents of a file\n"); + printf ("usage: cat FILE\n"); +} diff --git a/p1-sh/utils/chmod.c b/p1-sh/utils/chmod.c new file mode 100644 index 0000000..c7b23e8 --- /dev/null +++ b/p1-sh/utils/chmod.c @@ -0,0 +1,70 @@ +#include +#include +#include +#include +#include + +static void usage (void); + +mode_t +calculate_permission (char *arg_str) +{ + const char perms[4] = "rwx"; + + if (strlen (arg_str) != 3) + { + usage (); + exit (EXIT_FAILURE); + } + + mode_t permission = 0; + + for (size_t j = 0; j < 3; j++) + { + int perm = 1 << (2 - j); + if (arg_str[j] == perms[j]) + { + permission |= perm; + } + else if (arg_str[j] != '-') + { + usage (); + exit (EXIT_FAILURE); + } + } + + return permission; +} + +int +main (int argc, char **argv) +{ + if (argc != 5) + { + usage (); + return EXIT_FAILURE; + } + + mode_t user = calculate_permission (argv[1]); + mode_t group = calculate_permission (argv[2]); + mode_t other = calculate_permission (argv[3]); + + mode_t permissions = (user << 6) | (group << 3) | other; + + if (chmod (argv[4], permissions) == -1) + { + perror ("Failed to chmod"); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + +static void +usage (void) +{ + printf ("chmod, changes permissions on a file\n"); + printf ("usage: chmod USR GRP OTH FILE\n\n"); + printf ("USR, GRP, and OTH must be of the rwx format,\n"); + printf ("with - indicating a permission is not allwed.\n"); +} diff --git a/p1-sh/utils/cut.c b/p1-sh/utils/cut.c new file mode 100644 index 0000000..42c2869 --- /dev/null +++ b/p1-sh/utils/cut.c @@ -0,0 +1,75 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include + +// You may assume that lines are no longer than 1024 bytes +#define LINELEN 1024 + +static void usage (void); + +int +main (int argc, char *argv[]) +{ + int opt; + char *delimitter = " "; + long field = 1; + + while ((opt = getopt (argc, argv, "d:f:")) != -1) + { + switch (opt) + { + case 'd': + delimitter = optarg; + break; + case 'f': + field = strtol (optarg, NULL, 10); + break; + default: + printf ("./bin/cut: invalid option -- \'%c\'\n", optopt); + exit (EXIT_FAILURE); + } + } + + if (field < 1) + { + usage (); + exit (EXIT_FAILURE); + } + + FILE *file = optind < argc ? fopen (argv[optind], "r") : stdin; + if (file == NULL) + { + perror ("Failed to open input file"); + exit (EXIT_FAILURE); + } + + char buffer[LINELEN]; + + while (fgets (buffer, sizeof (buffer), file) != NULL) + { + buffer[strcspn (buffer, "\n")] = '\0'; + + char *token = strtok (buffer, delimitter); + long i = 1; + while (i++ < field) token = strtok (NULL, delimitter); + + printf("%s\n", token != NULL ? token : ""); + } + + return EXIT_SUCCESS; +} + +static void +usage (void) +{ + printf ("cut, splits each line based on a delimiter\n"); + printf ("usage: cut [FLAG] FILE\n"); + printf ("FLAG can be:\n"); + printf ( + " -d C split each line based on the character C (default ' ')\n"); + printf (" -f N print the Nth field (1 is first, default 1)\n"); + printf ("If no FILE specified, read from STDIN\n"); +} diff --git a/p1-sh/utils/env.c b/p1-sh/utils/env.c new file mode 100644 index 0000000..ca6f07e --- /dev/null +++ b/p1-sh/utils/env.c @@ -0,0 +1,65 @@ +#include +#include +#include +#include +#include + +static void usage (void); + +bool +is_util (char *str) +{ + char *utils[] = { "./bin/cat", "./bin/chmod", "./bin/cut", "./bin/env", + "./bin/head", "./bin/ls", "./bin/repeat" }; + + for (size_t i = 0; i < 7; i++) + { + if (strcmp (str, utils[i]) == 0) + { + return true; + } + } + + return false; +} + +int +main (int argc, char *argv[], char *envp[]) +{ + size_t envp_count = 0; + while (envp[envp_count] != NULL) envp_count++; + + size_t env_args = 0; + char* util = NULL; + + for (size_t i = 1; i < argc; i++) + { + if (is_util (argv[i])) { + util = argv[i]; + break; + } + + env_args++; + } + + if (util == NULL) { + usage(); + exit(EXIT_FAILURE); + } + + char **combined_envp = calloc (envp_count + env_args + 1, sizeof (char *)); + memcpy (combined_envp, envp, envp_count * sizeof (char *)); + memcpy (combined_envp + envp_count, &argv[1], env_args * sizeof (char *)); + combined_envp[envp_count + env_args] = NULL; + + execve(util, &argv[env_args + 1], combined_envp); + + return EXIT_SUCCESS; +} + +static void +usage (void) +{ + printf ("env, set environment variables and execute program\n"); + printf ("usage: env [name=value ...] PROG ARGS\n"); +} diff --git a/p1-sh/utils/head.c b/p1-sh/utils/head.c new file mode 100644 index 0000000..982c6ab --- /dev/null +++ b/p1-sh/utils/head.c @@ -0,0 +1,58 @@ +#define _GNU_SOURCE +#include +#include +#include +#include + +// You may assume that lines are no longer than 1024 bytes +#define LINELEN 1024 + +static void usage (void) __attribute__ ((unused)); + +int +main (int argc, char *argv[]) +{ + int opt; + int n = 5; + + while ((opt = getopt (argc, argv, "n:")) != -1) + { + switch (opt) + { + case 'n': + n = strtol (optarg, NULL, 10); + break; + default: + printf ("./bin/head: invalid option -- \'%c\'\n", optopt); + exit (EXIT_FAILURE); + } + } + + FILE *file = optind < argc ? fopen (argv[optind], "r") : stdin; + if (file == NULL) + { + perror ("Failed to open input file"); + exit (EXIT_FAILURE); + } + + char buffer[LINELEN]; + int count = 0; + + while (count < n && fgets (buffer, sizeof (buffer), file) != NULL) + { + printf ("%s", buffer); + count++; + } + + return EXIT_SUCCESS; +} + +static void +usage (void) +{ + printf ("head, prints the first few lines of a file\n"); + printf ("usage: head [FLAG] FILE\n"); + printf ("FLAG can be:\n"); + printf (" -n N show the first N lines (default 5)\n"); + printf ("If no FILE specified, read from STDIN\n"); +} diff --git a/p1-sh/utils/ls.c b/p1-sh/utils/ls.c new file mode 100644 index 0000000..c354d43 --- /dev/null +++ b/p1-sh/utils/ls.c @@ -0,0 +1,148 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_PATH_LEN 4096 + +static void usage (void) __attribute__ ((unused)); + +// WARNING WARNING WARNING: +// When using opendir and readdir to read directory listings, the files +// are returned in the order they were created. There is randomness to that +// creation that can cause tests to fail. You MUST sort the file names as +// expected in the test case files. To avoid making your task harder than +// it needs to be, we strongly suggest you use scandir instead. + +int +filename_cmp (const struct dirent **a, const struct dirent **b) +{ + const char *name1 = (*a)->d_name; + const char *name2 = (*b)->d_name; + + while (*name1 == '.') + name1++; + while (*name2 == '.') + name2++; + + return strcasecmp (name1, name2); +} + +void +print_permissions (mode_t mode) +{ + char perms[11]; + + perms[0] = S_ISDIR (mode) ? 'd' : '-'; + perms[1] = (mode & S_IRUSR) ? 'r' : '-'; + perms[2] = (mode & S_IWUSR) ? 'w' : '-'; + perms[3] = (mode & S_IXUSR) ? 'x' : '-'; + perms[4] = (mode & S_IRGRP) ? 'r' : '-'; + perms[5] = (mode & S_IWGRP) ? 'w' : '-'; + perms[6] = (mode & S_IXGRP) ? 'x' : '-'; + perms[7] = (mode & S_IROTH) ? 'r' : '-'; + perms[8] = (mode & S_IWOTH) ? 'w' : '-'; + perms[9] = (mode & S_IXOTH) ? 'x' : '-'; + perms[10] = '\0'; + + printf ("%s", perms); +} + +int +main (int argc, char *argv[]) +{ + int opt; + int a_flag = false, p_flag = false, s_flag = false; + + while ((opt = getopt (argc, argv, "aps")) != -1) + { + switch (opt) + { + case 'a': + a_flag = true; + break; + case 'p': + p_flag = true; + break; + case 's': + s_flag = true; + break; + default: + printf ("./bin/ls: invalid option -- \'%c\'\n", optopt); + exit (EXIT_FAILURE); + } + } + + char *dirname = optind < argc ? argv[optind] : "."; + struct stat dirstat; + + if (stat(dirname, &dirstat) != 0) { + return EXIT_FAILURE; + } + + struct dirent **namelist; + int n = scandir (dirname, &namelist, NULL, filename_cmp); + + for (int i = 0; i < n; i++) + { + char path[MAX_PATH_LEN]; + snprintf (path, sizeof (path), "%s/%s", dirname, namelist[i]->d_name); + + struct stat st; + lstat (path, &st); + + bool dot_dir = strcmp (namelist[i]->d_name, ".") == 0 + || strcmp (namelist[i]->d_name, "..") == 0; + + bool hidden = namelist[i]->d_name[0] == '.'; + + if (dot_dir || (!a_flag && hidden) || (s_flag && S_ISDIR (st.st_mode))) + { + free (namelist[i]); + continue; + } + + if (s_flag) + { + printf ("%ld ", st.st_size); + } + + if (p_flag) + { + print_permissions (st.st_mode); + printf (" "); + } + + printf ("%s\n", namelist[i]->d_name); + + free (namelist[i]); + } + free (namelist); + + return EXIT_SUCCESS; +} + +static void +usage (void) +{ + printf ("ls, list directory contents\n"); + printf ("usage: ls [FLAG ...] [DIR]\n"); + printf ("FLAG is one or more of:\n"); + printf (" -a list all files (even hidden ones)\n"); + printf (" -p list permission bitmask\n"); + printf (" -s list file sizes\n"); + printf ("If no DIR specified, list current directory contents.\n\n"); + printf ("Files must be sorted alphabetically, case insensitive.\n"); + printf ("Leading dots should be ignored when sorting.\n\n"); + printf ("With the -s flag, do not show entries for subdirectories.\n"); + printf ("Permission bitmasks are 10-character strings such as:\n"); + printf (" -rwxr-x---\n\n"); + printf ( + "The first character is d for directories and - for regular files.\n\n"); + printf ("Do not show the \".\" or \"..\" directory entries.\n"); +} diff --git a/p1-sh/utils/repeat.c b/p1-sh/utils/repeat.c new file mode 100644 index 0000000..5c026ca --- /dev/null +++ b/p1-sh/utils/repeat.c @@ -0,0 +1,76 @@ +#include +#include +#include + +static void usage (void) __attribute__ ((unused)); + +int +main (int argc, char *argv[], char *envp[]) +{ + if (argc < 3) + { + usage(); + return EXIT_FAILURE; + } + + int i = 1; + while (i < argc) + { + char *endptr; + long count = strtol (argv[i], &endptr, 10); + if (*endptr != '\0' || count <= 0) + { + usage(); + return EXIT_FAILURE; + } + i++; + + if (i >= argc) + { + usage(); + return EXIT_FAILURE; + } + + char *var = argv[i]; + char *val = NULL; + int x = 0; + while (envp[x] != NULL) + { + if (strstr(envp[x], var) == envp[x]) + { + val = envp[x]; + break; + } + x++; + } + + if (val == NULL) + { + val = ""; + for (long j = 0; j < count; j++) + { + printf("%s=%s\n", var, val); + } + val = ""; + } + else + { + for (long j = 0; j < count; j++) + { + printf("%s\n", val); + } + } + i++; + } + return EXIT_SUCCESS; +} + +static void +usage (void) +{ + printf ("repeat, a tool for printing repeated environment variables\n"); + printf ("usage: repeat N VAR ...\n"); + printf ("each N must be a positive integer\n"); + printf ("N VAR can be repeated, but each repetition must have both\n"); +} + diff --git a/p2-dns/.gitignore b/p2-dns/.gitignore new file mode 100644 index 0000000..f6c4f88 --- /dev/null +++ b/p2-dns/.gitignore @@ -0,0 +1,22 @@ +*.swp +*.swo + +# Ignore all compiled files regardless of location +build/*.o +tests/*.o + +# Ignore executables for this project +digduke +testsuite + +# Ignore test outputs +tests/ckstyle +tests/itests.txt +tests/outputs +tests/style.txt +tests/valgrind +tests/utests.txt + +# Ignore all vscode stuff and NFS files +**/.nfs* +**/.vscode diff --git a/p2-dns/Makefile b/p2-dns/Makefile new file mode 100644 index 0000000..a596ece --- /dev/null +++ b/p2-dns/Makefile @@ -0,0 +1,59 @@ +# +# Simple Makefile +# Mike Lam, James Madison University, August 2016 +# +# This makefile builds a simple application that contains a main module +# (specified by the EXE variable) and a predefined list of additional modules +# (specified by the MODS variable). If there are any external library +# dependencies (e.g., the math library, "-lm"), list them in the LIBS variable. +# If there are any precompiled object files, list them in the OBJS variable. +# +# By default, this makefile will build the project with debugging symbols and +# without optimization. To change this, edit or remove the "-g" and "-O0" +# options in CFLAGS and LDFLAGS accordingly. +# +# By default, this makefile build the application using the GNU C compiler, +# adhering to the C99 standard with all warnings enabled. + + +# application-specific settings and run target + +EXE=digduke +MODS=client.o dns.o utils.o +OBJS=port_utils.o +LIBS=-lm + +default: build $(EXE) + +build: + mkdir build + +test: build $(EXE) + make -C tests test + +style: $(EXE) + make -C tests style + +# compiler/linker settings + +CC=gcc +CFLAGS=-g -O0 -Wall -Werror --std=gnu99 -pedantic +LDFLAGS=-g -O0 -pthread + + +# build targets + +BUILD=$(addprefix build/, $(MODS)) + +$(EXE): $(BUILD) $(OBJS) + $(CC) $(LDFLAGS) -o $(EXE) $^ $(LIBS) + +build/%.o: src/%.c + $(CC) -c $(CFLAGS) -o $@ $< + +clean: + rm -rf $(EXE) build + make -C tests clean + +.PHONY: default clean + diff --git a/p2-dns/PHASE-1.txt b/p2-dns/PHASE-1.txt new file mode 100644 index 0000000..56269b0 --- /dev/null +++ b/p2-dns/PHASE-1.txt @@ -0,0 +1,39 @@ +Answer the following questions to describe your code submission. Please keep +all lines to a maximum of 80 characters wide. + +1 - Your implementation must send an array of bytes to the server. Explain the + meaning of these bytes and how the server interprets them. + + The bytes we send form a complete DNS request, which is hardcoded. The first + 12 bytes make up the DNS header, which includes a transaction ID, flags, and + counts of questions, answers, authority, and additional records. After the + header, we include the question section, which specifies the domain name + (which is a 0 length label which mean root server), the record type (A = IPv4), + and the class (IN = Internet). The server interprets these bytes as a query + asking for the IPv4 address of the root domain, and responds with the A + record containing the IP address of one of the root servers a.root-servers.net. + or b.root-servers.net. + + +2 - Show (in pseudocode) how you set up your socket to send your message, + including a description of how you specified the address and port number. + What would you have to change to send an HTTP message (which uses TCP) to + a server? + + const char *hostname = "127.0.0.1"; + const char *port = get_port (); + + struct addrinfo *server_list + = get_server_list (hostname, port, false, false); + + int sockfd = socket (server_list->ai_family, server_list->ai_socktype, 0); + + We do this to specify the hostname and port. + To send an HTTP message to a server we would hardcode the port to be 80 + and create the socket using SOCK_STREAM. We would have to call connect() to + establish the TCP handshake and then use send() to send the query and recv() + to get the response. + + + + diff --git a/p2-dns/PHASE-2.txt b/p2-dns/PHASE-2.txt new file mode 100644 index 0000000..e14222c --- /dev/null +++ b/p2-dns/PHASE-2.txt @@ -0,0 +1,26 @@ +Answer the following questions to describe your code submission. Please keep +all lines to a maximum of 80 characters wide. + +1 - Explain the algorithm that your code uses to convert a domain name from its + DNS (binary) format to the standard dotted notation for humans to read. + (I.e., how do you convert the bytes "01 61 01 62 00" into "a.b"?) + + The algorithm we use reads from a pointer to the front of the domain name. The first + byte gives the length of the next label. The code copies that many bytes + as text, appends a period, and moves forward by that label length. It + repeats until it encounters a 0x00 byte using a while loop, which is the null byte and the end of the + domain name. + +2 - Consider the following data: + + 12 34 81 80 00 01 00 01 00 00 00 00 03 6e 6f 74 02 68 69 03 6d 6f + 6d 00 00 01 00 01 c0 10 00 01 00 01 00 00 03 84 04 01 02 03 04 + + What is the domain name in the response? What is the IP address? + + The domain name is not.hi.mom + The IP address is 1.2.3.4 + + + + diff --git a/p2-dns/PHASE-3.txt b/p2-dns/PHASE-3.txt new file mode 100644 index 0000000..5ba3dd0 --- /dev/null +++ b/p2-dns/PHASE-3.txt @@ -0,0 +1,24 @@ +Answer the following questions to describe your code submission. Please keep +all lines to a maximum of 80 characters wide. + +1 - If a record is sought by there are no answers, what status code(s) are + returned and what does the RFC indicate these mean? + + The status code would be NOERROR and this means the question went through and was + processed fully but there was no corresponding data to return. The status code could also be NXDOMAIN + which indicates a domain name that does not exist and is an error. + + +2 - Consider the following data: + + 12 34 81 80 00 01 00 02 00 00 00 00 03 6e 6f 74 02 68 69 03 6d 6f + 6d 00 00 01 00 01 05 68 6f 77 64 79 c0 13 00 01 00 01 00 00 12 34 + 04 0a 00 00 01 c0 1c 00 1c 00 01 00 00 12 34 10 10 00 00 00 00 00 + 00 01 + + What are the domains name in the response? What are the IP addresses? + + howdy.mom. 10.0.0.1 + howdy.mom. 1010::1 + + diff --git a/p2-dns/PHASE-4.txt b/p2-dns/PHASE-4.txt new file mode 100644 index 0000000..372adf4 --- /dev/null +++ b/p2-dns/PHASE-4.txt @@ -0,0 +1,30 @@ +Answer the following questions to describe your code submission. Please keep +all lines to a maximum of 80 characters wide. + +1 - Explain how you convert the IP address 1.2.3.4 into the correct format for + a PTR request. + + We reversed the IP address to 4.3.2.1 by tokenizing at the periods and + then putting them back in correct order. Then concatenated the string portion on to the end of that + in-addr.arpa. , to give 4.3.2.1.in-addr.arpa. + + +2 - Explain the fields of an SOA record. Which of these would be interpreted as + an email address and how so? + + There are 7 different fields: + MNAME (The primary master name), + RNAME (The email address), + SERIAL (The version number of the zone data), + REFRESH (How often secondary servers check for updates), + RETRY (How long to wait before retrying after a failed refresh), + EXPIRE (How long secondary servers keep data if updates fail), + MINIMUM (Default TTL for negative responses). + + The RNAME would be interpreted as the email address and you would replace + the first . with an @ to turn into the normal email address structure. + + + + + diff --git a/p2-dns/port_utils.o b/p2-dns/port_utils.o new file mode 100644 index 0000000..a576d09 Binary files /dev/null and b/p2-dns/port_utils.o differ diff --git a/p2-dns/src/client.c b/p2-dns/src/client.c new file mode 100644 index 0000000..9ba154b --- /dev/null +++ b/p2-dns/src/client.c @@ -0,0 +1,255 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dns.h" +#include "port_utils.h" +#include "utils.h" + +#define BUFFER_MAX_SIZE 4096 + +const char *HOSTNAME = "127.0.0.1"; + +ssize_t +query_dns (unsigned char *query, size_t size, unsigned char **response) +{ + + // hardcoded for dukens server + const char *port_str = get_port (); // same port dukens listens on + + int port = strtol (port_str, NULL, 10); + if (port <= 0 || port > 65535) + { + fprintf (stderr, "Invalid port: %s\n", port_str); + exit (EXIT_FAILURE); + } + + struct sockaddr_in addr = { 0 }; + addr.sin_family = AF_INET; + addr.sin_port = htons (port); + if (inet_aton (HOSTNAME, &addr.sin_addr) != 1) + { + fprintf (stderr, "Invalid IPv4 address: %s\n", HOSTNAME); + exit (EXIT_FAILURE); + } + + // create socket + int sockfd = socket (AF_INET, SOCK_DGRAM, 0); + if (sockfd < 0) + { + perror ("socket"); + exit (EXIT_FAILURE); + } + + ssize_t sent = sendto (sockfd, query, size, 0, (struct sockaddr *)&addr, + sizeof (addr)); + if (sent < 0) + { + perror ("sendto"); + close (sockfd); + exit (EXIT_FAILURE); + } + + // receive and print response + *response = calloc (BUFFER_MAX_SIZE, sizeof (unsigned char)); + ssize_t received + = recvfrom (sockfd, *response, BUFFER_MAX_SIZE, 0, NULL, NULL); + if (received < 0) + { + perror ("recvfrom"); + close (sockfd); + exit (EXIT_FAILURE); + } + + // cleanup + close (sockfd); + + return received; +} + +void +phase_one () +{ + // hard coded DNS request + unsigned char query[17] + = { 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01 }; + + unsigned char *res_bytes; + ssize_t received = query_dns (query, sizeof (query), &res_bytes); + + printf ("Sending %zu bytes to %s:\n", sizeof (query), HOSTNAME); + print_byte_block (query, sizeof (query)); + + printf ("\nReceived %zd bytes from %s:\n", received, HOSTNAME); + print_byte_block (res_bytes, received); + printf ("\n"); + + dns_response_t res = { 0 }; + parse_dns_response (res_bytes, sizeof (query), &res); + dns_answer_t answer = res.answers[0]; + + printf ("Domain name: %s\n", answer.domain); + printf ("Record type: %s %s\n", qclass_to_str (answer.qclass), + record_to_str (answer.qtype)); + printf ("TTL: %d\n", answer.ttl); + printf ("IPv4 address: %s\n", answer.rdata); + + free (res_bytes); +} + +void +not_phase_one (int argc, char *argv[]) +{ + // building query + uint16_t xid = strtol (argv[2], NULL, 10); + + uint16_t record = get_record_from_str (argv[4]); + + char domain_name[BUFFER_LENGTH]; + + if (record == PTR) + { + ptr_ip (domain_name, BUFFER_LENGTH, argv[3]); + } + else + { + strncpy (domain_name, argv[3], sizeof (domain_name) - 1); + domain_name[sizeof (domain_name) - 1] = '\0'; + } + + unsigned char query_bytes[512]; + size_t offset = 0; + + // DNS Header + query_bytes[offset++] = (xid >> 8) & 0xFF; + query_bytes[offset++] = xid & 0xFF; + query_bytes[offset++] = 0x01; // flags + query_bytes[offset++] = 0x00; + query_bytes[offset++] = 0x00; // QDCOUNT high + query_bytes[offset++] = 0x01; // QDCOUNT low + // rest zeros (ANCOUNT, NSCOUNT, ARCOUNT) + for (int i = 0; i < 6; i++) + query_bytes[offset++] = 0x00; + + // Question section: domain name + char *token = strtok (domain_name, "."); + while (token) + { + size_t len = strlen (token); + query_bytes[offset++] = (unsigned char)len; + memcpy (&query_bytes[offset], token, len); + offset += len; + token = strtok (NULL, "."); + } + query_bytes[offset++] = 0x00; // end of domain + + query_bytes[offset++] = record >> 8; + query_bytes[offset++] = record & 0xff; + query_bytes[offset++] = 0x00; // QCLASS = IN + query_bytes[offset++] = 0x01; + + unsigned char *res_bytes; + query_dns (query_bytes, offset, &res_bytes); + + dns_query_t query = { 0 }; + parse_dns_query (&query_bytes[0], &query); + dns_response_t response = { 0 }; + parse_dns_response (&res_bytes[0], offset, &response); + + // print header + print_dns_header (&response.query.header); + + // print question section + print_question_section (&query); + + uint8_t status = (response.query.header.flags >> 0) & 0xF; + + if (status == 0 && response.query.header.ancount > 0) + { + printf ("\n;; ANSWER SECTION:\n"); + + for (size_t i = 0; i < response.query.header.ancount; i++) + { + dns_answer_t answer = response.answers[i]; + print_answer_section (&answer); + } + } + + if (response.query.header.nscount > 0) + { + printf ("\n;; AUTHORITY SECTION:\n"); + + for (size_t i = 0; i < response.query.header.nscount; i++) + { + dns_answer_t authority = response.authorities[i]; + print_answer_section (&authority); + } + } + + if (response.query.header.arcount > 0) + { + printf ("\n;; ADDITIONAL SECTION:\n"); + + for (size_t i = 0; i < response.query.header.arcount; i++) + { + dns_answer_t additional = response.additionals[i]; + print_answer_section (&additional); + } + } + + free (res_bytes); +} + +static bool +get_args (int argc, char **argv, bool *opendns, bool *xflag) +{ + int ch = 0; + while ((ch = getopt (argc, argv, "ox")) != -1) + { + switch (ch) + { + case 'o': + *opendns = true; + break; + case 'x': + *xflag = true; + break; + default: + return false; + } + } + + return true; +} + +int +main (int argc, char *argv[]) +{ + bool opendns = false; + bool xflag = false; + + if (!get_args (argc, argv, &opendns, &xflag)) + { + fprintf (stderr, "ERROR: Show usage\n"); + return EXIT_FAILURE; + } + + if (!xflag) + { + phase_one (); + } + else + { + not_phase_one (argc, argv); + } + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/p2-dns/src/dns.c b/p2-dns/src/dns.c new file mode 100644 index 0000000..cbf4deb --- /dev/null +++ b/p2-dns/src/dns.c @@ -0,0 +1,393 @@ +#include +#include +#include +#include +#include +#include + +#include "dns.h" + +#define BUFFER_MAX_SIZE 4096 + +// SUGGESTION: Use this file for DNS-related functionality +const char * +opcode_to_str (uint8_t opcode) +{ + switch (opcode) + { + case 0: + return "QUERY"; + case 1: + return "IQUERY"; + case 2: + return "STATUS"; + default: + return "UNKNOWN"; + } +} +const char * +status_to_str (uint8_t status) +{ + switch (status) + { + case 0: + return "NOERROR"; + case 1: + return "FORMERR"; + case 2: + return "SERVFAIL"; + case 3: + return "NXDOMAIN"; + case 4: + return "NOTIMP"; + case 5: + return "REFUSED"; + default: + return "UNKNOWN"; + } +} + +const char * +record_to_str (uint16_t record) +{ + switch (record) + { + case 1: + return "A"; + case 2: + return "NS"; + case 5: + return "CNAME"; + case 6: + return "SOA"; + case 12: + return "PTR"; + case 15: + return "MX"; + case 28: + return "AAAA"; + default: + return "UNKNOWN"; + } +} + +const char * +qclass_to_str (uint16_t qclass_bytes) +{ + switch (qclass_bytes) + { + case 1: + return "IN"; + default: + return "UNKNOWN"; + } +} + +uint16_t +get_record_from_str (const char *record_str) +{ + if (strcasecmp (record_str, "A") == 0) + return A; + else if (strcasecmp (record_str, "AAAA") == 0) + return AAAA; + else if (strcasecmp (record_str, "CNAME") == 0) + return CNAME; + else if (strcasecmp (record_str, "MX") == 0) + return MX; + else if (strcasecmp (record_str, "NS") == 0) + return NS; + else if (strcasecmp (record_str, "SOA") == 0) + return SOA; + else if (strcasecmp (record_str, "PTR") == 0) + return PTR; + else + { + fprintf (stderr, "Unsupported record type: %s\n", record_str); + return 0; + } +} + +uint32_t +merge_32 (unsigned char *bytes) +{ + return ((uint32_t)bytes[0] << 24) | ((uint32_t)bytes[1] << 16) + | ((uint32_t)bytes[2] << 8) | ((uint32_t)bytes[3]); +} + +void +recursive_domain_parse (unsigned char *bytes, ssize_t *pointer, char **domain) +{ + if (bytes[*pointer] == 0x00) + { + (*pointer)++; + return; + } + + bool is_compressed = (bytes[*pointer] & 0xc0) == 0xc0; + + if (!is_compressed) + { + size_t len = (size_t)bytes[(*pointer)++]; + strncat (*domain, (char *)&bytes[*pointer], len); + strncat (*domain, ".", 2); + *pointer += len; + + recursive_domain_parse (bytes, pointer, domain); + } + else + { + ssize_t offset = (bytes[*pointer] << 8 | bytes[*pointer + 1]) & 0x3fff; + recursive_domain_parse (bytes, &offset, domain); + *pointer += 2; + } +} + +void +parse_domain (unsigned char *bytes, ssize_t *pointer, char **domain) +{ + recursive_domain_parse (bytes, pointer, domain); + + if (strlen (*domain) == 0) + { + strncat (*domain, ".", 2); + } +} + +char * +parse_ip_addr (unsigned char *bytes, uint16_t rdlen) +{ + char ip_addr[INET6_ADDRSTRLEN]; + uint8_t ip_ver = rdlen == 4 ? AF_INET : AF_INET6; + + if (inet_ntop (ip_ver, bytes, ip_addr, sizeof (ip_addr)) == NULL) + { + perror ("inet_ntop"); + exit (EXIT_FAILURE); + } + + return strdup (ip_addr); +} + +void +print_dns_header (dns_header_t *header) +{ + + // switch endianness + uint16_t xid = header->xid; + uint16_t flags = header->flags; + uint16_t qdcount = header->qdcount; + uint16_t ancount = header->ancount; + uint16_t nscount = header->nscount; + uint16_t arcount = header->arcount; + + // find flags + uint8_t qr = (flags >> 15) & 0x1; + uint8_t opcode = (flags >> 11) & 0xF; + uint8_t aa = (flags >> 10) & 0x1; + uint8_t tc = (flags >> 9) & 0x1; + uint8_t rd = (flags >> 8) & 0x1; + uint8_t ra = (flags >> 7) & 0x1; + uint8_t status = (flags >> 0) & 0xF; + + // get string equivalent for opcode and status + const char *opcode_str = opcode_to_str (opcode); + const char *status_str = status_to_str (status); + + // print first line of header + printf (";; ->>HEADER<<- opcode: %s, status: %s, id: %u\n", opcode_str, + status_str, xid); + + // print set flags + printf (";; flags:"); + if (qr) + printf (" qr"); + if (aa) + printf (" aa"); + if (tc) + printf (" tc"); + if (rd) + printf (" rd"); + if (ra) + printf (" ra"); + + // print the last line + printf ("; QUERY: %u, ANSWER: %u, AUTHORITY: %u, ADDITIONAL: %u\n\n", + qdcount, ancount, nscount, arcount); +} + +// print question section +void +print_question_section (dns_query_t *query) +{ + // print first line + printf (";; QUESTION SECTION:\n"); + + char *domain = calloc (BUFFER_MAX_SIZE, sizeof (char)); + ssize_t pointer = 0; + parse_domain (query->domain, &pointer, &domain); + + printf (";%s %s %s\n", domain, qclass_to_str (query->qclass), + record_to_str (query->qtype)); + + free (domain); +} + +// print the answer section +void +print_answer_section (dns_answer_t *answer) +{ + char buf[BUFFER_LENGTH] = { 0 }; + snprintf (buf, sizeof (buf), "%s %s %u.", qclass_to_str (answer->qclass), + record_to_str (answer->qtype), answer->ttl); + + printf ("%-30s %-16s %s\n", answer->domain, buf, answer->rdata); +} + +ssize_t +parse_dns_header (unsigned char *bytes, dns_header_t *header) +{ + memcpy (header, bytes, sizeof (dns_header_t)); + + header->xid = ntohs (header->xid); + header->flags = ntohs (header->flags); + header->qdcount = ntohs (header->qdcount); + header->ancount = ntohs (header->ancount); + header->nscount = ntohs (header->nscount); + header->arcount = ntohs (header->arcount); + + return sizeof (dns_header_t); +} + +ssize_t +parse_dns_query (unsigned char *bytes, dns_query_t *query) +{ + ssize_t pointer = parse_dns_header (bytes, &(query->header)); + + query->domain = &bytes[pointer]; + while (bytes[pointer++] != 0x00) + ; + + query->qtype = (bytes[pointer] << 8) | bytes[pointer + 1]; + pointer += 2; + + query->qclass = (bytes[pointer] << 8) | bytes[pointer + 1]; + pointer += 2; + + return pointer; +} + +void +parse_dns_answer (unsigned char *bytes, ssize_t *pointer, dns_answer_t *answer) +{ + answer->domain = calloc (BUFFER_MAX_SIZE, sizeof (char)); + parse_domain (bytes, pointer, &(answer->domain)); + + answer->qtype = (bytes[*pointer] << 8) | bytes[*pointer + 1]; + *pointer += 2; + + answer->qclass = (bytes[*pointer] << 8) | bytes[*pointer + 1]; + *pointer += 2; + + for (int i = 3; i >= 0; i--) + { + answer->ttl |= bytes[(*pointer)++] << (8 * i); + } + + answer->rdlen = (bytes[*pointer] << 8) | bytes[*pointer + 1]; + *pointer += 2; + + answer->rdata = calloc (BUFFER_MAX_SIZE, sizeof (char)); + + if (answer->qtype == A || answer->qtype == AAAA) + { + answer->rdata = parse_ip_addr (&bytes[*pointer], answer->rdlen); + *pointer += answer->rdlen; + return; + } + + if (answer->qtype == NS || answer->qtype == CNAME || answer->qtype == PTR) + { + parse_domain (bytes, pointer, &(answer->rdata)); + return; + } + + if (answer->qtype == MX) + { + uint16_t preference = (bytes[*pointer] << 8) | bytes[*pointer + 1]; + *pointer += 2; + + char *exchange = calloc (BUFFER_LENGTH, sizeof (char)); + parse_domain (bytes, pointer, &exchange); + + snprintf (answer->rdata, BUFFER_MAX_SIZE, "%u %s", preference, exchange); + + free (exchange); + + return; + } + + if (answer->qtype == SOA) + { + char *mname = calloc (BUFFER_LENGTH, sizeof (char)); + parse_domain (bytes, pointer, &mname); + + char *rname = calloc (BUFFER_LENGTH, sizeof (char)); + parse_domain (bytes, pointer, &rname); + + uint32_t serial = merge_32 (&bytes[*pointer]); + uint32_t refresh = merge_32 (&bytes[*pointer + 4]); + uint32_t retry = merge_32 (&bytes[*pointer + 8]); + uint32_t expire = merge_32 (&bytes[*pointer + 12]); + uint32_t minimum = merge_32 (&bytes[*pointer + 16]); + + *pointer += 20; + + snprintf (answer->rdata, BUFFER_MAX_SIZE, "%s %s %u %u %u %u %u", mname, + rname, serial, refresh, retry, expire, minimum); + + free (mname); + free (rname); + + return; + } +} + +ssize_t +parse_dns_response (unsigned char *bytes, ssize_t query_len, + dns_response_t *response) +{ + ssize_t pointer = parse_dns_query (bytes, &(response->query)); + + response->answers + = calloc (response->query.header.ancount, sizeof (dns_answer_t)); + + for (size_t i = 0; i < response->query.header.ancount; i++) + { + dns_answer_t answer = { 0 }; + parse_dns_answer (bytes, &pointer, &answer); + memcpy (&(response->answers[i]), &answer, sizeof (dns_answer_t)); + } + + response->authorities + = calloc (response->query.header.nscount, sizeof (dns_answer_t)); + + for (size_t i = 0; i < response->query.header.nscount; i++) + { + dns_answer_t authoritative = { 0 }; + parse_dns_answer (bytes, &pointer, &authoritative); + memcpy (&(response->authorities[i]), &authoritative, + sizeof (dns_answer_t)); + } + + response->additionals + = calloc (response->query.header.arcount, sizeof (dns_answer_t)); + + for (size_t i = 0; i < response->query.header.arcount; i++) + { + dns_answer_t additional = { 0 }; + parse_dns_answer (bytes, &pointer, &additional); + memcpy (&(response->additionals[i]), &additional, sizeof (dns_answer_t)); + } + + return pointer; +} \ No newline at end of file diff --git a/p2-dns/src/dns.h b/p2-dns/src/dns.h new file mode 100644 index 0000000..941c17c --- /dev/null +++ b/p2-dns/src/dns.h @@ -0,0 +1,62 @@ +#ifndef __cs361_dns_h__ +#define __cs361_dns_h__ + +#include + +// Maximum buffer lengths allows +#define BUFFER_LENGTH 512 + +// Structure of the bytes for a DNS header +typedef struct +{ + uint16_t xid; + uint16_t flags; + uint16_t qdcount; + uint16_t ancount; + uint16_t nscount; + uint16_t arcount; +} dns_header_t; + +typedef struct +{ + dns_header_t header; + unsigned char *domain; + uint16_t qtype; + uint16_t qclass; +} dns_query_t; + +typedef struct +{ + char *domain; + uint16_t qtype; + uint16_t qclass; + uint32_t ttl; + uint16_t rdlen; + char *rdata; +} dns_answer_t; + +typedef struct +{ + dns_query_t query; + dns_answer_t *answers; + dns_answer_t *authorities; + dns_answer_t *additionals; +} dns_response_t; + +// Supported record types +#define A 1 +#define NS 2 +#define CNAME 5 +#define SOA 6 +#define PTR 12 +#define MX 15 +#define AAAA 28 + +void print_dns_header (dns_header_t *); +void print_question_section (dns_query_t *query); +void print_answer_section (dns_answer_t *answer); +ssize_t parse_dns_query (unsigned char *bytes, dns_query_t *query); +ssize_t parse_dns_response (unsigned char *bytes, ssize_t query_len, + dns_response_t *response); + +#endif diff --git a/p2-dns/src/port_utils.h b/p2-dns/src/port_utils.h new file mode 100644 index 0000000..b1700c9 --- /dev/null +++ b/p2-dns/src/port_utils.h @@ -0,0 +1,6 @@ +#ifndef __cs361_port_utils_h__ +#define __cs361_port_utils_h__ + +char *get_port (void); + +#endif diff --git a/p2-dns/src/utils.c b/p2-dns/src/utils.c new file mode 100644 index 0000000..562c2d0 --- /dev/null +++ b/p2-dns/src/utils.c @@ -0,0 +1,53 @@ +#include +#include +#include +#include + +#include "dns.h" +#include "port_utils.h" +#include "utils.h" + +// SUGGESTION: Use this file for any network-related functionality, such as +// sending or receiving messages. + +void +ptr_ip (char *dst, size_t dst_size, const char *ip) +{ + char tmp[64]; + strncpy (tmp, ip, sizeof (tmp)); + tmp[sizeof (tmp) - 1] = '\0'; + + char *octets[4]; + char *token = strtok (tmp, "."); + int count = 0; + while (token && count < 4) + { + octets[count++] = token; + token = strtok (NULL, "."); + } + + if (count != 4) + { + fprintf (stderr, "Invalid PTR IP"); + exit (EXIT_FAILURE); + } + + snprintf (dst, dst_size, "%s.%s.%s.%s.in-addr.arpa.", octets[3], octets[2], + octets[1], octets[0]); +} + +void +print_byte_block (uint8_t *bytes, size_t length) +{ + printf (" "); + for (int i = 0; i < length; i++) + { + printf ("%02x", bytes[i]); + if (i == length - 1) + printf ("\n"); + else if ((i + 1) % 16 == 0) + printf ("\n "); + else if ((i % 2) != 0) + printf (" "); + } +} diff --git a/p2-dns/src/utils.h b/p2-dns/src/utils.h new file mode 100644 index 0000000..95ffda9 --- /dev/null +++ b/p2-dns/src/utils.h @@ -0,0 +1,9 @@ +#ifndef __cs361_utils_h__ +#define __cs361_utils_h__ + +#include + +void ptr_ip (char *dst, size_t dst_size, const char *ip); +void print_byte_block (uint8_t *, size_t); + +#endif diff --git a/p2-dns/tests/Makefile b/p2-dns/tests/Makefile new file mode 100644 index 0000000..2d28414 --- /dev/null +++ b/p2-dns/tests/Makefile @@ -0,0 +1,87 @@ +# +# Simple Test Makefile +# Mike Lam, James Madison University, August 2016 +# +# This version of the Makefile includes support for building a test suite. The +# recommended framework is Check (http://check.sourceforge.net/). To build and +# run the test suite, execute the "test" target. The test suite must be located +# in a module called "testsuite". The MODS, LIBS, and OBJS variables work as +# they do in the main Makefile. +# +# To change the default build target (which executes when you just type +# "make"), change the right-hand side of the definition of the "default" +# target. +# +# By default, this makefile will build the project with debugging symbols and +# without optimization. To change this, edit or remove the "-g" and "-O0" +# options in CFLAGS and LDFLAGS accordingly. +# +# By default, this makefile build the application using the GNU C compiler, +# adhering to the C99 standard with all warnings enabled. + + +# application-specific settings and run target + +EXE=../digduke +TEST=testsuite +MODS=public.o +OBJS=../port_utils.o +LIBS= + +UTESTOUT=utests.txt +ITESTOUT=itests.txt +SCHECKOUT=style.txt + +default: $(TEST) + + +$(EXE): + make -C ../ + +test: itest style + @echo "========================================" + +utest: $(EXE) $(TEST) + @echo "========================================" + @echo " UNIT TESTS" + @./$(TEST) 2>/dev/null >$(UTESTOUT) + @cat $(UTESTOUT) | sed -n -e '/Checks/,$$p' | sed -e 's/^private.*:[EF]://g' + +itest: $(EXE) + @echo "========================================" + @echo " INTEGRATION TESTS" + @./integration.sh | tee $(ITESTOUT) + +style: $(EXE) + @echo "========================================" + @echo " CODING STYLE CHECK" + @./style.sh 2>/dev/null >$(SCHECKOUT) + @cat $(SCHECKOUT) + +# compiler/linker settings + +CC=gcc +CFLAGS=-g -O0 -Wall --std=gnu99 -pedantic +LDFLAGS=-g -O0 + +CFLAGS= +LIBS+=-lcheck -lm -lpthread + +ifeq ($(shell uname -s),Linux) + LIBS+=-lrt -lsubunit +endif + + +# build targets + +$(TEST): $(TEST).o $(MODS) $(OBJS) + $(CC) $(LDFLAGS) -o $(TEST) $^ $(LIBS) + +%.o: %.c + $(CC) -c $(CFLAGS) $< + +clean: + rm -rf $(TEST) $(TEST).o $(MODS) $(UTESTOUT) $(ITESTOUT) $(SCHECKOUT) outputs valgrind ckstyle + +.PHONY: default clean test unittest inttest + diff --git a/p2-dns/tests/dukens b/p2-dns/tests/dukens new file mode 100755 index 0000000..bd56065 Binary files /dev/null and b/p2-dns/tests/dukens differ diff --git a/p2-dns/tests/expected/A_cname_a.txt b/p2-dns/tests/expected/A_cname_a.txt new file mode 100644 index 0000000..3ff6b17 --- /dev/null +++ b/p2-dns/tests/expected/A_cname_a.txt @@ -0,0 +1,9 @@ +;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 32412 +;; flags: qr aa rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 0 + +;; QUESTION SECTION: +;stu. IN A + +;; ANSWER SECTION: +stu. IN CNAME 0. stu.cs.jmu.edu. +stu.cs.jmu.edu. IN A 0. 127.0.1.1 diff --git a/p2-dns/tests/expected/A_cname_additional.txt b/p2-dns/tests/expected/A_cname_additional.txt new file mode 100644 index 0000000..e9ff258 --- /dev/null +++ b/p2-dns/tests/expected/A_cname_additional.txt @@ -0,0 +1,11 @@ +;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 51120 +;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1 + +;; QUESTION SECTION: +;www.jmu.edu. IN CNAME + +;; ANSWER SECTION: +www.jmu.edu. IN CNAME 592. it-www1.jmu.edu. + +;; ADDITIONAL SECTION: +it-www1.jmu.edu. IN A 900. 134.126.126.99 diff --git a/p2-dns/tests/expected/A_cname_basic.txt b/p2-dns/tests/expected/A_cname_basic.txt new file mode 100644 index 0000000..7433f5b --- /dev/null +++ b/p2-dns/tests/expected/A_cname_basic.txt @@ -0,0 +1,8 @@ +;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 49602 +;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 + +;; QUESTION SECTION: +;stu.cs.jmu.edu. IN CNAME + +;; ANSWER SECTION: +stu.cs.jmu.edu. IN CNAME 1769. student.cs.jmu.edu. diff --git a/p2-dns/tests/expected/A_example_soa.txt b/p2-dns/tests/expected/A_example_soa.txt new file mode 100644 index 0000000..a6e7ae7 --- /dev/null +++ b/p2-dns/tests/expected/A_example_soa.txt @@ -0,0 +1,8 @@ +;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 52173 +;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 + +;; QUESTION SECTION: +;example.com. IN SOA + +;; ANSWER SECTION: +example.com. IN SOA 1803. ns.icann.org. noc.dns.icann.org. 2025011715 7200 3600 1209600 3600 diff --git a/p2-dns/tests/expected/A_jmu_soa.txt b/p2-dns/tests/expected/A_jmu_soa.txt new file mode 100644 index 0000000..8ea04be --- /dev/null +++ b/p2-dns/tests/expected/A_jmu_soa.txt @@ -0,0 +1,8 @@ +;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 62055 +;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 + +;; QUESTION SECTION: +;jmu.edu. IN SOA + +;; ANSWER SECTION: +jmu.edu. IN SOA 3600. it-ns.jmu.edu. network.jmu.edu. 735142168 86400 7200 3600000 3600 diff --git a/p2-dns/tests/expected/A_no_ipv6_authority.txt b/p2-dns/tests/expected/A_no_ipv6_authority.txt new file mode 100644 index 0000000..d4ab3d6 --- /dev/null +++ b/p2-dns/tests/expected/A_no_ipv6_authority.txt @@ -0,0 +1,11 @@ +;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 61906 +;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 0 + +;; QUESTION SECTION: +;www.jmu.edu. IN AAAA + +;; ANSWER SECTION: +www.jmu.edu. IN CNAME 592. it-www1.jmu.edu. + +;; AUTHORITY SECTION: +jmu.edu. IN SOA 3600. it-ns.jmu.edu. network.jmu.edu. 735142168 86400 7200 3600000 3600 diff --git a/p2-dns/tests/expected/A_no_ipv6_subauth.txt b/p2-dns/tests/expected/A_no_ipv6_subauth.txt new file mode 100644 index 0000000..2abcdf8 --- /dev/null +++ b/p2-dns/tests/expected/A_no_ipv6_subauth.txt @@ -0,0 +1,8 @@ +;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 10099 +;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 0 + +;; QUESTION SECTION: +;fac.cs.jmu.edu. IN AAAA + +;; AUTHORITY SECTION: +cs.jmu.edu. IN SOA 900. it-nios-gm.jmu.edu. network.jmu.edu. 14 10800 3600 2419200 900 diff --git a/p2-dns/tests/expected/A_no_ptr.txt b/p2-dns/tests/expected/A_no_ptr.txt new file mode 100644 index 0000000..8b85ea5 --- /dev/null +++ b/p2-dns/tests/expected/A_no_ptr.txt @@ -0,0 +1,8 @@ +;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 56769 +;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 0 + +;; QUESTION SECTION: +;5.67.101.151.in-addr.arpa. IN PTR + +;; AUTHORITY SECTION: +151.in-addr.arpa. IN SOA 3600. pri.authdns.ripe.net. dns.ripe.net. 1752675523 3600 600 864000 3600 diff --git a/p2-dns/tests/expected/A_ptr_basic.txt b/p2-dns/tests/expected/A_ptr_basic.txt new file mode 100644 index 0000000..42fea15 --- /dev/null +++ b/p2-dns/tests/expected/A_ptr_basic.txt @@ -0,0 +1,8 @@ +;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 870 +;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 + +;; QUESTION SECTION: +;221.141.126.134.in-addr.arpa. IN PTR + +;; ANSWER SECTION: +221.141.126.134.in-addr.arpa. IN PTR 3542. student.cs.jmu.edu. diff --git a/p2-dns/tests/expected/A_ptr_mixed.txt b/p2-dns/tests/expected/A_ptr_mixed.txt new file mode 100644 index 0000000..fdab6d7 --- /dev/null +++ b/p2-dns/tests/expected/A_ptr_mixed.txt @@ -0,0 +1,12 @@ +;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 45675 +;; flags: qr aa rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1 + +;; QUESTION SECTION: +;1.1.0.127.in-addr.arpa. IN PTR + +;; ANSWER SECTION: +1.1.0.127.in-addr.arpa. IN PTR 0. stu.cs.jmu.edu. +1.1.0.127.in-addr.arpa. IN PTR 0. stu. + +;; ADDITIONAL SECTION: +1.1.0.127.in-addr.arpa. IN CNAME 1769. student.cs.jmu.edu. diff --git a/p2-dns/tests/expected/A_ptr_multiple.txt b/p2-dns/tests/expected/A_ptr_multiple.txt new file mode 100644 index 0000000..db0daad --- /dev/null +++ b/p2-dns/tests/expected/A_ptr_multiple.txt @@ -0,0 +1,13 @@ +;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 57548 +;; flags: qr rd ra; QUERY: 1, ANSWER: 6, AUTHORITY: 0, ADDITIONAL: 0 + +;; QUESTION SECTION: +;99.126.126.134.in-addr.arpa. IN PTR + +;; ANSWER SECTION: +99.126.126.134.in-addr.arpa. IN PTR 1483. it-www1.jmu.edu. +99.126.126.134.in-addr.arpa. IN PTR 1483. spcecoms.jmu.edu. +99.126.126.134.in-addr.arpa. IN PTR 1483. groups.jmu.edu. +99.126.126.134.in-addr.arpa. IN PTR 1483. groups.dukes.jmu.edu. +99.126.126.134.in-addr.arpa. IN PTR 1483. it-www2.jmu.edu. +99.126.126.134.in-addr.arpa. IN PTR 1483. athletics.jmu.edu. diff --git a/p2-dns/tests/expected/B_a_multiple.txt b/p2-dns/tests/expected/B_a_multiple.txt new file mode 100644 index 0000000..256a023 --- /dev/null +++ b/p2-dns/tests/expected/B_a_multiple.txt @@ -0,0 +1,13 @@ +;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 7059 +;; flags: qr rd ra; QUERY: 1, ANSWER: 6, AUTHORITY: 0, ADDITIONAL: 0 + +;; QUESTION SECTION: +;example.com. IN A + +;; ANSWER SECTION: +example.com. IN A 300. 96.7.128.198 +example.com. IN A 300. 23.215.0.136 +example.com. IN A 300. 23.192.228.80 +example.com. IN A 300. 23.192.228.84 +example.com. IN A 300. 96.7.128.175 +example.com. IN A 300. 23.215.0.138 diff --git a/p2-dns/tests/expected/B_aaaa.txt b/p2-dns/tests/expected/B_aaaa.txt new file mode 100644 index 0000000..5616077 --- /dev/null +++ b/p2-dns/tests/expected/B_aaaa.txt @@ -0,0 +1,13 @@ +;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 4801 +;; flags: qr rd ra; QUERY: 1, ANSWER: 6, AUTHORITY: 0, ADDITIONAL: 0 + +;; QUESTION SECTION: +;example.com. IN AAAA + +;; ANSWER SECTION: +example.com. IN AAAA 21. 2600:1406:3a00:21::173e:2e65 +example.com. IN AAAA 21. 2600:1406:bc00:53::b81e:94c8 +example.com. IN AAAA 21. 2600:1408:ec00:36::1736:7f24 +example.com. IN AAAA 21. 2600:1406:3a00:21::173e:2e66 +example.com. IN AAAA 21. 2600:1408:ec00:36::1736:7f31 +example.com. IN AAAA 21. 2600:1406:bc00:53::b81e:94ce diff --git a/p2-dns/tests/expected/B_double_compress.txt b/p2-dns/tests/expected/B_double_compress.txt new file mode 100644 index 0000000..d1a5faf --- /dev/null +++ b/p2-dns/tests/expected/B_double_compress.txt @@ -0,0 +1,9 @@ +;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 42937 +;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 0 + +;; QUESTION SECTION: +;example.com. IN NS + +;; ANSWER SECTION: +example.com. IN NS 84682. b.iana-servers.net. +example.com. IN NS 84682. a.iana-servers.net. diff --git a/p2-dns/tests/expected/B_empty_domain.txt b/p2-dns/tests/expected/B_empty_domain.txt new file mode 100644 index 0000000..6510c0b --- /dev/null +++ b/p2-dns/tests/expected/B_empty_domain.txt @@ -0,0 +1,8 @@ +;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 57867 +;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 + +;; QUESTION SECTION: +;example.com. IN MX + +;; ANSWER SECTION: +example.com. IN MX 86400. 0 . diff --git a/p2-dns/tests/expected/B_jmu_mx.txt b/p2-dns/tests/expected/B_jmu_mx.txt new file mode 100644 index 0000000..b31325a --- /dev/null +++ b/p2-dns/tests/expected/B_jmu_mx.txt @@ -0,0 +1,8 @@ +;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 13835 +;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 + +;; QUESTION SECTION: +;jmu.edu. IN MX + +;; ANSWER SECTION: +jmu.edu. IN MX 1538. 0 jmu-edu.mail.protection.outlook.com. diff --git a/p2-dns/tests/expected/B_jmu_ns.txt b/p2-dns/tests/expected/B_jmu_ns.txt new file mode 100644 index 0000000..4901d19 --- /dev/null +++ b/p2-dns/tests/expected/B_jmu_ns.txt @@ -0,0 +1,9 @@ +;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 23379 +;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 0 + +;; QUESTION SECTION: +;jmu.edu. IN NS + +;; ANSWER SECTION: +jmu.edu. IN NS 1798. it-ns2-19.jmu.edu. +jmu.edu. IN NS 1798. it-ns1-19.jmu.edu. diff --git a/p2-dns/tests/expected/B_no_ipv6.txt b/p2-dns/tests/expected/B_no_ipv6.txt new file mode 100644 index 0000000..48e4b0c --- /dev/null +++ b/p2-dns/tests/expected/B_no_ipv6.txt @@ -0,0 +1,5 @@ +;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 54920 +;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 0 + +;; QUESTION SECTION: +;localhost. IN AAAA diff --git a/p2-dns/tests/expected/C_a_basic.txt b/p2-dns/tests/expected/C_a_basic.txt new file mode 100644 index 0000000..261ac39 --- /dev/null +++ b/p2-dns/tests/expected/C_a_basic.txt @@ -0,0 +1,8 @@ +;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 2 +;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 + +;; QUESTION SECTION: +;jmu.edu. IN A + +;; ANSWER SECTION: +jmu.edu. IN A 900. 134.126.126.99 diff --git a/p2-dns/tests/expected/C_a_compressed.txt b/p2-dns/tests/expected/C_a_compressed.txt new file mode 100644 index 0000000..c0ee7c9 --- /dev/null +++ b/p2-dns/tests/expected/C_a_compressed.txt @@ -0,0 +1,8 @@ +;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 46412 +;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 + +;; QUESTION SECTION: +;google.com. IN A + +;; ANSWER SECTION: +google.com. IN A 194. 142.250.31.113 diff --git a/p2-dns/tests/expected/C_a_localhost.txt b/p2-dns/tests/expected/C_a_localhost.txt new file mode 100644 index 0000000..27b3583 --- /dev/null +++ b/p2-dns/tests/expected/C_a_localhost.txt @@ -0,0 +1,8 @@ +;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 39713 +;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 + +;; QUESTION SECTION: +;localhost. IN A + +;; ANSWER SECTION: +localhost. IN A 0. 127.0.0.1 diff --git a/p2-dns/tests/expected/C_bad_domain.txt b/p2-dns/tests/expected/C_bad_domain.txt new file mode 100644 index 0000000..8cbe4af --- /dev/null +++ b/p2-dns/tests/expected/C_bad_domain.txt @@ -0,0 +1,5 @@ +;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 22016 +;; flags: qr aa rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 0 + +;; QUESTION SECTION: +;asdf. IN A diff --git a/p2-dns/tests/expected/D_ping-2.txt b/p2-dns/tests/expected/D_ping-2.txt new file mode 100644 index 0000000..e1c1651 --- /dev/null +++ b/p2-dns/tests/expected/D_ping-2.txt @@ -0,0 +1,14 @@ +Sending 17 bytes to 127.0.0.1: + 0001 0100 0001 0000 0000 0000 0000 0100 + 01 + +Received 51 bytes from 127.0.0.1: + 0001 8180 0001 0001 0000 0000 0000 0100 + 0101 620c 726f 6f74 2d73 6572 7665 7273 + 036e 6574 0000 0100 0100 008c a000 04aa + f7aa 02 + +Domain name: b.root-servers.net. +Record type: IN A +TTL: 36000 +IPv4 address: 170.247.170.2 diff --git a/p2-dns/tests/expected/D_ping.txt b/p2-dns/tests/expected/D_ping.txt new file mode 100644 index 0000000..05d93d1 --- /dev/null +++ b/p2-dns/tests/expected/D_ping.txt @@ -0,0 +1,14 @@ +Sending 17 bytes to 127.0.0.1: + 0001 0100 0001 0000 0000 0000 0000 0100 + 01 + +Received 51 bytes from 127.0.0.1: + 0001 8180 0001 0001 0000 0000 0000 0100 + 0101 610c 726f 6f74 2d73 6572 7665 7273 + 036e 6574 0000 0100 0100 0000 0a00 04c6 + 2900 04 + +Domain name: a.root-servers.net. +Record type: IN A +TTL: 10 +IPv4 address: 198.41.0.4 diff --git a/p2-dns/tests/integration.sh b/p2-dns/tests/integration.sh new file mode 100755 index 0000000..0739e3f --- /dev/null +++ b/p2-dns/tests/integration.sh @@ -0,0 +1,97 @@ +#!/bin/bash + +EXE="../digduke" + +function run_test { + + # parameters + TAG=$1 + shift + ARGS=$1 + shift + + PTAG=$(printf '%-30s' "$TAG") + if [[ $(echo $TAG | cut -d'_' -f1) == $SKIP_C || + $(echo $TAG | cut -d'_' -f1) == $SKIP_B || + $(echo $TAG | cut -d'_' -f1) == $SKIP_A ]] ; then + echo "$PTAG SKIPPED (previous phases not complete)" + return + fi + + # file paths + OUTPUT=outputs/$TAG.txt + DIFF=outputs/$TAG.diff + EXPECT=expected/$TAG.txt + VALGRND=valgrind/$TAG.txt + + # run test and compare output to the expected version + ./dukens -s 2 >/dev/null 2>&1 & + sleep 1 + $EXE $ARGS 2>/dev/null >"$OUTPUT" + for ARG in "$@" ; do + $EXE $ARG 2>/dev/null >>"$OUTPUT" + done + + diff -u "$OUTPUT" "$EXPECT" >"$DIFF" + passed=false + EFILES=$(find expected -type f -name "$TAG-*.txt") + if [ ! -s "$DIFF" ]; then + passed=true + elif [ "$EFILES" != "" ]; then + for EF in $EFILES ; do + DF=$(echo $EF | sed 's/^expected/outputs/' | sed 's/txt$/diff/') + diff -u "$OUTPUT" "$EF" >"$DF" + if [ ! -s "$DF" ]; then + passed=true + fi + done + fi + if [[ $passed = true ]] ; then + echo "$PTAG pass" + rm outputs/$TAG*diff + else + echo "$PTAG FAIL - Command line: $EXE $ARGS" + if [ "$EFILES" != "" ] ; then + echo "$(printf '%30s' ' ') *WARNING* This test has more than one possible output file." + echo "$(printf '%30s' ' ') *WARNING* Please inspect each manually for comparison." + else + echo "$(printf '%30s' ' ') See $DIFF for diff with expected output" + fi + echo "" + + if [[ $(echo $PTAG | cut -d'_' -f1) == 'D' ]] ; then + SKIP_C="C" + SKIP_B="B" + SKIP_A="A" + elif [[ $(echo $PTAG | cut -d'_' -f1) == 'C' ]] ; then + SKIP_B="B" + SKIP_A="A" + elif [[ $(echo $PTAG | cut -d'_' -f1) == 'B' ]] ; then + SKIP_A="A" + fi + fi + + # run valgrind +#./dukens -s 2 >/dev/null 2>&1 & +# sleep 1 +# valgrind $EXE $ARGS >"$VALGRND" 2>&1 +} + +# initialize output folders +mkdir -p outputs +mkdir -p valgrind +rm -f outputs/* valgrind/* + +# run individual tests +source itests.include + +# check for memory leaks +#LEAK=`cat valgrind/*.txt | grep 'definitely lost' | grep -v ' 0 bytes in 0 blocks'` +LEAK= +if [ -z "$LEAK" ]; then + echo "No memory leak found." +else + echo "Memory leak(s) found. See files listed below for details." + grep 'definitely lost' valgrind/*.txt | sed -e 's/:.*$//g' | sed -e 's/^/ - /g' +fi + diff --git a/p2-dns/tests/itests.include b/p2-dns/tests/itests.include new file mode 100644 index 0000000..c7551fe --- /dev/null +++ b/p2-dns/tests/itests.include @@ -0,0 +1,32 @@ +# list of integration tests +# format: run_test +# used as the root for all filenames (i.e., "expected/$TAG.txt") +# command-line arguments to test + +# sends a message to the server and prints the response in a formatted way +run_test D_ping "" + +run_test C_a_basic "-x 2 jmu.edu A" +run_test C_a_compressed "-x 46412 google.com A" +run_test C_a_localhost "-x 39713 localhost A" +run_test C_bad_domain "-x 22016 asdf A" + +run_test B_a_multiple "-x 7059 example.com A" +run_test B_aaaa "-x 4801 example.com AAAA" +run_test B_jmu_ns "-x 23379 jmu.edu NS" +run_test B_jmu_mx "-x 13835 jmu.edu MX" +run_test B_double_compress "-x 42937 example.com NS" +run_test B_empty_domain "-x 57867 example.com MX" +run_test B_no_ipv6 "-x 54920 localhost AAAA" + +run_test A_cname_basic "-x 49602 stu.cs.jmu.edu CNAME" +run_test A_cname_additional "-x 51120 www.jmu.edu CNAME" +run_test A_cname_a "-x 32412 stu A" +run_test A_jmu_soa "-x 62055 jmu.edu SOA" +run_test A_example_soa "-x 52173 example.com SOA" +run_test A_ptr_basic "-x 870 134.126.141.221 PTR" +run_test A_ptr_multiple "-x 57548 134.126.126.99 PTR" +run_test A_ptr_mixed "-x 45675 127.0.1.1 PTR" +run_test A_no_ptr "-x 56769 151.101.67.5 PTR" +run_test A_no_ipv6_authority "-x 61906 www.jmu.edu AAAA" +run_test A_no_ipv6_subauth "-x 10099 fac.cs.jmu.edu AAAA" diff --git a/p2-dns/tests/mappings.bin b/p2-dns/tests/mappings.bin new file mode 100644 index 0000000..763bb77 Binary files /dev/null and b/p2-dns/tests/mappings.bin differ diff --git a/p2-dns/tests/style.sh b/p2-dns/tests/style.sh new file mode 100755 index 0000000..0243934 --- /dev/null +++ b/p2-dns/tests/style.sh @@ -0,0 +1,53 @@ +#!/bin/bash + +STYLE="gnu" + +IGNORE=() +FAIL=0 + +function comp_file { + + SRC=$1 + SRC_NAME=$2 + + # file paths + FORMAT=ckstyle/${SRC_NAME}.$STYLE + DIFF=ckstyle/${SRC_NAME}.diff + + # run clang-format and compare results + clang-format --style=$STYLE $source > $FORMAT + diff -u $SRC $FORMAT >$DIFF + + PTAG=$(printf '%-30s' "$SRC_NAME") + if [ -s $DIFF ]; then + echo "$PTAG FAIL (see $DIFF for details)" + FAIL=1 + else + echo "$PTAG pass" + rm $DIFF + fi + rm $FORMAT +} + +mkdir -p ckstyle +rm -f ckstyle/* + +for source in $(ls ../src/*.c ../src/*.h) ; do + SKIP=0 + src=$(basename $source) + for ignore in ${IGNORE[*]} ; do + if [ "$src" = "$ignore" ] ; then + SKIP=1 + fi + done + if [ $SKIP = 0 ] ; then + comp_file $source $src + fi +done + +if [ $FAIL != 0 ] ; then + echo "Code that does not adhere to GNU standards will not be accepted." + echo "You must fix these files before submission." +else + echo "Code correctly adheres to required style." +fi diff --git a/p2-dns/tests/testsuite.c b/p2-dns/tests/testsuite.c new file mode 100644 index 0000000..e69de29 diff --git a/p3-dhcps/.gitignore b/p3-dhcps/.gitignore new file mode 100644 index 0000000..d6a7df0 --- /dev/null +++ b/p3-dhcps/.gitignore @@ -0,0 +1,27 @@ +*.swp +*.swo + +# Ignore all compiled files regardless of location +build/*.o +tests/public.o +tests/testsuite.o + +# Ignore executables for this project +tests/testsuite +dhcps + +# But don't ignore prebuilt test versions +!tests/client +!tests/threads + +# Ignore test outputs +tests/ckstyle +tests/itests.txt +tests/outputs +tests/style.txt +tests/valgrind +tests/utests.txt + +# Ignore all vscode stuff and NFS files +**/.nfs* +**/.vscode diff --git a/p3-dhcps/Makefile b/p3-dhcps/Makefile new file mode 100644 index 0000000..52eb99c --- /dev/null +++ b/p3-dhcps/Makefile @@ -0,0 +1,59 @@ +# +# Simple Makefile +# Mike Lam, James Madison University, August 2016 +# +# This makefile builds a simple application that contains a main module +# (specified by the EXE variable) and a predefined list of additional modules +# (specified by the MODS variable). If there are any external library +# dependencies (e.g., the math library, "-lm"), list them in the LIBS variable. +# If there are any precompiled object files, list them in the OBJS variable. +# +# By default, this makefile will build the project with debugging symbols and +# without optimization. To change this, edit or remove the "-g" and "-O0" +# options in CFLAGS and LDFLAGS accordingly. +# +# By default, this makefile build the application using the GNU C compiler, +# adhering to the C99 standard with all warnings enabled. + + +# application-specific settings and run target + +EXE=dhcps +MODS=dhcp.o format.o main.o server.o +OBJS=port_utils.o +LIBS=-lm + +default: build $(EXE) + +build: + mkdir build + +test: build $(EXE) + make -C tests test + +style: $(EXE) + make -C tests style + +# compiler/linker settings + +CC=gcc +CFLAGS=-g -O0 -Wall --std=gnu99 -pedantic +LDFLAGS=-g -O0 -pthread + + +# build targets + +BUILD=$(addprefix build/, $(MODS)) + +$(EXE): build/main.o $(BUILD) $(OBJS) + $(CC) $(LDFLAGS) -o $(EXE) $^ $(LIBS) + +build/%.o: src/%.c + $(CC) -c $(CFLAGS) -o $@ $< + +clean: + rm -rf $(EXE) build + make -C tests clean + +.PHONY: default clean + diff --git a/p3-dhcps/port_utils.o b/p3-dhcps/port_utils.o new file mode 100644 index 0000000..a576d09 Binary files /dev/null and b/p3-dhcps/port_utils.o differ diff --git a/p3-dhcps/src/dhcp.c b/p3-dhcps/src/dhcp.c new file mode 100644 index 0000000..3e61869 --- /dev/null +++ b/p3-dhcps/src/dhcp.c @@ -0,0 +1,193 @@ +#include "dhcp.h" +#include +#include +#include +#include + +void +dump_packet (uint8_t *packet, size_t length) +{ + for (size_t i = 0; i < length; i++) + { + printf ("%02x ", packet[i]); + if ((i + 1) % 16 == 0) + { + printf ("\n"); + } + } + if (length % 16 != 0) + { + printf ("\n"); + } +} + +void +free_options (options_t *options) +{ + if (options->request) + { + free (options->request); + options->request = NULL; + } + if (options->lease) + { + free (options->lease); + options->lease = NULL; + } + if (options->type) + { + free (options->type); + options->type = NULL; + } + if (options->sid) + { + free (options->sid); + options->sid = NULL; + } +} + +bool +get_options (uint8_t *packet, uint8_t *end, options_t *options) +{ + memset (options, 0, sizeof (options_t)); + + const size_t DHCP_FIXED_SIZE = sizeof (msg_t); + uint8_t *ptr = packet + DHCP_FIXED_SIZE; + + if (ptr + 4 > end) + { + return false; + } + + uint32_t cookie; + memcpy (&cookie, ptr, 4); + if (ntohl (cookie) != MAGIC_COOKIE) + { + return false; + } + ptr += 4; + + // Parse options + while (ptr < end) + { + uint8_t code = *ptr++; + + if (code == DHCP_opt_end) + { + break; + } + + if (ptr >= end) + { + break; + } + + uint8_t len = *ptr++; + + if (ptr + len > end) + { + break; + } + + switch (code) + { + case DHCP_opt_msgtype: + if (len == 1) + { + options->type = malloc (sizeof (uint8_t)); + *options->type = *ptr; + } + break; + case DHCP_opt_reqip: + if (len == 4) + { + options->request = malloc (sizeof (struct in_addr)); + memcpy (options->request, ptr, 4); + } + break; + case DHCP_opt_lease: + if (len == 4) + { + options->lease = malloc (sizeof (uint32_t)); + memcpy (options->lease, ptr, 4); + } + break; + case DHCP_opt_sid: + if (len == 4) + { + options->sid = malloc (sizeof (struct in_addr)); + memcpy (options->sid, ptr, 4); + } + break; + } + + ptr += len; + } + + return true; +} + +uint8_t * +append_cookie (uint8_t *packet, size_t *packet_size) +{ + size_t new_size = *packet_size + 4; + uint8_t *new_packet = realloc (packet, new_size); + + if (!new_packet) + { + return packet; + } + + uint32_t cookie = htonl (MAGIC_COOKIE); + memcpy (new_packet + *packet_size, &cookie, 4); + + *packet_size = new_size; + return new_packet; +} + +uint8_t * +append_option (uint8_t *packet, size_t *packet_size, uint8_t option, + uint8_t option_size, uint8_t *option_value) +{ + size_t new_size; + + if (option == DHCP_opt_end) + { + new_size = *packet_size + 1; + uint8_t *new_packet = realloc (packet, new_size); + + if (!new_packet) + { + return packet; + } + + // Add the end option code + new_packet[*packet_size] = DHCP_opt_end; + + *packet_size = new_size; + return new_packet; + } + + new_size = *packet_size + 2 + option_size; + uint8_t *new_packet = realloc (packet, new_size); + + if (!new_packet) + { + return packet; + } + + // Add option code + new_packet[*packet_size] = option; + + // Add option length + new_packet[*packet_size + 1] = option_size; + + // Add option value + if (option_size > 0 && option_value) + { + memcpy (new_packet + *packet_size + 2, option_value, option_size); + } + + *packet_size = new_size; + return new_packet; +} diff --git a/p3-dhcps/src/dhcp.h b/p3-dhcps/src/dhcp.h new file mode 100644 index 0000000..663b801 --- /dev/null +++ b/p3-dhcps/src/dhcp.h @@ -0,0 +1,120 @@ +#ifndef __cs361_dhcp_h__ +#define __cs361_dhcp_h__ + +#include +#include +#include + +// For reference, see: +// https://www.iana.org/assignments/bootp-dhcp-parameters/bootp-dhcp-parameters.xhtml +// https://www.iana.org/assignments/arp-parameters/arp-parameters.xhtml + +// Rules for MUST and MUST NOT options, as well as values of BOOTP fields: +// https://www.rfc-editor.org/rfc/rfc2131 + +// Option interpretations: +// https://www.rfc-editor.org/rfc/rfc2132 + +#define MAX_DHCP_LENGTH 576 + +// Possible BOOTP message op codes: +#define BOOTREQUEST 1 +#define BOOTREPLY 2 + +// Hardware types (htype) per ARP specifications: +#define ETH 1 +#define IEEE802 6 +#define ARCNET 7 +#define FRAME_RELAY 15 +#define FIBRE 18 + +// Hardware address lengths (hlen) per ARP specifications: +#define ETH_LEN 6 +#define IEEE802_LEN 6 +#define ARCNET_LEN 1 +#define FRAME_LEN 2 +#define FIBRE_LEN 3 + +// For DHCP messages, options must begin with this value: +#define MAGIC_COOKIE 0x63825363 + +// DHCP Message types +// If client detects offered address in use, MUST send DHCP Decline +// If server detects requested address in use, SHOULD send DHCP NAK +// Client can release its own IP address with DHCP Release +#define DHCPDISCOVER 1 +#define DHCPOFFER 2 +#define DHCPREQUEST 3 +#define DHCPDECLINE 4 +#define DHCPACK 5 +#define DHCPNAK 6 +#define DHCPRELEASE 7 + +// DHCP Option types +// All messages will use only these message types +#define DHCP_opt_reqip 50 +#define DHCP_opt_lease 51 +#define DHCP_opt_msgtype 53 +#define DHCP_opt_sid 54 +#define DHCP_opt_end 255 + +// BOOTP message type struct. This has an exact size. Add other fields to +// match the structure as defined in RFC 1542 and RFC 2131. This struct +// should NOT include the BOOTP vend field or the DHCP options field. +// (DHCP options replaced BOOTP vend, but does not have a fixed size and +// cannot be declared in a fixed-size struct.) +typedef struct +{ + uint8_t op; + uint8_t htype; + uint8_t hlen; + uint8_t hops; // always 0 + uint32_t xid; // selected by client/server + uint16_t secs; // seconds since DHCP process started + uint16_t flags; // leading bit is for broadcast...all others must be zero + struct in_addr ciaddr; // Client IP address + struct in_addr yiaddr; // "Your" IP address + struct in_addr siaddr; // Server IP address + struct in_addr giaddr; // Gateway IP address + uint8_t chaddr[16]; // Client hardware address 08002B2ED85E + // uint8_t padding[192]; // Skip the next 192 bytes...legacy BOOTP support + uint8_t sname[64]; // Server sending DHCPOFFER or DHCPACK (optional) + uint8_t file[128]; // Boot file (used in BOOTP from client) +} msg_t; + +// Helper struct for gathering and setting DHCP options +// DHCP: Magic Cookie 0x63825363 +typedef struct +{ + struct in_addr *request; // DHCP: [50] Requested IP address + uint32_t *lease; // DHCP: [51] IP Address Lease Time = 16 Days, 0:00:00 + uint8_t *type; // DHCP: [53] DHCP Message Type = DHCP ACK, 1+1+1 + struct in_addr *sid; // DHCP: [54] Server Identifier = 157.54.48.151, 1+1+N +} options_t; +// DHCP: [255] End (no data) + +// Utility function for printing the raw bytes of a packet: +void dump_packet (uint8_t *, size_t); + +// Utility functions for getting and freeing the DHCP options +// If you have read in a message from a socket, you can get the +// DHCP options as follows: +// nbytes = recvfrom (socketfd, buffer, length, 0, &addr, &addrlen); +// get_options (buffer, buffer + nbytes - 1, &opts); +// If an option is set (e.g., server ID), then options.sid would not be +// NULL. +void free_options (options_t *options); +bool get_options (uint8_t *packet, uint8_t *end, options_t *options); + +// Utility functions to append a DHCP cookie and a single DHCP option to the +// end of the packet. In both cases, packet_size is the current length of +// the array and packet points at the start of it. To set an option (e.g., +// message type), you can call: +// packet = append_option (packet, &size, DHCP_opt_msgtype, 1, DHCPOFFER); +// The return value is the packet but resized to also store the option. Note +// that the size variable is updated to specify the new length. +uint8_t *append_cookie (uint8_t *packet, size_t *packet_size); +uint8_t *append_option (uint8_t *packet, size_t *packet_size, uint8_t option, + uint8_t option_size, uint8_t *option_value); + +#endif diff --git a/p3-dhcps/src/format.c b/p3-dhcps/src/format.c new file mode 100644 index 0000000..9ee07c4 --- /dev/null +++ b/p3-dhcps/src/format.c @@ -0,0 +1,207 @@ +#include "format.h" +#include "dhcp.h" +#include +#include +#include + +const char * +get_htype_name (uint8_t htype) +{ + switch (htype) + { + case ETH: + return "Ethernet (10Mb)"; + case IEEE802: + return "IEEE 802 Networks"; + case ARCNET: + return "ARCNET"; + case FRAME_RELAY: + return "Frame Relay"; + case FIBRE: + return "Fibre Channel"; + default: + return "Unknown"; + } +} + +const char * +get_dhcp_msg_type (uint8_t type) +{ + switch (type) + { + case DHCPDISCOVER: + return "DHCP Discover"; + case DHCPOFFER: + return "DHCP Offer"; + case DHCPREQUEST: + return "DHCP Request"; + case DHCPDECLINE: + return "DHCP Decline"; + case DHCPACK: + return "DHCP ACK"; + case DHCPNAK: + return "DHCP NAK"; + case DHCPRELEASE: + return "DHCP Release"; + default: + return "UNKNOWN"; + } +} + +void +format_time (uint32_t seconds, char *buffer) +{ + uint32_t days = seconds / 86400; + uint32_t hours = (seconds % 86400) / 3600; + uint32_t mins = (seconds % 3600) / 60; + uint32_t secs = seconds % 60; + sprintf (buffer, "%u Days, %u:%02u:%02u", days, hours, mins, secs); +} + +void +dump_msg (FILE *out, msg_t *msg, size_t nbytes) +{ + fprintf (out, "\n"); + fprintf (out, "------------------------------------------------------\n"); + fprintf (out, "BOOTP Options\n"); + fprintf (out, "------------------------------------------------------\n"); + + // Print op code + fprintf (out, "Op Code (op) = %d [%s]\n", msg->op, + msg->op == BOOTREQUEST ? "BOOTREQUEST" : "BOOTREPLY"); + + // Print hardware type and length + fprintf (out, "Hardware Type (htype) = %d [%s]\n", msg->htype, + get_htype_name (msg->htype)); + fprintf (out, "Hardware Address Length (hlen) = %d\n", msg->hlen); + fprintf (out, "Hops (hops) = %d\n", msg->hops); + + // Print XID + fprintf (out, "Transaction ID (xid) = %u (0x%x)\n", ntohl (msg->xid), + ntohl (msg->xid)); + + // Print SECS + char time_str[64]; + format_time (ntohs (msg->secs), time_str); + fprintf (out, "Seconds (secs) = %s\n", time_str); + + // Print FLAGS + fprintf (out, "Flags (flags) = %u\n", ntohs (msg->flags)); + + // Print IP addresses + char ip_str[INET_ADDRSTRLEN]; + + inet_ntop (AF_INET, &msg->ciaddr, ip_str, INET_ADDRSTRLEN); + fprintf (out, "Client IP Address (ciaddr) = %s\n", ip_str); + + inet_ntop (AF_INET, &msg->yiaddr, ip_str, INET_ADDRSTRLEN); + fprintf (out, "Your IP Address (yiaddr) = %s\n", ip_str); + + inet_ntop (AF_INET, &msg->siaddr, ip_str, INET_ADDRSTRLEN); + fprintf (out, "Server IP Address (siaddr) = %s\n", ip_str); + + inet_ntop (AF_INET, &msg->giaddr, ip_str, INET_ADDRSTRLEN); + fprintf (out, "Relay IP Address (giaddr) = %s\n", ip_str); + + // Print CHADDR (hardware address) + fprintf (out, "Client Ethernet Address (chaddr) = "); + for (int i = 0; i < msg->hlen && i < 16; i++) + { + fprintf (out, "%02x", msg->chaddr[i]); + } + fprintf (out, "\n"); + + // Parse DHCP options + fprintf (out, "------------------------------------------------------\n"); + fprintf (out, "DHCP Options\n"); + fprintf (out, "------------------------------------------------------\n"); + + const size_t DHCP_FIXED_SIZE = sizeof (msg_t); + uint8_t *ptr = (uint8_t *)msg + DHCP_FIXED_SIZE; + uint8_t *end = (uint8_t *)msg + nbytes; + + // Check for magic cookie + if (ptr + 4 > end) + { + fprintf (out, "\n"); + return; + } + + uint32_t cookie; + memcpy (&cookie, ptr, 4); + if (ntohl (cookie) != MAGIC_COOKIE) + { + fprintf (out, "\n"); + return; + } + + fprintf (out, "Magic Cookie = [OK]\n"); + ptr += 4; + + // Temporary variables to store options + uint8_t msg_type = 0; + struct in_addr request_ip = { 0 }; + struct in_addr server_id = { 0 }; + uint32_t lease_time = 0; + + // Parse options and store them + while (ptr < end) + { + uint8_t code = *ptr++; + if (code == DHCP_opt_end) + break; + if (ptr >= end) + break; + + uint8_t len = *ptr++; + if (ptr + len > end) + break; + + switch (code) + { + case DHCP_opt_msgtype: + if (len == 1) + msg_type = *ptr; + break; + case DHCP_opt_reqip: + if (len == 4) + memcpy (&request_ip, ptr, 4); + break; + case DHCP_opt_sid: + if (len == 4) + memcpy (&server_id, ptr, 4); + break; + case DHCP_opt_lease: + if (len == 4) + memcpy (&lease_time, ptr, 4); + break; + } + + ptr += len; + } + + // Print options in fixed order + if (msg_type) + { + fprintf (out, "Message Type = %s\n", get_dhcp_msg_type (msg_type)); + } + + if (request_ip.s_addr) + { + inet_ntop (AF_INET, &request_ip, ip_str, INET_ADDRSTRLEN); + fprintf (out, "Request = %s\n", ip_str); + } + + if (lease_time) + { + char lease_str[64]; + format_time (ntohl (lease_time), lease_str); + fprintf (out, "IP Address Lease Time = %s\n", lease_str); + } + + if (server_id.s_addr) + { + inet_ntop (AF_INET, &server_id, ip_str, INET_ADDRSTRLEN); + fprintf (out, "Server Identifier = %s\n", ip_str); + } +} diff --git a/p3-dhcps/src/format.h b/p3-dhcps/src/format.h new file mode 100644 index 0000000..5aa1e81 --- /dev/null +++ b/p3-dhcps/src/format.h @@ -0,0 +1,12 @@ +#ifndef __cs361_format__ +#define __cs361_format__ + +#include "dhcp.h" +#include + +// Print the DHCP message to the specified output file stream +void dump_msg (FILE *, msg_t *, size_t); + +extern bool debug; + +#endif diff --git a/p3-dhcps/src/main.c b/p3-dhcps/src/main.c new file mode 100644 index 0000000..133fece --- /dev/null +++ b/p3-dhcps/src/main.c @@ -0,0 +1,54 @@ +#include +#include +#include +#include + +#include "format.h" +#include "port_utils.h" +#include "server.h" + +static bool get_args (int, char **, long *); + +bool debug = false; + +int +main (int argc, char **argv) +{ + long to_seconds = 2; + bool success = get_args (argc, argv, &to_seconds); + if (!success) + return EXIT_FAILURE; + + char *protocol = get_port (); + int socketfd = setup_server (protocol, to_seconds); + if (socketfd < 0) + return EXIT_FAILURE; + + // Indicate (for debugging) that the server is running + fprintf (stderr, "Server is started on port %s\n", protocol); + + if (debug) + fprintf (stderr, "Shutting down\n"); + return EXIT_SUCCESS; +} + +static bool +get_args (int argc, char **argv, long *to_seconds) +{ + int ch = 0; + while ((ch = getopt (argc, argv, "dhs:")) != -1) + { + switch (ch) + { + case 'd': + debug = true; + break; + case 's': + *to_seconds = atol (optarg); + break; + default: + return false; + } + } + return true; +} diff --git a/p3-dhcps/src/port_utils.h b/p3-dhcps/src/port_utils.h new file mode 100644 index 0000000..b1700c9 --- /dev/null +++ b/p3-dhcps/src/port_utils.h @@ -0,0 +1,6 @@ +#ifndef __cs361_port_utils_h__ +#define __cs361_port_utils_h__ + +char *get_port (void); + +#endif diff --git a/p3-dhcps/src/server.c b/p3-dhcps/src/server.c new file mode 100644 index 0000000..dd364f6 --- /dev/null +++ b/p3-dhcps/src/server.c @@ -0,0 +1,519 @@ +#include "server.h" +#include "dhcp.h" +#include "format.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct in_addr THIS_SERVER; +client_lease_t client_leases[MAX_CLIENTS] = { 0 }; + +void +reset_server_state () +{ + memset (client_leases, 0, sizeof (client_leases)); + for (int i = 0; i < MAX_CLIENTS; i++) + { + client_leases[i].xid = 0; + client_leases[i].hlen = 0; + client_leases[i].has_offer = 0; + client_leases[i].has_lease = 0; + memset (client_leases[i].chaddr, 0, 16); + memset (&client_leases[i].offered_ip, 0, sizeof (struct in_addr)); + memset (&client_leases[i].assigned_ip, 0, sizeof (struct in_addr)); + } +} + +// Find a client by XID (either with pending offer or confirmed lease) +client_lease_t * +find_client_by_xid (uint32_t xid) +{ + for (int i = 0; i < MAX_CLIENTS; i++) + { + if (client_leases[i].xid == xid + && (client_leases[i].has_offer || client_leases[i].has_lease)) + { + return &client_leases[i]; + } + } + return NULL; +} + +struct in_addr +get_next_available_ip (uint8_t *chaddr, uint8_t hlen) +{ + struct in_addr ip = { 0 }; + char ip_str[16]; + + for (int i = 0; i < MAX_CLIENTS; i++) + { + if (client_leases[i].hlen == hlen + && memcmp (client_leases[i].chaddr, chaddr, hlen) == 0) + { + // Prefer the assigned IP if present, otherwise offered IP. + if (client_leases[i].assigned_ip.s_addr != 0) + return client_leases[i].assigned_ip; + if (client_leases[i].offered_ip.s_addr != 0) + return client_leases[i].offered_ip; + } + } + + for (int i = 1; i <= MAX_CLIENTS; i++) + { + snprintf (ip_str, sizeof (ip_str), "192.168.1.%d", i); + + memset (&ip, 0, sizeof (ip)); + if (inet_pton (AF_INET, ip_str, &ip) != 1) + continue; + + int in_use = 0; + int ever_assigned = 0; + for (int j = 0; j < MAX_CLIENTS; j++) + { + if (client_leases[j].assigned_ip.s_addr == ip.s_addr + && client_leases[j].assigned_ip.s_addr != 0) + ever_assigned = 1; + if ((client_leases[j].has_offer + && client_leases[j].offered_ip.s_addr == ip.s_addr) + || (client_leases[j].has_lease + && client_leases[j].assigned_ip.s_addr == ip.s_addr)) + { + in_use = 1; + break; + } + } + + if (!ever_assigned && !in_use) + { + return ip; + } + } + + for (int last = 1; last <= MAX_CLIENTS; last++) + { + snprintf (ip_str, sizeof (ip_str), "192.168.1.%d", last); + memset (&ip, 0, sizeof (ip)); + if (inet_pton (AF_INET, ip_str, &ip) != 1) + continue; + + int owner_idx = -1; + int currently_offered_or_leased = 0; + for (int j = 0; j < MAX_CLIENTS; j++) + { + if (client_leases[j].assigned_ip.s_addr == ip.s_addr + && client_leases[j].assigned_ip.s_addr != 0) + owner_idx = j; + if (client_leases[j].has_offer + && client_leases[j].offered_ip.s_addr == ip.s_addr) + currently_offered_or_leased = 1; + if (client_leases[j].has_lease + && client_leases[j].assigned_ip.s_addr == ip.s_addr) + currently_offered_or_leased = 1; // treat leased as in-use + if (currently_offered_or_leased) + break; + } + + if (owner_idx != -1 && !client_leases[owner_idx].has_lease + && !currently_offered_or_leased) + { + return ip; + } + } + + ip.s_addr = 0; + return ip; +} + +// Find first empty slot in client_leases array +int +find_empty_slot () +{ + for (int i = 0; i < MAX_CLIENTS; i++) + { + if (!client_leases[i].has_offer && !client_leases[i].has_lease) + { + return i; + } + } + return -1; +} + +uint8_t +phase1_handle_request (msg_t *request, options_t *req_opts, msg_t *response) +{ + uint8_t msg_type; + if (req_opts->type && *req_opts->type == DHCPREQUEST) + { + msg_type = DHCPACK; + if (req_opts->request) + { + memcpy (&response->yiaddr, req_opts->request, + sizeof (struct in_addr)); + } + else + { + memcpy (&response->yiaddr, &request->ciaddr, + sizeof (struct in_addr)); + } + } + else + { + msg_type = DHCPOFFER; + inet_pton (AF_INET, "192.168.1.1", &response->yiaddr); + } + + return msg_type; +} + +uint8_t +handle_dhcp_discover (msg_t *request, options_t *req_opts, msg_t *response) +{ + uint8_t msg_type; + uint32_t xid = ntohl (request->xid); + + client_lease_t *existing_lease = NULL; + for (int i = 0; i < MAX_CLIENTS; i++) + { + if (client_leases[i].has_lease && client_leases[i].hlen == request->hlen + && memcmp (client_leases[i].chaddr, request->chaddr, request->hlen) + == 0) + { + existing_lease = &client_leases[i]; + break; + } + } + + if (existing_lease) + { + msg_type = DHCPOFFER; + memcpy (&response->yiaddr, &existing_lease->assigned_ip, + sizeof (struct in_addr)); + existing_lease->xid = xid; + existing_lease->offered_ip = existing_lease->assigned_ip; + existing_lease->has_offer = 1; + return msg_type; + } + + client_lease_t *pending_offer = NULL; + for (int i = 0; i < MAX_CLIENTS; i++) + { + if (client_leases[i].has_offer && !client_leases[i].has_lease + && client_leases[i].hlen == request->hlen + && memcmp (client_leases[i].chaddr, request->chaddr, request->hlen) + == 0) + { + pending_offer = &client_leases[i]; + break; + } + } + + if (pending_offer) + { + msg_type = DHCPOFFER; + memcpy (&response->yiaddr, &pending_offer->offered_ip, + sizeof (struct in_addr)); + pending_offer->xid = xid; + return msg_type; + } + + struct in_addr new_ip + = get_next_available_ip (request->chaddr, request->hlen); + + if (debug) + { + char ip_str[INET_ADDRSTRLEN]; + inet_ntop (AF_INET, &new_ip, ip_str, INET_ADDRSTRLEN); + fprintf (stderr, "New client - offering IP: %s\n", ip_str); + } + + if (new_ip.s_addr == 0) + { + msg_type = DHCPNAK; + memset (&response->yiaddr, 0, sizeof (struct in_addr)); + return msg_type; + } + + msg_type = DHCPOFFER; + memcpy (&response->yiaddr, &new_ip, sizeof (struct in_addr)); + + int slot = find_empty_slot (); + if (slot >= 0) + { + memset (&client_leases[slot], 0, sizeof (client_lease_t)); + + client_leases[slot].xid = xid; + memcpy (client_leases[slot].chaddr, request->chaddr, request->hlen); + client_leases[slot].hlen = request->hlen; + client_leases[slot].offered_ip = new_ip; + client_leases[slot].has_offer = 1; + client_leases[slot].has_lease = 0; + } + + return msg_type; +} + +uint8_t +handle_dhcp_request (msg_t *request, options_t *req_opts, msg_t *response) +{ + uint8_t msg_type; + uint32_t xid = ntohl (request->xid); + + client_lease_t *client = find_client_by_xid (xid); + + if (!client) + { + msg_type = DHCPNAK; + memset (&response->yiaddr, 0, sizeof (struct in_addr)); + return msg_type; + } + + int ip_match = 0; + if (req_opts->request) + { + ip_match = (memcmp (req_opts->request, &client->offered_ip, + sizeof (struct in_addr)) + == 0); + } + int server_match = 0; + if (req_opts->sid) + { + server_match + = (memcmp (req_opts->sid, &THIS_SERVER, sizeof (struct in_addr)) + == 0); + } + + if (ip_match && server_match) + { + msg_type = DHCPACK; + memcpy (&response->yiaddr, &client->offered_ip, sizeof (struct in_addr)); + + client->assigned_ip = client->offered_ip; + client->has_lease = 1; + client->has_offer = 0; + } + else + { + msg_type = DHCPNAK; + memset (&response->yiaddr, 0, sizeof (struct in_addr)); + + memset (client, 0, sizeof (client_lease_t)); + } + + return msg_type; +} + +// Create DHCP response +uint8_t * +create_response (msg_t *request, size_t *response_size, options_t *req_opts) +{ + msg_t response = { 0 }; + memcpy (&response, request, sizeof (msg_t)); + + response.op = BOOTREPLY; + memset (&response.siaddr, 0, sizeof (struct in_addr)); + + uint8_t msg_type; + uint32_t xid = ntohl (request->xid); + + // Phase 1 if the xid is 0 + if (xid == 0) + { + msg_type = phase1_handle_request (request, req_opts, &response); + } + // Phase 2 and beyond + else + { + if (req_opts->type && *req_opts->type == DHCPDISCOVER) + { + msg_type = handle_dhcp_discover (request, req_opts, &response); + } + else if (req_opts->type && *req_opts->type == DHCPREQUEST) + { + msg_type = handle_dhcp_request (request, req_opts, &response); + } + else + { + msg_type = DHCPOFFER; + inet_pton (AF_INET, "192.168.1.1", &response.yiaddr); + } + } + + size_t buffer_size = sizeof (msg_t); + uint8_t *response_buffer = calloc (1, buffer_size); + if (!response_buffer) + return NULL; + + memcpy (response_buffer, &response, sizeof (msg_t)); + + response_buffer = append_cookie (response_buffer, &buffer_size); + response_buffer + = append_option (response_buffer, &buffer_size, DHCP_opt_msgtype, + sizeof (msg_type), &msg_type); + + if (msg_type == DHCPOFFER || msg_type == DHCPACK) + { + uint32_t lease_time = htonl (60 * 60 * 24 * 30); + response_buffer + = append_option (response_buffer, &buffer_size, DHCP_opt_lease, + sizeof (lease_time), (uint8_t *)&lease_time); + } + + response_buffer + = append_option (response_buffer, &buffer_size, DHCP_opt_sid, + sizeof (THIS_SERVER), (uint8_t *)&THIS_SERVER); + response_buffer + = append_option (response_buffer, &buffer_size, DHCP_opt_end, 0, NULL); + + *response_size = buffer_size; + + return response_buffer; +} + +int +setup_server (char *port_str, long timeout_seconds) +{ + reset_server_state (); + + int sockfd; + struct sockaddr_in server_addr; + int port = atoi (port_str); + + // Create UDP socket + sockfd = socket (AF_INET, SOCK_DGRAM, 0); + if (sockfd < 0) + { + perror ("socket"); + return -1; + } + + if (timeout_seconds > 0) + { + struct timeval tv; + tv.tv_sec = timeout_seconds; + tv.tv_usec = 0; + setsockopt (sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof (tv)); + } + + // Bind socket + memset (&server_addr, 0, sizeof (server_addr)); + server_addr.sin_family = AF_INET; + server_addr.sin_addr.s_addr = INADDR_ANY; + server_addr.sin_port = htons (port); + + if (bind (sockfd, (struct sockaddr *)&server_addr, sizeof (server_addr)) < 0) + { + perror ("bind"); + close (sockfd); + return -1; + } + + // Set THIS_SERVER to 192.168.1.0 + inet_pton (AF_INET, "192.168.1.0", &THIS_SERVER); + + uint8_t buffer[MAX_DHCP_LENGTH]; + struct sockaddr_in client_addr; + socklen_t client_len = sizeof (client_addr); + + // Main server loop + while (1) + { + ssize_t nbytes = recvfrom (sockfd, buffer, MAX_DHCP_LENGTH, 0, + (struct sockaddr *)&client_addr, &client_len); + + if (nbytes < 0) + { + break; + } + + msg_t *msg = (msg_t *)buffer; + + // Print received message + printf ("++++++++++++++++++++++++++\n"); + printf ("SERVER RECEIVED %zd BYTES:\n", nbytes); + printf ("++++++++++++++++++++++++++\n"); + dump_msg (stdout, msg, nbytes); + fflush (stdout); + + // Get DHCP options + options_t opts; + if (!get_options (buffer, buffer + nbytes, &opts)) + { + fprintf (stderr, "Failed to parse DHCP options\n"); + fflush (stderr); + free_options (&opts); + continue; + } + + if (*opts.type == DHCPRELEASE) + { + uint32_t xid = ntohl (msg->xid); + client_lease_t *client = find_client_by_xid (xid); + if (client) + { + if (debug) + { + char ip_str[INET_ADDRSTRLEN]; + inet_ntop (AF_INET, &client->assigned_ip, ip_str, + INET_ADDRSTRLEN); + fprintf (stderr, "Releasing IP %s for client\n", ip_str); + } + client->has_lease = 0; + client->has_offer = 0; + } + free_options (&opts); + continue; + } + + // Create and send response + size_t response_size; + uint8_t *response = create_response (msg, &response_size, &opts); + + if (!response) + { + fprintf (stderr, "ERROR: create_response returned NULL!\n"); + fflush (stderr); + free_options (&opts); + continue; + } + + if (response_size == 0) + { + fprintf (stderr, "ERROR: response_size is 0!\n"); + fflush (stderr); + free (response); + free_options (&opts); + continue; + } + + ssize_t sent = sendto (sockfd, response, response_size, 0, + (struct sockaddr *)&client_addr, client_len); + + if (sent < 0) + { + perror ("sendto failed"); + fprintf (stderr, "Failed to send %zu bytes\n", response_size); + fflush (stderr); + } + + // Print sent message + printf ("\n"); + printf ("+++++++++++++++++++++++++\n"); + printf ("SERVER SENDING %zu BYTES:\n", response_size); + printf ("+++++++++++++++++++++++++\n"); + dump_msg (stdout, (msg_t *)response, response_size); + fflush (stdout); + + free (response); + + free_options (&opts); + } + + close (sockfd); + return 0; +} diff --git a/p3-dhcps/src/server.h b/p3-dhcps/src/server.h new file mode 100644 index 0000000..7176313 --- /dev/null +++ b/p3-dhcps/src/server.h @@ -0,0 +1,30 @@ +#ifndef __cs361_dhcp_server_h__ +#define __cs361_dhcp_server_h__ + +#include +#include + +#include "dhcp.h" + +void reset_server_state (); +int setup_server (char *, long to_seconds); + +// Maximum number of clients to support +#define MAX_CLIENTS 4 + +// Client lease information +typedef struct +{ + uint32_t xid; + uint8_t chaddr[16]; + uint8_t hlen; + struct in_addr offered_ip; + struct in_addr assigned_ip; + int has_offer; + int has_lease; +} client_lease_t; + +extern bool debug; +extern struct in_addr THIS_SERVER; + +#endif diff --git a/p3-dhcps/tests/Makefile b/p3-dhcps/tests/Makefile new file mode 100644 index 0000000..50dd48d --- /dev/null +++ b/p3-dhcps/tests/Makefile @@ -0,0 +1,93 @@ +# +# Simple Test Makefile +# Mike Lam, James Madison University, August 2016 +# +# This version of the Makefile includes support for building a test suite. The +# recommended framework is Check (http://check.sourceforge.net/). To build and +# run the test suite, execute the "test" target. The test suite must be located +# in a module called "testsuite". The MODS, LIBS, and OBJS variables work as +# they do in the main Makefile. +# +# To change the default build target (which executes when you just type +# "make"), change the right-hand side of the definition of the "default" +# target. +# +# By default, this makefile will build the project with debugging symbols and +# without optimization. To change this, edit or remove the "-g" and "-O0" +# options in CFLAGS and LDFLAGS accordingly. +# +# By default, this makefile build the application using the GNU C compiler, +# adhering to the C99 standard with all warnings enabled. + + +# application-specific settings and run target + +EXE=../dhcps +TEST=testsuite +MODS=public.o +OBJS=../port_utils.o +LIBS= + +UTESTOUT=utests.txt +ITESTOUT=itests.txt +SCHECKOUT=style.txt + +default: $(TEST) + +TOBJS=dhcp.o format.o threads.o + + +threads: threads.c $(TOBJS) + gcc -O2 -o threads $(TOBJS) $(OBJS) + +$(EXE): + make -C ../ + +test: utest itest style + @echo "========================================" + +utest: $(EXE) $(TEST) + @echo "========================================" + @echo " UNIT TESTS" + @./$(TEST) 2>/dev/null >$(UTESTOUT) + @cat $(UTESTOUT) | sed -n -e '/Checks/,$$p' | sed -e 's/^private.*:[EF]://g' + +itest: $(EXE) + @echo "========================================" + @echo " INTEGRATION TESTS" + @./integration.sh | tee $(ITESTOUT) + +style: $(EXE) + @echo "========================================" + @echo " CODING STYLE CHECK" + @./style.sh 2>/dev/null >$(SCHECKOUT) + @cat $(SCHECKOUT) + +# compiler/linker settings + +CC=gcc +CFLAGS=-g -O0 -Wall --std=gnu99 -pedantic +LDFLAGS=-g -O0 + +#CKCFLAGS= +CKLIBS+=-lcheck -lm -lpthread + +ifeq ($(shell uname -s),Linux) + CKLIBS+=-lrt -lsubunit +endif + + +# build targets + +$(TEST): $(TEST).o $(MODS) $(OBJS) + $(CC) -c testsuite.c -o testsuite.o + $(CC) $(LDFLAGS) -o $(TEST) $^ $(CKLIBS) + +%.o: %.c + $(CC) -c $(CFLAGS) $< + +clean: + rm -rf $(TEST) $(TEST).o $(MODS) $(UTESTOUT) $(ITESTOUT) $(SCHECKOUT) outputs valgrind ckstyle $(COBJS) + +.PHONY: default clean test unittest inttest + diff --git a/p3-dhcps/tests/client b/p3-dhcps/tests/client new file mode 100755 index 0000000..31ba445 Binary files /dev/null and b/p3-dhcps/tests/client differ diff --git a/p3-dhcps/tests/data/arc-req-bad b/p3-dhcps/tests/data/arc-req-bad new file mode 100644 index 0000000..e092296 --- /dev/null +++ b/p3-dhcps/tests/data/arc-req-bad @@ -0,0 +1 @@ +3 7 50 f0 192.168.10.0 192.168.1.8:DHCPREQUEST [htype=ARC, xid=50, chaddr=f0, server=192.168.10.0, reqid=192.168.1.8] diff --git a/p3-dhcps/tests/data/arc-two b/p3-dhcps/tests/data/arc-two new file mode 100644 index 0000000..5003430 --- /dev/null +++ b/p3-dhcps/tests/data/arc-two @@ -0,0 +1,4 @@ +1 7 12345678 c0:DHCPDISCOVER [htype=ARC, xid=12345678, chaddr=c0] +3 7 12345678 c0 192.168.1.0 192.168.1.1:DHCPREQUEST [htype=ARC, xid=12345678, chaddr=c0, server=192.168.1.0, reqid=192.168.1.1] +1 7 87654321 a8:DHCPDISCOVER [htype=ARC, xid=87654321, chaddr=a8] +3 7 87654321 a8 192.168.1.0 192.168.1.2:DHCPREQUEST [htype=ARC, xid=87654321, chaddr=a8, server=192.168.1.0, reqid=192.168.1.2] diff --git a/p3-dhcps/tests/data/arc-two-rel b/p3-dhcps/tests/data/arc-two-rel new file mode 100644 index 0000000..e4264b8 --- /dev/null +++ b/p3-dhcps/tests/data/arc-two-rel @@ -0,0 +1,6 @@ +1 7 12345678 c0:DHCPDISCOVER [htype=ARC, xid=12345678, chaddr=c0] +3 7 12345678 c0 192.168.1.0 192.168.1.1:DHCPREQUEST [htype=ARC, xid=12345678, chaddr=c0, server=192.168.1.0, reqid=192.168.1.1] +7 7 12345678 c0 192.168.1.0 192.168.1.1:DHCPRELEASE [htype=ARC, xid=12345678, chaddr=c0, server=192.168.1.0, reqid=192.168.1.1] +1 7 87654321 a8:DHCPDISCOVER [htype=ARC, xid=87654321, chaddr=a8] +3 7 87654321 a8 192.168.1.0 192.168.1.2:DHCPREQUEST [htype=ARC, xid=87654321, chaddr=a8, server=192.168.1.0, reqid=192.168.1.2] +7 7 87654321 a8 192.168.1.0 192.168.1.2:DHCPRELEASE [htype=ARC, xid=87654321, chaddr=a8, server=192.168.1.0, reqid=192.168.1.2] diff --git a/p3-dhcps/tests/data/eth-dhcp b/p3-dhcps/tests/data/eth-dhcp new file mode 100644 index 0000000..2d54b7e --- /dev/null +++ b/p3-dhcps/tests/data/eth-dhcp @@ -0,0 +1,2 @@ +1 1 42 010102020303:DHCPDISCOVER [htype=ETH, xid=42, chaddr=010102020303] +3 1 42 010102020303 192.168.1.0 192.168.1.1:DHCPREQUEST [htype=ETH, xid=42, chaddr=010102020303, server=192.168.1.0, reqid=192.168.1.1] diff --git a/p3-dhcps/tests/data/eth-disc b/p3-dhcps/tests/data/eth-disc new file mode 100644 index 0000000..4adeed9 --- /dev/null +++ b/p3-dhcps/tests/data/eth-disc @@ -0,0 +1 @@ +1 1 0 010102020303:DHCPDISCOVER [htype=ETH, xid=0, chaddr=010102020303] diff --git a/p3-dhcps/tests/data/fibre-repeat b/p3-dhcps/tests/data/fibre-repeat new file mode 100644 index 0000000..89267bd --- /dev/null +++ b/p3-dhcps/tests/data/fibre-repeat @@ -0,0 +1,9 @@ +1 18 45 c0d1e2:DHCPDISCOVER [htype=FIBRE, xid=45, chaddr=c0d1e2] +3 18 45 c0d1e2 192.168.1.0 192.168.1.1:DHCPREQUEST [htype=FIBRE, xid=45, chaddr=c0d1e2, server=192.168.1.0, reqid=192.168.1.1] +7 18 45 c0d1e2 192.168.1.0 192.168.1.1:DHCPRELEASE [htype=FIBRE, xid=45, chaddr=c0d1e2, server=192.168.1.0, reqid=192.168.1.1] +1 18 35 a8b6c4:DHCPDISCOVER [htype=FIBRE, xid=35, chaddr=a8b6c4] +3 18 35 a8b6c4 192.168.1.0 192.168.1.2:DHCPREQUEST [htype=FIBRE, xid=35, chaddr=a8b6c4, server=192.168.1.0, reqid=192.168.1.2] +7 18 35 a8b6c4 192.168.1.0 192.168.1.2:DHCPRELEASE [htype=FIBRE, xid=35, chaddr=a8b6c4, server=192.168.1.0, reqid=192.168.1.2] +1 18 55 c0d1e2:DHCPDISCOVER [htype=FIBRE, xid=55, chaddr=c0d1e2] +3 18 55 c0d1e2 192.168.1.0 192.168.1.1:DHCPREQUEST [htype=FIBRE, xid=55, chaddr=c0d1e2, server=192.168.1.0, reqid=192.168.1.1] +7 18 55 c0d1e2 192.168.1.0 192.168.1.1:DHCPRELEASE [htype=FIBRE, xid=55, chaddr=c0d1e2, server=192.168.1.0, reqid=192.168.1.1] diff --git a/p3-dhcps/tests/data/frame-inter b/p3-dhcps/tests/data/frame-inter new file mode 100644 index 0000000..1bec6cb --- /dev/null +++ b/p3-dhcps/tests/data/frame-inter @@ -0,0 +1,6 @@ +1 15 12345678 c0a8:DHCPDISCOVER [htype=ARC, xid=12345678, chaddr=c0a8] +1 15 87654321 a8c0:DHCPDISCOVER [htype=ARC, xid=87654321, chaddr=a8c0] +3 15 12345678 c0a8 192.168.1.0 192.168.1.1:DHCPREQUEST [htype=ARC, xid=12345678, chaddr=c0a8, server=192.168.1.0, reqid=192.168.1.1] +3 15 87654321 a8c0 192.168.1.0 192.168.1.2:DHCPREQUEST [htype=ARC, xid=87654321, chaddr=a8c0, server=192.168.1.0, reqid=192.168.1.2] +7 15 87654321 a8c0 192.168.1.0 192.168.1.2:DHCPRELEASE [htype=ARC, xid=87654321, chaddr=a8c0, server=192.168.1.0, reqid=192.168.1.2] +7 15 12345678 c0a8 192.168.1.0 192.168.1.1:DHCPRELEASE [htype=ARC, xid=12345678, chaddr=c0a8, server=192.168.1.0, reqid=192.168.1.1] diff --git a/p3-dhcps/tests/data/ieee-release b/p3-dhcps/tests/data/ieee-release new file mode 100644 index 0000000..24f2541 --- /dev/null +++ b/p3-dhcps/tests/data/ieee-release @@ -0,0 +1,3 @@ +1 6 65535 a1b1c2d2e3f3:DHCPDISCOVER [htype=IEEE802, xid=65535, chaddr=a1b1c2d2e3f3] +3 6 65535 a1b1c2d2e3f3 192.168.1.0 192.168.1.1:DHCPREQUEST [htype=IEEE802, xid=65535, chaddr=a1b1c2d2e3f3, server=192.168.1.0, reqid=192.168.1.1] +7 6 65535 a1b1c2d2e3f3 192.168.1.0 192.168.1.1:DHCPRELEASE [htype=IEEE802, xid=65535, chaddr=a1b1c2d2e3f3, server=192.168.1.0, reqid=192.168.1.1] diff --git a/p3-dhcps/tests/data/ieee-req b/p3-dhcps/tests/data/ieee-req new file mode 100644 index 0000000..63d1d93 --- /dev/null +++ b/p3-dhcps/tests/data/ieee-req @@ -0,0 +1 @@ +3 6 0 0123456789ab 192.168.1.0 192.168.1.1:DHCPREQUEST [htype=IEEE802, xid=0, chaddr=0123456789ab, server=192.168.1.0, reqid=192.168.1.1] diff --git a/p3-dhcps/tests/data/multi-dhcp b/p3-dhcps/tests/data/multi-dhcp new file mode 100644 index 0000000..4149cd8 --- /dev/null +++ b/p3-dhcps/tests/data/multi-dhcp @@ -0,0 +1,9 @@ +1 1 42 010102020303:DHCPDISCOVER [htype=ETH, xid=42, chaddr=010102020303] +3 1 42 010102020303 192.168.1.0 192.168.1.1:DHCPREQUEST [htype=ETH, xid=42, chaddr=010102020303, server=192.168.1.0, reqid=192.168.1.1] +1 7 87654321 a8:DHCPDISCOVER [htype=ARC, xid=87654321, chaddr=a8] +3 7 87654321 a8 192.168.1.0 192.168.1.2:DHCPREQUEST [htype=ARC, xid=87654321, chaddr=a8, server=192.168.1.0, reqid=192.168.1.2] +1 6 1234 0123456789ab:DHCPDISCOVER [htype=ETH, xid=1234, chaddr=0123456789ab] +3 6 1234 0123456789ab 192.168.1.0 192.168.1.3:DHCPREQUEST [htype=IEEE802, xid=1234, chaddr=0123456789ab, server=192.168.1.0, reqid=192.168.1.3] +1 18 55 c0d1e2:DHCPDISCOVER [htype=FIBRE, xid=55, chaddr=c0d1e2] +3 18 55 c0d1e2 192.168.1.0 192.168.1.4:DHCPREQUEST [htype=FIBRE, xid=55, chaddr=c0d1e2, server=192.168.1.0, reqid=192.168.1.4] +1 15 12345678 c0a8:DHCPDISCOVER [htype=ARC, xid=12345678, chaddr=c0a8] diff --git a/p3-dhcps/tests/data/multi-release b/p3-dhcps/tests/data/multi-release new file mode 100644 index 0000000..ddfb9d3 --- /dev/null +++ b/p3-dhcps/tests/data/multi-release @@ -0,0 +1,16 @@ +1 1 42 010102020303:DHCPDISCOVER [htype=ETH, xid=42, chaddr=010102020303] +3 1 42 010102020303 192.168.1.0 192.168.1.1:DHCPREQUEST [htype=ETH, xid=42, chaddr=010102020303, server=192.168.1.0, reqid=192.168.1.1] +1 7 87654321 a8:DHCPDISCOVER [htype=ARC, xid=87654321, chaddr=a8] +3 7 87654321 a8 192.168.1.0 192.168.1.2:DHCPREQUEST [htype=ARC, xid=87654321, chaddr=a8, server=192.168.1.0, reqid=192.168.1.2] +1 6 1234 0123456789ab:DHCPDISCOVER [htype=ETH, xid=1234, chaddr=0123456789ab] +3 6 1234 0123456789ab 192.168.1.0 192.168.1.3:DHCPREQUEST [htype=IEEE802, xid=1234, chaddr=0123456789ab, server=192.168.1.0, reqid=192.168.1.3] +1 18 55 c0d1e2:DHCPDISCOVER [htype=FIBRE, xid=55, chaddr=c0d1e2] +3 18 55 c0d1e2 192.168.1.0 192.168.1.4:DHCPREQUEST [htype=FIBRE, xid=55, chaddr=c0d1e2, server=192.168.1.0, reqid=192.168.1.4] +7 1 42 010102020303 192.168.1.0 192.168.1.1:DHCPREELEASE [htype=ETH, xid=42, chaddr=010102020303, server=192.168.1.0, reqid=192.168.1.1] +1 15 12345678 c0a8:DHCPDISCOVER [htype=ARC, xid=12345678, chaddr=c0a8] +3 15 12345678 c0a8 192.168.1.0 192.168.1.1:DHCPREQUEST [htype=ARC, xid=12345678, chaddr=c0a8, server=192.168.1.0, reqid=192.168.1.1] +7 7 87654321 a8 192.168.1.0 192.168.1.2:DHCPRELEASE [htype=ARC, xid=87654321, chaddr=a8, server=192.168.1.0, reqid=192.168.1.2] +7 6 1234 0123456789ab 192.168.1.0 192.168.1.3:DHCPRELEASE [htype=IEEE802, xid=1234, chaddr=0123456789ab, server=192.168.1.0, reqid=192.168.1.3] +7 18 55 c0d1e2 192.168.1.0 192.168.1.4:DHCPRELEASE [htype=FIBRE, xid=55, chaddr=c0d1e2, server=192.168.1.0, reqid=192.168.1.4] +1 6 5678 0123456789ab:DHCPDISCOVER [htype=ETH, xid=1234, chaddr=0123456789ab] +3 6 5678 0123456789ab 192.168.1.0 192.168.1.3:DHCPREQUEST [htype=IEEE802, xid=1234, chaddr=0123456789ab, server=192.168.1.0, reqid=192.168.1.3] diff --git a/p3-dhcps/tests/data/multi-reuse b/p3-dhcps/tests/data/multi-reuse new file mode 100644 index 0000000..a590ce5 --- /dev/null +++ b/p3-dhcps/tests/data/multi-reuse @@ -0,0 +1,11 @@ +1 1 42 010102020303:DHCPDISCOVER [htype=ETH, xid=42, chaddr=010102020303] +3 1 42 010102020303 192.168.1.0 192.168.1.1:DHCPREQUEST [htype=ETH, xid=42, chaddr=010102020303, server=192.168.1.0, reqid=192.168.1.1] +1 7 87654321 a8:DHCPDISCOVER [htype=ARC, xid=87654321, chaddr=a8] +3 7 87654321 a8 192.168.1.0 192.168.1.2:DHCPREQUEST [htype=ARC, xid=87654321, chaddr=a8, server=192.168.1.0, reqid=192.168.1.2] +1 6 1234 0123456789ab:DHCPDISCOVER [htype=ETH, xid=1234, chaddr=0123456789ab] +3 6 1234 0123456789ab 192.168.1.0 192.168.1.3:DHCPREQUEST [htype=IEEE802, xid=1234, chaddr=0123456789ab, server=192.168.1.0, reqid=192.168.1.3] +1 18 55 c0d1e2:DHCPDISCOVER [htype=FIBRE, xid=55, chaddr=c0d1e2] +3 18 55 c0d1e2 192.168.1.0 192.168.1.4:DHCPREQUEST [htype=FIBRE, xid=55, chaddr=c0d1e2, server=192.168.1.0, reqid=192.168.1.4] +7 1 42 010102020303 192.168.1.0 192.168.1.1:DHCPREELEASE [htype=ETH, xid=42, chaddr=010102020303, server=192.168.1.0, reqid=192.168.1.1] +1 15 12345678 c0a8:DHCPDISCOVER [htype=ARC, xid=12345678, chaddr=c0a8] +3 15 12345678 c0a8 192.168.1.0 192.168.1.1:DHCPREQUEST [htype=ARC, xid=12345678, chaddr=c0a8, server=192.168.1.0, reqid=192.168.1.1] diff --git a/p3-dhcps/tests/data/multi-threads b/p3-dhcps/tests/data/multi-threads new file mode 100644 index 0000000..3ea8fcb --- /dev/null +++ b/p3-dhcps/tests/data/multi-threads @@ -0,0 +1,11 @@ +1 1 42 010102020303:DHCPDISCOVER [htype=ETH, xid=42, chaddr=010102020303] +1 7 87654321 a8:DHCPDISCOVER [htype=ARC, xid=87654321, chaddr=a8] +1 6 1234 0123456789ab:DHCPDISCOVER [htype=ETH, xid=1234, chaddr=0123456789ab] +1 18 55 c0d1e2:DHCPDISCOVER [htype=FIBRE, xid=55, chaddr=c0d1e2] +3 1 42 010102020303 192.168.1.0 192.168.1.1:DHCPREQUEST [htype=ETH, xid=42, chaddr=010102020303, server=192.168.1.0, reqid=192.168.1.1] +3 7 87654321 a8 192.168.1.0 192.168.1.2:DHCPREQUEST [htype=ARC, xid=87654321, chaddr=a8, server=192.168.1.0, reqid=192.168.1.2] +3 6 1234 0123456789ab 192.168.1.0 192.168.1.3:DHCPREQUEST [htype=IEEE802, xid=1234, chaddr=0123456789ab, server=192.168.1.0, reqid=192.168.1.3] +3 18 55 c0d1e2 192.168.1.0 192.168.1.4:DHCPREQUEST [htype=FIBRE, xid=55, chaddr=c0d1e2, server=192.168.1.0, reqid=192.168.1.4] +7 1 42 010102020303 192.168.1.0 192.168.1.1:DHCPRELEASE [htype=ETH, xid=42, chaddr=010102020303, server=192.168.1.0, reqid=192.168.1.1] +1 15 12345678 c0a8:DHCPDISCOVER [htype=ARC, xid=12345678, chaddr=c0a8] +3 15 12345678 c0a8 192.168.1.0 192.168.1.1:DHCPREQUEST [htype=ARC, xid=12345678, chaddr=c0a8, server=192.168.1.0, reqid=192.168.1.1]] diff --git a/p3-dhcps/tests/data/multi-threads.old b/p3-dhcps/tests/data/multi-threads.old new file mode 100644 index 0000000..3ea8fcb --- /dev/null +++ b/p3-dhcps/tests/data/multi-threads.old @@ -0,0 +1,11 @@ +1 1 42 010102020303:DHCPDISCOVER [htype=ETH, xid=42, chaddr=010102020303] +1 7 87654321 a8:DHCPDISCOVER [htype=ARC, xid=87654321, chaddr=a8] +1 6 1234 0123456789ab:DHCPDISCOVER [htype=ETH, xid=1234, chaddr=0123456789ab] +1 18 55 c0d1e2:DHCPDISCOVER [htype=FIBRE, xid=55, chaddr=c0d1e2] +3 1 42 010102020303 192.168.1.0 192.168.1.1:DHCPREQUEST [htype=ETH, xid=42, chaddr=010102020303, server=192.168.1.0, reqid=192.168.1.1] +3 7 87654321 a8 192.168.1.0 192.168.1.2:DHCPREQUEST [htype=ARC, xid=87654321, chaddr=a8, server=192.168.1.0, reqid=192.168.1.2] +3 6 1234 0123456789ab 192.168.1.0 192.168.1.3:DHCPREQUEST [htype=IEEE802, xid=1234, chaddr=0123456789ab, server=192.168.1.0, reqid=192.168.1.3] +3 18 55 c0d1e2 192.168.1.0 192.168.1.4:DHCPREQUEST [htype=FIBRE, xid=55, chaddr=c0d1e2, server=192.168.1.0, reqid=192.168.1.4] +7 1 42 010102020303 192.168.1.0 192.168.1.1:DHCPRELEASE [htype=ETH, xid=42, chaddr=010102020303, server=192.168.1.0, reqid=192.168.1.1] +1 15 12345678 c0a8:DHCPDISCOVER [htype=ARC, xid=12345678, chaddr=c0a8] +3 15 12345678 c0a8 192.168.1.0 192.168.1.1:DHCPREQUEST [htype=ARC, xid=12345678, chaddr=c0a8, server=192.168.1.0, reqid=192.168.1.1]] diff --git a/p3-dhcps/tests/expected/A_threads.txt b/p3-dhcps/tests/expected/A_threads.txt new file mode 100644 index 0000000..6e17be6 --- /dev/null +++ b/p3-dhcps/tests/expected/A_threads.txt @@ -0,0 +1,432 @@ +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 6 [IEEE 802 Networks] +Hardware Address Length (hlen) = 6 +Hops (hops) = 0 +Transaction ID (xid) = 500 (0x1f4) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 192.168.1.1 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = 0123456789ab +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Offer +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 1 [Ethernet (10Mb)] +Hardware Address Length (hlen) = 6 +Hops (hops) = 0 +Transaction ID (xid) = 2000 (0x7d0) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 192.168.1.1 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = 010102020303 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Offer +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 15 [Frame Relay] +Hardware Address Length (hlen) = 2 +Hops (hops) = 0 +Transaction ID (xid) = 7500 (0x1d4c) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 192.168.1.1 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = c0a8 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Offer +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 7 [ARCNET] +Hardware Address Length (hlen) = 1 +Hops (hops) = 0 +Transaction ID (xid) = 10000 (0x2710) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 192.168.1.1 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = 65 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Offer +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 7 [ARCNET] +Hardware Address Length (hlen) = 1 +Hops (hops) = 0 +Transaction ID (xid) = 40000 (0x9c40) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 192.168.1.1 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = 87 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Offer +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 1 [Ethernet (10Mb)] +Hardware Address Length (hlen) = 6 +Hops (hops) = 0 +Transaction ID (xid) = 100000 (0x186a0) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 192.168.1.1 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = 010203040506 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Offer +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 18 [Fibre Channel] +Hardware Address Length (hlen) = 3 +Hops (hops) = 0 +Transaction ID (xid) = 250000 (0x3d090) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 192.168.1.1 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = ccddee +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Offer +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 18 [Fibre Channel] +Hardware Address Length (hlen) = 3 +Hops (hops) = 0 +Transaction ID (xid) = 500000 (0x7a120) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 192.168.1.1 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = c0d1e2 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Offer +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 6 [IEEE 802 Networks] +Hardware Address Length (hlen) = 6 +Hops (hops) = 0 +Transaction ID (xid) = 500 (0x1f4) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = 0123456789ab +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP ACK +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 1 [Ethernet (10Mb)] +Hardware Address Length (hlen) = 6 +Hops (hops) = 0 +Transaction ID (xid) = 2000 (0x7d0) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = 010102020303 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP ACK +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 15 [Frame Relay] +Hardware Address Length (hlen) = 2 +Hops (hops) = 0 +Transaction ID (xid) = 7500 (0x1d4c) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = c0a8 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP ACK +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 7 [ARCNET] +Hardware Address Length (hlen) = 1 +Hops (hops) = 0 +Transaction ID (xid) = 10000 (0x2710) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = 65 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP ACK +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 7 [ARCNET] +Hardware Address Length (hlen) = 1 +Hops (hops) = 0 +Transaction ID (xid) = 40000 (0x9c40) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = 87 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP ACK +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 1 [Ethernet (10Mb)] +Hardware Address Length (hlen) = 6 +Hops (hops) = 0 +Transaction ID (xid) = 100000 (0x186a0) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = 010203040506 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP ACK +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 18 [Fibre Channel] +Hardware Address Length (hlen) = 3 +Hops (hops) = 0 +Transaction ID (xid) = 250000 (0x3d090) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = ccddee +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP ACK +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 18 [Fibre Channel] +Hardware Address Length (hlen) = 3 +Hops (hops) = 0 +Transaction ID (xid) = 500000 (0x7a120) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = c0d1e2 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP ACK +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + diff --git a/p3-dhcps/tests/expected/B_arc_two.txt b/p3-dhcps/tests/expected/B_arc_two.txt new file mode 100644 index 0000000..9615dfb --- /dev/null +++ b/p3-dhcps/tests/expected/B_arc_two.txt @@ -0,0 +1,272 @@ ++++++++++++++++++++++++++ +CLIENT SENDING 244 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 7 [ARCNET] +Hardware Address Length (hlen) = 1 +Hops (hops) = 0 +Transaction ID (xid) = 12345678 (0xbc614e) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = c0 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Discover + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 7 [ARCNET] +Hardware Address Length (hlen) = 1 +Hops (hops) = 0 +Transaction ID (xid) = 12345678 (0xbc614e) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 192.168.1.1 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = c0 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Offer +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + ++++++++++++++++++++++++++ +CLIENT SENDING 256 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 7 [ARCNET] +Hardware Address Length (hlen) = 1 +Hops (hops) = 0 +Transaction ID (xid) = 12345678 (0xbc614e) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = c0 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Request +Request = 192.168.1.1 +Server Identifier = 192.168.1.0 + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 7 [ARCNET] +Hardware Address Length (hlen) = 1 +Hops (hops) = 0 +Transaction ID (xid) = 12345678 (0xbc614e) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 192.168.1.1 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = c0 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP ACK +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + ++++++++++++++++++++++++++ +CLIENT SENDING 250 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 7 [ARCNET] +Hardware Address Length (hlen) = 1 +Hops (hops) = 0 +Transaction ID (xid) = 12345678 (0xbc614e) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 192.168.1.1 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = c0 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Release +Server Identifier = 192.168.1.0 + ++++++++++++++++++++++++++++++++++++++ +NO RESPONSE IS NEEDED FOR DHCPRELEASE ++++++++++++++++++++++++++++++++++++++ + ++++++++++++++++++++++++++ +CLIENT SENDING 244 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 7 [ARCNET] +Hardware Address Length (hlen) = 1 +Hops (hops) = 0 +Transaction ID (xid) = 87654321 (0x5397fb1) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = a8 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Discover + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 7 [ARCNET] +Hardware Address Length (hlen) = 1 +Hops (hops) = 0 +Transaction ID (xid) = 87654321 (0x5397fb1) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 192.168.1.2 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = a8 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Offer +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + ++++++++++++++++++++++++++ +CLIENT SENDING 256 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 7 [ARCNET] +Hardware Address Length (hlen) = 1 +Hops (hops) = 0 +Transaction ID (xid) = 87654321 (0x5397fb1) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = a8 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Request +Request = 192.168.1.2 +Server Identifier = 192.168.1.0 + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 7 [ARCNET] +Hardware Address Length (hlen) = 1 +Hops (hops) = 0 +Transaction ID (xid) = 87654321 (0x5397fb1) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 192.168.1.2 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = a8 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP ACK +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + ++++++++++++++++++++++++++ +CLIENT SENDING 250 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 7 [ARCNET] +Hardware Address Length (hlen) = 1 +Hops (hops) = 0 +Transaction ID (xid) = 87654321 (0x5397fb1) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 192.168.1.2 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = a8 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Release +Server Identifier = 192.168.1.0 + ++++++++++++++++++++++++++++++++++++++ +NO RESPONSE IS NEEDED FOR DHCPRELEASE ++++++++++++++++++++++++++++++++++++++ + diff --git a/p3-dhcps/tests/expected/B_fibre_repeat.txt b/p3-dhcps/tests/expected/B_fibre_repeat.txt new file mode 100644 index 0000000..289fe6d --- /dev/null +++ b/p3-dhcps/tests/expected/B_fibre_repeat.txt @@ -0,0 +1,408 @@ ++++++++++++++++++++++++++ +CLIENT SENDING 244 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 18 [Fibre Channel] +Hardware Address Length (hlen) = 3 +Hops (hops) = 0 +Transaction ID (xid) = 45 (0x2d) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = c0d1e2 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Discover + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 18 [Fibre Channel] +Hardware Address Length (hlen) = 3 +Hops (hops) = 0 +Transaction ID (xid) = 45 (0x2d) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 192.168.1.1 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = c0d1e2 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Offer +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + ++++++++++++++++++++++++++ +CLIENT SENDING 256 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 18 [Fibre Channel] +Hardware Address Length (hlen) = 3 +Hops (hops) = 0 +Transaction ID (xid) = 45 (0x2d) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = c0d1e2 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Request +Request = 192.168.1.1 +Server Identifier = 192.168.1.0 + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 18 [Fibre Channel] +Hardware Address Length (hlen) = 3 +Hops (hops) = 0 +Transaction ID (xid) = 45 (0x2d) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 192.168.1.1 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = c0d1e2 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP ACK +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + ++++++++++++++++++++++++++ +CLIENT SENDING 250 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 18 [Fibre Channel] +Hardware Address Length (hlen) = 3 +Hops (hops) = 0 +Transaction ID (xid) = 45 (0x2d) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 192.168.1.1 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = c0d1e2 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Release +Server Identifier = 192.168.1.0 + ++++++++++++++++++++++++++++++++++++++ +NO RESPONSE IS NEEDED FOR DHCPRELEASE ++++++++++++++++++++++++++++++++++++++ + ++++++++++++++++++++++++++ +CLIENT SENDING 244 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 18 [Fibre Channel] +Hardware Address Length (hlen) = 3 +Hops (hops) = 0 +Transaction ID (xid) = 35 (0x23) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = a8b6c4 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Discover + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 18 [Fibre Channel] +Hardware Address Length (hlen) = 3 +Hops (hops) = 0 +Transaction ID (xid) = 35 (0x23) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 192.168.1.2 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = a8b6c4 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Offer +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + ++++++++++++++++++++++++++ +CLIENT SENDING 256 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 18 [Fibre Channel] +Hardware Address Length (hlen) = 3 +Hops (hops) = 0 +Transaction ID (xid) = 35 (0x23) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = a8b6c4 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Request +Request = 192.168.1.2 +Server Identifier = 192.168.1.0 + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 18 [Fibre Channel] +Hardware Address Length (hlen) = 3 +Hops (hops) = 0 +Transaction ID (xid) = 35 (0x23) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 192.168.1.2 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = a8b6c4 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP ACK +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + ++++++++++++++++++++++++++ +CLIENT SENDING 250 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 18 [Fibre Channel] +Hardware Address Length (hlen) = 3 +Hops (hops) = 0 +Transaction ID (xid) = 35 (0x23) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 192.168.1.2 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = a8b6c4 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Release +Server Identifier = 192.168.1.0 + ++++++++++++++++++++++++++++++++++++++ +NO RESPONSE IS NEEDED FOR DHCPRELEASE ++++++++++++++++++++++++++++++++++++++ + ++++++++++++++++++++++++++ +CLIENT SENDING 244 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 18 [Fibre Channel] +Hardware Address Length (hlen) = 3 +Hops (hops) = 0 +Transaction ID (xid) = 55 (0x37) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = c0d1e2 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Discover + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 18 [Fibre Channel] +Hardware Address Length (hlen) = 3 +Hops (hops) = 0 +Transaction ID (xid) = 55 (0x37) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 192.168.1.1 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = c0d1e2 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Offer +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + ++++++++++++++++++++++++++ +CLIENT SENDING 256 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 18 [Fibre Channel] +Hardware Address Length (hlen) = 3 +Hops (hops) = 0 +Transaction ID (xid) = 55 (0x37) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = c0d1e2 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Request +Request = 192.168.1.1 +Server Identifier = 192.168.1.0 + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 18 [Fibre Channel] +Hardware Address Length (hlen) = 3 +Hops (hops) = 0 +Transaction ID (xid) = 55 (0x37) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 192.168.1.1 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = c0d1e2 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP ACK +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + ++++++++++++++++++++++++++ +CLIENT SENDING 250 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 18 [Fibre Channel] +Hardware Address Length (hlen) = 3 +Hops (hops) = 0 +Transaction ID (xid) = 55 (0x37) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 192.168.1.1 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = c0d1e2 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Release +Server Identifier = 192.168.1.0 + ++++++++++++++++++++++++++++++++++++++ +NO RESPONSE IS NEEDED FOR DHCPRELEASE ++++++++++++++++++++++++++++++++++++++ + diff --git a/p3-dhcps/tests/expected/B_frame_interleave.txt b/p3-dhcps/tests/expected/B_frame_interleave.txt new file mode 100644 index 0000000..605b948 --- /dev/null +++ b/p3-dhcps/tests/expected/B_frame_interleave.txt @@ -0,0 +1,272 @@ ++++++++++++++++++++++++++ +CLIENT SENDING 244 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 15 [Frame Relay] +Hardware Address Length (hlen) = 2 +Hops (hops) = 0 +Transaction ID (xid) = 12345678 (0xbc614e) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = c0a8 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Discover + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 15 [Frame Relay] +Hardware Address Length (hlen) = 2 +Hops (hops) = 0 +Transaction ID (xid) = 12345678 (0xbc614e) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 192.168.1.1 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = c0a8 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Offer +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + ++++++++++++++++++++++++++ +CLIENT SENDING 244 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 15 [Frame Relay] +Hardware Address Length (hlen) = 2 +Hops (hops) = 0 +Transaction ID (xid) = 87654321 (0x5397fb1) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = a8c0 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Discover + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 15 [Frame Relay] +Hardware Address Length (hlen) = 2 +Hops (hops) = 0 +Transaction ID (xid) = 87654321 (0x5397fb1) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 192.168.1.2 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = a8c0 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Offer +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + ++++++++++++++++++++++++++ +CLIENT SENDING 256 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 15 [Frame Relay] +Hardware Address Length (hlen) = 2 +Hops (hops) = 0 +Transaction ID (xid) = 12345678 (0xbc614e) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = c0a8 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Request +Request = 192.168.1.1 +Server Identifier = 192.168.1.0 + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 15 [Frame Relay] +Hardware Address Length (hlen) = 2 +Hops (hops) = 0 +Transaction ID (xid) = 12345678 (0xbc614e) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 192.168.1.1 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = c0a8 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP ACK +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + ++++++++++++++++++++++++++ +CLIENT SENDING 256 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 15 [Frame Relay] +Hardware Address Length (hlen) = 2 +Hops (hops) = 0 +Transaction ID (xid) = 87654321 (0x5397fb1) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = a8c0 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Request +Request = 192.168.1.2 +Server Identifier = 192.168.1.0 + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 15 [Frame Relay] +Hardware Address Length (hlen) = 2 +Hops (hops) = 0 +Transaction ID (xid) = 87654321 (0x5397fb1) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 192.168.1.2 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = a8c0 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP ACK +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + ++++++++++++++++++++++++++ +CLIENT SENDING 250 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 15 [Frame Relay] +Hardware Address Length (hlen) = 2 +Hops (hops) = 0 +Transaction ID (xid) = 87654321 (0x5397fb1) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 192.168.1.2 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = a8c0 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Release +Server Identifier = 192.168.1.0 + ++++++++++++++++++++++++++++++++++++++ +NO RESPONSE IS NEEDED FOR DHCPRELEASE ++++++++++++++++++++++++++++++++++++++ + ++++++++++++++++++++++++++ +CLIENT SENDING 250 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 15 [Frame Relay] +Hardware Address Length (hlen) = 2 +Hops (hops) = 0 +Transaction ID (xid) = 12345678 (0xbc614e) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 192.168.1.1 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = c0a8 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Release +Server Identifier = 192.168.1.0 + ++++++++++++++++++++++++++++++++++++++ +NO RESPONSE IS NEEDED FOR DHCPRELEASE ++++++++++++++++++++++++++++++++++++++ + diff --git a/p3-dhcps/tests/expected/B_ieee_release.txt b/p3-dhcps/tests/expected/B_ieee_release.txt new file mode 100644 index 0000000..b03161f --- /dev/null +++ b/p3-dhcps/tests/expected/B_ieee_release.txt @@ -0,0 +1,136 @@ ++++++++++++++++++++++++++ +CLIENT SENDING 244 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 6 [IEEE 802 Networks] +Hardware Address Length (hlen) = 6 +Hops (hops) = 0 +Transaction ID (xid) = 65535 (0xffff) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = a1b1c2d2e3f3 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Discover + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 6 [IEEE 802 Networks] +Hardware Address Length (hlen) = 6 +Hops (hops) = 0 +Transaction ID (xid) = 65535 (0xffff) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 192.168.1.1 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = a1b1c2d2e3f3 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Offer +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + ++++++++++++++++++++++++++ +CLIENT SENDING 256 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 6 [IEEE 802 Networks] +Hardware Address Length (hlen) = 6 +Hops (hops) = 0 +Transaction ID (xid) = 65535 (0xffff) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = a1b1c2d2e3f3 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Request +Request = 192.168.1.1 +Server Identifier = 192.168.1.0 + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 6 [IEEE 802 Networks] +Hardware Address Length (hlen) = 6 +Hops (hops) = 0 +Transaction ID (xid) = 65535 (0xffff) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 192.168.1.1 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = a1b1c2d2e3f3 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP ACK +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + ++++++++++++++++++++++++++ +CLIENT SENDING 250 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 6 [IEEE 802 Networks] +Hardware Address Length (hlen) = 6 +Hops (hops) = 0 +Transaction ID (xid) = 65535 (0xffff) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 192.168.1.1 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = a1b1c2d2e3f3 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Release +Server Identifier = 192.168.1.0 + ++++++++++++++++++++++++++++++++++++++ +NO RESPONSE IS NEEDED FOR DHCPRELEASE ++++++++++++++++++++++++++++++++++++++ + diff --git a/p3-dhcps/tests/expected/B_multi_release.txt b/p3-dhcps/tests/expected/B_multi_release.txt new file mode 100644 index 0000000..89fbb01 --- /dev/null +++ b/p3-dhcps/tests/expected/B_multi_release.txt @@ -0,0 +1,756 @@ ++++++++++++++++++++++++++ +CLIENT SENDING 244 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 1 [Ethernet (10Mb)] +Hardware Address Length (hlen) = 6 +Hops (hops) = 0 +Transaction ID (xid) = 42 (0x2a) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = 010102020303 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Discover + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 1 [Ethernet (10Mb)] +Hardware Address Length (hlen) = 6 +Hops (hops) = 0 +Transaction ID (xid) = 42 (0x2a) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 192.168.1.1 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = 010102020303 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Offer +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + ++++++++++++++++++++++++++ +CLIENT SENDING 256 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 1 [Ethernet (10Mb)] +Hardware Address Length (hlen) = 6 +Hops (hops) = 0 +Transaction ID (xid) = 42 (0x2a) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = 010102020303 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Request +Request = 192.168.1.1 +Server Identifier = 192.168.1.0 + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 1 [Ethernet (10Mb)] +Hardware Address Length (hlen) = 6 +Hops (hops) = 0 +Transaction ID (xid) = 42 (0x2a) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 192.168.1.1 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = 010102020303 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP ACK +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + ++++++++++++++++++++++++++ +CLIENT SENDING 244 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 7 [ARCNET] +Hardware Address Length (hlen) = 1 +Hops (hops) = 0 +Transaction ID (xid) = 87654321 (0x5397fb1) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = a8 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Discover + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 7 [ARCNET] +Hardware Address Length (hlen) = 1 +Hops (hops) = 0 +Transaction ID (xid) = 87654321 (0x5397fb1) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 192.168.1.2 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = a8 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Offer +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + ++++++++++++++++++++++++++ +CLIENT SENDING 256 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 7 [ARCNET] +Hardware Address Length (hlen) = 1 +Hops (hops) = 0 +Transaction ID (xid) = 87654321 (0x5397fb1) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = a8 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Request +Request = 192.168.1.2 +Server Identifier = 192.168.1.0 + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 7 [ARCNET] +Hardware Address Length (hlen) = 1 +Hops (hops) = 0 +Transaction ID (xid) = 87654321 (0x5397fb1) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 192.168.1.2 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = a8 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP ACK +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + ++++++++++++++++++++++++++ +CLIENT SENDING 244 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 6 [IEEE 802 Networks] +Hardware Address Length (hlen) = 6 +Hops (hops) = 0 +Transaction ID (xid) = 1234 (0x4d2) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = 0123456789ab +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Discover + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 6 [IEEE 802 Networks] +Hardware Address Length (hlen) = 6 +Hops (hops) = 0 +Transaction ID (xid) = 1234 (0x4d2) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 192.168.1.3 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = 0123456789ab +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Offer +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + ++++++++++++++++++++++++++ +CLIENT SENDING 256 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 6 [IEEE 802 Networks] +Hardware Address Length (hlen) = 6 +Hops (hops) = 0 +Transaction ID (xid) = 1234 (0x4d2) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = 0123456789ab +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Request +Request = 192.168.1.3 +Server Identifier = 192.168.1.0 + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 6 [IEEE 802 Networks] +Hardware Address Length (hlen) = 6 +Hops (hops) = 0 +Transaction ID (xid) = 1234 (0x4d2) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 192.168.1.3 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = 0123456789ab +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP ACK +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + ++++++++++++++++++++++++++ +CLIENT SENDING 244 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 18 [Fibre Channel] +Hardware Address Length (hlen) = 3 +Hops (hops) = 0 +Transaction ID (xid) = 55 (0x37) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = c0d1e2 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Discover + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 18 [Fibre Channel] +Hardware Address Length (hlen) = 3 +Hops (hops) = 0 +Transaction ID (xid) = 55 (0x37) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 192.168.1.4 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = c0d1e2 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Offer +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + ++++++++++++++++++++++++++ +CLIENT SENDING 256 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 18 [Fibre Channel] +Hardware Address Length (hlen) = 3 +Hops (hops) = 0 +Transaction ID (xid) = 55 (0x37) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = c0d1e2 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Request +Request = 192.168.1.4 +Server Identifier = 192.168.1.0 + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 18 [Fibre Channel] +Hardware Address Length (hlen) = 3 +Hops (hops) = 0 +Transaction ID (xid) = 55 (0x37) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 192.168.1.4 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = c0d1e2 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP ACK +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + ++++++++++++++++++++++++++ +CLIENT SENDING 250 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 1 [Ethernet (10Mb)] +Hardware Address Length (hlen) = 6 +Hops (hops) = 0 +Transaction ID (xid) = 42 (0x2a) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 192.168.1.1 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = 010102020303 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Release +Server Identifier = 192.168.1.0 + ++++++++++++++++++++++++++++++++++++++ +NO RESPONSE IS NEEDED FOR DHCPRELEASE ++++++++++++++++++++++++++++++++++++++ + ++++++++++++++++++++++++++ +CLIENT SENDING 244 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 15 [Frame Relay] +Hardware Address Length (hlen) = 2 +Hops (hops) = 0 +Transaction ID (xid) = 12345678 (0xbc614e) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = c0a8 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Discover + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 15 [Frame Relay] +Hardware Address Length (hlen) = 2 +Hops (hops) = 0 +Transaction ID (xid) = 12345678 (0xbc614e) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 192.168.1.1 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = c0a8 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Offer +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + ++++++++++++++++++++++++++ +CLIENT SENDING 256 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 15 [Frame Relay] +Hardware Address Length (hlen) = 2 +Hops (hops) = 0 +Transaction ID (xid) = 12345678 (0xbc614e) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = c0a8 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Request +Request = 192.168.1.1 +Server Identifier = 192.168.1.0 + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 15 [Frame Relay] +Hardware Address Length (hlen) = 2 +Hops (hops) = 0 +Transaction ID (xid) = 12345678 (0xbc614e) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 192.168.1.1 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = c0a8 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP ACK +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + ++++++++++++++++++++++++++ +CLIENT SENDING 250 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 7 [ARCNET] +Hardware Address Length (hlen) = 1 +Hops (hops) = 0 +Transaction ID (xid) = 87654321 (0x5397fb1) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 192.168.1.2 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = a8 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Release +Server Identifier = 192.168.1.0 + ++++++++++++++++++++++++++++++++++++++ +NO RESPONSE IS NEEDED FOR DHCPRELEASE ++++++++++++++++++++++++++++++++++++++ + ++++++++++++++++++++++++++ +CLIENT SENDING 250 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 6 [IEEE 802 Networks] +Hardware Address Length (hlen) = 6 +Hops (hops) = 0 +Transaction ID (xid) = 1234 (0x4d2) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 192.168.1.3 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = 0123456789ab +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Release +Server Identifier = 192.168.1.0 + ++++++++++++++++++++++++++++++++++++++ +NO RESPONSE IS NEEDED FOR DHCPRELEASE ++++++++++++++++++++++++++++++++++++++ + ++++++++++++++++++++++++++ +CLIENT SENDING 250 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 18 [Fibre Channel] +Hardware Address Length (hlen) = 3 +Hops (hops) = 0 +Transaction ID (xid) = 55 (0x37) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 192.168.1.4 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = c0d1e2 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Release +Server Identifier = 192.168.1.0 + ++++++++++++++++++++++++++++++++++++++ +NO RESPONSE IS NEEDED FOR DHCPRELEASE ++++++++++++++++++++++++++++++++++++++ + ++++++++++++++++++++++++++ +CLIENT SENDING 244 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 6 [IEEE 802 Networks] +Hardware Address Length (hlen) = 6 +Hops (hops) = 0 +Transaction ID (xid) = 5678 (0x162e) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = 0123456789ab +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Discover + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 6 [IEEE 802 Networks] +Hardware Address Length (hlen) = 6 +Hops (hops) = 0 +Transaction ID (xid) = 5678 (0x162e) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 192.168.1.3 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = 0123456789ab +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Offer +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + ++++++++++++++++++++++++++ +CLIENT SENDING 256 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 6 [IEEE 802 Networks] +Hardware Address Length (hlen) = 6 +Hops (hops) = 0 +Transaction ID (xid) = 5678 (0x162e) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = 0123456789ab +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Request +Request = 192.168.1.3 +Server Identifier = 192.168.1.0 + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 6 [IEEE 802 Networks] +Hardware Address Length (hlen) = 6 +Hops (hops) = 0 +Transaction ID (xid) = 5678 (0x162e) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 192.168.1.3 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = 0123456789ab +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP ACK +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + diff --git a/p3-dhcps/tests/expected/B_multi_reuse.txt b/p3-dhcps/tests/expected/B_multi_reuse.txt new file mode 100644 index 0000000..14977f3 --- /dev/null +++ b/p3-dhcps/tests/expected/B_multi_reuse.txt @@ -0,0 +1,560 @@ ++++++++++++++++++++++++++ +CLIENT SENDING 244 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 1 [Ethernet (10Mb)] +Hardware Address Length (hlen) = 6 +Hops (hops) = 0 +Transaction ID (xid) = 42 (0x2a) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = 010102020303 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Discover + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 1 [Ethernet (10Mb)] +Hardware Address Length (hlen) = 6 +Hops (hops) = 0 +Transaction ID (xid) = 42 (0x2a) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 192.168.1.1 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = 010102020303 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Offer +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + ++++++++++++++++++++++++++ +CLIENT SENDING 256 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 1 [Ethernet (10Mb)] +Hardware Address Length (hlen) = 6 +Hops (hops) = 0 +Transaction ID (xid) = 42 (0x2a) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = 010102020303 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Request +Request = 192.168.1.1 +Server Identifier = 192.168.1.0 + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 1 [Ethernet (10Mb)] +Hardware Address Length (hlen) = 6 +Hops (hops) = 0 +Transaction ID (xid) = 42 (0x2a) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 192.168.1.1 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = 010102020303 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP ACK +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + ++++++++++++++++++++++++++ +CLIENT SENDING 244 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 7 [ARCNET] +Hardware Address Length (hlen) = 1 +Hops (hops) = 0 +Transaction ID (xid) = 87654321 (0x5397fb1) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = a8 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Discover + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 7 [ARCNET] +Hardware Address Length (hlen) = 1 +Hops (hops) = 0 +Transaction ID (xid) = 87654321 (0x5397fb1) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 192.168.1.2 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = a8 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Offer +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + ++++++++++++++++++++++++++ +CLIENT SENDING 256 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 7 [ARCNET] +Hardware Address Length (hlen) = 1 +Hops (hops) = 0 +Transaction ID (xid) = 87654321 (0x5397fb1) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = a8 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Request +Request = 192.168.1.2 +Server Identifier = 192.168.1.0 + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 7 [ARCNET] +Hardware Address Length (hlen) = 1 +Hops (hops) = 0 +Transaction ID (xid) = 87654321 (0x5397fb1) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 192.168.1.2 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = a8 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP ACK +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + ++++++++++++++++++++++++++ +CLIENT SENDING 244 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 6 [IEEE 802 Networks] +Hardware Address Length (hlen) = 6 +Hops (hops) = 0 +Transaction ID (xid) = 1234 (0x4d2) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = 0123456789ab +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Discover + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 6 [IEEE 802 Networks] +Hardware Address Length (hlen) = 6 +Hops (hops) = 0 +Transaction ID (xid) = 1234 (0x4d2) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 192.168.1.3 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = 0123456789ab +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Offer +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + ++++++++++++++++++++++++++ +CLIENT SENDING 256 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 6 [IEEE 802 Networks] +Hardware Address Length (hlen) = 6 +Hops (hops) = 0 +Transaction ID (xid) = 1234 (0x4d2) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = 0123456789ab +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Request +Request = 192.168.1.3 +Server Identifier = 192.168.1.0 + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 6 [IEEE 802 Networks] +Hardware Address Length (hlen) = 6 +Hops (hops) = 0 +Transaction ID (xid) = 1234 (0x4d2) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 192.168.1.3 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = 0123456789ab +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP ACK +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + ++++++++++++++++++++++++++ +CLIENT SENDING 244 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 18 [Fibre Channel] +Hardware Address Length (hlen) = 3 +Hops (hops) = 0 +Transaction ID (xid) = 55 (0x37) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = c0d1e2 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Discover + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 18 [Fibre Channel] +Hardware Address Length (hlen) = 3 +Hops (hops) = 0 +Transaction ID (xid) = 55 (0x37) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 192.168.1.4 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = c0d1e2 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Offer +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + ++++++++++++++++++++++++++ +CLIENT SENDING 256 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 18 [Fibre Channel] +Hardware Address Length (hlen) = 3 +Hops (hops) = 0 +Transaction ID (xid) = 55 (0x37) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = c0d1e2 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Request +Request = 192.168.1.4 +Server Identifier = 192.168.1.0 + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 18 [Fibre Channel] +Hardware Address Length (hlen) = 3 +Hops (hops) = 0 +Transaction ID (xid) = 55 (0x37) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 192.168.1.4 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = c0d1e2 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP ACK +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + ++++++++++++++++++++++++++ +CLIENT SENDING 250 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 1 [Ethernet (10Mb)] +Hardware Address Length (hlen) = 6 +Hops (hops) = 0 +Transaction ID (xid) = 42 (0x2a) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 192.168.1.1 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = 010102020303 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Release +Server Identifier = 192.168.1.0 + ++++++++++++++++++++++++++++++++++++++ +NO RESPONSE IS NEEDED FOR DHCPRELEASE ++++++++++++++++++++++++++++++++++++++ + ++++++++++++++++++++++++++ +CLIENT SENDING 244 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 15 [Frame Relay] +Hardware Address Length (hlen) = 2 +Hops (hops) = 0 +Transaction ID (xid) = 12345678 (0xbc614e) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = c0a8 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Discover + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 15 [Frame Relay] +Hardware Address Length (hlen) = 2 +Hops (hops) = 0 +Transaction ID (xid) = 12345678 (0xbc614e) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 192.168.1.1 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = c0a8 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Offer +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + ++++++++++++++++++++++++++ +CLIENT SENDING 256 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 15 [Frame Relay] +Hardware Address Length (hlen) = 2 +Hops (hops) = 0 +Transaction ID (xid) = 12345678 (0xbc614e) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = c0a8 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Request +Request = 192.168.1.1 +Server Identifier = 192.168.1.0 + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 15 [Frame Relay] +Hardware Address Length (hlen) = 2 +Hops (hops) = 0 +Transaction ID (xid) = 12345678 (0xbc614e) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 192.168.1.1 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = c0a8 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP ACK +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + diff --git a/p3-dhcps/tests/expected/C_arc_bad.txt b/p3-dhcps/tests/expected/C_arc_bad.txt new file mode 100644 index 0000000..95aec50 --- /dev/null +++ b/p3-dhcps/tests/expected/C_arc_bad.txt @@ -0,0 +1,53 @@ +++++++++++++++++++++++++++ +SERVER RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 7 [ARCNET] +Hardware Address Length (hlen) = 1 +Hops (hops) = 0 +Transaction ID (xid) = 50 (0x0) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = f0 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Request +Request = 192.168.1.8 +Server Identifier = 192.168.10.0 +ERROR: Invalid server ID in request 192.168.10.0 + ++++++++++++++++++++++++++ +SERVER SENDING 250 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 7 [ARCNET] +Hardware Address Length (hlen) = 1 +Hops (hops) = 0 +Transaction ID (xid) = 50 (0x0) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = f0 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP NAK +Server Identifier = 192.168.1.0 diff --git a/p3-dhcps/tests/expected/C_arc_two.txt b/p3-dhcps/tests/expected/C_arc_two.txt new file mode 100644 index 0000000..9612183 --- /dev/null +++ b/p3-dhcps/tests/expected/C_arc_two.txt @@ -0,0 +1,212 @@ ++++++++++++++++++++++++++ +CLIENT SENDING 244 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 7 [ARCNET] +Hardware Address Length (hlen) = 1 +Hops (hops) = 0 +Transaction ID (xid) = 12345678 (0xbc614e) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = c0 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Discover + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 7 [ARCNET] +Hardware Address Length (hlen) = 1 +Hops (hops) = 0 +Transaction ID (xid) = 12345678 (0xbc614e) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 192.168.1.1 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = c0 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Offer +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + ++++++++++++++++++++++++++ +CLIENT SENDING 256 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 7 [ARCNET] +Hardware Address Length (hlen) = 1 +Hops (hops) = 0 +Transaction ID (xid) = 12345678 (0xbc614e) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = c0 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Request +Request = 192.168.1.1 +Server Identifier = 192.168.1.0 + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 7 [ARCNET] +Hardware Address Length (hlen) = 1 +Hops (hops) = 0 +Transaction ID (xid) = 12345678 (0xbc614e) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 192.168.1.1 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = c0 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP ACK +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + ++++++++++++++++++++++++++ +CLIENT SENDING 244 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 7 [ARCNET] +Hardware Address Length (hlen) = 1 +Hops (hops) = 0 +Transaction ID (xid) = 87654321 (0x5397fb1) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = a8 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Discover + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 7 [ARCNET] +Hardware Address Length (hlen) = 1 +Hops (hops) = 0 +Transaction ID (xid) = 87654321 (0x5397fb1) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 192.168.1.2 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = a8 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Offer +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + ++++++++++++++++++++++++++ +CLIENT SENDING 256 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 7 [ARCNET] +Hardware Address Length (hlen) = 1 +Hops (hops) = 0 +Transaction ID (xid) = 87654321 (0x5397fb1) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = a8 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Request +Request = 192.168.1.2 +Server Identifier = 192.168.1.0 + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 7 [ARCNET] +Hardware Address Length (hlen) = 1 +Hops (hops) = 0 +Transaction ID (xid) = 87654321 (0x5397fb1) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 192.168.1.2 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = a8 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP ACK +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + diff --git a/p3-dhcps/tests/expected/C_eth_dhcp.txt b/p3-dhcps/tests/expected/C_eth_dhcp.txt new file mode 100644 index 0000000..dd8e945 --- /dev/null +++ b/p3-dhcps/tests/expected/C_eth_dhcp.txt @@ -0,0 +1,106 @@ ++++++++++++++++++++++++++ +CLIENT SENDING 244 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 1 [Ethernet (10Mb)] +Hardware Address Length (hlen) = 6 +Hops (hops) = 0 +Transaction ID (xid) = 42 (0x2a) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = 010102020303 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Discover + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 1 [Ethernet (10Mb)] +Hardware Address Length (hlen) = 6 +Hops (hops) = 0 +Transaction ID (xid) = 42 (0x2a) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 192.168.1.1 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = 010102020303 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Offer +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + ++++++++++++++++++++++++++ +CLIENT SENDING 256 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 1 [Ethernet (10Mb)] +Hardware Address Length (hlen) = 6 +Hops (hops) = 0 +Transaction ID (xid) = 42 (0x2a) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = 010102020303 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Request +Request = 192.168.1.1 +Server Identifier = 192.168.1.0 + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 1 [Ethernet (10Mb)] +Hardware Address Length (hlen) = 6 +Hops (hops) = 0 +Transaction ID (xid) = 42 (0x2a) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 192.168.1.1 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = 010102020303 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP ACK +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + diff --git a/p3-dhcps/tests/expected/C_multi.txt b/p3-dhcps/tests/expected/C_multi.txt new file mode 100644 index 0000000..553f30e --- /dev/null +++ b/p3-dhcps/tests/expected/C_multi.txt @@ -0,0 +1,475 @@ ++++++++++++++++++++++++++ +CLIENT SENDING 244 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 1 [Ethernet (10Mb)] +Hardware Address Length (hlen) = 6 +Hops (hops) = 0 +Transaction ID (xid) = 42 (0x2a) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = 010102020303 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Discover + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 1 [Ethernet (10Mb)] +Hardware Address Length (hlen) = 6 +Hops (hops) = 0 +Transaction ID (xid) = 42 (0x2a) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 192.168.1.1 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = 010102020303 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Offer +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + ++++++++++++++++++++++++++ +CLIENT SENDING 256 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 1 [Ethernet (10Mb)] +Hardware Address Length (hlen) = 6 +Hops (hops) = 0 +Transaction ID (xid) = 42 (0x2a) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = 010102020303 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Request +Request = 192.168.1.1 +Server Identifier = 192.168.1.0 + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 1 [Ethernet (10Mb)] +Hardware Address Length (hlen) = 6 +Hops (hops) = 0 +Transaction ID (xid) = 42 (0x2a) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 192.168.1.1 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = 010102020303 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP ACK +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + ++++++++++++++++++++++++++ +CLIENT SENDING 244 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 7 [ARCNET] +Hardware Address Length (hlen) = 1 +Hops (hops) = 0 +Transaction ID (xid) = 87654321 (0x5397fb1) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = a8 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Discover + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 7 [ARCNET] +Hardware Address Length (hlen) = 1 +Hops (hops) = 0 +Transaction ID (xid) = 87654321 (0x5397fb1) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 192.168.1.2 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = a8 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Offer +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + ++++++++++++++++++++++++++ +CLIENT SENDING 256 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 7 [ARCNET] +Hardware Address Length (hlen) = 1 +Hops (hops) = 0 +Transaction ID (xid) = 87654321 (0x5397fb1) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = a8 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Request +Request = 192.168.1.2 +Server Identifier = 192.168.1.0 + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 7 [ARCNET] +Hardware Address Length (hlen) = 1 +Hops (hops) = 0 +Transaction ID (xid) = 87654321 (0x5397fb1) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 192.168.1.2 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = a8 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP ACK +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + ++++++++++++++++++++++++++ +CLIENT SENDING 244 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 6 [IEEE 802 Networks] +Hardware Address Length (hlen) = 6 +Hops (hops) = 0 +Transaction ID (xid) = 1234 (0x4d2) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = 0123456789ab +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Discover + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 6 [IEEE 802 Networks] +Hardware Address Length (hlen) = 6 +Hops (hops) = 0 +Transaction ID (xid) = 1234 (0x4d2) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 192.168.1.3 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = 0123456789ab +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Offer +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + ++++++++++++++++++++++++++ +CLIENT SENDING 256 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 6 [IEEE 802 Networks] +Hardware Address Length (hlen) = 6 +Hops (hops) = 0 +Transaction ID (xid) = 1234 (0x4d2) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = 0123456789ab +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Request +Request = 192.168.1.3 +Server Identifier = 192.168.1.0 + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 6 [IEEE 802 Networks] +Hardware Address Length (hlen) = 6 +Hops (hops) = 0 +Transaction ID (xid) = 1234 (0x4d2) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 192.168.1.3 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = 0123456789ab +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP ACK +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + ++++++++++++++++++++++++++ +CLIENT SENDING 244 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 18 [Fibre Channel] +Hardware Address Length (hlen) = 3 +Hops (hops) = 0 +Transaction ID (xid) = 55 (0x37) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = c0d1e2 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Discover + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 18 [Fibre Channel] +Hardware Address Length (hlen) = 3 +Hops (hops) = 0 +Transaction ID (xid) = 55 (0x37) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 192.168.1.4 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = c0d1e2 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Offer +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + ++++++++++++++++++++++++++ +CLIENT SENDING 256 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 18 [Fibre Channel] +Hardware Address Length (hlen) = 3 +Hops (hops) = 0 +Transaction ID (xid) = 55 (0x37) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = c0d1e2 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Request +Request = 192.168.1.4 +Server Identifier = 192.168.1.0 + +++++++++++++++++++++++++++ +CLIENT RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 18 [Fibre Channel] +Hardware Address Length (hlen) = 3 +Hops (hops) = 0 +Transaction ID (xid) = 55 (0x37) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 192.168.1.4 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = c0d1e2 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP ACK +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 + ++++++++++++++++++++++++++ +CLIENT SENDING 244 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 15 [Frame Relay] +Hardware Address Length (hlen) = 2 +Hops (hops) = 0 +Transaction ID (xid) = 12345678 (0xbc614e) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = c0a8 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Discover + +++++++++++++++++++++++++++ +CLIENT RECEIVED 250 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 15 [Frame Relay] +Hardware Address Length (hlen) = 2 +Hops (hops) = 0 +Transaction ID (xid) = 12345678 (0xbc614e) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = c0a8 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP NAK +Server Identifier = 192.168.1.0 + diff --git a/p3-dhcps/tests/expected/D_eth_disc.txt b/p3-dhcps/tests/expected/D_eth_disc.txt new file mode 100644 index 0000000..204cf11 --- /dev/null +++ b/p3-dhcps/tests/expected/D_eth_disc.txt @@ -0,0 +1,51 @@ +++++++++++++++++++++++++++ +SERVER RECEIVED 244 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 1 [Ethernet (10Mb)] +Hardware Address Length (hlen) = 6 +Hops (hops) = 0 +Transaction ID (xid) = 0 (0x0) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = 010102020303 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Discover + ++++++++++++++++++++++++++ +SERVER SENDING 256 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 1 [Ethernet (10Mb)] +Hardware Address Length (hlen) = 6 +Hops (hops) = 0 +Transaction ID (xid) = 0 (0x0) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 192.168.1.1 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = 010102020303 +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Offer +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 diff --git a/p3-dhcps/tests/expected/D_ieee_req.txt b/p3-dhcps/tests/expected/D_ieee_req.txt new file mode 100644 index 0000000..f228937 --- /dev/null +++ b/p3-dhcps/tests/expected/D_ieee_req.txt @@ -0,0 +1,53 @@ +++++++++++++++++++++++++++ +SERVER RECEIVED 256 BYTES: +++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 1 [BOOTREQUEST] +Hardware Type (htype) = 6 [IEEE 802 Networks] +Hardware Address Length (hlen) = 6 +Hops (hops) = 0 +Transaction ID (xid) = 0 (0x0) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 0.0.0.0 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = 0123456789ab +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP Request +Request = 192.168.1.1 +Server Identifier = 192.168.1.0 + ++++++++++++++++++++++++++ +SERVER SENDING 256 BYTES: ++++++++++++++++++++++++++ + +------------------------------------------------------ +BOOTP Options +------------------------------------------------------ +Op Code (op) = 2 [BOOTREPLY] +Hardware Type (htype) = 6 [IEEE 802 Networks] +Hardware Address Length (hlen) = 6 +Hops (hops) = 0 +Transaction ID (xid) = 0 (0x0) +Seconds (secs) = 0 Days, 0:00:00 +Flags (flags) = 0 +Client IP Address (ciaddr) = 0.0.0.0 +Your IP Address (yiaddr) = 192.168.1.1 +Server IP Address (siaddr) = 0.0.0.0 +Relay IP Address (giaddr) = 0.0.0.0 +Client Ethernet Address (chaddr) = 0123456789ab +------------------------------------------------------ +DHCP Options +------------------------------------------------------ +Magic Cookie = [OK] +Message Type = DHCP ACK +IP Address Lease Time = 30 Days, 0:00:00 +Server Identifier = 192.168.1.0 diff --git a/p3-dhcps/tests/integration.sh b/p3-dhcps/tests/integration.sh new file mode 100755 index 0000000..51c59c1 --- /dev/null +++ b/p3-dhcps/tests/integration.sh @@ -0,0 +1,93 @@ +#!/bin/bash + +EXE="../dhcps" + +function run_test { + + # parameters + TAG=$1 + shift + ARGS=$1 + shift + + PTAG=$(printf '%-30s' "$TAG") + if [[ $(echo $TAG | cut -d'_' -f1) == $SKIP_C || + $(echo $TAG | cut -d'_' -f1) == $SKIP_B || + $(echo $TAG | cut -d'_' -f1) == $SKIP_A ]] ; then + echo "$PTAG SKIPPED (previous phases not complete)" + return + fi + + # file paths + OUTPUT=outputs/$TAG.txt + DIFF=outputs/$TAG.diff + EXPECT=expected/$TAG.txt + VALGRND=valgrind/$TAG.txt + + SARGS="-s 1" + # run test and compare output to the expected version + if [[ $TAG == "A_threads" ]] ; then + SARGS="-t ${SARGS}0" + fi + if [[ $(echo $TAG | cut -d'_' -f1) == 'D' ]] ; then + $EXE $SARGS >"$OUTPUT" 2>/dev/null & + else + $EXE $SARGS >/dev/null 2>&1 & + fi + sleep .5 + if [[ $TAG == "A_threads" ]] ; then + ./threads 2>/dev/null >"$OUTPUT" + elif [[ $(echo $TAG | cut -d'_' -f1) == 'D' ]] ; then + ./client $ARGS >/dev/null 2>&1 + else + ./client $ARGS 2>/dev/null >"$OUTPUT" + fi + sleep 1.5 + + diff -u "$OUTPUT" "$EXPECT" >"$DIFF" + if [ -s "$DIFF" ]; then + + if [[ $TAG == "A_threads" ]] ; then + echo "$PTAG FAIL - Command line: $EXE $SARGS ; ./threads $ARGS" + else + echo "$PTAG FAIL - Command line: $EXE $SARGS ; ./client $ARGS" + fi + if [[ $(echo $PTAG | cut -d'_' -f1) == 'D' ]] ; then + SKIP_C="C" + SKIP_B="B" + SKIP_A="A" + elif [[ $(echo $PTAG | cut -d'_' -f1) == 'C' ]] ; then + SKIP_B="B" + SKIP_A="A" + elif [[ $(echo $PTAG | cut -d'_' -f1) == 'B' ]] ; then + SKIP_A="A" + fi + else + echo "$PTAG pass" + fi + + # run valgrind + #valgrind $EXE -s 2 >"$VALGRND" 2>&1 & + #sleep 1 + #./client $ARGS >/dev/null 2>&1 + #sleep 2 +} + +# initialize output folders +mkdir -p outputs +mkdir -p valgrind +rm -f outputs/* valgrind/* + +# run individual tests +source itests.include + +# check for memory leaks +#LEAK=`cat valgrind/*.txt | grep 'definitely lost' | grep -v ' 0 bytes in 0 blocks'` +#LEAK= +#if [ -z "$LEAK" ]; then +# echo "No memory leak found." +#else +# echo "Memory leak(s) found. See files listed below for details." +# grep 'definitely lost' valgrind/*.txt | sed -e 's/:.*$//g' | sed -e 's/^/ - /g' +#fi + diff --git a/p3-dhcps/tests/itests.include b/p3-dhcps/tests/itests.include new file mode 100644 index 0000000..b825936 --- /dev/null +++ b/p3-dhcps/tests/itests.include @@ -0,0 +1,21 @@ +# list of integration tests +# format: run_test +# used as the root for all filenames (i.e., "expected/$TAG.txt") +# command-line arguments to test + +run_test D_eth_disc "data/eth-disc" +run_test D_ieee_req "data/ieee-req" + +run_test C_eth_dhcp "data/eth-dhcp" +run_test C_arc_two "data/arc-two" +run_test C_multi "data/multi-dhcp" +#run_test C_arc_bad "data/arc-req-bad" + +run_test B_ieee_release "data/ieee-release" +run_test B_arc_two "data/arc-two-rel" +run_test B_frame_interleave "data/frame-inter" +run_test B_fibre_repeat "data/fibre-repeat" +run_test B_multi_reuse "data/multi-reuse" +run_test B_multi_release "data/multi-release" + +run_test A_threads "" diff --git a/p3-dhcps/tests/public.c b/p3-dhcps/tests/public.c new file mode 100644 index 0000000..c9d0f62 --- /dev/null +++ b/p3-dhcps/tests/public.c @@ -0,0 +1,29 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +START_TEST (C_test_template) +{ + int x = 5; + int y = 5; + ck_assert_int_eq (x, y); +} +END_TEST + +void public_tests (Suite *s) +{ + TCase *tc_public = tcase_create ("Public"); + tcase_add_test (tc_public, C_test_template); + suite_add_tcase (s, tc_public); +} + diff --git a/p3-dhcps/tests/style.sh b/p3-dhcps/tests/style.sh new file mode 100755 index 0000000..ba2210a --- /dev/null +++ b/p3-dhcps/tests/style.sh @@ -0,0 +1,53 @@ +#!/bin/bash + +STYLE="gnu" + +IGNORE=() +FAIL=0 + +function comp_file { + + SRC=$1 + SRC_NAME=$2 + + # file paths + FORMAT=ckstyle/${SRC_NAME}.$STYLE + DIFF=ckstyle/${SRC_NAME}.diff + + # run clang-format and compare results + clang-format --style=$STYLE $source > $FORMAT + diff -u $SRC $FORMAT >$DIFF + + PTAG=$(printf '%-30s' "$SRC_NAME") + if [ -s $DIFF ]; then + echo "$PTAG FAIL (see $DIFF for details)" + FAIL=1 + else + echo "$PTAG pass" + rm $DIFF + fi + rm $FORMAT +} + +mkdir -p ckstyle +rm -f ckstyle/* + +for source in $(ls ../*.c ../*.h) ; do + SKIP=0 + src=$(basename $source) + for ignore in ${IGNORE[*]} ; do + if [ "$src" = "$ignore" ] ; then + SKIP=1 + fi + done + if [ $SKIP = 0 ] ; then + comp_file $source $src + fi +done + +if [ $FAIL != 0 ] ; then + echo "Code that does not adhere to GNU standards will not be accepted." + echo "You must fix these files before submission." +else + echo "Code correctly adheres to required style." +fi diff --git a/p3-dhcps/tests/testsuite.c b/p3-dhcps/tests/testsuite.c new file mode 100644 index 0000000..79db20b --- /dev/null +++ b/p3-dhcps/tests/testsuite.c @@ -0,0 +1,32 @@ +#include +#include +#include +#include +#include +#include + +#include + +extern void public_tests (Suite *s); + +Suite * test_suite (void) +{ + Suite *s = suite_create ("Default"); + public_tests (s); + return s; +} + +void run_testsuite () +{ + Suite *s = test_suite (); + SRunner *sr = srunner_create (s); + srunner_run_all (sr, CK_NORMAL); + srunner_free (sr); +} + +int main (void) +{ + srand((unsigned)time(NULL)); + run_testsuite (); + return EXIT_SUCCESS; +} diff --git a/p3-dhcps/tests/threads b/p3-dhcps/tests/threads new file mode 100755 index 0000000..10ead32 Binary files /dev/null and b/p3-dhcps/tests/threads differ