initial set of files
This commit is contained in:
commit
4d589ee8bf
10
README.md
Normal file
10
README.md
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
|
||||||
|
This repository contains the base files for the CS 3214
|
||||||
|
"Personal Secure Server" project.
|
||||||
|
|
||||||
|
To get started, run the script:
|
||||||
|
|
||||||
|
. install-dependencies.sh
|
||||||
|
|
||||||
|
Then cd into src and type make.
|
||||||
|
|
20
install-dependencies.sh
Normal file
20
install-dependencies.sh
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
#
|
||||||
|
# Run this script to install the required Jansson and JWT packages.
|
||||||
|
#
|
||||||
|
BASE=`pwd`
|
||||||
|
test -d ${BASE}/deps || mkdir ${BASE}/deps
|
||||||
|
git clone git@github.com:akheron/jansson.git
|
||||||
|
(cd jansson;
|
||||||
|
autoreconf -fi;
|
||||||
|
./configure --prefix=${BASE}/deps;
|
||||||
|
make install
|
||||||
|
)
|
||||||
|
|
||||||
|
git clone git@github.com:benmcollins/libjwt.git
|
||||||
|
(cd libjwt;
|
||||||
|
autoreconf -fi;
|
||||||
|
env PKG_CONFIG_PATH=../deps/lib/pkgconfig:${PKG_CONFIG_PATH} ./configure --prefix=${BASE}/deps;
|
||||||
|
make install
|
||||||
|
)
|
22
src/Makefile
Normal file
22
src/Makefile
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
DEP_BASE_DIR=../deps
|
||||||
|
DEP_INCLUDE_DIR=$(DEP_BASE_DIR)/include
|
||||||
|
DEP_LIB_DIR=$(abspath $(DEP_BASE_DIR)/lib)
|
||||||
|
|
||||||
|
CFLAGS=-g -pthread -std=gnu11 -Wall -Werror -Wmissing-prototypes -I$(DEP_INCLUDE_DIR)
|
||||||
|
|
||||||
|
# include lib directory into runtime path to facilitate dynamic linking
|
||||||
|
LDFLAGS=-Wl,-rpath -Wl,$(DEP_LIB_DIR)
|
||||||
|
LDLIBS=-L$(DEP_LIB_DIR) -ljwt -ljansson -lcrypto -ldl
|
||||||
|
|
||||||
|
HEADERS=socket.h http.h hexdump.h buffer.h bufio.h
|
||||||
|
OBJ=main.o socket.o hexdump.o http.o bufio.o
|
||||||
|
|
||||||
|
all: server
|
||||||
|
|
||||||
|
$(OBJ) : $(HEADERS)
|
||||||
|
|
||||||
|
server: $(OBJ)
|
||||||
|
$(CC) $(LDFLAGS) -o $@ $(OBJ) $(LDLIBS)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
/bin/rm $(OBJ)
|
106
src/buffer.h
Normal file
106
src/buffer.h
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
#ifndef __BUFFER_H_
|
||||||
|
#define __BUFFER_H_
|
||||||
|
/*
|
||||||
|
* A realloc-based dynamic buffer implementation for C.
|
||||||
|
*
|
||||||
|
* Written by Scott Pruett.
|
||||||
|
*
|
||||||
|
* Note: the buffer implementation is similar to Java's StringBuffer
|
||||||
|
* in that it supports a dynamically growing array of bytes.
|
||||||
|
* It supports append* operations that copy data to the buffer,
|
||||||
|
* extending it as necessary.
|
||||||
|
*
|
||||||
|
* Aiming at efficiency, this buffer is not written as a fully encapsulated
|
||||||
|
* data type. Rather, some implementation details are exposed.
|
||||||
|
*
|
||||||
|
* Because the buffer uses realloc, you cannot keep pointers into the
|
||||||
|
* buffer around across calls that may reallocate the buffer.
|
||||||
|
*
|
||||||
|
* The buffer is not thread-safe.
|
||||||
|
* This buffer handles out-of-memory situations by exiting the process.
|
||||||
|
*/
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char* buf; // underlying storage
|
||||||
|
int len; // current end; last valid byte is buf[len-1]
|
||||||
|
int cap; // allocated amount of storage
|
||||||
|
} buffer_t;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialize a buffer and allocate initialsize storage.
|
||||||
|
*/
|
||||||
|
static inline void buffer_init(buffer_t *buf, int initialsize)
|
||||||
|
{
|
||||||
|
buf->len = 0;
|
||||||
|
buf->cap = initialsize;
|
||||||
|
buf->buf = malloc(buf->cap);
|
||||||
|
if (buf->buf == NULL) {
|
||||||
|
perror("can't alloc memory: ");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reset this buffer, truncating storage to size. */
|
||||||
|
static inline void buffer_reset(buffer_t *buf, int size)
|
||||||
|
{
|
||||||
|
buf->len = 0;
|
||||||
|
if (buf->cap > size) {
|
||||||
|
buf->buf = realloc(buf->buf, size);
|
||||||
|
buf->cap = size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Delete this buffer, freeing any storage. */
|
||||||
|
static inline void buffer_delete(buffer_t *buf)
|
||||||
|
{
|
||||||
|
free(buf->buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* To minimize copying, the buffer also has the ability to reserve
|
||||||
|
* space for I/O operations, see buffer_ensure_capacity below.
|
||||||
|
* In this case, the caller must adjust the buffer's len following
|
||||||
|
* the operation.
|
||||||
|
*
|
||||||
|
* Returns a pointer p to the first usable byte; it is guaranteed that
|
||||||
|
* bytes buf[p:p+len] point to usable memory. Once written, the
|
||||||
|
* caller should increment buf.len by the actual number of bytes
|
||||||
|
* written into the buffer.
|
||||||
|
*/
|
||||||
|
static inline char *buffer_ensure_capacity(buffer_t *buf, int len) {
|
||||||
|
if (buf->len + len >= buf->cap) {
|
||||||
|
int cap = buf->cap * 2 + len;
|
||||||
|
buf->buf = realloc(buf->buf, cap);
|
||||||
|
if (buf->buf == NULL) {
|
||||||
|
perror("can't alloc memory: ");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
buf->cap = cap;
|
||||||
|
}
|
||||||
|
return &buf->buf[buf->len];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Append mem[0:len] to the buffer, expanding it as necessary. */
|
||||||
|
static inline void buffer_append(buffer_t *buf, void *mem, int len) {
|
||||||
|
buffer_ensure_capacity(buf, len);
|
||||||
|
memcpy(&buf->buf[buf->len], mem, len);
|
||||||
|
buf->len += len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Append c to the buffer, expanding it as necessary. */
|
||||||
|
static inline void buffer_appendc(buffer_t *buf, char c) {
|
||||||
|
buffer_ensure_capacity(buf, 1);
|
||||||
|
buf->buf[buf->len] = c;
|
||||||
|
buf->len++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Append zero-terminated string str to the buffer, expanding it as necessary. */
|
||||||
|
static inline void buffer_appends(buffer_t *buf, char *str) {
|
||||||
|
int len = strlen(str);
|
||||||
|
buffer_append(buf, str, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
209
src/bufio.c
Normal file
209
src/bufio.c
Normal file
@ -0,0 +1,209 @@
|
|||||||
|
/*
|
||||||
|
* Support for buffered I/O
|
||||||
|
*
|
||||||
|
* The 'bufio' struct implements buffered I/O for a socket, similar
|
||||||
|
* to Java's BufferedReader.
|
||||||
|
*
|
||||||
|
* Since it encapsulates a connection's socket, it also provides
|
||||||
|
* methods for sending data.
|
||||||
|
*
|
||||||
|
* Written by G. Back for CS 3214 Spring 2018
|
||||||
|
*/
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/sendfile.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include "bufio.h"
|
||||||
|
|
||||||
|
/*****************************************************************/
|
||||||
|
struct bufio {
|
||||||
|
int socket; // underlying socket file descriptor
|
||||||
|
size_t bufpos; // offset of next byte to be read
|
||||||
|
buffer_t buf; // holds data that was received
|
||||||
|
};
|
||||||
|
|
||||||
|
static const int BUFSIZE = 1536;
|
||||||
|
static int min(int a, int b) { return a < b ? a : b; }
|
||||||
|
|
||||||
|
/* Create a new bufio object from a socket. */
|
||||||
|
struct bufio *
|
||||||
|
bufio_create(int socket)
|
||||||
|
{
|
||||||
|
struct bufio * rc = malloc(sizeof(*rc));
|
||||||
|
if (rc == NULL) {
|
||||||
|
perror("malloc");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
rc->bufpos = 0;
|
||||||
|
rc->socket = socket;
|
||||||
|
buffer_init(&rc->buf, BUFSIZE);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Close a bufio object, freeing its storage and closing its socket. */
|
||||||
|
void
|
||||||
|
bufio_close(struct bufio * self)
|
||||||
|
{
|
||||||
|
if (close(self->socket))
|
||||||
|
perror("close");
|
||||||
|
|
||||||
|
buffer_delete(&self->buf);
|
||||||
|
free(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t
|
||||||
|
bytes_buffered(struct bufio *self)
|
||||||
|
{
|
||||||
|
return self->buf.len - self->bufpos;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int TRUNCATE_THRESHOLD = 10000; // tune me
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Discard already read data, shifting any buffered data into a new buffer.
|
||||||
|
* This will invalidate all offsets.
|
||||||
|
*
|
||||||
|
* This method is provided to avoid accumulating all data received
|
||||||
|
* on a long-running HTTP/1.1 connection into a single buffer.
|
||||||
|
*/
|
||||||
|
void bufio_truncate(struct bufio * self)
|
||||||
|
{
|
||||||
|
if (self->buf.len > TRUNCATE_THRESHOLD) {
|
||||||
|
int unread = bytes_buffered(self);
|
||||||
|
assert(unread >= 0);
|
||||||
|
if (unread == 0) {
|
||||||
|
buffer_reset(&self->buf, BUFSIZE);
|
||||||
|
} else {
|
||||||
|
buffer_t oldbuf = self->buf;
|
||||||
|
buffer_init(&self->buf, BUFSIZE);
|
||||||
|
buffer_append(&self->buf, oldbuf.buf + self->bufpos, unread);
|
||||||
|
buffer_delete(&oldbuf);
|
||||||
|
}
|
||||||
|
self->bufpos = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t
|
||||||
|
read_more(struct bufio *self)
|
||||||
|
{
|
||||||
|
char * buf = buffer_ensure_capacity(&self->buf, BUFSIZE);
|
||||||
|
int bread = recv(self->socket, buf, BUFSIZE, MSG_NOSIGNAL);
|
||||||
|
if (bread < 1)
|
||||||
|
return bread;
|
||||||
|
|
||||||
|
self->buf.len += bread;
|
||||||
|
return bread;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Given an offset into the buffer, return a char *.
|
||||||
|
* This pointer will be valid only until the next call
|
||||||
|
* to any of the bufio_read* function.
|
||||||
|
*/
|
||||||
|
char *
|
||||||
|
bufio_offset2ptr(struct bufio *self, size_t offset)
|
||||||
|
{
|
||||||
|
assert (offset < self->buf.len);
|
||||||
|
return self->buf.buf + offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Given an pointer into the buffer, return an offset
|
||||||
|
* that can be used to mark the position of something
|
||||||
|
* in the buffer (e.g., a header)
|
||||||
|
*/
|
||||||
|
size_t
|
||||||
|
bufio_ptr2offset(struct bufio *self, char *ptr)
|
||||||
|
{
|
||||||
|
size_t offset = ptr - self->buf.buf;
|
||||||
|
assert (0 <= offset && offset < self->buf.len);
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read one byte from the socket into the buffer.
|
||||||
|
* Returns 0 on EOF, -1 on error, else returns 1.
|
||||||
|
*/
|
||||||
|
ssize_t
|
||||||
|
bufio_readbyte(struct bufio *self, char *out)
|
||||||
|
{
|
||||||
|
if (bytes_buffered(self) == 0) {
|
||||||
|
int rc = read_more(self);
|
||||||
|
if (rc <= 0)
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
*out = self->buf.buf[self->bufpos++];
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read until newline (\n) is encountered.
|
||||||
|
* Sets *line_offset to the buffer offset where the next line
|
||||||
|
* starts.
|
||||||
|
*
|
||||||
|
* Returns number of bytes read, and -1 on error.
|
||||||
|
*
|
||||||
|
* If EOF is encountered, the line may not be terminated
|
||||||
|
* with a \n.
|
||||||
|
*/
|
||||||
|
ssize_t
|
||||||
|
bufio_readline(struct bufio *self, size_t *line_offset)
|
||||||
|
{
|
||||||
|
*line_offset = self->bufpos;
|
||||||
|
char p = 0;
|
||||||
|
while (p != '\n') {
|
||||||
|
int rc = bufio_readbyte(self, &p);
|
||||||
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
if (rc == 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return self->bufpos - *line_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Attempt to read a fixed number of bytes into the buffer.
|
||||||
|
* Sets *buf_offset to the offset in the buffer pointing to
|
||||||
|
* the first byte read.
|
||||||
|
*
|
||||||
|
* Returns the actual number of bytes read, or -1 on error.
|
||||||
|
*/
|
||||||
|
ssize_t
|
||||||
|
bufio_read(struct bufio *self, size_t count, size_t *buf_offset)
|
||||||
|
{
|
||||||
|
*buf_offset = self->bufpos;
|
||||||
|
while (bytes_buffered(self) < count) {
|
||||||
|
int rc = read_more(self);
|
||||||
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
if (rc == 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ssize_t bytes_read = min(bytes_buffered(self), count);
|
||||||
|
self->bufpos += bytes_read;
|
||||||
|
return bytes_read;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send a file out to the socket.
|
||||||
|
* See sendfile(2) for return value.
|
||||||
|
*/
|
||||||
|
ssize_t
|
||||||
|
bufio_sendfile(struct bufio *self, int fd, off_t *off, int filesize)
|
||||||
|
{
|
||||||
|
return sendfile(self->socket, fd, off, filesize);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Send data contained in 'resp' to the socket.
|
||||||
|
* See send(2) for return value.
|
||||||
|
*/
|
||||||
|
ssize_t
|
||||||
|
bufio_sendbuffer(struct bufio *self, buffer_t * resp)
|
||||||
|
{
|
||||||
|
return send(self->socket, resp->buf, resp->len, MSG_NOSIGNAL);
|
||||||
|
}
|
18
src/bufio.h
Normal file
18
src/bufio.h
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#ifndef _BUFIO_H
|
||||||
|
#define _BUFIO_H
|
||||||
|
|
||||||
|
#include "buffer.h"
|
||||||
|
|
||||||
|
struct bufio;
|
||||||
|
struct bufio * bufio_create(int socket);
|
||||||
|
void bufio_close(struct bufio * self);
|
||||||
|
void bufio_truncate(struct bufio * self);
|
||||||
|
ssize_t bufio_readbyte(struct bufio *self, char *out);
|
||||||
|
ssize_t bufio_readline(struct bufio *self, size_t *line_offset);
|
||||||
|
ssize_t bufio_read(struct bufio *self, size_t count, size_t *buf_offset);
|
||||||
|
char * bufio_offset2ptr(struct bufio *self, size_t offset);
|
||||||
|
size_t bufio_ptr2offset(struct bufio *self, char *ptr);
|
||||||
|
ssize_t bufio_sendfile(struct bufio *self, int fd, off_t *off, int filesize);
|
||||||
|
ssize_t bufio_sendbuffer(struct bufio *self, buffer_t *response);
|
||||||
|
|
||||||
|
#endif /* _BUFIO_H */
|
71
src/hexdump.c
Normal file
71
src/hexdump.c
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 1996 The University of Utah and
|
||||||
|
* the Computer Systems Laboratory at the University of Utah (CSL).
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify and distribute this software is hereby
|
||||||
|
* granted provided that (1) source code retains these copyright, permission,
|
||||||
|
* and disclaimer notices, and (2) redistributions including binaries
|
||||||
|
* reproduce the notices in supporting documentation, and (3) all advertising
|
||||||
|
* materials mentioning features or use of this software display the following
|
||||||
|
* acknowledgement: ``This product includes software developed by the
|
||||||
|
* Computer Systems Laboratory at the University of Utah.''
|
||||||
|
*
|
||||||
|
* THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS
|
||||||
|
* IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF
|
||||||
|
* ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
* CSL requests users of this software to return to csl-dist@cs.utah.edu any
|
||||||
|
* improvements that they make and grant CSL redistribution rights.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "hexdump.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Print a buffer hexdump style. Example:
|
||||||
|
*
|
||||||
|
* .---------------------------------------------------------------------------.
|
||||||
|
* | 00000000 2e2f612e 6f757400 5445524d 3d787465 ./a.out.TERM=xte |
|
||||||
|
* | 00000010 726d0048 4f4d453d 2f616673 2f63732e rm.HOME=/afs/cs. |
|
||||||
|
* | 00000020 75746168 2e656475 2f686f6d 652f6c6f utah.edu/home/lo |
|
||||||
|
* | 00000030 6d657700 5348454c 4c3d2f62 696e2f74 mew.SHELL=/bin/t |
|
||||||
|
* | 00000040 63736800 4c4f474e 414d453d 6c6f6d65 csh.LOGNAME=lome |
|
||||||
|
* | 00000050 77005553 45523d6c 6f6d6577 00504154 w.USER=lomew.PAT |
|
||||||
|
* | 00000060 483d2f61 66732f63 732e7574 61682e65 H=/afs/cs.utah.e |
|
||||||
|
* | 00000070 64752f68 6f6d652f 6c6f6d65 772f6269 du/home/lomew/bi |
|
||||||
|
* | 00000080 6e2f4073 79733a2f 6166732f 63732e75 n/@sys:/afs/cs.u |
|
||||||
|
* | 00000090 7461682e 6564 tah.ed |
|
||||||
|
* `---------------------------------------------------------------------------'
|
||||||
|
*
|
||||||
|
* It might be useful to have an option for printing out little-endianly.
|
||||||
|
* Adapted from Godmar's hook.c.
|
||||||
|
*/
|
||||||
|
void hexdump(void *buf, size_t len)
|
||||||
|
{
|
||||||
|
size_t i, j;
|
||||||
|
char *b = (char *)buf;
|
||||||
|
|
||||||
|
printf(".---------------------------------------------------------------------------.\n");
|
||||||
|
for (i = 0; i < len; i += 16) {
|
||||||
|
printf("| %08lx ", i);
|
||||||
|
for (j = i; j < i+16; j++) {
|
||||||
|
if (j % 4 == 0)
|
||||||
|
printf(" ");
|
||||||
|
if (j >= len)
|
||||||
|
printf(" ");
|
||||||
|
else
|
||||||
|
printf("%02x", (unsigned char)b[j]);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf(" ");
|
||||||
|
for (j = i; j < i+16; j++)
|
||||||
|
if (j >= len)
|
||||||
|
printf(" ");
|
||||||
|
else
|
||||||
|
printf("%c", isgraph(b[j]) ? b[j] : '.');
|
||||||
|
printf(" |\n");
|
||||||
|
}
|
||||||
|
printf("`---------------------------------------------------------------------------'\n");
|
||||||
|
}
|
2
src/hexdump.h
Normal file
2
src/hexdump.h
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
void hexdump(void *buf, size_t len);
|
369
src/http.c
Normal file
369
src/http.c
Normal file
@ -0,0 +1,369 @@
|
|||||||
|
/*
|
||||||
|
* A partial implementation of HTTP/1.0
|
||||||
|
*
|
||||||
|
* This code is mainly intended as a replacement for the book's 'tiny.c' server
|
||||||
|
* It provides a *partial* implementation of HTTP/1.0 which can form a basis for
|
||||||
|
* the assignment.
|
||||||
|
*
|
||||||
|
* @author G. Back for CS 3214 Spring 2018
|
||||||
|
*/
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <linux/limits.h>
|
||||||
|
|
||||||
|
#include "http.h"
|
||||||
|
#include "hexdump.h"
|
||||||
|
#include "socket.h"
|
||||||
|
#include "bufio.h"
|
||||||
|
|
||||||
|
// Need macros here because of the sizeof
|
||||||
|
#define CRLF "\r\n"
|
||||||
|
#define STARTS_WITH(field_name, header) \
|
||||||
|
(!strncasecmp(field_name, header, sizeof(header) - 1))
|
||||||
|
|
||||||
|
char * server_root; // root from which static files are served
|
||||||
|
|
||||||
|
|
||||||
|
/* Parse HTTP request line, setting req_method, req_path, and req_version. */
|
||||||
|
static int
|
||||||
|
http_parse_request(struct http_transaction *ta)
|
||||||
|
{
|
||||||
|
size_t req_offset;
|
||||||
|
ssize_t len = bufio_readline(ta->client->bufio, &req_offset);
|
||||||
|
if (len <= 0)
|
||||||
|
return len;
|
||||||
|
|
||||||
|
char *request = bufio_offset2ptr(ta->client->bufio, req_offset);
|
||||||
|
char *endptr;
|
||||||
|
char *method = strtok_r(request, " ", &endptr);
|
||||||
|
if (method == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (!strcmp(method, "GET"))
|
||||||
|
ta->req_method = HTTP_GET;
|
||||||
|
else if (!strcmp(method, "POST"))
|
||||||
|
ta->req_method = HTTP_POST;
|
||||||
|
else
|
||||||
|
ta->req_method = HTTP_UNKNOWN;
|
||||||
|
|
||||||
|
char *req_path = strtok_r(NULL, " ", &endptr);
|
||||||
|
if (req_path == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
ta->req_path = bufio_ptr2offset(ta->client->bufio, req_path);
|
||||||
|
|
||||||
|
char *http_version = strtok_r(NULL, CRLF, &endptr);
|
||||||
|
if (http_version == NULL) // would be HTTP 0.9
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (!strcmp(http_version, "HTTP/1.1"))
|
||||||
|
ta->req_version = HTTP_1_1;
|
||||||
|
else if (!strcmp(http_version, "HTTP/1.0"))
|
||||||
|
ta->req_version = HTTP_1_0;
|
||||||
|
else
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Process HTTP headers. */
|
||||||
|
static int
|
||||||
|
http_process_headers(struct http_transaction *ta)
|
||||||
|
{
|
||||||
|
for (;;) {
|
||||||
|
size_t header_offset;
|
||||||
|
ssize_t len = bufio_readline(ta->client->bufio, &header_offset);
|
||||||
|
char *header = bufio_offset2ptr(ta->client->bufio, header_offset);
|
||||||
|
if (len <= 0)
|
||||||
|
return len;
|
||||||
|
|
||||||
|
if (len == 2 && STARTS_WITH(header, CRLF)) // empty CRLF
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
header[len-2] = '\0';
|
||||||
|
/* Each header field consists of a name followed by a
|
||||||
|
* colon (":") and the field value. Field names are
|
||||||
|
* case-insensitive. The field value MAY be preceded by
|
||||||
|
* any amount of LWS, though a single SP is preferred.
|
||||||
|
*/
|
||||||
|
char *endptr;
|
||||||
|
char *field_name = strtok_r(header, ":", &endptr);
|
||||||
|
char *field_value = strtok_r(NULL, " \t", &endptr); // skip leading & trailing OWS
|
||||||
|
|
||||||
|
if (field_name == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
// printf("Header: %s: %s\n", field_name, field_value);
|
||||||
|
if (!strcasecmp(field_name, "Content-Length")) {
|
||||||
|
ta->req_content_len = atoi(field_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle other headers here. */
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int MAX_HEADER_LEN = 2048;
|
||||||
|
|
||||||
|
/* add a formatted header to the response buffer. */
|
||||||
|
void
|
||||||
|
http_add_header(buffer_t * resp, char* key, char* fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
buffer_appends(resp, key);
|
||||||
|
buffer_appends(resp, ": ");
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
char *error = buffer_ensure_capacity(resp, MAX_HEADER_LEN);
|
||||||
|
resp->len += vsnprintf(error, MAX_HEADER_LEN, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
buffer_appends(resp, "\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* add a content-length header. */
|
||||||
|
static void
|
||||||
|
add_content_length(buffer_t *res, size_t len)
|
||||||
|
{
|
||||||
|
http_add_header(res, "Content-Length", "%ld", len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* start the response by writing the first line of the response
|
||||||
|
* to the response buffer. Used in send_response_header */
|
||||||
|
static void
|
||||||
|
start_response(struct http_transaction * ta, buffer_t *res)
|
||||||
|
{
|
||||||
|
buffer_appends(res, "HTTP/1.0 ");
|
||||||
|
|
||||||
|
switch (ta->resp_status) {
|
||||||
|
case HTTP_OK:
|
||||||
|
buffer_appends(res, "200 OK");
|
||||||
|
break;
|
||||||
|
case HTTP_BAD_REQUEST:
|
||||||
|
buffer_appends(res, "400 Bad Request");
|
||||||
|
break;
|
||||||
|
case HTTP_PERMISSION_DENIED:
|
||||||
|
buffer_appends(res, "403 Permission Denied");
|
||||||
|
break;
|
||||||
|
case HTTP_NOT_FOUND:
|
||||||
|
buffer_appends(res, "404 Not Found");
|
||||||
|
break;
|
||||||
|
case HTTP_METHOD_NOT_ALLOWED:
|
||||||
|
buffer_appends(res, "405 Method Not Allowed");
|
||||||
|
break;
|
||||||
|
case HTTP_REQUEST_TIMEOUT:
|
||||||
|
buffer_appends(res, "408 Request Timeout");
|
||||||
|
break;
|
||||||
|
case HTTP_REQUEST_TOO_LONG:
|
||||||
|
buffer_appends(res, "414 Request Too Long");
|
||||||
|
break;
|
||||||
|
case HTTP_NOT_IMPLEMENTED:
|
||||||
|
buffer_appends(res, "501 Not Implemented");
|
||||||
|
break;
|
||||||
|
case HTTP_SERVICE_UNAVAILABLE:
|
||||||
|
buffer_appends(res, "503 Service Unavailable");
|
||||||
|
break;
|
||||||
|
case HTTP_INTERNAL_ERROR:
|
||||||
|
default:
|
||||||
|
buffer_appends(res, "500 Internal Server Error");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
buffer_appends(res, CRLF);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send response headers to client */
|
||||||
|
static int
|
||||||
|
send_response_header(struct http_transaction *ta)
|
||||||
|
{
|
||||||
|
buffer_t response;
|
||||||
|
buffer_init(&response, 80);
|
||||||
|
|
||||||
|
start_response(ta, &response);
|
||||||
|
if (bufio_sendbuffer(ta->client->bufio, &response) == -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
buffer_appends(&ta->resp_headers, CRLF);
|
||||||
|
if (bufio_sendbuffer(ta->client->bufio, &ta->resp_headers) == -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
buffer_delete(&response);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send a full response to client with the content in resp_body. */
|
||||||
|
static bool
|
||||||
|
send_response(struct http_transaction *ta)
|
||||||
|
{
|
||||||
|
// add content-length. All other headers must have already been set.
|
||||||
|
add_content_length(&ta->resp_headers, ta->resp_body.len);
|
||||||
|
|
||||||
|
if (send_response_header(ta) == -1)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return bufio_sendbuffer(ta->client->bufio, &ta->resp_body) != -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int MAX_ERROR_LEN = 2048;
|
||||||
|
|
||||||
|
/* Send an error response. */
|
||||||
|
static bool
|
||||||
|
send_error(struct http_transaction * ta, enum http_response_status status, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
char *error = buffer_ensure_capacity(&ta->resp_body, MAX_ERROR_LEN);
|
||||||
|
ta->resp_body.len += vsnprintf(error, MAX_ERROR_LEN, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
ta->resp_status = status;
|
||||||
|
return send_response(ta);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send Not Found response. */
|
||||||
|
static bool
|
||||||
|
send_not_found(struct http_transaction *ta)
|
||||||
|
{
|
||||||
|
return send_error(ta, HTTP_NOT_FOUND, "File %s not found",
|
||||||
|
bufio_offset2ptr(ta->client->bufio, ta->req_path));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* A start at assigning an appropriate mime type. Real-world
|
||||||
|
* servers use more extensive lists such as /etc/mime.types
|
||||||
|
*/
|
||||||
|
static const char *
|
||||||
|
guess_mime_type(char *filename)
|
||||||
|
{
|
||||||
|
char *suffix = strrchr(filename, '.');
|
||||||
|
if (suffix == NULL)
|
||||||
|
return "text/plain";
|
||||||
|
|
||||||
|
if (!strcasecmp(suffix, ".html"))
|
||||||
|
return "text/html";
|
||||||
|
|
||||||
|
if (!strcasecmp(suffix, ".gif"))
|
||||||
|
return "image/gif";
|
||||||
|
|
||||||
|
if (!strcasecmp(suffix, ".png"))
|
||||||
|
return "image/png";
|
||||||
|
|
||||||
|
if (!strcasecmp(suffix, ".jpg"))
|
||||||
|
return "image/jpeg";
|
||||||
|
|
||||||
|
if (!strcasecmp(suffix, ".js"))
|
||||||
|
return "text/javascript";
|
||||||
|
|
||||||
|
return "text/plain";
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle HTTP transaction for static files. */
|
||||||
|
static bool
|
||||||
|
handle_static_asset(struct http_transaction *ta, char *basedir)
|
||||||
|
{
|
||||||
|
char fname[PATH_MAX];
|
||||||
|
|
||||||
|
char *req_path = bufio_offset2ptr(ta->client->bufio, ta->req_path);
|
||||||
|
// The code below is vulnerable to an attack. Can you see
|
||||||
|
// which? Fix it to avoid indirect object reference (IDOR) attacks.
|
||||||
|
snprintf(fname, sizeof fname, "%s%s", basedir, req_path);
|
||||||
|
|
||||||
|
if (access(fname, R_OK)) {
|
||||||
|
if (errno == EACCES)
|
||||||
|
return send_error(ta, HTTP_PERMISSION_DENIED, "Permission denied.");
|
||||||
|
else
|
||||||
|
return send_not_found(ta);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine file size
|
||||||
|
struct stat st;
|
||||||
|
int rc = stat(fname, &st);
|
||||||
|
if (rc == -1)
|
||||||
|
return send_error(ta, HTTP_INTERNAL_ERROR, "Could not stat file.");
|
||||||
|
|
||||||
|
int filefd = open(fname, O_RDONLY);
|
||||||
|
if (filefd == -1) {
|
||||||
|
return send_not_found(ta);
|
||||||
|
}
|
||||||
|
|
||||||
|
ta->resp_status = HTTP_OK;
|
||||||
|
add_content_length(&ta->resp_headers, st.st_size);
|
||||||
|
http_add_header(&ta->resp_headers, "Content-Type", "%s", guess_mime_type(fname));
|
||||||
|
|
||||||
|
rc = send_response_header(ta);
|
||||||
|
if (rc == -1)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
rc = bufio_sendfile(ta->client->bufio, filefd, NULL, st.st_size);
|
||||||
|
out:
|
||||||
|
close(filefd);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
handle_api(struct http_transaction *ta)
|
||||||
|
{
|
||||||
|
return send_error(ta, HTTP_NOT_IMPLEMENTED, "API not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set up an http client, associating it with a bufio buffer. */
|
||||||
|
void
|
||||||
|
http_setup_client(struct http_client *self, struct bufio *bufio)
|
||||||
|
{
|
||||||
|
self->bufio = bufio;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle a single HTTP transaction. Returns true on success. */
|
||||||
|
bool
|
||||||
|
http_handle_transaction(struct http_client *self)
|
||||||
|
{
|
||||||
|
struct http_transaction ta;
|
||||||
|
memset(&ta, 0, sizeof ta);
|
||||||
|
ta.client = self;
|
||||||
|
|
||||||
|
if (http_parse_request(&ta) == -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (http_process_headers(&ta) == -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (ta.req_content_len > 0) {
|
||||||
|
int rc = bufio_read(self->bufio, ta.req_content_len, &ta.req_body);
|
||||||
|
if (rc != ta.req_content_len)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
// To see the body, use this:
|
||||||
|
// char *body = bufio_offset2ptr(ta.client->bufio, ta.req_body);
|
||||||
|
// hexdump(body, ta.req_content_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer_init(&ta.resp_headers, 1024);
|
||||||
|
http_add_header(&ta.resp_headers, "Server", "CS3214-Personal-Server");
|
||||||
|
buffer_init(&ta.resp_body, 0);
|
||||||
|
|
||||||
|
bool rc;
|
||||||
|
char *req_path = bufio_offset2ptr(ta.client->bufio, ta.req_path);
|
||||||
|
if (STARTS_WITH(req_path, "/api")) {
|
||||||
|
rc = handle_api(&ta);
|
||||||
|
} else
|
||||||
|
if (STARTS_WITH(req_path, "/private")) {
|
||||||
|
/* not implemented */
|
||||||
|
} else {
|
||||||
|
rc = handle_static_asset(&ta, server_root);
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer_delete(&ta.resp_headers);
|
||||||
|
buffer_delete(&ta.resp_body);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
61
src/http.h
Normal file
61
src/http.h
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
#ifndef _HTTP_H
|
||||||
|
#define _HTTP_H
|
||||||
|
|
||||||
|
#include <jwt.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include "buffer.h"
|
||||||
|
struct bufio;
|
||||||
|
|
||||||
|
enum http_method {
|
||||||
|
HTTP_GET,
|
||||||
|
HTTP_POST,
|
||||||
|
HTTP_UNKNOWN
|
||||||
|
};
|
||||||
|
|
||||||
|
enum http_version {
|
||||||
|
HTTP_1_0,
|
||||||
|
HTTP_1_1
|
||||||
|
};
|
||||||
|
|
||||||
|
enum http_response_status {
|
||||||
|
HTTP_OK = 200,
|
||||||
|
HTTP_BAD_REQUEST = 400,
|
||||||
|
HTTP_PERMISSION_DENIED = 403,
|
||||||
|
HTTP_NOT_FOUND = 404,
|
||||||
|
HTTP_METHOD_NOT_ALLOWED = 405,
|
||||||
|
HTTP_REQUEST_TIMEOUT = 408,
|
||||||
|
HTTP_REQUEST_TOO_LONG = 414,
|
||||||
|
HTTP_INTERNAL_ERROR = 500,
|
||||||
|
HTTP_NOT_IMPLEMENTED = 501,
|
||||||
|
HTTP_SERVICE_UNAVAILABLE = 503
|
||||||
|
};
|
||||||
|
|
||||||
|
struct http_transaction {
|
||||||
|
/* request related fields */
|
||||||
|
enum http_method req_method;
|
||||||
|
enum http_version req_version;
|
||||||
|
size_t req_path; // expressed as offset into the client's bufio.
|
||||||
|
size_t req_body; // ditto
|
||||||
|
int req_content_len; // content length of request body
|
||||||
|
|
||||||
|
|
||||||
|
/* response related fields */
|
||||||
|
enum http_response_status resp_status;
|
||||||
|
buffer_t resp_headers;
|
||||||
|
buffer_t resp_body;
|
||||||
|
|
||||||
|
struct http_client *client;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct http_client {
|
||||||
|
struct bufio *bufio;
|
||||||
|
};
|
||||||
|
|
||||||
|
void http_setup_client(struct http_client *, struct bufio *bufio);
|
||||||
|
bool http_handle_transaction(struct http_client *);
|
||||||
|
void http_add_header(buffer_t * resp, char* key, char* fmt, ...);
|
||||||
|
|
||||||
|
extern char *server_root;
|
||||||
|
|
||||||
|
#endif /* _HTTP_H */
|
69
src/main.c
Normal file
69
src/main.c
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* Skeleton files for personal server assignment.
|
||||||
|
*
|
||||||
|
* @author Godmar Back
|
||||||
|
* written for CS3214, Spring 2018.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "buffer.h"
|
||||||
|
#include "hexdump.h"
|
||||||
|
#include "http.h"
|
||||||
|
#include "socket.h"
|
||||||
|
#include "bufio.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A non-concurrent, iterative server that serves one client at a time.
|
||||||
|
* For each client, it handles exactly 1 HTTP transaction.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
server_loop(char *port_string)
|
||||||
|
{
|
||||||
|
int accepting_socket = socket_open_bind_listen(port_string, 1024);
|
||||||
|
for (;;) {
|
||||||
|
int client_socket = socket_accept_client(accepting_socket);
|
||||||
|
if (client_socket == -1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
struct http_client client;
|
||||||
|
http_setup_client(&client, bufio_create(client_socket));
|
||||||
|
http_handle_transaction(&client);
|
||||||
|
bufio_close(client.bufio);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
usage(char * av0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Usage: %s [-p port] [-R rootdir] [-h]\n", av0);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int ac, char *av[])
|
||||||
|
{
|
||||||
|
int opt;
|
||||||
|
char *port_string = NULL;
|
||||||
|
while ((opt = getopt(ac, av, "hp:R:")) != -1) {
|
||||||
|
switch (opt) {
|
||||||
|
case 'p':
|
||||||
|
port_string = optarg;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'R':
|
||||||
|
server_root = optarg;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'h':
|
||||||
|
default: /* '?' */
|
||||||
|
usage(av[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "Using port %s\n", port_string);
|
||||||
|
server_loop(port_string);
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
|
138
src/socket.c
Normal file
138
src/socket.c
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
/*
|
||||||
|
* Support functions for dealing with sockets.
|
||||||
|
*
|
||||||
|
* Note: these functions cannot be used out of the box.
|
||||||
|
* In particular, support for protocol independent programming
|
||||||
|
* is not fully implemented. See below.
|
||||||
|
*
|
||||||
|
* Written by G. Back for CS 3214 Spring 2018.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/sendfile.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include "socket.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Find a suitable IPv4 address to bind to, create a socket, bind it,
|
||||||
|
* invoke listen to get the socket ready for accepting clients.
|
||||||
|
*
|
||||||
|
* This function does not implement proper support for protocol-independent/
|
||||||
|
* dual-stack binding. Adding this is part of the assignment.
|
||||||
|
*
|
||||||
|
* Returns -1 on error, setting errno.
|
||||||
|
* Returns socket file descriptor otherwise.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
socket_open_bind_listen(char * port_number_string, int backlog)
|
||||||
|
{
|
||||||
|
struct addrinfo *info, *pinfo;
|
||||||
|
struct addrinfo hint;
|
||||||
|
|
||||||
|
memset(&hint, 0, sizeof hint);
|
||||||
|
|
||||||
|
hint.ai_flags = AI_PASSIVE | // we're looking for an address to bind to
|
||||||
|
AI_NUMERICSERV; // service port is numeric, don't look in /etc/services
|
||||||
|
|
||||||
|
hint.ai_protocol = IPPROTO_TCP; // only interested in TCP
|
||||||
|
int rc = getaddrinfo(NULL, port_number_string, &hint, &info);
|
||||||
|
if (rc != 0) {
|
||||||
|
fprintf(stderr, "getaddrinfo error: %s\n", gai_strerror(rc));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
char printed_addr[1024];
|
||||||
|
for (pinfo = info; pinfo; pinfo = pinfo->ai_next) {
|
||||||
|
assert (pinfo->ai_protocol == IPPROTO_TCP);
|
||||||
|
int rc = getnameinfo(pinfo->ai_addr, pinfo->ai_addrlen,
|
||||||
|
printed_addr, sizeof printed_addr, NULL, 0,
|
||||||
|
NI_NUMERICHOST);
|
||||||
|
if (rc != 0) {
|
||||||
|
fprintf(stderr, "getnameinfo error: %s\n", gai_strerror(rc));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Uncomment this to see the address returned
|
||||||
|
printf("%s: %s\n", pinfo->ai_family == AF_INET ? "AF_INET" :
|
||||||
|
pinfo->ai_family == AF_INET6 ? "AF_INET6" : "?",
|
||||||
|
printed_addr);
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Skip any non-IPv4 addresses.
|
||||||
|
* Adding support for protocol independence/IPv6 is part of the project.
|
||||||
|
*/
|
||||||
|
if (pinfo->ai_family != AF_INET)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
int s = socket(pinfo->ai_family, pinfo->ai_socktype, pinfo->ai_protocol);
|
||||||
|
if (s == -1) {
|
||||||
|
perror("socket");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// See https://stackoverflow.com/a/3233022 for a good explanation of what this does
|
||||||
|
int opt = 1;
|
||||||
|
setsockopt (s, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof (opt));
|
||||||
|
|
||||||
|
rc = bind(s, pinfo->ai_addr, pinfo->ai_addrlen);
|
||||||
|
if (rc == -1) {
|
||||||
|
perror("bind");
|
||||||
|
close(s);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = listen(s, backlog);
|
||||||
|
if (rc == -1) {
|
||||||
|
perror("listen");
|
||||||
|
close(s);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
freeaddrinfo(info);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
fprintf(stderr, "No suitable address to bind port %s found\n", port_number_string);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accept a client, blocking if necessary.
|
||||||
|
*
|
||||||
|
* Returns file descriptor of client accepted on success, returns
|
||||||
|
* -1 on error.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
socket_accept_client(int accepting_socket)
|
||||||
|
{
|
||||||
|
struct sockaddr peer;
|
||||||
|
socklen_t peersize = sizeof(peer);
|
||||||
|
int client = accept(accepting_socket, &peer, &peersize);
|
||||||
|
if (client == -1) {
|
||||||
|
perror("accept");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The following will debug with debugging your server.
|
||||||
|
* Adjust as necessary.
|
||||||
|
*/
|
||||||
|
char peer_addr[1024], peer_port[10];
|
||||||
|
int rc = getnameinfo(&peer, sizeof peer,
|
||||||
|
peer_addr, sizeof peer_addr, peer_port, sizeof peer_port,
|
||||||
|
NI_NUMERICHOST | NI_NUMERICSERV);
|
||||||
|
if (rc != 0) {
|
||||||
|
fprintf(stderr, "getnameinfo error: %s\n", gai_strerror(rc));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
fprintf(stderr, "Accepted connection from %s:%s\n", peer_addr, peer_port);
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
7
src/socket.h
Normal file
7
src/socket.h
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#ifndef _SOCKET_H
|
||||||
|
#define _SOCKET_H
|
||||||
|
|
||||||
|
int socket_open_bind_listen(char * port_number_string, int backlog);
|
||||||
|
int socket_accept_client(int socket);
|
||||||
|
|
||||||
|
#endif /* _SOCKET_H */
|
Loading…
x
Reference in New Issue
Block a user