This commit is contained in:
Nikolaj
2021-11-24 15:15:19 +01:00
parent a833af7f36
commit c89cbffb99
46 changed files with 1248070 additions and 0 deletions

3
A3/src/group.txt Normal file
View File

@ -0,0 +1,3 @@
gsr530
qhp695
bfs115

BIN
A3/src/report.pdf Normal file

Binary file not shown.

BIN
A3/src/src.zip Normal file

Binary file not shown.

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
A4/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()

27
A4/src/Makefile Normal file
View File

@ -0,0 +1,27 @@
GCC=gcc -O3 -g -Wall -Wextra -pedantic -std=gnu11
#GCC=gcc -O3
LD_FLAGS= -lpthread
all: 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)
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 cascade sha256 vgcore*

BIN
A4/src/cascade Executable file

Binary file not shown.

634
A4/src/cascade.c Normal file
View File

@ -0,0 +1,634 @@
#include <stdlib.h>
#include <stdio.h>
#include <netdb.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <math.h>
#ifdef __APPLE__
#include "./endian.h"
#else
#include <endian.h>
#endif
#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;
/*
* Frees global resources that are malloc'ed during peer downloads.
*/
void free_resources()
{
free(queue);
free(peers);
csc_free_file(casc_file);
}
/*
* Gets a sha256 hash of a specified file, sourcefile. The hash itself is
* placed into the given variable 'hash'. Any size can be created, but a
* a normal size for the hash would be given by the global variable
* 'SHA256_HASH_SIZE', that has been defined in sha256.h
*/
void get_file_sha(const char* sourcefile, uint8_t hash[32], int size)
{
int casc_file_size;
FILE* fp = fopen(sourcefile, "rb");
if (fp == 0)
{
printf("Failed to open source: %s\n", sourcefile);
return;
}
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);
get_data_sha(buffer, hash, casc_file_size, size);
}
csc_file_t* check_blocks(const char* destination, csc_file_t* casc_file_data) {
casc_file_data->completed = 1;
FILE* 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: %ld\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++)
{
uint8_t* shabuffer = malloc(sizeof(uint8_t) * 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);
if (memcmp((&(&casc_file_data->blocks[i])->hash)->x, shabuffer, 32) == 0) {
(&casc_file_data->blocks[i])->completed = 1;
} else {
(&casc_file_data->blocks[i])->completed = 0;
casc_file_data->completed = 0;
}
}
free(buffer);
if (casc_file_data->completed) {
rewind(fp);
buffer = malloc(casc_file_data->targetsize);
uint8_t* shabuffer = malloc(sizeof(uint8_t) * SHA256_HASH_SIZE);
fread(buffer, casc_file_data->targetsize, 1, fp);
sha256_init(&shactx);
sha256_update(&shactx, buffer, casc_file_data->targetsize);
sha256_final(&shactx, shabuffer);
if (!(memcmp((&casc_file_data->targethash)->x, shabuffer, 32) == 0)) {
casc_file_data->completed = 0;
for (unsigned long long i = 0;i < casc_file_data->blockcount;i++) {
(&casc_file_data->blocks[i])->completed = 0;
}
}
free(buffer);
}
fclose(fp);
return casc_file_data;
}
/*
* Gets a sha256 hash of specified data, sourcedata. The hash itself is
* placed into the given variable 'hash'. Any size can be created, but a
* a normal size for the hash would be given by the global variable
* 'SHA256_HASH_SIZE', that has been defined in sha256.h
*/
void get_data_sha(const char* sourcedata, unsigned char* hash, uint32_t data_size, int hash_size)
{
SHA256_CTX shactx;
uint8_t* shabuffer = malloc(sizeof(uint8_t) * hash_size);
sha256_init(&shactx);
sha256_update(&shactx, sourcedata, data_size);
sha256_final(&shactx, shabuffer);
for (int i=0; i<hash_size; i++)
{
hash[i] = shabuffer[i];
}
}
/*
* Perform all client based interactions in the P2P network for a given cascade file.
* E.g. parse a cascade file and get all the relevent data from somewhere else on the
* network.
*/
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);
int uncomp_count = 0;
queue = malloc(casc_file->blockcount * sizeof(csc_block_t*));
for (unsigned long long i = 0;i<casc_file->blockcount;i++) {
if ((&casc_file->blocks[i])->completed == 0) {
queue[uncomp_count] = &casc_file->blocks[i];
uncomp_count++;
}
}
uint8_t hash_buf[32];
get_file_sha(cascade_file, hash_buf, 32);
while (!casc_file->completed) {
int peercount = 0;
while (peercount == 0)
{
peercount = get_peers_list(&peers, hash_buf);
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);
}
printf("\n");
casc_file = check_blocks(output_file,casc_file);
}
printf("File fully downloaded\n");
free_resources();
}
/*
* Count how many times a character occurs in a string
*/
int count_occurences(char string[], char c)
{
size_t 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/
/*
* Convert a string of hexidecimal into a string of bytes
*/
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));
casc_file_data->targetsize = be64toh(*((unsigned long long*)&header[16]));
casc_file_data->blocksize = be64toh(*((unsigned long long*)&header[24]));
uint8_t x[32];
for (int i = 0; i < 32; i++) {
x[i] = (uint8_t)(*((unsigned long long*)&header[32+i]));
}
csc_hashdata_t* hash = malloc(sizeof(csc_hashdata_t));
memcpy(hash->x,x,SHA256_HASH_SIZE);
casc_file_data->targethash = *hash;
casc_file_data->blockcount = 1 + floor(
(casc_file_data->targetsize - 1.0)/casc_file_data->blocksize
);
casc_file_data->trailblocksize = casc_file_data->targetsize - (
(casc_file_data->blockcount - 1) * casc_file_data->blocksize
);
casc_file_data->completed = 0;
csc_block_t* block_list = malloc(
sizeof(csc_block_t) * casc_file_data->blockcount
);
casc_file_data->blocks = block_list;
for (unsigned long long b = 0;b < casc_file_data->blockcount; b++) {
csc_block_t* block = &(block_list[b]);
block->index = b;
block->offset = b * casc_file_data->blocksize;
if (b == casc_file_data->blockcount - 1 ) {
block->length = casc_file_data->trailblocksize;
} else {
block->length = casc_file_data->blocksize;
}
block->completed = 0;
uint8_t block_x[32];
if (fread(block_x, 1, 32, fp) != 32) {
printf("Cascade file not readable\n");
fclose(fp);
return NULL;
}
csc_hashdata_t* hash = malloc(sizeof(csc_hashdata_t));
memcpy(hash->x,block_x,SHA256_HASH_SIZE);
block->hash = *hash;
}
fclose(fp);
return check_blocks(destination, 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, uint8_t* buffer, size_t count)
{
size_t remaining = count;
while(remaining > 0)
{
size_t read = recv(sock, buffer, remaining, 0);
if (read == 0)
return 0;
buffer += read;
remaining -= read;
}
return count;
}
/*
* Get a specified block from a peer on the network. The block is retrieved and then inserted directly into
* the appropriate data file at the appropriate location.
*/
void get_block(csc_block_t* block, csc_peer_t peer, unsigned char* hash, char* output_file)
{
printf("Attempting to get block %ld from %s:%s for %s\n", block->index, peer.ip, peer.port, output_file);
int buffer_size;
if (block->length + PEER_RESPONSE_HEADER_SIZE > MAX_LINE) {
buffer_size = block->length + PEER_RESPONSE_HEADER_SIZE;
} else {
buffer_size = MAX_LINE;
}
char rio_buf[buffer_size];
rio_t rio;
int peer_socket;
peer_socket = Open_clientfd(peer.ip, peer.port);
Rio_readinitb(&rio, peer_socket);
struct ClientRequest request;
memcpy(request.protocol, "CASCADE1", 8);
for (int i = 0;i<16;i++) {
request.reserved[i] = 0;
}
request.block_num = be64toh(block->index);
memcpy(request.hash, hash, 32);
memcpy(rio_buf, &request, PEER_REQUEST_HEADER_SIZE);
Rio_writen(peer_socket, rio_buf, PEER_REQUEST_HEADER_SIZE);
Rio_readnb(&rio, rio_buf, buffer_size);
char reply_header[PEER_RESPONSE_HEADER_SIZE];
memcpy(reply_header, rio_buf, PEER_RESPONSE_HEADER_SIZE);
uint64_t msglen = be64toh(*((unsigned long long*)&reply_header[1]));
if (msglen == 0)
{
Close(peer_socket);
return;
}
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(peer_socket);
return;
}
memset(error_buf, 0, msglen + 1);
memcpy(error_buf, &rio_buf[PEER_RESPONSE_HEADER_SIZE], msglen);
printf("Peer gave error: %d - %s\n", reply_header[0], error_buf);
free(error_buf);
Close(peer_socket);
return;
}
uint8_t* shabuffer = malloc(sizeof(uint8_t) * SHA256_HASH_SIZE);
SHA256_CTX shactx;
sha256_init(&shactx);
sha256_update(&shactx, &rio_buf[PEER_RESPONSE_HEADER_SIZE], msglen);
sha256_final(&shactx, shabuffer);
if (memcmp(shabuffer, (&block->hash)->x, SHA256_HASH_SIZE) != 0) {
printf("Not the same hash\n");
Close(peer_socket);
return;
}
FILE* fp = fopen(output_file, "rb+");
if (fp == 0)
{
printf("Failed to open destination: %s\n", output_file);
Close(peer_socket);
return;
}
fseek(fp, block->offset, SEEK_SET);
fwrite(&rio_buf[PEER_RESPONSE_HEADER_SIZE],msglen,1,fp);
printf("Got block %ld. Wrote from %ld to %ld\n", block->index, block->offset, block->offset+msglen-1);
Close(peer_socket);
fclose(fp);
}
/*
* Get a list of peers on the network from a tracker. Note that this query is doing double duty according to
* the protocol, and by asking for a list of peers we are also enrolling on the network ourselves.
*/
int get_peers_list(csc_peer_t** peers, uint8_t hash[32])
{
rio_t rio;
uint8_t rio_buf[MAX_LINE];
int tracker_socket;
tracker_socket = Open_clientfd(tracker_ip, tracker_port);
Rio_readinitb(&rio, tracker_socket);
struct RequestHeader request_header;
memcpy(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);
struct RequestBody request_body;
memcpy(request_body.hash, hash, 32);
inet_aton(my_ip, &request_body.ip);
request_body.port = be16toh(atol(my_port));
memcpy(&rio_buf[HEADER_SIZE], &request_body, BODY_SIZE);
Rio_writen(tracker_socket, rio_buf, MESSAGE_SIZE);
Rio_readnb(&rio, rio_buf, MAX_LINE);
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 0;
}
memset(error_buf, 0, msglen + 1);
memcpy(error_buf, &rio_buf[REPLY_HEADER_SIZE], msglen); // Fixed by Rune
printf("Tracker gave error: %d - %s\n", reply_header[0], error_buf);
free(error_buf);
Close(tracker_socket);
return 0;
}
if (msglen % 12 != 0)
{
printf("LIST response from tracker was length %u but should be evenly divisible by 12\n", msglen);
Close(tracker_socket);
return 0;
}
int peercount = msglen/12;
*peers = malloc(peercount * sizeof(csc_peer_t));
for (int i = 0;i<peercount;i++) {
csc_peer_t peer;
uint8_t* peer_data = &rio_buf[REPLY_HEADER_SIZE + 12*i];
sprintf(peer.ip, "%u.%u.%u.%u", peer_data[0], peer_data[1], peer_data[2], peer_data[3]);
sprintf(peer.port, "%u", be16toh(*((uint16_t*)&peer_data[4])));
(*peers)[i] = peer;
}
Close(tracker_socket);
return peercount;
}
/*
* The entry point for the code. Parses command line arguments and starts up the appropriate peer code.
*/
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);
}

