1
0

proj: Add exam-2020W-2A

This commit is contained in:
2025-05-05 12:05:50 +02:00
parent 1e40a1b3c6
commit 57405421fa
9 changed files with 1006 additions and 0 deletions

327
proj/exam-2020W-2A/check.py Normal file
View File

@@ -0,0 +1,327 @@
import contextlib
import logging
import multiprocessing
import os
import random
import signal
import socket
import string
import struct
import subprocess
import sys
import tempfile
import traceback
from libosue import *
from libosue import CSnippet as C
from libosue import FunctionPrototype as FP
################################################################################
# INITIALIZE THE EXAM OBJECT
################################################################################
# These functions are not allowed.
not_allowed = [
"DEMO_setup_connection",
"DEMO_server_request_handler",
"DEMO_execute_command",
]
prototypes = {
"parse_arguments": FP("void")("int argc", "char **argv", "struct arguments *args"),
"DEMO_setup_connection": FP("int")("const char *port_str"),
"DEMO_server_request_handler": FP("void")("int sockfd", "const char *command", "int flags"),
"DEMO_execute_command": FP("FILE *")("const char *command", "const char *argument"),
}
PORT = 692
exam.initialize(
"./server",
server_port=7220,
not_allowed=not_allowed + exam.DEFAULT_RESTRICTED_FUNCTIONS,
call_limit=200,
prototypes=prototypes,
report_file="report.txt",
result_file="result.txt",
debug_file="libosue.dbg",
make_targets=["clean", "all"],
initialize_stack=["setup_connection", "server_request_handler", "execute_command"],
)
class FunctionAborted(EvalError):
"""Raised when a function should be aborted."""
pass
# Kill programs on port 692.
# This is slow, so we only do it once every deliver call and not for every session.
utils.free_port(PORT)
################################################################################
# TASK 1: SETUP CONNECTION
################################################################################
def bind_cb(s, args):
"""Call setsockopt(...) before binding."""
reuse = Value.from_type("char")
reuse.value = 1
s.c.setsockopt(args["sockfd"], socket.SOL_SOCKET, socket.SO_REUSEADDR, reuse.address, 1)
with exam.task("Task 1: setup connection", 10) as task:
with task.subtask("initialize server socket", 8) as st:
with exam.session([]) as s:
# Setup tracing for hints.
s.c.socket.tracing = True
s.c.bind.tracing = True
s.c.listen.tracing = True
# Make sure setsockopt(...) is called before bind().
s.c.bind.call_cb = bind_cb
# Call the student's setup_connection connection function
sockfd = s.c.setup_connection(str(PORT).encode())
# Check if the server socket works and creates valid connection sockets.
utils.check_server_socket(s, sockfd, PORT)
# Give the students some hints for their implementation
if st.points != 8:
if "socket" not in s.trace:
exam.log("[INFO] socket() was never called")
if "bind" not in s.trace:
exam.log("[INFO] bind() was never called")
if "listen" not in s.trace:
exam.log("[INFO] listen() was never called")
exam.log("The following subtasks are skipped because of errors:")
exam.log("* error handling (2 points)")
task.skip_subtasks()
with task.subtask("error handling", 2) as st:
with st.subtask("bind()", 1):
with exam.session([]) as s:
s.c.listen.tracing = True
s.c.bind.override_return = C("((int) -1)")
with exam.raises(ExitError, "program did not exit after bind() failed") as excinfo:
sockfd = s.c.setup_connection(str(PORT).encode())
assert excinfo.value.exitcode == 1, "wrong exit code"
assert "listen" not in s.trace, "listen() called after bind() failed"
with st.subtask("listen()", 1):
with exam.session([]) as s:
s.c.listen.override_return = C("((int) -1)")
with exam.raises(ExitError, "program did not exit after listen() failed") as excinfo:
sockfd = s.c.setup_connection(str(PORT).encode())
assert excinfo.value.exitcode == 1, "wrong exit code"
with exam.task("Task 2: handle requests", 20) as task:
with task.subtask("read argument and execute command", 7) as st:
# Check if the program correctly accepts a connection and reads the argument
# from the connection socket. Execution stops when task 3 is called.
def accept_return_cb(s, rv):
rv = rv.convert()
if rv > 0:
s["connfd"] = rv
def read_call_cb(s, args):
connfd = args.get("sockfd") or args.get("fd")
s["read"] = connfd.convert() == s["connfd"]
s["buffer"] = args.get("buf")
def read_return_cb(s, rv):
if s["read"]:
n = rv.convert()
s["result"] += s["buffer"].convert(shape=n, cast="char *")
def execute_command_cb(s, args):
s["ec_called"] = True
try:
s["command"] = args["command"].convert()
s["argument"] = args["argument"].convert()
except InvalidMemoryError:
pass
raise FunctionAborted("This is where execution ends.")
with exam.session([]) as s:
s.c.accept.return_cb = accept_return_cb
s.c.read.call_cb = read_call_cb
s.c.read.return_cb = read_return_cb
s.c.recv.call_cb = read_call_cb
s.c.recv.return_cb = read_return_cb
s.c.execute_command.call_cb = execute_command_cb
s.c.DEMO_execute_command.call_cb = execute_command_cb
s.c.DEMO_execute_command.allowed = True
# Setup the tracing variables.
s["connfd"] = None
s["result"] = b""
s["ec_called"] = False
s["command"] = None
s["argument"] = None
argument = "".join(random.choices(string.ascii_letters, k=24)).encode()
sockfd = s.c.DEMO_setup_connection(str(PORT).encode())
with utils.SingleShotClient(PORT, argument) as client:
with contextlib.suppress(FunctionAborted):
s.c.server_request_handler(sockfd, b"./osue-tool")
assert s["result"], "no argument read from client"
assert s["result"] == argument, "read wrong argument from client"
assert s["ec_called"], "execute_command() not called"
assert s["command"] == b"./osue-tool", "invalid command passt to execute_command()"
assert (
s["argument"] == argument
), "Invalid argument passed to execute_command()\nDid you terminate it?"
with task.subtask("response", 8) as st:
# Execute the full function and check if the client receives the correct result.
def execute_command_cb(s, args):
with s.c.fork.save():
s.c.fork.allowed = True
return s.c.DEMO_execute_command(args["command"], args["argument"])
with st.subtask("single line output", 4) as pt:
with exam.session([]) as s:
s.c.execute_command.call_cb = execute_command_cb
s.c.DEMO_execute_command.call_cb = execute_command_cb
s.c.DEMO_execute_command.allowed = True
sockfd = s.c.DEMO_setup_connection(str(PORT).encode())
argument = "".join(random.choices(string.ascii_letters, k=24)).encode()
expected = argument + b"\n"
with utils.SingleShotClient(PORT, argument) as client:
s.c.server_request_handler(sockfd, b"./osue-tool")
assert client.response, "didn't receive a response for single-line output"
assert client.response == expected, "received wrong response for single-line output"
with st.subtask("multi line output", 4) as pt:
with exam.session([]) as s:
s.c.execute_command.call_cb = execute_command_cb
s.c.DEMO_execute_command.call_cb = execute_command_cb
s.c.DEMO_execute_command.allowed = True
sockfd = s.c.DEMO_setup_connection(str(PORT).encode())
argument = "".join(random.choices(string.ascii_letters, k=128)).encode()
expected = b"\n".join(argument[i : i + 32] for i in range(0, len(argument), 32)) + b"\n"
with utils.SingleShotClient(PORT, argument) as client:
s.c.server_request_handler(sockfd, b"./osue-tool")
assert client.response, "didn't receive a response for multi-line output"
assert client.response == expected, "received wrong response for multi-line output"
if task.points != 15:
exam.log("The following subtasks are skipped because of errors:")
exam.log("* repeated write/send calls (5 points)")
task.skip_subtasks()
with task.subtask("repeated write/send calls", 5) as st:
def accept_return_cb(s, rv):
s["connfd"] = rv.convert()
def write_cb(s, args):
fd = (args.get("fd") or args.get("sockfd")).convert()
if fd == s["connfd"]:
n = args.get("count") or args.get("len")
buf = args.get("buf")
s["messages"].append(buf.convert(shape=n.convert(), cast="char *"))
return 1
with exam.session([]) as s:
s.c.execute_command.call_cb = execute_command_cb
s.c.DEMO_execute_command.call_cb = execute_command_cb
s.c.DEMO_execute_command.allowed = True
s.c.accept.return_cb = accept_return_cb
s.c.write.call_cb = write_cb
s.c.send.call_cb = write_cb
s["connfd"] = None
s["messages"] = []
argument = "".join(random.choices(string.ascii_letters, k=24)).encode()
expected = argument + b"\n"
sockfd = s.c.DEMO_setup_connection(str(PORT).encode())
with utils.SingleShotClient(PORT, argument) as client:
s.c.server_request_handler(sockfd, b"./osue-tool")
assert len(s["messages"]) > 1, "write/send not called multiple times"
message = b"".join(m[:1] for m in s["messages"])
assert message == expected, "received wrong response"
with exam.task("Task 3: execute command", 20) as task:
with task.subtask("fork/exec pattern", 5) as st:
pattern = utils.ForkNode(parent=None, child=utils.ExecNode("./osue-tool"))
with utils.ForkExecTraces(pattern) as tracegen:
for fork_exec_run in tracegen:
with exam.session() as s:
with fork_exec_run(s):
s.c.execute_command(b"./osue-tool", b"just a random string")
assert tracegen.correct, tracegen.default_message
if task.points != 5:
exam.log("The following subtasks are skipped because of errors:")
exam.log("* wait for child process to finish (5 points)")
exam.log("* result (10 points)")
task.skip_subtasks()
with task.subtask("wait for child process to finish", 5) as st:
# Correctly wait for the child process to finish.
# Follow the parent process and trace the return value of fork() and wait().
def fork_return_cb(s, rv):
s["fork_result"] = rv.convert()
def wait_return_cb(s, rv):
s["wait_result"] = rv.convert()
with exam.session([]) as s:
s.c.fork.allowed = True
s.c.fork.return_cb = fork_return_cb
s.c.wait.return_cb = wait_return_cb
s.c.waitpid.return_cb = wait_return_cb
s.c.execute_command(b"./osue-tool", b"just a random string")
assert "wait_result" in s, "wait never called"
assert s.get("wait_result") == s.get("fork_result"), "program did not wait for child process"
if task.points != 10:
exam.log("The following subtasks are skipped because of errors:")
exam.log("* result (10 points)")
task.skip_subtasks()
with task.subtask("result", 10) as st:
# Handle a single request and check the result.
with exam.session([]) as s:
s.c.fork.allowed = True
argument = "".join(random.choices(string.ascii_letters, k=24)).encode()
expected = argument + b"\n"
fp = s.c.execute_command(b"./osue-tool", argument)
assert fp.intaddr(), "returned file pointer not valid"
bufsize = 8192
try:
buf = s.c.malloc(bufsize, cast="char *")
n = s.c.fread(buf, 1, bufsize, fp)
except FunctionKilledError:
st.fail("reading from returned file pointer never terminated")
except SignalError:
st.fail("reading from returned file pointer failed")
result = buf.convert(shape=n.convert())
assert result == expected, "invalid response"

