aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJack Sangdahl <[email protected]>2024-04-02 23:09:33 -0600
committerJack Sangdahl <[email protected]>2024-04-02 23:09:33 -0600
commit7ddf686201d052e7c55d622dddd62b7e2ede880d (patch)
tree1ce98638df78223b5a42ef8ba7aa7b1f5d49941a
parentf3172b837cc728dec1daa74ce98a72548f204b21 (diff)
downloadcsci3753-7ddf686201d052e7c55d622dddd62b7e2ede880d.tar.gz
csci3753-7ddf686201d052e7c55d622dddd62b7e2ede880d.zip
pa6
-rw-r--r--pa6_threaded_resolver/Makefile37
-rw-r--r--pa6_threaded_resolver/array.c48
-rw-r--r--pa6_threaded_resolver/array.h62
-rw-r--r--pa6_threaded_resolver/multi-lookup.c172
-rw-r--r--pa6_threaded_resolver/multi-lookup.h44
-rw-r--r--pa6_threaded_resolver/process.pngbin0 -> 65316 bytes
-rw-r--r--pa6_threaded_resolver/readme.md180
-rw-r--r--pa6_threaded_resolver/util.c85
-rw-r--r--pa6_threaded_resolver/util.h42
9 files changed, 670 insertions, 0 deletions
diff --git a/pa6_threaded_resolver/Makefile b/pa6_threaded_resolver/Makefile
new file mode 100644
index 0000000..80efc14
--- /dev/null
+++ b/pa6_threaded_resolver/Makefile
@@ -0,0 +1,37 @@
+# Makefile v1 for CSCI3753-F23 PA6
+
+# Add any additional source files you'd like to submit by appending
+# .c filenames to the MSRCS line and .h filenames to the MHDRS line
+MSRCS = multi-lookup.c array.c
+MHDRS = multi-lookup.h array.h
+
+# Do not modify anything after this line
+CC = gcc
+CFLAGS = -Wextra -Wall -g -std=gnu99
+INCLUDES =
+LFLAGS =
+LIBS = -lpthread
+
+MAIN = multi-lookup
+
+SRCS = $(MSRCS) util.c
+HDRS = $(MHDRS) util.h
+
+OBJS = $(SRCS:.c=.o)
+
+$(MAIN): $(OBJS)
+ $(CC) $(CFLAGS) $(INCLUDES) -o $(MAIN) $(OBJS) $(LFLAGS) $(LIBS)
+
+%.o: %.c $(HDRS)
+ $(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@
+
+.PHONY: clean
+clean:
+ $(RM) *.o *~ $(MAIN)
+
+SUBMITFILES = $(MSRCS) $(MHDRS) Makefile README
+submit:
+ @read -r -p "Enter your identikey username: " username; \
+ echo; echo Bundling the following files for submission; \
+ tar --transform "s|^|PA6-$$username/|" -cvf PA6-$$username.txt $(SUBMITFILES); \
+ echo; echo Please upload the file PA6-$$username.txt to Canvas to complete your submission; echo
diff --git a/pa6_threaded_resolver/array.c b/pa6_threaded_resolver/array.c
new file mode 100644
index 0000000..e43dbdf
--- /dev/null
+++ b/pa6_threaded_resolver/array.c
@@ -0,0 +1,48 @@
+// Jack Sangdahl
+// CSCI3753 SP24
+// PA6 Threaded Resolver
+#include "array.h"
+
+int queue_init(queue *q, int queue_size) {
+ q->data = malloc(queue_size * sizeof(node));
+ q->maxsize = queue_size;
+ q->front = 0;
+ q->back = -1;
+ q->size = 0;
+ return EXIT_SUCCESS;
+}
+
+int queue_size(queue *q) { return q->size; }
+
+bool queue_full(queue *q) { return (q->size == q->maxsize); }
+
+int queue_push(queue *q, void *element) {
+ if (queue_full(q)) {
+ fprintf(stderr, "push error: queue full\n");
+ return EXIT_FAILURE;
+ }
+ q->back = (q->back + 1) % q->maxsize;
+ q->data[q->back].value = element;
+ q->size++;
+ return EXIT_SUCCESS;
+}
+
+void *queue_pop(queue *q) {
+ if (!queue_size(q)) {
+ fprintf(stderr, "pop error: queue empty\n");
+ return NULL;
+ }
+ void *value = q->data[q->front].value;
+ q->data[q->front].value = NULL;
+ q->front = (q->front + 1) % q->maxsize;
+ q->size--;
+ return value;
+}
+
+void queue_free(queue *q) {
+ while (q->size > 0) {
+ void *val = queue_pop(q);
+ free(val);
+ }
+ free(q->data);
+}
diff --git a/pa6_threaded_resolver/array.h b/pa6_threaded_resolver/array.h
new file mode 100644
index 0000000..adceca8
--- /dev/null
+++ b/pa6_threaded_resolver/array.h
@@ -0,0 +1,62 @@
+// Jack Sangdahl
+// CSCI3753 SP24
+// PA6 Threaded Resolver
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+
+typedef struct {
+ void *value;
+} node;
+
+typedef struct {
+ node *data;
+ int size;
+ int maxsize;
+ int front;
+ int back;
+} queue;
+
+/**
+ * @brief initialise the queue
+ * @param q
+ * @param size
+ * @return int
+ */
+int queue_init(queue *q, int size);
+
+/**
+ * @brief return size of queue
+ * @param q
+ * @return int
+ */
+int queue_size(queue *q);
+
+/**
+ * @brief return true if que size is
+ * greater than ARRAY_SIZE. false otherwise
+ * @param q
+ * @return bool
+ */
+bool queue_full(queue *q);
+
+/**
+ * @brief add element to fifo queue. block when full
+ * @param q
+ * @param val
+ * @return int
+ */
+int queue_push(queue *q, void *val);
+
+/**
+ * @brief return top element of queue in fifo order
+ * return null pointer if queue empty
+ * @param q
+ */
+void *queue_pop(queue *q);
+
+/**
+ * @brief free queue's resources
+ * @param q
+ */
+void queue_free(queue *q);
diff --git a/pa6_threaded_resolver/multi-lookup.c b/pa6_threaded_resolver/multi-lookup.c
new file mode 100644
index 0000000..cc96478
--- /dev/null
+++ b/pa6_threaded_resolver/multi-lookup.c
@@ -0,0 +1,172 @@
+// Jack Sangdahl
+// CSCI3753 SP24
+// PA6 Threaded Resolver
+#include "multi-lookup.h"
+#include <sys/time.h>
+
+int main(int argc, char *argv[]) {
+ if (argc < 6) {
+ fprintf(stderr,
+ "usage: %s <# requester> <# resolver> <requester log> <resolver "
+ "log> [ <data file> ... ]\n",
+ argv[0]);
+ return EXIT_FAILURE;
+ } else if (argc > MAX_INPUT_FILES + 6) {
+ fprintf(stderr, "%s error: too many input files\n", argv[0]);
+ return EXIT_FAILURE;
+ }
+ struct timeval proc_begin, proc_end;
+ gettimeofday(&proc_begin, NULL);
+ uint8_t req_thread = atoi(argv[1]);
+ if (req_thread < 1) {
+ fprintf(stderr, "%s error: minimum 1 requester thread\n", argv[0]);
+ return EXIT_FAILURE;
+ } else if (req_thread > MAX_REQUESTER_THREADS) {
+ fprintf(stderr, "%s error: requester threads cannot exceed %d\n", argv[0],
+ MAX_REQUESTER_THREADS);
+ return EXIT_FAILURE;
+ }
+ uint8_t res_threads = atoi(argv[2]);
+ if (res_threads < 1) {
+ fprintf(stderr, "%s error: minimum 1 resolver thread\n", argv[0]);
+ return EXIT_FAILURE;
+ } else if (res_threads > MAX_RESOLVER_THREADS) {
+ fprintf(stderr, "%s error: resolver threads cannot exceed %d\n", argv[0],
+ MAX_RESOLVER_THREADS);
+ return EXIT_FAILURE;
+ }
+ queue input_files;
+ queue_init(&input_files, MAX_INPUT_FILES);
+ for (int i = 5; i < argc; i++) {
+ if (access(argv[i], F_OK) == -1)
+ fprintf(stderr, "%s error: invalid data file '%s'\n", argv[0], argv[i]);
+ else
+ queue_push(&input_files, strndup(argv[i], strlen(argv[i])));
+ }
+ if (!queue_size(&input_files)) {
+ fprintf(stderr, "%s error: no valid data file given\n", argv[0]);
+ return EXIT_FAILURE;
+ }
+ pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
+ pthread_cond_t c_req = PTHREAD_COND_INITIALIZER;
+ pthread_cond_t c_res = PTHREAD_COND_INITIALIZER;
+ pthread_mutex_t input_lock = PTHREAD_MUTEX_INITIALIZER;
+ pthread_mutex_t req_log_lock = PTHREAD_MUTEX_INITIALIZER;
+ pthread_mutex_t res_log_lock = PTHREAD_MUTEX_INITIALIZER;
+ bool req_done = 0;
+ queue buffer;
+ queue_init(&buffer, ARRAY_SIZE);
+ request_data req_data;
+ req_data.req_log = fopen(argv[3], "w");
+ req_data.file_names = &input_files;
+ req_data.buffer = &buffer;
+ req_data.m = &m;
+ req_data.c_req = &c_req;
+ req_data.c_res = &c_res;
+ req_data.input_lock = &input_lock;
+ req_data.req_log_lock = &req_log_lock;
+ resolve_data res_data;
+ res_data.res_log = fopen(argv[4], "w");
+ res_data.buffer = &buffer;
+ res_data.m = &m;
+ res_data.c_req = &c_req;
+ res_data.c_res = &c_res;
+ res_data.res_log_lock = &res_log_lock;
+ res_data.req_done = &req_done;
+ pthread_t request_tid[req_thread];
+ pthread_t resolve_tid[res_threads];
+ for (int i = 0; i < req_thread; i++)
+ pthread_create(&request_tid[i], NULL, request, &req_data);
+ for (int i = 0; i < res_threads; i++)
+ pthread_create(&resolve_tid[i], NULL, resolve, &res_data);
+ for (int i = 0; i < req_thread; i++)
+ pthread_join(request_tid[i], NULL);
+ pthread_mutex_lock(&m);
+ req_done = 1;
+ pthread_mutex_unlock(&m);
+ for (int i = 0; i < res_threads; i++)
+ pthread_join(resolve_tid[i], NULL);
+ fclose(req_data.req_log);
+ fclose(res_data.res_log);
+ queue_free(&input_files);
+ queue_free(&buffer);
+ printf("Number of Requestor Threads: %d\n", req_thread);
+ printf("Number of Resolver Threads: %d\n", res_threads);
+ gettimeofday(&proc_end, NULL);
+ fprintf(stdout, "%s: total time is %f seconds\n", argv[0],
+ ELAPSED_SECONDS(proc_begin, proc_end));
+ return EXIT_SUCCESS;
+}
+
+void *request(void *data) {
+ request_data *req_data = (request_data *)data;
+ uint8_t file_count = 0;
+ while (1) {
+ pthread_mutex_lock(req_data->input_lock);
+ if (queue_size(req_data->file_names) > 0) {
+ char *data_file = queue_pop(req_data->file_names);
+ file_count++;
+ pthread_mutex_unlock(req_data->input_lock);
+ FILE *file = fopen(data_file, "r");
+ char line[MAX_NAME_LENGTH];
+ while (fgets(line, sizeof(line), file)) {
+ pthread_mutex_lock(req_data->m);
+ while (queue_full(req_data->buffer))
+ pthread_cond_wait(req_data->c_req, req_data->m);
+ pthread_mutex_lock(req_data->req_log_lock);
+ fprintf(req_data->req_log, "%s", line);
+ pthread_mutex_unlock(req_data->req_log_lock);
+ queue_push(req_data->buffer, strndup(strtok(line, "\n"), strlen(line)));
+ pthread_mutex_unlock(req_data->m);
+ pthread_cond_signal(req_data->c_res);
+ }
+ fclose(file);
+ free(data_file);
+ } else {
+ pthread_mutex_unlock(req_data->input_lock);
+ break;
+ }
+ }
+ fprintf(stdout, "thread %ld serviced %d files\n", syscall(SYS_gettid),
+ file_count);
+ return 0;
+}
+
+void *resolve(void *data) {
+ resolve_data *res_data = (resolve_data *)data;
+ char *ip = (char *)malloc(MAX_IP_LENGTH);
+ uint16_t domain_cnt = 0;
+ struct timeval thread_begin, thread_end;
+ gettimeofday(&thread_begin, NULL);
+ while (1) {
+ pthread_mutex_lock(res_data->m);
+ if (!(*(res_data->req_done)) || queue_size(res_data->buffer)) {
+ while (!queue_size(res_data->buffer))
+ pthread_cond_wait(res_data->c_res, res_data->m);
+ char *domain = queue_pop(res_data->buffer);
+ pthread_mutex_unlock(res_data->m);
+ pthread_cond_signal(res_data->c_req);
+ if (dnslookup(domain, ip, MAX_IP_LENGTH)) {
+ pthread_mutex_lock(res_data->res_log_lock);
+ fprintf(res_data->res_log, "%s, NOT_RESOLVED\n", domain);
+ pthread_mutex_unlock(res_data->res_log_lock);
+ domain_cnt++;
+ } else {
+ pthread_mutex_lock(res_data->res_log_lock);
+ fprintf(res_data->res_log, "%s, %s\n", domain, ip);
+ pthread_mutex_unlock(res_data->res_log_lock);
+ domain_cnt++;
+ }
+ free(domain);
+ } else {
+ pthread_mutex_unlock(res_data->m);
+ break;
+ }
+ }
+ free(ip);
+ gettimeofday(&thread_end, NULL);
+ fprintf(stdout, "thread %ld resolved %d hostnames in %f seconds\n",
+ syscall(SYS_gettid), domain_cnt,
+ ELAPSED_SECONDS(thread_begin, thread_end));
+ return 0;
+}
diff --git a/pa6_threaded_resolver/multi-lookup.h b/pa6_threaded_resolver/multi-lookup.h
new file mode 100644
index 0000000..41e74d5
--- /dev/null
+++ b/pa6_threaded_resolver/multi-lookup.h
@@ -0,0 +1,44 @@
+// Jack Sangdahl
+// CSCI3753 SP24
+// PA6 Threaded Resolver
+#include "array.h"
+#include "util.h"
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/syscall.h>
+#include <sys/time.h>
+#include <unistd.h>
+
+#define ARRAY_SIZE 8
+#define MAX_INPUT_FILES 100
+#define MAX_REQUESTER_THREADS 10
+#define MAX_RESOLVER_THREADS 10
+#define MAX_NAME_LENGTH 16
+#define MAX_IP_LENGTH INET6_ADDRSTRLEN
+#define ELAPSED_SECONDS(a, b) \
+ ((double)(b.tv_usec - a.tv_usec) / 1000000 + (double)(b.tv_sec - a.tv_sec))
+
+void *request(void *data);
+void *resolve(void *data);
+
+typedef struct {
+ FILE *req_log;
+ queue *file_names;
+ queue *buffer;
+ pthread_mutex_t *m;
+ pthread_cond_t *c_req;
+ pthread_cond_t *c_res;
+ pthread_mutex_t *input_lock;
+ pthread_mutex_t *req_log_lock;
+} request_data;
+
+typedef struct {
+ FILE *res_log;
+ queue *buffer;
+ pthread_mutex_t *m;
+ pthread_cond_t *c_req;
+ pthread_cond_t *c_res;
+ pthread_mutex_t *res_log_lock;
+ bool *req_done;
+} resolve_data;
diff --git a/pa6_threaded_resolver/process.png b/pa6_threaded_resolver/process.png
new file mode 100644
index 0000000..0d492ac
--- /dev/null
+++ b/pa6_threaded_resolver/process.png
Binary files differ
diff --git a/pa6_threaded_resolver/readme.md b/pa6_threaded_resolver/readme.md
new file mode 100644
index 0000000..6cd9dc7
--- /dev/null
+++ b/pa6_threaded_resolver/readme.md
@@ -0,0 +1,180 @@
+## PA6 - Threaded Resolver
+### overview
+this assignment will develop a multi-threaded application that resolves domain names
+to ip addresses. this is similar to the operation performed each time you access a new
+website in your web browser. the application will utilise two types of worker threads:
+**requesters** and **resolvers**. these threads will communicate with eachother using a
+shared array, sometimes referred to as a bounded buffer. the diagram below outlines
+the desired configuration
+![image](process.png)
+provided is a set of input files that containe one hostname per line. your program
+will process each of these files using a single requester thread per file, where each
+thread will read the file line by line, place the hostname into the shared array, and
+write this hostname into the requester logfile. you will also create some number of
+resolver threads, again determined by a command line argument, that will remove
+hostnames from the shared array, lookup the ip address for that hostname, and write
+the results to the resolver logfile. once all the input files have been parsed, the
+requester threads will terminate. once all the hostnames have been looked up, the
+resolver threads will terminate and the program will end.
+
+### mutli-lookup man page
+```
+NAME
+multi-lookup - resolve a set of hostnames to IP addresses
+
+SYNOPSIS
+multi-lookup <# requester> <# resolver> <requester log> <resolver log> [ <data file> ... ]
+
+DESCRIPTION
+The file names specified by <data file> are passed to the pool of requester threads which place
+information into a shared data area. Resolver threads read the shared data area and find the
+corresponding IP address.
+
+<# requesters> number of requestor threads to place into the thread pool
+<# resolvers> number of resolver threads to place into the thread pool
+<requester log> name of the file into which requested hostnames are written
+<resolver log> name of the file into which hostnames and resolved IP addresses are written
+<data file> filename to be processed. Each file contains a list of host names, one per line, that
+are to be resolved
+
+SAMPLE INVOCATION
+./multi-lookup 5 5 serviced.txt resolved.txt input/names*.txt
+
+SAMPLE CONSOLE OUTPUT
+thread d2fa8700 serviced 6 files in 0.200960 seconds
+thread d37a9700 serviced 6 files in 0.201183 seconds
+thread d27a7700 serviced 6 files in 0.201345 seconds
+thread d3faa700 serviced 6 files in 0.201562 seconds
+thread d47ab700 serviced 6 files in 0.201787 seconds
+thread d4fac700 resolved 177 hosts in 0.202039 seconds
+thread d57ad700 resolved 89 hosts in 0.202088 seconds
+thread d67af700 resolved 171 hosts in 0.202204 seconds
+thread d6fb0700 resolved 32 hosts in 0.202244 seconds
+thread d5fae700 resolved 137 hosts in 0.202215 seconds
+./multi-lookup: total time is 0.202741 seconds
+```
+
+### requester threads
+your application will take an argument for the number of requester threads. this pool
+of threads service a set of text files as input, each of which contains a list of
+hostnames. each name read should be placed on the shared array. if a requester thread
+tries to write to the array but finds that it is full, it should block until a space
+opens up in the array. after servicing an input file, a requester thread checks if
+there are any remaining input files left to service. if so, it requests one of the
+remaining files next. this process goes on until all input files have been processed.
+if there are no more input files remaining, the thread writes the number of files it
+serviced to stdout in the following format:
+```
+thread <thread_id> serviced ### files
+```
+to get the id of a thread on linux systems, use `gettid()`, or more preferably,
+`pthread_self()` for a posix compliant implementation.
+
+### resolver threads
+the second thread pool is comprised of a set of resolver threads. a resolver thread
+consumers the shared array by taking a name off the array and resolving its ip
+address. after the name has been mapped to an ip address, the output is written to a
+line in the resolver logfile in the following format:
+```
+google.com, 74.125.224.81
+```
+if the resolver is unable to find the ip address for a hostname, it should leave
+`NOT_RESOLVED` instead of an ip address. if a resolver tries to read from the array
+but find that it is empty, it should block until there is a new item in the array or
+all names in the input files have been serviced. if there are no more hostnames
+remaining, the thread writes the number of names it successfully resolved to stdout
+in the following format:
+```
+thread <thread_id> resolved ### hostnames
+```
+
+### shared array
+thre thread safe shared array will require you to implement a solution to the bounded
+buffer problem as discussed in lecture and textbook. the shared array must be built
+on top of a single, contiguous, linear array of memory. you can organise this array
+as a queue (fifo), circular queue, or stack (lifo). solutions that rely on linked
+lists, trees, dictionaries, etc. will not be accepted.
+
+### synchronisation, deadlock, & starvation
+application should synchronise access to shared resources and avoid any deadlock or
+busying wait. you should use some combination of mutexes, semaphores, and/or
+condition variables to meet this requirement. there are several shared resources that
+must be protected: the shared array, the logfiles, stdout/stderr, and argc/argv. none
+of these resources are thread safe by default. we will be measuring the runtime of
+your solution to determine if it is experiencing any sort of starvation. if you
+misuse a synchronisation mechanism and essentially create a serialised solution (
+where your threads run in sequence, rather than in parallel), your grade will be
+reduced.
+
+### ending program
+your program must end after all the names in each input file have been serviced by
+the application. this means that all the hostnames in all the input files have
+received a corresponding line in the output file. just prior to exiting, your
+program will print the total runtime to stdout:
+```
+./multi-lookup: total time is 2.420237 seconds
+```
+
+### limits
+several macros should be defined to impose the following limits on your program. if
+the user specifies input that violate an imposed limit, your program should alert
+the user gracefully to the limit and exit with an error and appropriate return
+value. you should define these macros in corresponding header files:
+* `ARRAY_SIZE`: 8 - number of elements in the shared array used by the requester
+and resolved threads to communicate
+* `MAX_INPUT_FILES`: 100 - maximum number of hostname file arguments allowed
+* `MAX_REQUESTER_THREADS`: 10 - maximum number of concurrent requester threads
+allowed
+* `MAX_RESOLVER_THREADS`: 10 - maximum number of concurrent resolver threads allowed
+* `MAX_IP_LENGTH`: `INET6_ADDRSTRLEN` - maximum size ip address string `util.c` will
+return
+
+### error handling
+program must accept arguments as detailed in the above manual page. progam should
+deal with the following conditions:
+* missing arguments - terminate with a usage synopsis to stdout
+* arguments that are out of range (too many input files) - terminate with an error
+message to stderr
+* if a log file exists and is writeable, it should overwritten
+* if a log file does not exist, it should be created by program
+* missing or unreadable input files - print `invalid file <filename>` to stderr,
+then move onto next file
+
+### valgrind
+to verify there are no memory leaks, valgrind will be used to test program. usage:
+```
+valgrind ./multi-lookup 5 5 serviced.txt results.txt input/names1*.txt
+```
+valgrind should report that you have freed all allocated memory and should produce
+any additional warnings or errors:
+```
+==21728== Memcheck, a memory error detector
+==21728== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
+==21728== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
+==21728== Command: ./multi-lookup 5 5 serviced.txt results.txt input/names11.txt input/names12.txt input/names13.txt input/names14.txt input/names15.txt input/names1.txt
+==21728==
+./multi-lookup: total time is 2.420237 seconds
+==21728==
+==21728== HEAP SUMMARY:
+==21728== in use at exit: 0 bytes in 0 blocks
+==21728== total heap usage: 1,723 allocs, 1,723 frees, 9,394,035 bytes allocated
+==21728==
+==21728== All heap blocks were freed -- no leaks are possible
+==21728==
+==21728== For counts of detected and suppressed errors, rerun with: -v
+==21728== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
+```
+for comparison, here's output from a leaky program:
+```
+==22400== HEAP SUMMARY:
+==22400== in use at exit: 7,380 bytes in 618 blocks
+==22400== total heap usage: 8,115 allocs, 7,497 frees, 45,674,418 bytes allocated
+==22400==
+==22400== LEAK SUMMARY:
+==22400== definitely lost: 7,380 bytes in 618 blocks
+==22400== indirectly lost: 0 bytes in 0 blocks
+==22400== possibly lost: 0 bytes in 0 blocks
+==22400== still reachable: 0 bytes in 0 blocks
+==22400== suppressed: 0 bytes in 0 blocks
+==22400== Rerun with --leak-check=full to see details of leaked memory
+```
diff --git a/pa6_threaded_resolver/util.c b/pa6_threaded_resolver/util.c
new file mode 100644
index 0000000..0b4a825
--- /dev/null
+++ b/pa6_threaded_resolver/util.c
@@ -0,0 +1,85 @@
+/*
+ * File: util.c
+ * Author: Andy Sayler
+ * Modified: Shiv Mishra
+ * Project: CSCI 3753 Programming Assignment 5
+ * Create Date: 2012/02/01
+ * Modify Date: 2012/02/01
+ * Modify Date: 2016/09/26
+ * Description:
+ * This file contains declarations of utility functions for
+ * Programming Assignment 5.
+ *
+ */
+
+#include "util.h"
+
+int dnslookup(const char* hostname, char* firstIPstr, int maxSize){
+
+ /* Local vars */
+ struct addrinfo* headresult = NULL;
+ struct addrinfo* result = NULL;
+ struct sockaddr_in* ipv4sock = NULL;
+ struct in_addr* ipv4addr = NULL;
+ char ipv4str[INET_ADDRSTRLEN];
+ char ipstr[INET6_ADDRSTRLEN];
+ int addrError = 0;
+
+ /* DEBUG: Print Hostname*/
+#ifdef UTIL_DEBUG
+ fprintf(stderr, "%s\n", hostname);
+#endif
+
+ /* Lookup Hostname */
+ addrError = getaddrinfo(hostname, NULL, NULL, &headresult);
+ if(addrError){
+ fprintf(stderr, "Error looking up Address: %s\n",
+ gai_strerror(addrError));
+ return UTIL_FAILURE;
+ }
+ /* Loop Through result Linked List */
+ for(result=headresult; result != NULL; result = result->ai_next){
+ /* Extract IP Address and Convert to String */
+ if(result->ai_addr->sa_family == AF_INET){
+ /* IPv4 Address Handling */
+ ipv4sock = (struct sockaddr_in*)(result->ai_addr);
+ ipv4addr = &(ipv4sock->sin_addr);
+ if(!inet_ntop(result->ai_family, ipv4addr,
+ ipv4str, sizeof(ipv4str))){
+ perror("Error Converting IP to String");
+ return UTIL_FAILURE;
+ }
+#ifdef UTIL_DEBUG
+ fprintf(stdout, "%s\n", ipv4str);
+#endif
+ strncpy(ipstr, ipv4str, sizeof(ipstr));
+ ipstr[sizeof(ipstr)-1] = '\0';
+ }
+ else if(result->ai_addr->sa_family == AF_INET6){
+ /* IPv6 Handling */
+#ifdef UTIL_DEBUG
+ fprintf(stdout, "IPv6 Address: Not Handled\n");
+#endif
+ strncpy(ipstr, "UNHANDELED", sizeof(ipstr));
+ ipstr[sizeof(ipstr)-1] = '\0';
+ }
+ else{
+ /* Unhandlded Protocol Handling */
+#ifdef UTIL_DEBUG
+ fprintf(stdout, "Unknown Protocol: Not Handled\n");
+#endif
+ strncpy(ipstr, "UNHANDELED", sizeof(ipstr));
+ ipstr[sizeof(ipstr)-1] = '\0';
+ }
+ /* Save First IP Address */
+ if(result==headresult){
+ strncpy(firstIPstr, ipstr, maxSize);
+ firstIPstr[maxSize-1] = '\0';
+ }
+ }
+
+ /* Cleanup */
+ freeaddrinfo(headresult);
+
+ return UTIL_SUCCESS;
+}
diff --git a/pa6_threaded_resolver/util.h b/pa6_threaded_resolver/util.h
new file mode 100644
index 0000000..d0f7559
--- /dev/null
+++ b/pa6_threaded_resolver/util.h
@@ -0,0 +1,42 @@
+/*
+ * File: util.h
+ * Author: Andy Sayler
+ * Modified: Shiv Mishra
+ * Project: CSCI 3753 Programming Assignment 3
+ * Create Date: 2012/02/01
+ * Modify Date: 2012/02/01
+ * Modify Date: 2016/09/26
+ * Description:
+ * This file contains declarations of utility functions for
+ * Programming Assignment 3.
+ *
+ */
+
+#ifndef UTIL_H
+#define UTIL_H
+
+/* Define the following to enable debug statments */
+// #define UTIL_DEBUG
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+
+#define UTIL_FAILURE -1
+#define UTIL_SUCCESS 0
+
+/* Fuction to return the first IP address found
+ * for hostname. IP address returned as string
+ * firstIPstr of size maxsize
+ */
+int dnslookup(const char* hostname,
+ char* firstIPstr,
+ int maxSize);
+
+#endif