A4
This commit is contained in:
3
A3/src/group.txt
Normal file
3
A3/src/group.txt
Normal file
@ -0,0 +1,3 @@
|
||||
gsr530
|
||||
qhp695
|
||||
bfs115
|
BIN
A3/src/report.pdf
Normal file
BIN
A3/src/report.pdf
Normal file
Binary file not shown.
BIN
A3/src/src.zip
Normal file
BIN
A3/src/src.zip
Normal file
Binary file not shown.
64
A4/python/filegen/filegen.py
Normal file
64
A4/python/filegen/filegen.py
Normal 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!")
|
BIN
A4/python/filegen/shakespeare.100kib.txt.cascade
Normal file
BIN
A4/python/filegen/shakespeare.100kib.txt.cascade
Normal file
Binary file not shown.
BIN
A4/python/filegen/shakespeare.10kib.txt.cascade
Normal file
BIN
A4/python/filegen/shakespeare.10kib.txt.cascade
Normal file
Binary file not shown.
BIN
A4/python/filegen/shakespeare.10mib.txt.cascade
Normal file
BIN
A4/python/filegen/shakespeare.10mib.txt.cascade
Normal file
Binary file not shown.
BIN
A4/python/filegen/shakespeare.1kib.txt.cascade
Normal file
BIN
A4/python/filegen/shakespeare.1kib.txt.cascade
Normal file
Binary file not shown.
BIN
A4/python/filegen/shakespeare.1mib.txt.cascade
Normal file
BIN
A4/python/filegen/shakespeare.1mib.txt.cascade
Normal file
Binary file not shown.
124456
A4/python/filegen/shakespeare.txt
Normal file
124456
A4/python/filegen/shakespeare.txt
Normal file
File diff suppressed because it is too large
Load Diff
435
A4/python/peer/peer.py
Normal file
435
A4/python/peer/peer.py
Normal 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()
|
||||
|
||||
|
||||
|
||||
|
||||
|
124456
A4/python/peer/shakespeare.100kib.txt
Normal file
124456
A4/python/peer/shakespeare.100kib.txt
Normal file
File diff suppressed because it is too large
Load Diff
BIN
A4/python/peer/shakespeare.100kib.txt.cascade
Normal file
BIN
A4/python/peer/shakespeare.100kib.txt.cascade
Normal file
Binary file not shown.
124456
A4/python/peer/shakespeare.10kib.txt
Normal file
124456
A4/python/peer/shakespeare.10kib.txt
Normal file
File diff suppressed because it is too large
Load Diff
BIN
A4/python/peer/shakespeare.10kib.txt.cascade
Normal file
BIN
A4/python/peer/shakespeare.10kib.txt.cascade
Normal file
Binary file not shown.
124456
A4/python/peer/shakespeare.10mib.txt
Normal file
124456
A4/python/peer/shakespeare.10mib.txt
Normal file
File diff suppressed because it is too large
Load Diff
BIN
A4/python/peer/shakespeare.10mib.txt.cascade
Normal file
BIN
A4/python/peer/shakespeare.10mib.txt.cascade
Normal file
Binary file not shown.
124456
A4/python/peer/shakespeare.1kib.txt
Normal file
124456
A4/python/peer/shakespeare.1kib.txt
Normal file
File diff suppressed because it is too large
Load Diff
BIN
A4/python/peer/shakespeare.1kib.txt.cascade
Normal file
BIN
A4/python/peer/shakespeare.1kib.txt.cascade
Normal file
Binary file not shown.
124456
A4/python/peer/shakespeare.1mib.txt
Normal file
124456
A4/python/peer/shakespeare.1mib.txt
Normal file
File diff suppressed because it is too large
Load Diff
BIN
A4/python/peer/shakespeare.1mib.txt.cascade
Normal file
BIN
A4/python/peer/shakespeare.1mib.txt.cascade
Normal file
Binary file not shown.
14
A4/python/tracker/config.json
Normal file
14
A4/python/tracker/config.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"port": 8888,
|
||||
"ban-private": false,
|
||||
"allowedhashes": [
|
||||
"713a203b7e89753a8893f7d452e8bcc19de65024595f50ddf7899f1da9d95fff",
|
||||
"e29fcee1b3fbd186ae430f39e66661d7e0aec549998d63df438b51736fcf7a8e",
|
||||
"28bccac30019632135bed1e273d259b3c31ad135d92b9db907f120a1c801b40c",
|
||||
"729578b33120e7c9ba3c8a88cc6820cda3162adf8ce5294888ed62b435c5da7f",
|
||||
"81b831d167d74a11d4a95fd11c41e895edbec56d79e5a29b162217a805b539a6"
|
||||
],
|
||||
"good-ips": [
|
||||
"127.0.0.1"
|
||||
]
|
||||
}
|
193
A4/python/tracker/tracker.py
Normal file
193
A4/python/tracker/tracker.py
Normal 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
27
A4/src/Makefile
Normal 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
BIN
A4/src/cascade
Executable file
Binary file not shown.
634
A4/src/cascade.c
Normal file
634
A4/src/cascade.c
Normal 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
117
A4/src/cascade.h
Normal 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
97
A4/src/common.c
Normal 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
67
A4/src/common.h
Normal 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
1076
A4/src/csapp.c
Normal file
File diff suppressed because it is too large
Load Diff
199
A4/src/csapp.h
Normal file
199
A4/src/csapp.h
Normal 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
35
A4/src/endian.h
Normal 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
3
A4/src/group.txt
Normal file
@ -0,0 +1,3 @@
|
||||
gsr530
|
||||
qhp695
|
||||
bfs115
|
BIN
A4/src/report.pdf
Normal file
BIN
A4/src/report.pdf
Normal file
Binary file not shown.
480
A4/src/sha256.c
Normal file
480
A4/src/sha256.c
Normal 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
66
A4/src/sha256.h
Normal 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
124456
A4/src/shakespeare.txt
Normal file
File diff suppressed because it is too large
Load Diff
BIN
A4/src/src.zip
Normal file
BIN
A4/src/src.zip
Normal file
Binary file not shown.
124456
A4/src/tests/shakespeare.100kib.txt
Normal file
124456
A4/src/tests/shakespeare.100kib.txt
Normal file
File diff suppressed because it is too large
Load Diff
BIN
A4/src/tests/shakespeare.100kib.txt.cascade
Normal file
BIN
A4/src/tests/shakespeare.100kib.txt.cascade
Normal file
Binary file not shown.
BIN
A4/src/tests/shakespeare.10kib.txt.cascade
Normal file
BIN
A4/src/tests/shakespeare.10kib.txt.cascade
Normal file
Binary file not shown.
BIN
A4/src/tests/shakespeare.10mib.txt.cascade
Normal file
BIN
A4/src/tests/shakespeare.10mib.txt.cascade
Normal file
Binary file not shown.
124456
A4/src/tests/shakespeare.1kib.txt
Normal file
124456
A4/src/tests/shakespeare.1kib.txt
Normal file
File diff suppressed because it is too large
Load Diff
BIN
A4/src/tests/shakespeare.1kib.txt.cascade
Normal file
BIN
A4/src/tests/shakespeare.1kib.txt.cascade
Normal file
Binary file not shown.
124456
A4/src/tests/shakespeare.1mib.txt
Normal file
124456
A4/src/tests/shakespeare.1mib.txt
Normal file
File diff suppressed because it is too large
Load Diff
BIN
A4/src/tests/shakespeare.1mib.txt.cascade
Normal file
BIN
A4/src/tests/shakespeare.1mib.txt.cascade
Normal file
Binary file not shown.
Reference in New Issue
Block a user