View File

@@ -0,0 +1,37 @@
##
# Makefile for server and client libs.
#
# config
DEFS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_SVID_SOURCE -D_POSIX_C_SOURCE=200809 -D_XOPEN_SOURCE
override CFLAGS += -Wall -g -std=c99 -pedantic $(DEFS)
override LDFLAGS +=
override LIBS += -pthread -lrt
# objects for the server to build
OBJS = server_lib.o
# rules
all: $(OBJS) client osue-tool
make -f Makefile.student all
client: client.o
gcc $(LDFLAGS) -o $@ $^ $(LIBS)
osue-tool: osue-tool.c
gcc -o $@ $^
%.o: %.c
gcc $(CFLAGS) -c -o $@ $<
strip -S $@
clean:
make -f Makefile.student clean
rm -f client client.o $(OBJS) osue-tool
distclean: clean
rm -rf *.txt __pycache__

View File

@@ -0,0 +1,38 @@
##
# Makefile for server.
#
# config
DEFS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_SVID_SOURCE -D_POSIX_C_SOURCE=200809 -D_XOPEN_SOURCE
override CFLAGS += -Wall -g -std=c99 -pedantic $(DEFS)
override LDFLAGS +=
override LIBS += -pthread -lrt
# objects to build
OBJS = server.o
# objects to link (already built)
LDOBJS = server_lib.o
# rules
.PHONY : all clean
all: server
server: $(OBJS) $(LDOBJS)
gcc $(LDFLAGS) -o $@ $^ $(LIBS)
%.o: %.c
gcc $(CFLAGS) -c -o $@ $<
clean:
rm -f server $(OBJS)
# dependencies
server.o: server.c

