This commit is contained in:
NikolajDanger
2021-11-02 11:55:48 +01:00
parent aad5b42090
commit 74ec5b4ad1
34 changed files with 750021 additions and 0 deletions

View File

@ -0,0 +1,64 @@
#!/bin/python3
import argparse
import hashlib
import os
import shutil
import struct
def gen_cascade_file(sourcefile, targetfile, cascade_file, blocksize):
with open(sourcefile, 'rb') as src:
# Place entire file in memory :)
data = src.read()
fullsize = len(data)
fullhash = hashlib.sha256(data).digest()
with open(cascade_file, 'wb') as dst:
# Header part
dst.write(bytes('CASCADE1', 'ascii')) # Magic signature
dst.write(struct.pack('!Q', 0)) # Reserved
dst.write(struct.pack('!Q', fullsize)) # File length
dst.write(struct.pack('!Q', blocksize)) # Block size
dst.write(fullhash)
with open(sourcefile, 'rb') as src:
while True:
data = src.read(blocksize)
if not data:
break
dst.write(hashlib.sha256(data).digest())
if not os.path.exists(targetfile):
shutil.copy(sourcefile, targetfile)
blocksizes = [
(1024, '1kib'),
(10240, '10kib'),
(102400, '100kib'),
(1048576, '1mib'),
(10485760, '10mib')
]
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("source", help="The file to generate the cascade file for")
args = parser.parse_args()
sourcefile = os.path.abspath(args.source)
if not os.path.isfile(sourcefile):
print(f"Source file not found: {sourcefile}")
exit(1)
for bs in blocksizes:
basename, ext = os.path.splitext(sourcefile)
targetfile = f"{basename}.{bs[1]}{ext}"
cascade_file = f"{targetfile}.cascade"
if os.path.isfile(targetfile):
print(f"File already exists, deleting: {targetfile}")
os.unlink(targetfile)
print(f"Generating file: {targetfile}")
gen_cascade_file(sourcefile, targetfile, cascade_file, bs[0])
print("Done!")

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

435
A3/python/peer/peer.py Normal file
View File