117
A4/src/cascade.h Normal file
View File

@ -0,0 +1,117 @@
#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
{
uint8_t hash[32];
struct in_addr ip;
unsigned short port;
};
struct ClientRequest
{
char protocol[8];
char reserved[16];
uint64_t block_num;
uint8_t 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
uint8_t completed;
} 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;
/*
* 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);
int get_peers_list(csc_peer_t** peers, uint8_t hash[32]);
void get_block(csc_block_t* block, csc_peer_t peer, unsigned char* hash, char* output_file);
void get_data_sha(const char* sourcedata, unsigned char* hash, uint32_t data_size, int hash_size);
#endif

97
A4/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
A4/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
A4/src/csapp.c Normal file

File diff suppressed because it is too large Load Diff

199
A4/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 */

35
A4/src/endian.h Normal file
View File

@ -0,0 +1,35 @@
#ifndef __FINK_ENDIANDEV_PKG_ENDIAN_H__
#define __FINK_ENDIANDEV_PKG_ENDIAN_H__ 1
#include <machine/endian.h>
/** compatibility header for endian.h
* This is a simple compatibility shim to convert
* BSD/Linux endian macros to the Mac OS X equivalents.
* It is public domain.
* */
#ifndef __APPLE__
#warning "This header file (endian.h) is MacOS X specific.\n"
#endif /* __APPLE__ */
#include <libkern/OSByteOrder.h>
#define htobe16(x) OSSwapHostToBigInt16(x)
#define htole16(x) OSSwapHostToLittleInt16(x)
#define be16toh(x) OSSwapBigToHostInt16(x)
#define le16toh(x) OSSwapLittleToHostInt16(x)
#define htobe32(x) OSSwapHostToBigInt32(x)
#define htole32(x) OSSwapHostToLittleInt32(x)
#define be32toh(x) OSSwapBigToHostInt32(x)
#define le32toh(x) OSSwapLittleToHostInt32(x)
#define htobe64(x) OSSwapHostToBigInt64(x)
#define htole64(x) OSSwapHostToLittleInt64(x)
#define be64toh(x) OSSwapBigToHostInt64(x)
#define le64toh(x) OSSwapLittleToHostInt64(x)
#endif /* __FINK_ENDIANDEV_PKG_ENDIAN_H__ */

3
A4/src/group.txt Normal file
View File

@ -0,0 +1,3 @@
gsr530
qhp695
bfs115

BIN
A4/src/report.pdf Normal file

Binary file not shown.

480
A4/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
A4/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 */

124456
A4/src/shakespeare.txt Normal file

File diff suppressed because it is too large Load Diff

BIN
A4/src/src.zip Normal file

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

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.