View File

@@ -0,0 +1,114 @@
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <unistd.h>
#define BUFSIZE 1024
/** Name of the executable (for printing messages). */
char *program_name = "<not yet set>";
/** Structure for the arguments. */
struct arguments {
const char *port_str;
const char *argument;
};
/** Print a usage message and exit. */
void usage(const char *msg) {
fprintf(stderr, "Usage: %s [-p PORT] ARGUMENT\n%s\n", program_name, msg);
exit(EXIT_FAILURE);
}
void print_message(const char *msg) {
fprintf(stderr, "[%s] %s\n", program_name, msg);
}
/** Print an error message and exit with EXIT_FAILURE. */
void error_exit(const char *msg) {
if (errno == 0)
fprintf(stderr, "[%s] [ERROR] %s\n", program_name, msg);
else
fprintf(stderr, "[%s] [ERROR] %s - %s\n", program_name, msg,
strerror(errno));
exit(EXIT_FAILURE);
}
/** Parse arguments. */
void parse_arguments(int argc, char *argv[], struct arguments *args) {
memset(args, 0, sizeof(*args));
int opt;
while ((opt = getopt(argc, argv, "p:")) != -1) {
switch (opt) {
case 'p':
if (args->port_str)
usage("-p option allowed only once");
args->port_str = optarg;
break;
default:
usage("invalid option");
}
}
if (args->port_str == NULL)
args->port_str = "2020";
if (argc - optind != 1)
usage("invalid number of arguments");
args->argument = argv[optind];
}
FILE *setup_connection(const char *port_str) {
struct addrinfo hints, *ai;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
int res = getaddrinfo("localhost", port_str, &hints, &ai);
if (res != 0)
error_exit("getaddrinfo()");
int connfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
if (connfd < 0)
error_exit("socket()");
if (connect(connfd, ai->ai_addr, ai->ai_addrlen) < 0)
error_exit("connect()");
freeaddrinfo(ai);
return fdopen(connfd, "w+");
}
int main(int argc, char *argv[]) {
struct arguments args;
program_name = argv[0];
parse_arguments(argc, argv, &args);
FILE *fp = setup_connection(args.port_str);
fprintf(fp, "%s", args.argument);
fflush(fp);
shutdown(fileno(fp), SHUT_WR);
char *lineptr = NULL;
size_t n = 0;
print_message("reading response:");
while (getline(&lineptr, &n, fp) != -1) {
fprintf(stdout, "[%s] >>> %s", program_name, lineptr);
fflush(stdout);
}
fprintf(stderr, "\n");
print_message("exiting regularly");
return 0;
}