@ -0,0 +1,435 @@
#!/bin/python3
import argparse
import os
import hashlib
import struct
import math
import random
import socket
import time
import socketserver
import threading
from typing import Any, List, Union
HASH_LENGTH=32
# Helper method to mask network package fragmentation
def readbytes(s, count) -> bytes:
res = bytearray()
while count > 0:
msg = s.recv(count)
if not msg:
raise Exception('Server did not respond')
res.extend(msg)
count -= len(msg)
return bytes(res)
class CascadeBlock(object):
def __init__(self, index: int, offset: int, length: int, hash: bytes) -> None:
self.index = index
self.offset = offset
self.length = length
self.hash = hash
if len(self.hash) != HASH_LENGTH:
raise Exception(f'Hash length should be {HASH_LENGTH} but was {len(self.hash)} (offset: {self.offset})')
self.completed = False
class CascadeFile(object):
def __init__(self, path: str, destination: str = None) -> None:
self.destination = destination
with open(path, 'rb') as f:
self.cascadehash = hashlib.sha256(f.read()).digest()
with open(path, 'rb') as f:
self.header = f.read(64)
if bytes('CASCADE1', 'ascii') != self.header[:8]:
raise Exception(f'Signature in file header is invalid: {path}')
self.targetsize = struct.unpack('!Q', self.header[16:24])[0]
self.blocksize = struct.unpack('!Q', self.header[24:32])[0]
self.targethash = self.header[32:64]
self.trailblocksize = self.targetsize % self.blocksize
if self.trailblocksize == 0: # In case the file size is evenly divisible with the block size
self.trailblocksize = self.blocksize
blockcount = math.ceil(self.targetsize / self.blocksize)
self.blocks = []
for n in range(blockcount):
hash = f.read(HASH_LENGTH)
if not hash:
raise Exception(f'Incorrect number of hashes in file: {path}, got {n}, but expected {blockcount}')
self.blocks.append(
CascadeBlock(
n,
n * self.blocksize,
self.trailblocksize if n == blockcount - 1 else self.blocksize,
hash
)
)
if destination is not None:
self.prepare_download(destination)
def prepare_download(self, destination: str) -> None:
# Make sure the target exists
if not os.path.isfile(destination):
with open(destination, 'wb'):
pass
# Check existing blocks for completion
with open(destination, 'rb') as f:
for n in range(len(self.blocks)):
size = self.blocksize if n != len(self.blocks) - 1 else self.trailblocksize
data = f.read(size)
if not data:
break
if len(data) != size:
break
if hashlib.sha256(data).digest() == self.blocks[n].hash:
self.blocks[n].completed = True
# Create a queue of missing blocks
self.queue = list([x for x in self.blocks if not x.completed])
class Peer(object):
def __init__(self, ip: str, port: int, timestamp: int, good: bool):
self.ip = ip
self.port = port
self.timestamp = timestamp
self.good = good
def download_block(self, cascadehash, blockno, blocksize) -> bytes:
req = bytearray(bytes('CASCADE1', 'ascii'))
req.extend(struct.pack('!Q', 0))
req.extend(struct.pack('!Q', 0))
req.extend(struct.pack('!Q', blockno))
req.extend(cascadehash)
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect((self.ip, self.port))
s.sendall(req)
data = s.recv(9)
size = struct.unpack('!Q', data[1:9])[0]
if data[0] == 0:
if size != blocksize:
raise Exception(f'The reported block size was {size} but {blocksize} was expected')
return readbytes(s, size)
elif data[0] == 1:
msg = readbytes(s, size).decode('utf-8')
raise Exception(f'Client does not have file: {msg}')
elif data[0] == 2:
msg = readbytes(s, size).decode('utf-8')
print(f'Client does not have block {blockno}: {msg}')
return None
elif data[0] == 3:
msg = readbytes(s, size).decode('utf-8')
raise Exception(f'Client reported blockno was out-of-bounds: {msg}')
elif data[0] == 4:
msg = readbytes(s, size).decode('utf-8')
raise Exception(f'Client reported invalid request: {msg}')
else:
raise Exception(f'Client gave unsupported error code: {data[0]}')
class Tracker(object):
def __init__(self, ip: str, port: int):
self.ip = ip
self.port = port
def list(self, hash: bytes, ip: Union[bytes, str], port: int) -> List[Peer]:
return self.send_to_server(self.build_request(1, hash, ip, port))
def subscribe(self, hash: bytes, ip: Union[bytes, str], port: int) -> List[Peer]:
return self.send_to_server(self.build_request(2, hash, ip, port))
def build_request(self, command: int, hash: bytes, ip: Union[bytes, str], port: int) -> bytes:
if isinstance(ip, bytes) or isinstance(ip, bytearray):
ipbytes = ip
else:
ipbytes = bytes(map(int, ip.split('.')))
if len(ipbytes) != 4:
raise Exception('Incorrect IP address?')
req = bytearray(bytes('CASC', 'ascii'))
req.extend(struct.pack('!I', 1))
req.extend(struct.pack('!I', command))
req.extend(struct.pack('!I', len(hash) + 4 + 2))
req.extend(hash)
req.extend(ipbytes)
req.extend(struct.pack('!H', port))
return req
def unparse_peer(self, data: bytes) -> Peer:
return Peer(
'.'.join([str(x) for x in data[0:4]]),
struct.unpack('!H', data[4:6])[0],
struct.unpack('!I', data[6:10])[0],
True if data[10] == 1 else False
)
def send_to_server(self, request: bytes) -> List[Peer]:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect((self.ip, self.port))
s.sendall(request)
data = s.recv(5)
if not data:
raise Exception('Server did not respond')
if len(data) != 5:
raise Exception('Network protocol error')
size = struct.unpack('!I', data[1:5])[0]
if size > 1024 * 1024:
raise Exception(f'Too big server response: {size}')
if data[0] != 0:
msg = readbytes(s, size).decode('utf-8')
raise Exception(f'Tracker gave error: {msg}')
else:
if size % 12 != 0:
raise Exception('Tracker gave a peer list with a size not a modulo of two')
blob = readbytes(s, size)
peers = size // 12
return list([self.unparse_peer(blob[x*12:(x+1)*12]) for x in range(peers)])
class CascadePeerServe(socketserver.StreamRequestHandler):
activefiles = {}
activefileslock = threading.Lock()
HEADER_LENGTH = 8+16+8+HASH_LENGTH
def handle(self) -> None:
while True:
try:
header = self.request.recv(self.HEADER_LENGTH)
except:
return
if header is None:
return
if len(header) != self.HEADER_LENGTH:
self.reporterror(4, f'Invalid header length, got {len(header)} but expected {self.HEADER_LENGTH}')
return
if header[:8] != bytes('CASCADE1', 'ascii'):
self.reporterror(4, f'Invalid header, should start with "CASCADE1", got {header[:8].hex()}')
return
blockno = struct.unpack('!Q', header[24:32])[0]
hash = header[32:64]
file = None
with self.activefileslock:
if hash in self.activefiles:
file = self.activefiles[hash]
if file is None:
self.reporterror(1, f'Not serving file with hash: {hash.hex()}')
return
if blockno >= len(file.blocks):
self.reporterror(3, f'File has {len(file.blocks)} blocks, but {blockno} was requested')
return
block = file.blocks[blockno]
if not block.completed:
self.reporterror(2, f'{blockno} is not currently held by this peer')
continue
with open(file.destination, 'rb') as f:
if f.seek(block.offset) != block.offset:
self.reporterror(2, f'{blockno} is not currently held by this peer (corrupt state)')
return
data = f.read(block.length)
if len(data) != block.length:
self.reporterror(2, f'{blockno} is not currently held by this peer (corrupt state)')
return
resp = bytearray()
resp.extend(struct.pack('!B', 0))
resp.extend(struct.pack('!Q', block.length))
resp.extend(data)
self.request.sendall(resp)
def reporterror(self, code: int, msg: str) -> None:
msgdata = bytes(msg, 'utf-8')
data = bytearray(struct.pack('!B', code))
data.extend(struct.pack('!Q', len(msgdata)))
data.extend(msgdata)
self.request.sendall(data)
class P2PServer(object):
def __init__(self, tracker, ip, port) -> None:
self.tracker = tracker
self.ip = ip
self.ipbytes = bytes(map(int, selfaddr.split('.')))
self.port = port
self.stopped = False
self.refreshsemaphore = threading.Semaphore(0)
self.peers = {}
self.serverthread = threading.Thread(target=self.run_peer_server, daemon=True)
self.refreshthread = threading.Thread(target=self.run_peer_subscribe, daemon=True)
self.serverthread.start()
self.refreshthread.start()
def resubscribe(self) -> None:
self.refreshsemaphore.release()
def join(self) -> None:
self.serverthread.join()
def stop(self) -> None:
self.stopped = True
self.server.shutdown()
self.refreshsemaphore.release()
self.refreshthread.join()
self.serverthread.join()
def run_peer_server(self) -> None:
print(f"Running peer server on {self.ip}:{self.port}")
with socketserver.ThreadingTCPServer((self.ip, self.port), CascadePeerServe) as server:
self.server = server
server.serve_forever(poll_interval=10)
def run_peer_subscribe(self) -> None:
while not self.stopped:
with CascadePeerServe.activefileslock:
hashes = list(CascadePeerServe.activefiles)
for h in hashes:
try:
self.peers[h] = self.tracker.subscribe(h, self.ipbytes, self.port)
except Exception as e:
print(f"Tracker register failed: {e}")
self.refreshsemaphore.acquire(timeout=60*10)
def run_peer_download(tracker: Tracker, source: str, localip: str, localport: int, server:P2PServer = None, output:str = None, randomseq:bool =True) -> None:
if not os.path.isfile(source):
print("File not found: {args.source}")
exit(1)
if output is None:
output = os.path.splitext(source)[0]
print(f"Preparing download of {source} to {output}")
file = CascadeFile(source, output)
if len(file.queue) != 0:
print(f"Download will require {len(file.queue)} blocks of size {file.blocksize}")
with CascadePeerServe.activefileslock:
CascadePeerServe.activefiles[file.cascadehash] = file
if server is not None:
server.resubscribe() # Trigger server subscription with new file
peers = []
last_peer_update = 0
while len(file.queue) > 0:
# Ensure we have peers
while len(peers) == 0 or time.time() - last_peer_update > 60*5:
if time.time() - last_peer_update < 30:
print(f'Throttling peer update; wait for 30s')
time.sleep(30)
try:
peers = tracker.list(file.cascadehash, localip, localport)
except Exception as e:
print(f"Tracker error: {e}")
finally:
last_peer_update = time.time()
if len(peers) == 0:
print(f"We have no peers, sleeping 10s before trying again")
time.sleep(10)
else:
print(f"We got {len(peers)} peer{'s' if len(peers) == 1 else ''}")
# Pick a block
blockid = 0 if not randomseq else random.randrange(0, len(file.queue))
peerid = 0 if not randomseq else random.randrange(0, len(peers))
block = file.queue[blockid]
peer = peers[peerid]
# Grab it
print(f"Attempting to fetch block {block.index} from {peer.ip}:{peer.port}")
print(f"Attempting to fetch block with hash {file.cascadehash}")
try:
data = peer.download_block(file.cascadehash, block.index, block.length)
if data is None:
raise Exception('Peer did not have the requested block')
datahash = hashlib.sha256(data).digest()
if datahash != block.hash:
print(f"Invalid hash for block {block.index} from {peer.ip}:{peer.port}. Got {datahash.hex()} but expected {block.hash.hex()}")
raise Exception('Downloaded block was incorrect')
with open(output, 'r+b') as f:
f.seek(block.offset)
f.write(data)
block.completed = True
file.queue.remove(block)
print(f"Retrieved block {block.index}")
except Exception as e:
peers.remove(peer)
print(f"Download failure ignoring peer. {e}")
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("source", help="The cascade source file(s) with information about the download")
parser.add_argument("tracker", help="The tracker ip and port, eg: localhost:8888", default="127.0.0.1:8888")
parser.add_argument("self", help="The address to report serving files from, e.g.: 1.2.3.4:5555", default="127.0.0.1:7777")
parser.add_argument("-o", "--output", help="The target output file(s)", required=False)
parser.add_argument("-r", "--random", help="Download blocks in random order from random peers", type=bool, required=False, default=True)
parser.add_argument("-c", "--clientonly", help="Flag to set client-only mode (i.e. no serving)", type=bool, required=False, default=False)
args = parser.parse_args()
tracker = Tracker(args.tracker.split(':')[0], int(args.tracker.split(':')[1]))
selfaddr, selfport = args.self.split(':')
selfport = int(selfport)
server = None
if not args.clientonly:
server = P2PServer(tracker, selfaddr, selfport)
source_files = args.source.split(os.pathsep)
if args.output is None:
target_files = []
else:
target_files = args.output.split(os.pathsep)
for ix, f in zip(range(len(source_files)), source_files):
target = None
if ix < len(target_files):
target = target_files[ix]
run_peer_download(tracker, f, selfaddr, selfport, server, target, args.random)
if args.clientonly:
print("Download complete in client mode, stopping")
else:
print("Download complete serving forever ...")
server.join()

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -0,0 +1,14 @@
{
"port": 8888,
"ban-private": false,
"allowedhashes": [
"713a203b7e89753a8893f7d452e8bcc19de65024595f50ddf7899f1da9d95fff",
"e29fcee1b3fbd186ae430f39e66661d7e0aec549998d63df438b51736fcf7a8e",
"28bccac30019632135bed1e273d259b3c31ad135d92b9db907f120a1c801b40c",
"729578b33120e7c9ba3c8a88cc6820cda3162adf8ce5294888ed62b435c5da7f",
"81b831d167d74a11d4a95fd11c41e895edbec56d79e5a29b162217a805b539a6"
],
"good-ips": [
"127.0.0.1"
]
}

View File

