-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathserver.py
404 lines (303 loc) · 9.92 KB
/
server.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
# first of all import the socket library
import socket
import signal
import sys
import os
import time
import helperFunctions as helper
import ledgerFunctions as ledger
import lock
import encryption
import time
import threading
from _thread import *
#
# SERVER ERRORS
# 100 - Invaalid client request
# 102 - File doesnt exist on server
# 123 - Lock failure, server currently locked and cannot perform write/update
# 124 - Lock failure, server already locked by another client
# 125 - Lock failure, server not locked before write
# 126 - Lock failure, server not locked before ledger update
# 145 - Server unable to decrypt command
#
# macro for min bytes
BYTES_TO_SEND = 1024
CLIENTS_ALLOWED = 5
REQUEST_MAX_LENGTH = 128
# global socket declaration
s = socket.socket()
#
# Catch the ctrl+c signal
#
def handle_interrupt():
s.close()
print(" Server out...")
sys.exit(0)
#
# Function to establish connection and send or recieve file
#
def run_server():
# starting up server
print("Starting up server.....")
# reserve a port on your computer
port = 12345
# get the current hostname and ip
hostName = socket.gethostname()
# hostIp = socket.gethostbyname(hostName)
# bind the port with the socket
s.bind(("", port))
print("Server running on", port)
# put the socket into listening mode to connect
s.listen(CLIENTS_ALLOWED)
print("Socket is listening")
# a forever loop until we interrupt it or error occurs
while True:
# establish connection with client.
c, addr = s.accept()
# terminal output
print("*************************************")
print('Got connection from', addr)
# recieve request and call relevant function
ip = str(addr[0])
start_new_thread(get_request, (c,ip))
# terminal output
print("*************************************")
#
# Function to check request format
#
def check_request(request):
# should be able to convert to string
try:
str(request)
except:
return False
# have to have at most two strings separated
if (len(request.split()) != 2):
return False
# have only pull or push request
if (request.split()[0].lower() not in ["pull", "push", "pull_ledger", "update_ledger", "lock", "load_balance"]):
return False
return True
#
# Send error to client
#
def send_error(c, errorMessage):
error = helper.pad_string(errorMessage)
c.send(error.encode())
print(error)
#
# Function to recieve requests from the client
#
def get_request(c, ip):
# read the request from the client
encrypted_request = c.recv(REQUEST_MAX_LENGTH)
# make sure the command sent is encrypted using the servers pubkey
try:
request = encryption.decrypt_using_private_key(encrypted_request).decode()
except:
send_error(c, "Error 145: Server unable to decrypt command")
print("Error in decryption")
return
# check error and send back if error
if (check_request(request) == False):
send_error(c, "Error 100: Invalid request format from client")
return
# get type and filename from request
[requestType, filename] = str(request).lower().split()
print(requestType, filename)
# send request
if (requestType == "pull"):
send_file(c, filename)
# recieve request
elif (requestType == "push"):
receive_file(c, filename, ip)
# send a copy of the ledger to the client
elif (requestType == "pull_ledger"):
send_ledger(c, ip)
# send a copy of the ledger to the client
elif (requestType == "update_ledger"):
update_ledger(c, filename, ip)
elif (requestType == "lock"):
lock_server(c, ip)
elif (requestType == "load_balance"):
load_balance(c)
#
# Load balance all the files this ip has
#
def load_balance(c):
# get the servers current ip
ip = helper.find_ip()
# get the list of files
list_of_files = ledger.get_files_for_owner(ip)
# send confirmation
c.send(helper.pad_string("Server is ready load balance its files").encode())
# call the clients to get all the files locally and push them back
for file in list_of_files:
# run a os call to get the file
os.system("python3 client.py pull " + file)
# run an os call to send back the file to all the network
os.system("python3 client.py push " + file)
# remove the local copy
os.system("rm directory/" + file)
# send confirmation
c.send(helper.pad_string("Server has done load balancing its files").encode())
#
# Lock a server to prevent multiple writes from occuring at the same time
#
def lock_server(c, ip):
if lock.unlocked():
lock.acquire(ip)
print("Server locked by ", ip)
c.send(helper.pad_string("Server locked").encode())
else:
send_error("Error 124: Server already locked")
#
# Function to update the ledger with the new stuff to the client
#
def update_ledger(c, filename, ip):
if(ip == helper.find_ip()):
print("Same server as client")
c.send(helper.pad_string("Server doesnt need your ledger").encode())
if lock.locked():
lock.release()
return
if lock.locked() and not lock.check_lock(ip):
send_error(c, "Error 123: Server currently busy")
return
# start with the time
start = time.time()
# open a temporary file to store the received bytes
file = open(filename, 'wb')
byte = 0
# send confirmation
c.send(helper.pad_string("Server is ready to update its ledger").encode())
while True:
# receive 1024 bytes at a time and write them to a file
bytes = c.recv(BYTES_TO_SEND)
bytes = encryption.decrypt_using_private_key(bytes)
file.write(bytes)
byte += BYTES_TO_SEND
# break infinite loop once all bytes are transferred
if not bytes:
break
# close the file once transfer is complete
file.close()
# time and space prints
end = time.time()
print("Finished running download of file %s in %.2f seconds" % (filename, float(end - start)))
print(byte, "bytes sent")
# Release the lock if one is present
if lock.locked() and lock.check_lock(ip):
print("Server lock released by ", lock.return_lock())
lock.release()
#
# Function to send the ledger to the new client
#
def send_ledger(c, ip):
start = time.time()
# open the ledger if it exists
try:
f = open(ledger.LEDGER_PATH, 'rb')
except:
send_error(c, "Error 176: Ledger doesn't exist on server machine")
c.close()
return
# send confirmation in plaintext
c.send(helper.pad_string("Server is ready to send ledger").encode())
# initialize an empty bytestring for the new pubkey
encryptedClientPubkey = b''
# client pubkey is received in 2 parts
for i in range(2):
encryptedClientPubkey += c.recv(REQUEST_MAX_LENGTH)
# decrypt the client pubkey
clientPubkey = encryption.decrypt_using_private_key(encryptedClientPubkey).decode()
# read bytes and set up counter
l = f.read(encryption.MESSAGE_CHUNK_LIMIT)
byte = BYTES_TO_SEND
# a forever loop untill file gets sent
while (l):
encrypted_l = encryption.encrypt_using_public_key(l, clientPubkey)
# send the bytes
c.send(encrypted_l)
# read more bytes and incrementing counter
l = f.read(encryption.MESSAGE_CHUNK_LIMIT)
byte += BYTES_TO_SEND
# time and space prints
end = time.time()
print("Sent ledger in %.2f seconds" % float(end - start))
print(byte, "bytes sent")
# close the connection with the client
c.close()
#
# Function to send out bytes of data from filename
#
def send_file(c, filename):
start = time.time()
# opening a file if possible
try:
f = open("fico/" + filename, 'rb')
except:
send_error(c, "Error 102: File doesn't exist on server machine")
c.close()
return
# send confirmation
print("Server is ready to send file")
c.send(helper.pad_string("Server is ready to send file").encode())
# read bytes and set up counter
l = f.read(BYTES_TO_SEND)
byte = BYTES_TO_SEND
# a forever loop untill file gets sent
while (l):
# send the bytes
c.send(l)
# read more bytes and incrementing counter
l = f.read(BYTES_TO_SEND)
byte += BYTES_TO_SEND
# time and space prints
end = time.time()
print("Finished running download of file in %.2f seconds" % float(end - start))
print(byte, "bytes sent")
time.sleep(0.5)
# Close the connection with the client
c.close()
#
# Receives a file
#
def receive_file(c, filename, ip):
if lock.locked() and not lock.check_lock(ip):
send_error(c, "Error 123: Server currently busy")
return
if lock.unlocked():
send_error(c, "Error 125: Server needs to be locked before write")
return
start = time.time()
# open a temporary file to store the received bytes
try:
file = open("fico/" + filename, 'wb')
except:
os.system("mkdir fico")
file = open("fico/" + filename, 'wb')
byte = 0
# send confirmation
c.send(helper.pad_string("Server is ready to recieve file").encode())
while True:
# receive 1024 bytes at a time and write them to a file
bytes = c.recv(BYTES_TO_SEND)
file.write(bytes)
byte += BYTES_TO_SEND
# break infinite loop once all bytes are transferred
if not bytes:
break
# close the file once transfer is complete
file.close()
# time and space prints
end = time.time()
print("Finished running download of file %s in %.2f seconds" % (filename, float(end - start)))
print(byte, "bytes sent")
try:
# calls the main function to run the server
run_server()
# handling the keyboard interrupt
except KeyboardInterrupt:
handle_interrupt()