View File

@@ -0,0 +1,25 @@
/*
* Take a single string as an argument and format it into 32-character blocks.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv) {
if (argc != 2) {
fprintf(stderr, "invalid number of arguments\n");
exit(1);
}
int i;
for (i = 0; i < strlen(argv[1]); i++) {
int c = argv[1][i];
printf("%c", c);
if (i % 32 == 31)
printf("\n");
}
if (i % 32 != 0)
printf("\n");
return 0;
}

View File

@@ -0,0 +1,171 @@
#include "server.h"
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <netdb.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
char *program_name = "<not yet set>";
/*
* Signal handler and quit flag for synchronization loop.
* DO NOT CHANGE THIS!
*/
volatile sig_atomic_t quit = 0;
static void signal_handler(int sig) {
quit = 1;
}
/*
* Prints a usage message and exits.
* @param msg Additional output message.
*/
void usage(const char *msg) {
fprintf(stderr, "Usage: %s [-p PORT] COMMAND\n%s\n", program_name, msg);
exit(EXIT_FAILURE);
}
/*
* Print an error message and exit with EXIT_FAILURE.
* @param msg Additional output message.
*/
void error_exit(const char *msg) {
if (errno == 0)
fprintf(stderr, "[%s] [ERROR] %s\n", program_name, msg);
else
fprintf(stderr, "[%s] [ERROR] %s - %s\n", program_name, msg,
strerror(errno));
exit(EXIT_FAILURE);
}
/************************************************************************
* Task 1 - Setup the connection for the server
*
* Create a passive socket of domain AF_INET and type SOCK_STREAM.
* Listen for connections on the port given by the argument `port_str'.
* Return the file descriptor of the communication socket.
*
* WARNING: The communication socket is NOT the connection socket,
* DO NOT USE accept(2) IN THIS FUNCTION!
*
* @param port_str The port string.
* @return File descriptor of the communication socket.
*
* Hints: getaddrinfo(3), socket(2), listen(2)
************************************************************************/
int setup_connection(const char *port_str) {
// put your code here
// Replace with a meaningful return value
return -1;
}
/*******************************************************************************
* Task 2 - Handle client connections
*
* Wait for a connection on the communication socket and accept it.
*
* Read the argument transmitted by the client from the connection and save it
* to a buffer that can hold a C-string with up to MAX_ARGUMENT_LEN characters.
*
* Call execute_command() (or the respective DEMO function) with the command
* and the argument.
*
* Read the result from the file pointer returned by execute_command() and send
* it back to the client. Send the string "COMMAND_FAILED" if execute_command()
* fails.
*
* @param sockfd File descriptor of the communication socket.
* @param command Command to execute.
*
* Hints: accept(2), fdopen(3), fgets(3), fprintf(3), fclose(3)
******************************************************************************/
void server_request_handler(int sockfd, const char *command) {
// put your code here
}
/*******************************************************************************
* Task 3 - Execute command
*
* Create a pipe for redirection of standard output of the child process.
*
* Fork a child process, close stdin of the new process and redirect stdout to
* the pipe.
*
* Execute the command in the variable 'command' with the single argument in
* the variable 'argument'.
*
* Wait for the child process to finish. If the child process exits with
* exit-status 0, create a FILE object for the read-end of the pipe and return
* it. Return NULL if the child process fails.
*
* Do not read from the pipe in this function.
*
* @param command The command that will be executed.
* @param argument The argument for the command.
*
* @return Address of the FILE-object for the read end of the child's standard
* output pipe.
*
* Hints: pipe(2), dup2(2), fork(2), exec(3), fdopen(3), wait(3)
******************************************************************************/
FILE *execute_command(const char *command, const char *argument) {
// put your code here
// Replace with a meaningful return value
return NULL;
}
/*
* Main program
* @param argc Number of elements in argv
* @param argv Array of command line arguments
* @return EXIT_SUCCESS if everything is okay
*/
int main(int argc, char *argv[]) {
// DO NOT CHANGE THE FOLLOWING DECLARATIONS
int sockfd = -1;
struct arguments args; // struct for storing the parsed arguments
program_name = argv[0];
// Register signal handlers
struct sigaction s;
s.sa_handler = signal_handler;
s.sa_flags = 0; // no SA_RESTART!
if (sigfillset(&s.sa_mask) < 0) {
error_exit("sigfillset");
}
if (sigaction(SIGINT, &s, NULL) < 0) {
error_exit("sigaction SIGINT");
}
if (sigaction(SIGTERM, &s, NULL) < 0) {
error_exit("sigaction SIGTERM");
}
// register function to free resources at normal process termination
if (atexit(free_resources) == -1) {
error_exit("atexit failed");
}
parse_arguments(argc, argv, &args);
sockfd = setup_connection(args.port_str);
//sockfd = DEMO_setup_connection(args.port_str);
while (!quit) {
server_request_handler(sockfd, args.command);
//DEMO_server_request_handler(sockfd, args.command, 0); // internally calls execute_command()
//DEMO_server_request_handler(sockfd, args.command, USE_DEMO); // internally calls DEMO_execute_command()
}
(void)fprintf(stderr, "server exiting regularly\n");
return EXIT_SUCCESS;
}