@ -0,0 +1,193 @@
#!/bin/python3
import os
import argparse
import struct
import socketserver
import json
import time
import random
import threading
import ipaddress
HEADER_LENGTH=16
HASH_LENGTH=32
CMD_LENGTH=HASH_LENGTH + 4 + 2
IP_LENGTH=4
PORT_LENGTH=2
MAX_PEERS_REPORTED=20
MAX_PEER_AGE_SECONDS=1800
def is_good_client(ip, goods):
return len([x for x in goods if ip == x]) > 0
class TrackerServer(socketserver.StreamRequestHandler):
active_clients = {}
lock = threading.Lock()
last_cleanup = 0
def handle(self):
# self.request is the TCP socket connected to the client
header = self.request.recv(HEADER_LENGTH)
if len(header) != HEADER_LENGTH:
self.respond_error(f'Header must be at least {HEADER_LENGTH} bytes, got {len(header)}')
return
if header[:4] != bytes('CASC', 'ascii'):
self.respond_error(f'Header must start with CASC ({bytes("CASC", "ascii").hex()}), got: {header[:4].hex()}')
return
protocol_version = struct.unpack('!I', header[4:8])[0]
command = struct.unpack('!I', header[8:12])[0]
datalen = struct.unpack('!I', header[12:16])[0]
if protocol_version != 1:
self.respond_error(f'Protocol version must be 1, got: {protocol_version}')
return
if command == 1 or command == 2:
if datalen != CMD_LENGTH:
self.respond_error(f'Data for command=1 must be of length {CMD_LENGTH}, got {datalen}')
return
data = self.request.recv(CMD_LENGTH)
if len(data) != CMD_LENGTH:
self.respond_error(f'Data from socket had length {len(data)}, expected {CMD_LENGTH}')
return
hash = data[:32]
hashhex = hash.hex()
ipportkey = data[32:].hex()
with self.lock:
hashfound = hashhex in self.active_clients
if not hashfound:
self.respond_error(f"The supplied hash '{hashhex}' is not tracked by this tracker")
return
if command == 2:
# For local testing you may need to remove this check
# if data[32:36] == b'\0\0\0\0':
# self.respond_error(f'The IP {({".".join([str(x) for x in data[32:36]])})} is not supported')
# return
if self.banprivateips and ipaddress.IPv4Address(data[32:36]).is_private:
self.respond_error(f'The IP reported to the tracker ({".".join([str(x) for x in data[32:36]])}) appears to be a private IP')
return
# Avoid peers registering as good peers without approval
if is_good_client(data[32:36], self.goodips):
if str(ipaddress.IPv4Address(data[32:36])) == self.client_address[0]:
print("Registering a good client")
else:
self.respond_error(f'The IP reported to the tracker ({".".join([str(x) for x in data[32:36]])}) is a reserved IP')
return
else:
print("Not a good client")
with self.lock:
self.active_clients[hashhex][ipportkey] = int(time.time())
self.report_clients(hash.hex(), ipportkey)
else:
self.respond_error(f'Only commands (list=1, subscribe=2) supported, got: {command}')
return
def report_clients(self, hashhex, selfipport=None):
now = int(time.time())
with self.lock:
# Perform cleanup, if required
if self.last_cleanup == 0:
self.last_cleanup = now
elif now - self.last_cleanup > (MAX_PEER_AGE_SECONDS * 2):
self.last_cleanup = now
for h in self.active_clients:
for x in list([n for n in h if now - n > MAX_PEER_AGE_SECONDS]):
del h[x]
# Build list of peers
d = self.active_clients[hashhex]
peers = list([(
bytes.fromhex(x)[:IP_LENGTH], # Ip is tupple item 0
bytes.fromhex(x)[IP_LENGTH:IP_LENGTH+PORT_LENGTH], # Port is tupple item 1
d[x], # Timestamp is tupple item 2
is_good_client(bytes.fromhex(x)[:IP_LENGTH], self.goodips) # Good flag is tupple item 3
) for x in d if now - d[x] < MAX_PEER_AGE_SECONDS and x != selfipport])
# Random order
random.shuffle(peers)
peers.sort(key=lambda x: x[3] != 1) # Keep good ones at top
# Build response
result = bytearray()
for n,ix in zip(peers, range(MAX_PEERS_REPORTED)):
result.extend(n[0]) # ip
result.extend(n[1]) # port
result.extend(struct.pack('!l', n[2])) #timestamp
result.extend(struct.pack('!B', 1 if n[3] else 0)) # Good flag
result.extend(struct.pack('!B', 0)) # reserved
header = bytearray()
header.extend(struct.pack('!B', 0))
header.extend(struct.pack('!I', len(result)))
header.extend(result)
self.request.sendall(header)
def respond_error(self, msg):
msgdata = bytes(msg, 'utf-8')
data = bytearray(struct.pack('!B', 1))
data.extend(struct.pack('!I', len(msgdata)))
data.extend(msgdata)
self.request.sendall(data)
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("config", help="The configuration file")
args = parser.parse_args()
if not os.path.isfile(args.config):
print(f"Config file not found: {args.config}")
with open(args.config) as jf:
config = json.load(jf)
if not "port" in config:
print("Config is missing port")
exit(1)
if not "allowedhashes" in config:
print("Config is missing allowedhashes")
exit(1)
if len(config["allowedhashes"]) <= 0:
print("Need at least one allowed hash")
exit(1)
if not "good-ips" in config:
config["good-ips"] = []
HOST, PORT = "0.0.0.0", int(config['port'])
hashes = list(bytes.fromhex(x) for x in config['allowedhashes'])
invalids = [x for x in hashes if len(x) != 32]
if len(invalids) > 0:
print(f"Invalid hashes: {', '.join([x.hex() for x in invalids])}")
exit(1)
goodips = list([bytes(map(int, ip.split('.'))) for ip in config["good-ips"]])
invalids = [x for x in goodips if len(x) != 4]
if len(invalids) > 0:
print(f"Invalid IPs: {', '.join(['.'.join([str(x) for x in ip]) for ip in invalids])}")
exit(1)
TrackerServer.active_clients = dict(zip([x.hex() for x in hashes], [{} for x in range(len(hashes))]))
TrackerServer.goodips = goodips
TrackerServer.banprivateips = False
if 'ban-private' in config:
TrackerServer.banprivateips = config['ban-private']
with socketserver.ThreadingTCPServer((HOST, PORT), TrackerServer) as server:
server.serve_forever()

29
A3/src/Makefile Normal file
View File

@ -0,0 +1,29 @@
GCC=gcc -O3 -g -Wall -Wextra -pedantic -std=gnu11
LD_FLAGS= -lpthread
all: peer name_server cascade
rebuild: clean all
csapp.o: csapp.c csapp.h
$(GCC) -c $< -o $@
common.o: common.c common.h
$(GCC) -c $< -o $@
cascade: cascade.c cascade.h common.o csapp.o sha256.o
$(GCC) $< *.o -o $@ $(LD_FLAGS)
name_server: name_server.c name_server.h common.o csapp.o
$(CC) $(CFLAGS) $< *.o -o $@ $(LD_FLAGS)
sha256.o : sha256.c sha256.h
$(CC) $(CFLAGS) -c $< -o $@
zip: ../src.zip
../src.zip: clean
cd .. && zip -r src.zip src/Makefile src/*.c src/*.h src/tests/*
clean:
rm -rf *.o peer name_server cascade sha256 vgcore*

450
A3/src/cascade.c Normal file
View File

@ -0,0 +1,450 @@
#include <stdlib.h>
#include <stdio.h>
#include <netdb.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <endian.h>
#include <string.h>
#include "./cascade.h"
#include "./sha256.h"
char tracker_ip[IP_LEN];
char tracker_port[PORT_LEN];
char my_ip[IP_LEN];
char my_port[PORT_LEN];
struct csc_file *casc_file;
csc_block_t** queue;
csc_peer_t* peers;
void free_resources()
{
free(queue);
free(peers);
csc_free_file(casc_file);
}
unsigned char* get_file_sha(const char* sourcefile, csc_file_t* res, char* hash, int size)
{
int n;
int casc_file_size;
FILE* fp = fopen(sourcefile, "rb");
if (fp == 0)
{
printf("Failed to open source: %s\n", sourcefile);
return NULL;
}
fseek(fp, 0L, SEEK_END);
casc_file_size = ftell(fp);
fseek(fp, 0L, SEEK_SET);
char buffer[casc_file_size];
fread(buffer, casc_file_size, 1, fp);
fclose(fp);
SHA256_CTX shactx;
unsigned char shabuffer[size];
sha256_init(&shactx);
sha256_update(&shactx, buffer, casc_file_size);
sha256_final(&shactx, &shabuffer);
for (int i=0; i<size; i++)
{
hash[i] = shabuffer[i];
}
}
void download_only_peer(char *cascade_file)
{
printf("Managing download only for: %s\n", cascade_file);
if (access(cascade_file, F_OK ) != 0 )
{
fprintf(stderr, ">> File %s does not exist\n", cascade_file);
exit(EXIT_FAILURE);
}
char output_file[strlen(cascade_file)];
memcpy(output_file, cascade_file, strlen(cascade_file));
char* r = strstr(cascade_file, "cascade");
int cutoff = r - cascade_file ;
output_file[cutoff-1] = '\0';
printf("Downloading to: %s\n", output_file);
casc_file = csc_parse_file(cascade_file, output_file);
/*
TODO Create a list of missing blocks
*/
/*
TODO Compute the hash of the cascade file
HINT: Do not implement hashing from scratch. Use the provided 'get_file_sha' function
*/
int peercount = 0;
while (peercount == 0)
{
peercount = get_peers_list(&peers, hash_buf, tracker_ip, tracker_port);
if (peercount == 0)
{
printf("No peers were found. Will try again in %d seconds\n", PEER_REQUEST_DELAY);
fflush(stdout);
sleep(PEER_REQUEST_DELAY);
}
else
{
printf("Found %d peer(s)\n", peercount);
}
}
csc_peer_t peer = (peers[0]);
// Get a good peer if one is available
for (int i=0; i<peercount; i++)
{
if (peers[i].good)
{
peer = peers[i];
}
}
for (int i=0; i<uncomp_count; i++)
{
get_block(queue[i], peer, hash_buf, output_file);
}
free_resources();
}
int count_occurences(char string[], char c)
{
int i=0;
int count=0;
for(i=0; i<strlen(string); i++)
{
if(string[i] == c)
{
count++;
}
}
return count;
}
// Adapted from: https://stackoverflow.com/a/35452093/
uint8_t* hex_to_bytes(const char* string) {
if(string == NULL)
return NULL;
size_t slength = strlen(string);
if((slength % 2) != 0) // must be even
return NULL;
size_t dlength = slength / 2;
uint8_t* data = malloc(dlength);
memset(data, 0, dlength);
size_t index = 0;
while (index < slength) {
char c = string[index];
int value = 0;
if(c >= '0' && c <= '9')
value = (c - '0');
else if (c >= 'A' && c <= 'F')
value = (10 + (c - 'A'));
else if (c >= 'a' && c <= 'f')
value = (10 + (c - 'a'));
else {
free(data);
return NULL;
}
data[(index/2)] += value << (((index + 1) % 2) * 4);
index++;
}
return data;
}
/*
* Parses a cascade file, given the sourcepath input and destination, which may or may not exist.
* Returns a pointer to a datastructure describing the file, or NULL if the file could not be parsed
*/
csc_file_t* csc_parse_file(const char* sourcefile, const char* destination)
{
FILE* fp = fopen(sourcefile, "rb");
if (fp == 0)
{
printf("Failed to open source: %s\n", sourcefile);
return NULL;
}
const int FILE_HEADER_SIZE = 8+8+8+8+32;
char header[FILE_HEADER_SIZE];
if (fread(header, 1, FILE_HEADER_SIZE, fp) != FILE_HEADER_SIZE)
{
printf("Failed to read magic 8 bytes header from file\n");
fclose(fp);
return NULL;
}
if (memcmp(header, "CASCADE1", 8) != 0)
{
printf("File does not contain magic 8 bytes in header\n");
fclose(fp);
return NULL;
}
csc_file_t* casc_file_data = (csc_file_t*)malloc(sizeof(csc_file_t));
/*
TODO Parse the cascade file and store the data in an appropriate data structure
HINT Use the definition of the 'csc_file' struct in cascade.h, as well as the
assignment handout for guidance on what each attribute is and where it is stored
in the files header/body.
*/
fclose(fp);
fp = fopen(destination, "a+w");
if (fp == NULL)
{
printf("Failed to open destination file %s\n", destination);
csc_free_file(casc_file_data);
return NULL;
}
void* buffer = malloc(casc_file_data->blocksize);
if (buffer == NULL)
{
printf("No block buffer asigned: %d\n", casc_file_data->blocksize);
csc_free_file(casc_file_data);
fclose(fp);
return NULL;
}
SHA256_CTX shactx;
for(unsigned long long i = 0; i < casc_file_data->blockcount; i++)
{
char shabuffer[SHA256_HASH_SIZE];
unsigned long long size = casc_file_data->blocks[i].length;
if (fread(buffer, size, 1, fp) != 1)
{
break;
}
sha256_init(&shactx);
sha256_update(&shactx, buffer, size);
sha256_final(&shactx, &shabuffer);
/*
TODO Compare the hashes taken from the Cascade file with those of the local data
file and keep a record of any missing blocks
HINT The code above takes a hash of each block of data in the local file in turn
and stores it in the 'shabuffer' variable. You can compare then compare 'shabuffer'
directly to the hashes of each block you have hopefully already assigned as part
of the 'casc_file_data' struct
*/
}
fclose(fp);
return casc_file_data;
}
/*
* Releases the memory allocated by a file datastructure
*/
void csc_free_file(csc_file_t* file)
{
free(file->blocks);
file->blocks = NULL;
free(file);
}
size_t readbytes(int sock, void* buffer, size_t count)
{
size_t remaining = count;
while(remaining > 0)
{
size_t read = recv(sock, buffer, remaining, NULL);
if (read == 0)
return 0;
buffer += read;
remaining -= read;
}
return count;
}
void get_block(csc_block_t* block, csc_peer_t peer, unsigned char* hash, char* output_file)
{
printf("Attempting to get block %d from %s:%s for %s\n", block->index, peer.ip, peer.port, output_file);
rio_t rio;
char rio_buf[MAX_LINE];
int peer_socket;
/*
TODO Request a block from a peer
*/
FILE* fp = fopen(output_file, "rb+");
if (fp == 0)
{
printf("Failed to open destination: %s\n", output_file);
Close(peer_socket);
return NULL;
}
/*
TODO Write the block into the data file
*/
printf("Got block %d. Wrote from %d to %d\n", block->index, block->offset, block->offset+write_count-1);
Close(peer_socket);
fclose(fp);
}
int get_peers_list(csc_peer_t** peers, unsigned char* hash, char* tracker_ip, char* tracker_port, char* my_ip, char* my_port)
{
rio_t rio;
char rio_buf[MAX_LINE];
int tracker_socket;
/*
TODO Setup a connection to the tracker
*/
struct RequestHeader request_header;
strncpy(request_header.protocol, "CASC", 4);
request_header.version = htonl(1);
request_header.command = htonl(1);
request_header.length = htonl(BODY_SIZE);
memcpy(rio_buf, &request_header, HEADER_SIZE);
/*
TODO Complete the peer list request and
HINT The header has been provided above as a guide
*/
Rio_writen(tracker_socket, rio_buf, MESSAGE_SIZE);
Rio_readnb(&rio, rio_buf, MAXLINE);
char reply_header[REPLY_HEADER_SIZE];
memcpy(reply_header, rio_buf, REPLY_HEADER_SIZE);
uint32_t msglen = ntohl(*(uint32_t*)&reply_header[1]);
if (msglen == 0)
{
Close(tracker_socket);
return 0;
}
if (reply_header[0] != 0)
{
char* error_buf = malloc(msglen + 1);
if (error_buf == NULL)
{
printf("Tracker error %d and out-of-memory reading error\n", reply_header[0]);
Close(tracker_socket);
return NULL;
}
memset(error_buf, 0, msglen + 1);
memcpy(reply_header, error_buf, msglen);
printf("Tracker gave error: %d - %s\n", reply_header[0], error_buf);
free(error_buf);
Close(tracker_socket);
return NULL;
}
if (msglen % 12 != 0)
{
printf("LIST response from tracker was length %llu but should be evenly divisible by 12\n", msglen);
Close(tracker_socket);
return NULL;
}
/*
TODO Parse the body of the response to get a list of peers
HINT Some of the later provided code expects the peers to be stored in the ''peers' variable, which
is an array of 'csc_peer's, as defined in cascade.h
*/
Close(tracker_socket);
return peercount;
}
int main(int argc, char **argv)
{
if (argc != MAIN_ARGNUM + 1)
{
fprintf(stderr, "Usage: %s <cascade file(s)> <tracker server ip> <tracker server port> <peer ip> <peer port>.\n", argv[0]);
exit(EXIT_FAILURE);
}
else if (!is_valid_ip(argv[2]))
{
fprintf(stderr, ">> Invalid tracker IP: %s\n", argv[2]);
exit(EXIT_FAILURE);
}
else if (!is_valid_port(argv[3]))
{
fprintf(stderr, ">> Invalid tracker port: %s\n", argv[3]);
exit(EXIT_FAILURE);
}
else if (!is_valid_ip(argv[4]))
{
fprintf(stderr, ">> Invalid peer IP: %s\n", argv[4]);
exit(EXIT_FAILURE);
}
else if (!is_valid_port(argv[5]))
{
fprintf(stderr, ">> Invalid peer port: %s\n", argv[5]);
exit(EXIT_FAILURE);
}
snprintf(tracker_ip, IP_LEN, argv[2]);
snprintf(tracker_port, PORT_LEN, argv[3]);
snprintf(my_ip, IP_LEN, argv[4]);
snprintf(my_port, PORT_LEN, argv[5]);
char cas_str[strlen(argv[1])];
snprintf(cas_str, strlen(argv[1])+1, argv[1]);
char delim[] = ":";
int casc_count = count_occurences(argv[1], ':') + 1;
char *cascade_files[casc_count];
char *ptr = strtok(cas_str, delim);
int i = 0;
while (ptr != NULL)
{
if (strstr(ptr, ".cascade") != NULL)
{
cascade_files[i++] = ptr;
ptr = strtok(NULL, delim);
}
else
{
printf("Abort on %s\n", ptr);
fprintf(stderr, ">> Invalid cascade file: %s\n", ptr);
exit(EXIT_FAILURE);
}
}
for (int j=0; j<casc_count; j++)
{
download_only_peer(cascade_files[j]);
}
exit(EXIT_SUCCESS);
}