View File

@@ -0,0 +1,33 @@
/**
* @brief Declarations used by the server application.
**/
#ifndef _SERVE_H_
#define _SERVE_H_
#include <stdio.h>
#define USE_DEMO 0x01
#define MAX_ARGUMENT_LEN 1024
/** Structure for the arguments. */
struct arguments {
const char *port_str;
const char *command;
};
void parse_arguments(int argc, char *argv[], struct arguments *args);
void free_resources(void);
// forward declaration of student functions.
int setup_connection(const char *port_str);
void server_request_handler(int sockfd, const char *command);
FILE *execute_command(const char *command, const char *argument);
// demo solutions
int DEMO_setup_connection(const char *port_str);
void DEMO_server_request_handler(int sockfd, const char *command, int flags);
FILE *DEMO_execute_command(const char *command, const char *argument);
#endif

View File

@@ -0,0 +1,260 @@
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <netdb.h>
#include <netinet/in.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include "server.h"
extern char *program_name;
extern volatile sig_atomic_t quit;
/*
* Prints a usage message and exits.
* @param msg Additional output message.
*/
static void usage(const char *msg) {
fprintf(stderr, "Usage: %s [-p PORT] COMMAND\n%s\n", program_name, msg);
exit(EXIT_FAILURE);
}
/*
* Print an error message and exit with EXIT_FAILURE.
* @param msg Additional output message.
*/
static void error_exit(const char *msg) {
if (errno == 0)
fprintf(stderr, "[%s] [ERROR] %s\n", program_name, msg);
else
fprintf(stderr, "[%s] [ERROR] %s - %s\n", program_name, msg,
strerror(errno));
exit(EXIT_FAILURE);
}
static void print_error(const char *msg) {
fprintf(stderr, "[%s] [ERROR] %s - %s\n", program_name, msg,
strerror(errno));
}
static void print_message(const char *msg) {
fprintf(stderr, "[%s] %s\n", program_name, msg);
}
/**
* @brief Function freeing the resources.
*/
void free_resources(void) {
}
/*
* Parse Arguments
*/
void parse_arguments(int argc, char *argv[], struct arguments *args) {
memset(args, 0, sizeof(*args));
int opt;
while ((opt = getopt(argc, argv, "p:")) != -1) {
switch (opt) {
case 'p':
if (args->port_str)
usage("-p option allowed only once");
args->port_str = optarg;
break;
default:
usage("invalid option");
}
}
if (args->port_str == NULL)
args->port_str = "2020";
if (argc - optind != 1)
usage("invalid number of arguments");
args->command = argv[optind];
}
/*
* Custom getaddrinfo implementation for localhost only.
*/
int getaddrinfo(const char *node, const char *service,
const struct addrinfo *hints, struct addrinfo **res) {
struct in_addr localhost;
localhost.s_addr = htonl(0x7f000001); // 127.0.0.1
// check hostname (allow only `localhost' or any string which `inet_aton()'
// converts to 127.0.0.1)
struct in_addr node_addr;
if (node != NULL &&
(inet_aton(node, &node_addr) == 0 ||
node_addr.s_addr != localhost.s_addr) &&
strcmp(node, "localhost") != 0)
return EAI_NONAME;
// check port (allow only numerical ports)
char *endptr;
int port = strtol(service, &endptr, 10);
if (*endptr != '\0')
return EAI_NONAME;
// check hints (NULL is acceptable)
if (hints != NULL) {
// check family
if (hints->ai_family != AF_UNSPEC && hints->ai_family != AF_INET)
return EAI_FAMILY;
// check socket type
if (hints->ai_socktype != 0 && hints->ai_socktype != SOCK_STREAM)
return EAI_SOCKTYPE;
// check protocol
if (hints->ai_protocol != 0 && hints->ai_protocol != IPPROTO_TCP)
return EAI_SOCKTYPE;
}
// allocate memory for result
*res = malloc(sizeof(struct addrinfo));
struct sockaddr_in *sin = malloc(sizeof(struct sockaddr_in));
if (*res == NULL || sin == NULL)
return EAI_MEMORY;
memset(sin, 0, sizeof(struct sockaddr_in));
sin->sin_family = AF_INET;
sin->sin_addr = localhost;
sin->sin_port = htons(port);
(*res)->ai_flags = (AI_V4MAPPED | AI_ADDRCONFIG);
(*res)->ai_family = AF_INET;
(*res)->ai_socktype = SOCK_STREAM;
(*res)->ai_protocol = IPPROTO_TCP;
(*res)->ai_addrlen = sizeof(struct sockaddr_in);
(*res)->ai_addr = (struct sockaddr *)sin;
(*res)->ai_canonname = NULL;
(*res)->ai_next = NULL;
return 0;
}
/******************************************************************************
* DEMO FUNCTIONS
*****************************************************************************/
int DEMO_setup_connection(const char *port_str) {
struct addrinfo hints, *ai;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
int res = getaddrinfo(NULL, port_str, &hints, &ai);
if (res != 0)
error_exit("getaddrinfo() failed");
int sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
if (sockfd < 0)
error_exit("socket() failed");
int reuse = 1;
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)
error_exit("setsockopt() failed");
if (bind(sockfd, ai->ai_addr, ai->ai_addrlen) < 0)
error_exit("bind() failed");
freeaddrinfo(ai);
if (listen(sockfd, 1) < 0)
error_exit("listen() failed");
return sockfd;
}
void DEMO_server_request_handler(int sockfd, const char *command, int flags) {
int connfd = accept(sockfd, NULL, NULL);
if (connfd < 0)
error_exit("accept() failed");
print_message("Accepted connection");
FILE *fps = fdopen(connfd, "w+");
char argument[MAX_ARGUMENT_LEN + 1] = {0};
fgets(argument, sizeof(argument), fps);
FILE *fpc = NULL;
if (flags & USE_DEMO) {
print_message("Calling DEMO_execute_command()");
fpc = DEMO_execute_command(command, argument);
} else {
print_message("Calling execute_command()");
fpc = execute_command(command, argument);
}
if (fpc) {
int c;
while ((c = fgetc(fpc)) != EOF)
fputc(c, fps);
fclose(fpc);
} else {
fputs("COMMAND_FAILED", fps);
}
fflush(fps);
fclose(fps);
}
FILE *DEMO_execute_command(const char *command, const char *argument) {
// file stream to read the output of the process
FILE *proc_output = NULL;
// file descriptors for the pipe for parent-child process communication
int c2p[2];
// create the pipes
if (pipe(c2p) == -1)
error_exit("cannot create pipe");
pid_t pid = fork();
switch (pid) {
case 0:
// duplicate write end to stdout
if (dup2(c2p[1], STDOUT_FILENO) == -1) {
error_exit("dup2() failed");
}
close(c2p[0]);
close(STDIN_FILENO);
// exec the command
if (execlp(command, command, argument, NULL) == -1) {
error_exit("exec() failed");
}
case -1:
error_exit("fork() failed");
default:
// parent process
proc_output = fdopen(c2p[0], "r");
if (proc_output == NULL) {
error_exit("fdopen() failed");
}
close(c2p[1]);
int stat = 0;
wait(&stat);
if (WIFEXITED(stat) && WEXITSTATUS(stat) == 0) {
print_message("child process exited normally");
} else {
print_error("child process exited abnormally");
proc_output = NULL;
}
}
return proc_output;
}