115
A3/src/cascade.h Normal file
View File

@ -0,0 +1,115 @@
#pragma once
#include "common.h"
#ifndef CASCADE_H
#define CASCADE_H
#define MAIN_ARGNUM 5 // number of command line arguments to main().
#define BODY_SIZE 38
#define HEADER_SIZE 16
#define MESSAGE_SIZE 54
#define REPLY_HEADER_SIZE 5
#define PEER_REQUEST_DELAY 10
#define PEER_REQUEST_HEADER_SIZE 64
#define PEER_RESPONSE_HEADER_SIZE 9
#define MAX_LINE 128
struct RequestHeader
{
char protocol[4];
unsigned int version;
unsigned int command;
unsigned int length;
};
struct RequestBody
{
char hash[32];
struct in_addr ip;
unsigned short port;
};
struct ClientRequest
{
char protocol[8];
char reserved[12];
uint64_t block_num;
char hash[32];
};
struct ClientResponseHeader
{
char error[1];
uint64_t length;
};
#include <stdint.h>
typedef struct csc_hashdata { uint8_t x[32]; } csc_hashdata_t;
typedef struct csc_ipport {
uint32_t ip;
uint16_t port;
} csc_ipport_t;
typedef struct csc_block {
uint64_t index; // The index of this block in the data file
uint64_t offset; // Number of bytes the start of this block is offset from the start of the file
uint64_t length; // Number of bytes within this block
uint8_t completed; // Flag of if this block is present in local file system or not
csc_hashdata_t hash; // Hash of this blocks bytes
} csc_block_t;
typedef struct csc_file {
uint64_t targetsize; // Size of completed data file
uint64_t blocksize; // Size of individual blocks data is divided into
csc_hashdata_t targethash; // Hash of complete data file
uint64_t trailblocksize; // Size of last block. Will differ from standard block size if data does not evenly divide amoungst blocks
uint64_t blockcount; // Number of blocks data is divided into
csc_block_t* blocks; // Pointer to array of all blocks
} csc_file_t;
typedef struct csc_peer {
char ip[16]; // IP of a peer
char port[8]; // Port of a peer
uint32_t lastseen; // Timestamp of last time Tracker saw this Peer
uint8_t good; // Flag for if this is 'Good' peer. e.g. Always provides valid responses according to protocol
} csc_peer_t;
typedef struct csc_server {
} csc_server_t;
/*
* Parses a hex-string and returns the bytes corresponding to the value
*/
uint8_t* hex_to_bytes(const char* string);
/*
* Parses a cascade file, given the sourcepath input and destination, which may or may not exist.
* Returns a pointer to a datastructure describing the file, or NULL if the file could not be parsed
*/
csc_file_t* csc_parse_file(const char* sourcefile, const char* destination);
/*
* Releases the memory allocated by a file datastructure
*/
void csc_free_file(csc_file_t* sourcefile);
/*
* Attempts to get a list of peers from the tracker.
* The peers should contain a pre-allocated set of memory that can hold at least peercount elements.
* If the function is successful, the return code is zero and the value of peercount has been updated to contain the number of peers.
* If the function fails, the return code is non-zero
*/
int csc_get_peers(csc_ipport_t tracker, csc_hashdata_t cascadehash, csc_ipport_t localaddress, uint8_t subscribe, uint32_t* peercount, csc_peer_t* peers);
/*
* Downloads a block of data from a client.
* The supplied buffer should be at least large enough to contain blocklength-bytes.
* If the function is successful, the return code is zero, and the buffer contains data of the size requested by blocklength.
* If the function fails, the return code is non-zero, and will reflect the peer statuscode (if given).
*/
int csc_download_block(csc_ipport_t client, csc_hashdata_t cascadehash, uint64_t blockno, uint64_t blocklength, void* buffer);
#endif

97
A3/src/common.c Normal file
View File

@ -0,0 +1,97 @@
#include "common.h"
/*
* below two utility functions are for parsing user input.
* YOU ONLY NEED A WORKING UNDERSTANDING OF parse_command,
* and how it stores parsed arguments (see common.h).
*/
command_t parse_command(char *user_input,
args_t args) {
for (int i = 0; i < MAX_USER_ARGNUM; i++)
args[i] = NULL;
char *saveptr;
char *command_str = strtok_r(user_input, " \n", &saveptr);
if (command_str == NULL) return ERROR;
if (string_equal(command_str, "/msg")) { // if command_str is /msg, recipient username
args[0] = strtok_r(NULL, " \n", &saveptr); // and actual message will be stored in
args[1] = strtok_r(NULL, "\n\x0", &saveptr); // first two arguments, respectively.
if (args[0] == NULL || args[1] == NULL) return ERROR;
else return MSG;
}
unsigned int num_args = extract_args(saveptr, args); // args now contains arguments to command_str.
return string_equal(command_str, "/login") && num_args == 4 ? LOGIN
: string_equal(command_str, "/lookup") && num_args <= 1 ? LOOKUP
: string_equal(command_str, "/show") && num_args <= 1 ? SHOW
: string_equal(command_str, "/logout") && num_args == 0 ? LOGOUT
: string_equal(command_str, "/exit") && num_args == 0 ? EXIT
: ERROR;
}
/*
* auxiliary function used by parse_command.
*/
inline size_t extract_args(char *input,
args_t args) {
char *saveptr;
size_t num_args = 0;
while ((input = strtok_r(input, " \n\x0", &saveptr)) != NULL) { // and still tokens left to extract
num_args++;
if (num_args > MAX_USER_ARGNUM)
break;
args[num_args-1] = input;
input = NULL; // strtok needs to be called with a null pointer to keep searching.
}
return num_args;
}
/*
* returns 1 if ip_string is a valid IP address; else 0
*/
int is_valid_ip(char *ip_string) {
int ip[4];
int num_parsed = sscanf(ip_string, "%d.%d.%d.%d", ip+0, ip+1, ip+2, ip+3);
printf("ip_string: a%sa\n", ip_string);
if (num_parsed < 0) {
fprintf(stderr, "sscanf() error: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
else if (num_parsed != 4)
return string_equal(ip_string, "localhost");
for (int i = 0; i < 4; i++)
if (ip[i] > 255 || ip[i] < 0)
return 0;
return 1;
}
/*
* returns 1 if port_string is a valid port number; else 0
*/
int is_valid_port(char *port_string) {
int port;
int num_parsed = sscanf(port_string, "%d", &port);
if (num_parsed < 0) {
fprintf(stderr, "sscanf() error: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
else if (num_parsed != 1 || port < 0 || port > 65535)
return 0;
return 1;
}

67
A3/src/common.h Normal file
View File

@ -0,0 +1,67 @@
#pragma once
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include "csapp.h"
/*
* add macros and function declarations which should be
* common between the name server and peer programs in this file.
*/
#define USERNAME_LEN 32 // max length of a valid username.
#define MAX_LINE 128 // max length of a line of user input.
#define MAX_USER_ARGNUM 4 // max number of arguments to peer commands.
#define IP_LEN 16
#define PORT_LEN 8
/* slightly less awkward string equality check */
#define string_equal(x, y) (strncmp((x), (y), strlen(y)) == 0)
/* enum type used to represent client commands */
typedef enum command_t {
LOGIN,
LOGOUT,
EXIT,
LOOKUP,
MSG,
SHOW,
ERROR
} command_t;
/* string array type of size MAX_USER_ARGNUM used to hold arguments to a command */
typedef char *args_t[MAX_USER_ARGNUM];
/*
* given "input", a newline- or NULL-terminated line of
* user input containing command, followed by 0 or more
* space-separated arguments, and "args", an args_t array:
* - parses command and stores arguments sequentially in args.
* - returns a command_t code representing the command.
* - on an invalid number of arguments, ERROR is returned.
*
* example: if command is "/login anders topsecret 130.225.245.178 1337", then
* args is the array ["anders", "topsecret", "130.225.245.178", "1337"],
* and the function returns LOGIN.
* (if command is a /msg, then recipient username and actual message stored in args[0..1]).
*/
command_t parse_command(char *input,
args_t args);
/*
* extract arguments from a line of user input and store in args.
*/
size_t extract_args(char *input,
args_t args);
/*
* naive validity checks of IP addresses and port numbers given as strings,
* eg. at command line. both return zero on invalid IP/port, else non-zero.
*/
int is_valid_ip(char *ip_string);
int is_valid_port(char *port_string);

1076
A3/src/csapp.c Normal file

File diff suppressed because it is too large Load Diff

199
A3/src/csapp.h Normal file
View File

@ -0,0 +1,199 @@
/*
* csapp.h - prototypes and definitions for the CS:APP3e book
*/
/* $begin csapp.h */
#ifndef __CSAPP_H__
#define __CSAPP_H__
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <setjmp.h>
#include <signal.h>
#include <dirent.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <errno.h>
#include <math.h>
#include <pthread.h>
#include <semaphore.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
/* Default file permissions are DEF_MODE & ~DEF_UMASK */
/* $begin createmasks */
#define DEF_MODE S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
#define DEF_UMASK S_IWGRP|S_IWOTH
/* $end createmasks */
/* Simplifies calls to bind(), connect(), and accept() */
/* $begin sockaddrdef */
typedef struct sockaddr SA;
/* $end sockaddrdef */
/* Persistent state for the robust I/O (Rio) package */
/* $begin rio_t */
#define RIO_BUFSIZE 8192
typedef struct {
int rio_fd; /* Descriptor for this internal buf */
int rio_cnt; /* Unread bytes in internal buf */
char *rio_bufptr; /* Next unread byte in internal buf */
char rio_buf[RIO_BUFSIZE]; /* Internal buffer */
} rio_t;
/* $end rio_t */
/* External variables */
extern int h_errno; /* Defined by BIND for DNS errors */
extern char **environ; /* Defined by libc */
/* Misc constants */
#define MAXLINE 8192 /* Max text line length */
#define MAXBUF 8192 /* Max I/O buffer size */
#define LISTENQ 1024 /* Second argument to listen() */
/* Our own error-handling functions */
void unix_error(char *msg);
void posix_error(int code, char *msg);
void dns_error(char *msg);
void gai_error(int code, char *msg);
void app_error(char *msg);
/* Process control wrappers */
pid_t Fork(void);
void Execve(const char *filename, char *const argv[], char *const envp[]);
pid_t Wait(int *status);
pid_t Waitpid(pid_t pid, int *iptr, int options);
void Kill(pid_t pid, int signum);
unsigned int Sleep(unsigned int secs);
void Pause(void);
unsigned int Alarm(unsigned int seconds);
void Setpgid(pid_t pid, pid_t pgid);
pid_t Getpgrp();
/* Signal wrappers */
typedef void handler_t(int);
handler_t *Signal(int signum, handler_t *handler);
void Sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
void Sigemptyset(sigset_t *set);
void Sigfillset(sigset_t *set);
void Sigaddset(sigset_t *set, int signum);
void Sigdelset(sigset_t *set, int signum);
int Sigismember(const sigset_t *set, int signum);
int Sigsuspend(const sigset_t *set);
/* Sio (Signal-safe I/O) routines */
ssize_t sio_puts(char s[]);
ssize_t sio_putl(long v);
void sio_error(char s[]);
/* Sio wrappers */
ssize_t Sio_puts(char s[]);
ssize_t Sio_putl(long v);
void Sio_error(char s[]);
/* Unix I/O wrappers */
int Open(const char *pathname, int flags, mode_t mode);
ssize_t Read(int fd, void *buf, size_t count);
ssize_t Write(int fd, const void *buf, size_t count);
off_t Lseek(int fildes, off_t offset, int whence);
void Close(int fd);
int Select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
struct timeval *timeout);
int Dup2(int fd1, int fd2);
void Stat(const char *filename, struct stat *buf);
void Fstat(int fd, struct stat *buf) ;
/* Directory wrappers */
DIR *Opendir(const char *name);
struct dirent *Readdir(DIR *dirp);
int Closedir(DIR *dirp);
/* Memory mapping wrappers */
void *Mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset);
void Munmap(void *start, size_t length);
/* Standard I/O wrappers */
void Fclose(FILE *fp);
FILE *Fdopen(int fd, const char *type);
char *Fgets(char *ptr, int n, FILE *stream);
FILE *Fopen(const char *filename, const char *mode);
void Fputs(const char *ptr, FILE *stream);
size_t Fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
void Fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
/* Dynamic storage allocation wrappers */
void *Malloc(size_t size);
void *Realloc(void *ptr, size_t size);
void *Calloc(size_t nmemb, size_t size);
void Free(void *ptr);
/* Sockets interface wrappers */
int Socket(int domain, int type, int protocol);
void Setsockopt(int s, int level, int optname, const void *optval, int optlen);
void Bind(int sockfd, struct sockaddr *my_addr, int addrlen);
void Listen(int s, int backlog);
int Accept(int s, struct sockaddr *addr, socklen_t *addrlen);
void Connect(int sockfd, struct sockaddr *serv_addr, int addrlen);
/* Protocol independent wrappers */
void Getaddrinfo(const char *node, const char *service,
const struct addrinfo *hints, struct addrinfo **res);
void Getnameinfo(const struct sockaddr *sa, socklen_t salen, char *host,
size_t hostlen, char *serv, size_t servlen, int flags);
void Freeaddrinfo(struct addrinfo *res);
void Inet_ntop(int af, const void *src, char *dst, socklen_t size);
void Inet_pton(int af, const char *src, void *dst);
/* DNS wrappers */
struct hostent *Gethostbyname(const char *name);
struct hostent *Gethostbyaddr(const char *addr, int len, int type);
/* Pthreads thread control wrappers */
void Pthread_create(pthread_t *tidp, pthread_attr_t *attrp,
void * (*routine)(void *), void *argp);
void Pthread_join(pthread_t tid, void **thread_return);
void Pthread_cancel(pthread_t tid);
void Pthread_detach(pthread_t tid);
void Pthread_exit(void *retval);
pthread_t Pthread_self(void);
void Pthread_once(pthread_once_t *once_control, void (*init_function)());
/* POSIX semaphore wrappers */
void Sem_init(sem_t *sem, int pshared, unsigned int value);
void P(sem_t *sem);
void V(sem_t *sem);
/* Rio (Robust I/O) package */
ssize_t rio_readn(int fd, void *usrbuf, size_t n);
ssize_t rio_writen(int fd, void *usrbuf, size_t n);
void rio_readinitb(rio_t *rp, int fd);
ssize_t rio_readnb(rio_t *rp, void *usrbuf, size_t n);
ssize_t rio_readlineb(rio_t *rp, void *usrbuf, size_t maxlen);
/* Wrappers for Rio package */
ssize_t Rio_readn(int fd, void *usrbuf, size_t n);
void Rio_writen(int fd, void *usrbuf, size_t n);
void Rio_readinitb(rio_t *rp, int fd);
ssize_t Rio_readnb(rio_t *rp, void *usrbuf, size_t n);
ssize_t Rio_readlineb(rio_t *rp, void *usrbuf, size_t maxlen);
/* Reentrant protocol-independent client/server helpers */
int open_clientfd(char *hostname, char *port);
int open_listenfd(char *port);
/* Wrappers for reentrant protocol-independent client/server helpers */
int Open_clientfd(char *hostname, char *port);
int Open_listenfd(char *port);
#endif /* __CSAPP_H__ */
/* $end csapp.h */

480
A3/src/sha256.c Normal file
View File

@ -0,0 +1,480 @@
/*-
* Copyright (c) 2001-2003 Allan Saddi <allan@saddi.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY ALLAN SADDI AND HIS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL ALLAN SADDI OR HIS CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* $Id: sha256.c 680 2003-07-25 21:57:49Z asaddi $
*/
/*
* Define WORDS_BIGENDIAN if compiling on a big-endian architecture.
*
* Define SHA256_TEST to test the implementation using the NIST's
* sample messages. The output should be:
*
* ba7816bf 8f01cfea 414140de 5dae2223 b00361a3 96177a9c b410ff61 f20015ad
* 248d6a61 d20638b8 e5c02693 0c3e6039 a33ce459 64ff2167 f6ecedd4 19db06c1
* cdc76e5c 9914fb92 81a1c7e2 84d73e67 f1809a48 a497200e 046d39cc c7112cd0
*/
#ifdef HAVE_CONFIG_H
#include "clamav-config.h"
#endif /* HAVE_CONFIG_H */
#if HAVE_INTTYPES_H
# include <inttypes.h>
#else
# if HAVE_STDINT_H
# include <stdint.h>
# endif
#endif
#include <string.h>
#include "sha256.h"
#ifndef lint
static const char rcsid[] =
"$Id: sha256.c 680 2003-07-25 21:57:49Z asaddi $";
#endif /* !lint */
#define ROTL(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
#define ROTR(x, n) (((x) >> (n)) | ((x) << (32 - (n))))
#define Ch(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
#define Maj(x, y, z) (((x) & ((y) | (z))) | ((y) & (z)))
#define SIGMA0(x) (ROTR((x), 2) ^ ROTR((x), 13) ^ ROTR((x), 22))
#define SIGMA1(x) (ROTR((x), 6) ^ ROTR((x), 11) ^ ROTR((x), 25))
#define sigma0(x) (ROTR((x), 7) ^ ROTR((x), 18) ^ ((x) >> 3))
#define sigma1(x) (ROTR((x), 17) ^ ROTR((x), 19) ^ ((x) >> 10))
#define DO_ROUND() { \
t1 = h + SIGMA1(e) + Ch(e, f, g) + *(Kp++) + *(W++); \
t2 = SIGMA0(a) + Maj(a, b, c); \
h = g; \
g = f; \
f = e; \
e = d + t1; \
d = c; \
c = b; \
b = a; \
a = t1 + t2; \
}
static const uint32_t K[64] = {
0x428a2f98L, 0x71374491L, 0xb5c0fbcfL, 0xe9b5dba5L,
0x3956c25bL, 0x59f111f1L, 0x923f82a4L, 0xab1c5ed5L,
0xd807aa98L, 0x12835b01L, 0x243185beL, 0x550c7dc3L,
0x72be5d74L, 0x80deb1feL, 0x9bdc06a7L, 0xc19bf174L,
0xe49b69c1L, 0xefbe4786L, 0x0fc19dc6L, 0x240ca1ccL,
0x2de92c6fL, 0x4a7484aaL, 0x5cb0a9dcL, 0x76f988daL,
0x983e5152L, 0xa831c66dL, 0xb00327c8L, 0xbf597fc7L,
0xc6e00bf3L, 0xd5a79147L, 0x06ca6351L, 0x14292967L,
0x27b70a85L, 0x2e1b2138L, 0x4d2c6dfcL, 0x53380d13L,
0x650a7354L, 0x766a0abbL, 0x81c2c92eL, 0x92722c85L,
0xa2bfe8a1L, 0xa81a664bL, 0xc24b8b70L, 0xc76c51a3L,
0xd192e819L, 0xd6990624L, 0xf40e3585L, 0x106aa070L,
0x19a4c116L, 0x1e376c08L, 0x2748774cL, 0x34b0bcb5L,
0x391c0cb3L, 0x4ed8aa4aL, 0x5b9cca4fL, 0x682e6ff3L,
0x748f82eeL, 0x78a5636fL, 0x84c87814L, 0x8cc70208L,
0x90befffaL, 0xa4506cebL, 0xbef9a3f7L, 0xc67178f2L
};
#ifndef RUNTIME_ENDIAN
#if WORDS_BIGENDIAN == 1
#define BYTESWAP(x) (x)
#define BYTESWAP64(x) (x)
#else /* WORDS_BIGENDIAN */
#define BYTESWAP(x) ((ROTR((x), 8) & 0xff00ff00L) | \
(ROTL((x), 8) & 0x00ff00ffL))
#define BYTESWAP64(x) _byteswap64(x)
static inline uint64_t _byteswap64(uint64_t x)
{
uint32_t a = x >> 32;
uint32_t b = (uint32_t) x;
return ((uint64_t) BYTESWAP(b) << 32) | (uint64_t) BYTESWAP(a);
}
#endif /* WORDS_BIGENDIAN */
#else /* !RUNTIME_ENDIAN */
#define BYTESWAP(x) _byteswap(sc->littleEndian, x)
#define BYTESWAP64(x) _byteswap64(sc->littleEndian, x)
#define _BYTESWAP(x) ((ROTR((x), 8) & 0xff00ff00L) | \
(ROTL((x), 8) & 0x00ff00ffL))
#define _BYTESWAP64(x) __byteswap64(x)
static inline uint64_t __byteswap64(uint64_t x)
{
uint32_t a = x >> 32;
uint32_t b = (uint32_t) x;
return ((uint64_t) _BYTESWAP(b) << 32) | (uint64_t) _BYTESWAP(a);
}
static inline uint32_t _byteswap(int littleEndian, uint32_t x)
{
if (!littleEndian)
return x;
else
return _BYTESWAP(x);
}
static inline uint64_t _byteswap64(int littleEndian, uint64_t x)
{
if (!littleEndian)
return x;
else
return _BYTESWAP64(x);
}
static inline void setEndian(int *littleEndianp)
{
union {
uint32_t w;
uint8_t b[4];
} endian;
endian.w = 1L;
*littleEndianp = endian.b[0] != 0;
}
#endif /* !RUNTIME_ENDIAN */
static const uint8_t padding[64] = {
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
void
sha256_init (SHA256_CTX *sc)
{
#ifdef RUNTIME_ENDIAN
setEndian (&sc->littleEndian);
#endif /* RUNTIME_ENDIAN */
sc->totalLength = 0LL;
sc->hash[0] = 0x6a09e667L;
sc->hash[1] = 0xbb67ae85L;
sc->hash[2] = 0x3c6ef372L;
sc->hash[3] = 0xa54ff53aL;
sc->hash[4] = 0x510e527fL;
sc->hash[5] = 0x9b05688cL;
sc->hash[6] = 0x1f83d9abL;
sc->hash[7] = 0x5be0cd19L;
sc->bufferLength = 0L;
}
static void
burnStack (int size)
{
char buf[128];
memset (buf, 0, sizeof (buf));
size -= sizeof (buf);
if (size > 0)
burnStack (size);
}
static void
SHA256Guts (SHA256_CTX *sc, const uint32_t *cbuf)
{
uint32_t buf[64];
uint32_t *W, *W2, *W7, *W15, *W16;
uint32_t a, b, c, d, e, f, g, h;
uint32_t t1, t2;
const uint32_t *Kp;
int i;
W = buf;
for (i = 15; i >= 0; i--) {
*(W++) = BYTESWAP(*cbuf);
cbuf++;
}
W16 = &buf[0];
W15 = &buf[1];
W7 = &buf[9];
W2 = &buf[14];
for (i = 47; i >= 0; i--) {
*(W++) = sigma1(*W2) + *(W7++) + sigma0(*W15) + *(W16++);
W2++;
W15++;
}
a = sc->hash[0];
b = sc->hash[1];
c = sc->hash[2];
d = sc->hash[3];
e = sc->hash[4];
f = sc->hash[5];
g = sc->hash[6];
h = sc->hash[7];
Kp = K;
W = buf;
#ifndef SHA256_UNROLL
#define SHA256_UNROLL 4
#endif /* !SHA256_UNROLL */
#if SHA256_UNROLL == 1
for (i = 63; i >= 0; i--)
DO_ROUND();
#elif SHA256_UNROLL == 2
for (i = 31; i >= 0; i--) {
DO_ROUND(); DO_ROUND();
}
#elif SHA256_UNROLL == 4
for (i = 15; i >= 0; i--) {
DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND();
}
#elif SHA256_UNROLL == 8
for (i = 7; i >= 0; i--) {
DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND();
DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND();
}
#elif SHA256_UNROLL == 16
for (i = 3; i >= 0; i--) {
DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND();
DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND();
DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND();
DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND();
}
#elif SHA256_UNROLL == 32
for (i = 1; i >= 0; i--) {
DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND();
DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND();
DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND();
DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND();
DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND();
DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND();
DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND();
DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND();
}
#elif SHA256_UNROLL == 64
DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND();
DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND();
DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND();
DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND();
DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND();
DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND();
DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND();
DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND();
DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND();
DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND();
DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND();
DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND();
DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND();
DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND();
DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND();
DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND();
#else
#error "SHA256_UNROLL must be 1, 2, 4, 8, 16, 32, or 64!"
#endif
sc->hash[0] += a;
sc->hash[1] += b;
sc->hash[2] += c;
sc->hash[3] += d;
sc->hash[4] += e;
sc->hash[5] += f;
sc->hash[6] += g;
sc->hash[7] += h;
}
void
sha256_update (SHA256_CTX *sc, const void *vdata, uint32_t len)
{
const uint8_t *data = vdata;
uint32_t bufferBytesLeft;
uint32_t bytesToCopy;
int needBurn = 0;
#ifdef SHA256_FAST_COPY
if (sc->bufferLength) {
bufferBytesLeft = 64L - sc->bufferLength;
bytesToCopy = bufferBytesLeft;
if (bytesToCopy > len)
bytesToCopy = len;
memcpy (&sc->buffer.bytes[sc->bufferLength], data, bytesToCopy);
sc->totalLength += bytesToCopy * 8L;
sc->bufferLength += bytesToCopy;
data += bytesToCopy;
len -= bytesToCopy;
if (sc->bufferLength == 64L) {
SHA256Guts (sc, sc->buffer.words);
needBurn = 1;
sc->bufferLength = 0L;
}
}
while (len > 63L) {
sc->totalLength += 512L;
SHA256Guts (sc, data);
needBurn = 1;
data += 64L;
len -= 64L;
}
if (len) {
memcpy (&sc->buffer.bytes[sc->bufferLength], data, len);
sc->totalLength += len * 8L;
sc->bufferLength += len;
}
#else /* SHA256_FAST_COPY */
while (len) {
bufferBytesLeft = 64L - sc->bufferLength;
bytesToCopy = bufferBytesLeft;
if (bytesToCopy > len)
bytesToCopy = len;
memcpy (&sc->buffer.bytes[sc->bufferLength], data, bytesToCopy);
sc->totalLength += bytesToCopy * 8L;
sc->bufferLength += bytesToCopy;
data += bytesToCopy;
len -= bytesToCopy;
if (sc->bufferLength == 64L) {
SHA256Guts (sc, sc->buffer.words);
needBurn = 1;
sc->bufferLength = 0L;
}
}
#endif /* SHA256_FAST_COPY */
if (needBurn)
burnStack (sizeof (uint32_t[74]) + sizeof (uint32_t *[6]) + sizeof (int));
}
void
sha256_final (SHA256_CTX *sc, uint8_t hash[SHA256_HASH_SIZE])
{
uint32_t bytesToPad;
uint64_t lengthPad;
int i;
bytesToPad = 120L - sc->bufferLength;
if (bytesToPad > 64L)
bytesToPad -= 64L;
lengthPad = BYTESWAP64(sc->totalLength);
sha256_update (sc, padding, bytesToPad);
sha256_update (sc, &lengthPad, 8L);
if (hash) {
for (i = 0; i < SHA256_HASH_WORDS; i++) {
#ifdef SHA256_FAST_COPY
*((uint32_t *) hash) = BYTESWAP(sc->hash[i]);
#else /* SHA256_FAST_COPY */
hash[0] = (uint8_t) (sc->hash[i] >> 24);
hash[1] = (uint8_t) (sc->hash[i] >> 16);
hash[2] = (uint8_t) (sc->hash[i] >> 8);
hash[3] = (uint8_t) sc->hash[i];
#endif /* SHA256_FAST_COPY */
hash += 4;
}
}
}
#ifdef SHA256_TEST
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int
main (int argc, char *argv[])
{
SHA256_CTX foo;
uint8_t hash[SHA256_HASH_SIZE];
char buf[1000];
int i;
sha256_init (&foo);
sha256_update (&foo, "abc", 3);
sha256_final (&foo, hash);
for (i = 0; i < SHA256_HASH_SIZE;) {
printf ("%02x", hash[i++]);
if (!(i % 4))
printf (" ");
}
printf ("\n");
sha256_init (&foo);
sha256_update (&foo,
"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
56);
sha256_final (&foo, hash);
for (i = 0; i < SHA256_HASH_SIZE;) {
printf ("%02x", hash[i++]);
if (!(i % 4))
printf (" ");
}
printf ("\n");
sha256_init (&foo);
memset (buf, 'a', sizeof (buf));
for (i = 0; i < 1000; i++)
sha256_update (&foo, buf, sizeof (buf));
sha256_final (&foo, hash);
for (i = 0; i < SHA256_HASH_SIZE;) {
printf ("%02x", hash[i++]);
if (!(i % 4))
printf (" ");
}
printf ("\n");
exit (0);
}
#endif /* SHA256_TEST */

66
A3/src/sha256.h Normal file
View File

@ -0,0 +1,66 @@
/*-
* Copyright (c) 2001-2003 Allan Saddi <allan@saddi.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY ALLAN SADDI AND HIS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL ALLAN SADDI OR HIS CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* $Id: sha256.h 348 2003-02-23 22:12:06Z asaddi $
*/
#ifndef _SHA256_H
#define _SHA256_H
#include <stdint.h>
#define SHA256_HASH_SIZE 32
/* Hash size in 32-bit words */
#define SHA256_HASH_WORDS 8
struct _SHA256Context {
uint64_t totalLength;
uint32_t hash[SHA256_HASH_WORDS];
uint32_t bufferLength;
union {
uint32_t words[16];
uint8_t bytes[64];
} buffer;
#ifdef RUNTIME_ENDIAN
int littleEndian;
#endif /* RUNTIME_ENDIAN */
};
typedef struct _SHA256Context SHA256_CTX;
#ifdef __cplusplus
extern "C" {
#endif
void sha256_init (SHA256_CTX *sc);
void sha256_update (SHA256_CTX *sc, const void *data, uint32_t len);
void sha256_final (SHA256_CTX *sc, uint8_t hash[SHA256_HASH_SIZE]);
#ifdef __cplusplus
}
#endif
#endif /* !_SHA256_H */

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.