Only byte strings can be passed to C code. python paramiko - python

I have a simple client-server program that uses python Paramiko.
The client sends an encrypted string to the server and the server needs to decrypt it to process it.
This is the client:
def collect_stats():
try:
cpu = psutil.cpu_percent(interval=1)
memory = psutil.virtual_memory().percent
disk = psutil.disk_usage('/').percent
str_to_send_back = "{} {} {}".format(cpu, memory, disk).strip()
str_to_send_back = str_to_send_back.encode()
str_to_send_back = encrypt(str_to_send_back)
print(str_to_send_back)
The server picks up the string and tries to decode it (at the bottom of this code snipper):
class server:
command='cd %temp% && cd' # Necessary to pull client temp directory path
run_client_script = 'cd %temp% && python client.py' # To run client script
def __init__(self, client_ip, cliet_port, username, password):
self.client_ip = client_ip
self.cliet_port = cliet_port
self.username = username
self.password = password
self.ssh = ssh(self.client_ip, self.username, self.password)
def upload_script_and_get_stats(self):
try:
# Built temp directory path for client machine
client_temp_dir_location = str(self.ssh.send_command(self.command)).strip()
# Client script file name
file_name = "\\client.py"
# Build absolute path of source file
source_file_location = os.path.dirname(os.path.abspath(__file__)) + file_name
# Build absolute path of destination file
destination_file_location = client_temp_dir_location + file_name
# Upload client script
ftp_client = self.ssh.open_sftp(source_file_location, destination_file_location)
# Run client script and get result
result = self.ssh.send_command(self.run_client_script)
# SERVER DECODES STRING HERE
result = self.decrypt(result)
return str(result)
except Exception as e:
print("Oops this error occured in transfer_files_to_client(): " + str(e))
finally:
self.ssh.close()
def decrypt(self, ciphertext):
try:
print(ciphertext)
obj2 = AES.new(b'This is a key123', AES.MODE_CFB, b'This is an IV456')
return obj2.decrypt(ciphertext)
except Exception as e:
print("FDSDSFDSF: "+str(e))
However, the decode method in server throws this error:
FDSDSFDSF: Only byte strings can be passed to C code
However what gets passed to the method is this:
b'\xb5\xf7\xbc\xd5\xfc\xff;\x83\xd3\xab\xb1mc\xc3'
which is already encoded.

Your ciphertext is a string that contains the string representation of a bytes object. I assume your server and client are running on Python 3 and so
print(str_to_send_back)
in the client just prints the representation of the bytes object to standard output. Try using some string friendly encoding for your bytes, such as base64:
from base64 import b64encode
def collect_stats():
try:
...
# str_to_send_back is actually bytes
# Decode the bytes that contain ASCII text for printing
print(b64encode(str_to_send_back).decode('ascii'))
and decode in the receiving end:
from base64 import b64decode
def decrypt(self, ciphertext):
# Removed the over eager "exception handling". The traceback is a
# lot more informative and useful. Add *specific* exception handling
# as necessary
aes = AES.new(b'This is a key123', AES.MODE_CFB, b'This is an IV456')
return aes.decrypt(b64decode(ciphertext))

Related

send file using socket python

I wrote a function to send a file using a socket. It sends first the size of the file and then sends blocks of 9999 bytes to the client until the file ends. the problem is when the server reads the file, the file ends too fast. for example, for a file with 180,000 bytes, it ends after 2 blocks. I couldn't understand why this happens and I tried to run the read on this file in the terminal and it worked fine but here it doesn't. As much as I know the problem is supposed to be in the sever because when I checked, the server doesn't send all the data he is supposed to send because after about 2 read() calls it returns an empty string even though most of the file haven't been read.
here is the server code:
MAX_LENGTH_OF_SEND = 9999
def send_file(path, client_socket):
"""send a file to the client. if succeeded return success, else return error massage
:param path: path of the file to send
:param client_socket: the socket of the client to send to
:return: if success return 'success sending a file' else, return error massage
"""
try:
size = os.stat(path).st_size
print(size)
# send the length of the file to the client
send(str(size), client_socket)
read = 0
with open(path, "rb") as file1:
massage = file1.read(MAX_LENGTH_OF_SEND)
read += MAX_LENGTH_OF_SEND
while read < size:
binary_send(massage, client_socket)
massage = file1.read(MAX_LENGTH_OF_SEND)
read += MAX_LENGTH_OF_SEND
print(f"The file {path} has been sent")
except Exception as e:
print(f"Couldn't send file because {e}")
return "Error: " + str(e)
return "success sending a file"
def send(msg, client_socket):
""" sending a massage to a client by sending the length of the massage first
:param msg: the massage to send
:param client_socket: the client to send the massage
"""
client_socket.send(str(len(msg)).encode())
client_socket.send(msg.encode())
def binary_send(msg, client_socket):
""" sending a massage to a client by sending the length of the massage first without encoding the massage
:param msg: the massage to send
:param client_socket: the client to send the massage
"""
print(f"len = {str(len(msg)).encode()}")
client_socket.send(str(len(msg)).encode())
client_socket.send(msg)
Here is the client code:
data = receive_msg(my_socket)
if data.startswith("Error:"):
print(data)
else:
if SENT_PICTURE_PATH.endswith("\\"):
path = SENT_PICTURE_PATH + request[1]
else:
path = SENT_PICTURE_PATH + "\\" + request[1]
if not os.path.exists(path):
os.makedirs(path)
with open(path, "wb") as file:
size = int(data)
given = 0
error = False
while not error and given < size:
length = int(my_socket.recv(4).decode())
print(length)
data = my_socket.recv(length)
print(data)
try:
if data.decode().startswith("Error:"):
error = True
print(data.decode())
except UnicodeDecodeError or ValueError:
pass
if not error:
file.write(data)
size += length
def receive_msg(my_socket):
"""receive a massage from the server with a length of 99 max
:param my_socket: the socket to get from
:return: the massage
"""
length = int(my_socket.recv(2).decode())
return my_socket.recv(length).decode()

Error 65537 while instatiating the CBC mode

I have been using the PyCryptoDome library to encrypt some communications between a client and a server. Here is how the protocol goes.
The server sends an RSA public key to the client.
The client generates an AES key
The client encrypts it. encrypts it using the public key (with the PKCS1_OAEP encrypt function)
The encrypted key is sent to the server.
The server decrypts the key
The server and client switch to AES-CBC to encrypt communications.
After that, when the client sends a message, the server decrypts it, uses it for what needs to be done with it, and then sends it back. All goes well until the client tries to decrypt the message that is sent back by the server. The thread for receiving messages stops, due to:
Error 65537 while instatiating the CBC mode
Different error codes happen when using different AES modes.
Here is my modified AESCipher Class:
import binascii
from Crypto import Random
from Crypto.Cipher import AES
class AESCipher(object):
def __init__(self, key):
self.key = key
self.pad = lambda s: s + (AES.block_size - len(s) % AES.block_size) * \
chr(AES.block_size - len(s) % AES.block_size)
self.unpad = lambda s: s[:-ord(s[len(s) - 1:])]
def encrypt(self, raw):
iv = Random.new().read(AES.block_size)
cipher = AES.new(self.key, AES.MODE_CBC, iv)
return binascii.b2a_hex(iv + cipher.encrypt(self.pad(raw)))
def decrypt(self, enc):
enc = binascii.a2b_hex(enc)
iv = enc[:AES.block_size]
cipher = AES.new(self.key, AES.MODE_CBC, iv)
return self.unpad(cipher.decrypt(enc[AES.block_size:]))
Client receiving side (the error-causing side):
def recvMesgThread(netcat, output, aescipher):
while True:
try:
data = netcat.recv_until('\r\n').replace('\r\n', '')
except NetcatError:
print('Lost connection to server!')
sys.exit(0)
if data[:5] == '/MESG' and data[-5:] =='MESG/' :
try:
output.append(aescipher.decrypt(buf[5:-5]))
except Exception as e:
print(e)
Well, this is embarrassing...
The problem was a typo. Line 11 tries to decrypt the buf variable, which doesn't exist. The data variable was the one that contained what I needed.

SSH/SCP through Paramiko with key in string

I got a great Paramiko Python script to transfer file over SCP protocol. But My need is a single file (script.py) without any other file dependencies so I don't want to have an external file for my SSH private key.
What I'm trying to do is to embed the private key into a string variable and use this variable as the file needed by the script to connect. I tried with the StringIO library but it doesn't seems to work :
hostname = '1.1.1.1' # remote hostname where SSH server is running
port = 22
username = 'user'
password = 'pswd'
dir_local='/home/paramikouser/local_data'
dir_remote = "remote_machine_folder/subfolder"
glob_pattern='*.*'
import os
import glob
import paramiko
import md5
import StringIO
private_key = StringIO.StringIO('-----BEGIN RSA PRIVATE KEY-----\n-----END RSA PRIVATE KEY-----')
def agent_auth(transport, username):
"""
Attempt to authenticate to the given transport using any of the private
keys available from an SSH agent or from a local private RSA key file (assumes no pass phrase).
"""
try:
ki = paramiko.RSAKey.from_private_key_file(private_key)
except Exception, e:
print 'Failed loading' % (private_key, e)
agent = paramiko.Agent()
agent_keys = agent.get_keys() + (ki,)
if len(agent_keys) == 0:
return
for key in agent_keys:
print 'Trying ssh-agent key %s' % key.get_fingerprint().encode('hex'),
try:
transport.auth_publickey(username, key)
print '... success!'
return
except paramiko.SSHException, e:
print '... failed!', e
# get host key, if we know one
hostkeytype = None
hostkey = None
files_copied = 0
try:
host_keys = paramiko.util.load_host_keys(os.path.expanduser('~/.ssh/known_hosts'))
except IOError:
try:
# try ~/ssh/ too, e.g. on windows
host_keys = paramiko.util.load_host_keys(os.path.expanduser('~/ssh/known_hosts'))
except IOError:
print '*** Unable to open host keys file'
host_keys = {}
if host_keys.has_key(hostname):
hostkeytype = host_keys[hostname].keys()[0]
hostkey = host_keys[hostname][hostkeytype]
print 'Using host key of type %s' % hostkeytype
# now, connect and use paramiko Transport to negotiate SSH2 across the connection
try:
print 'Establishing SSH connection to:', hostname, port, '...'
t = paramiko.Transport((hostname, port))
t.start_client()
agent_auth(t, username)
if not t.is_authenticated():
print 'RSA key auth failed! Trying password login...'
t.connect(username=username, password=password, hostkey=hostkey)
else:
sftp = t.open_session()
sftp = paramiko.SFTPClient.from_transport(t)
# dirlist on remote host
# dirlist = sftp.listdir('.')
# print "Dirlist:", dirlist
How can I use this string as a key?
Use RSAKey.from_private_key:
ki = paramiko.RSAKey.from_private_key(private_key)
See How do use paramiko.RSAKey.from_private_key()?
The answer on the above question shows code for Python 3.
In Python 2.7, this works:
import os
import glob
import paramiko
import StringIO
private_key_file = StringIO.StringIO()
private_key_file.write('-----BEGIN RSA PRIVATE KEY-----\nMIIEoQIBAAKCAQEAvG9YlF2da0jJ5PvvlmVnVnYYFc7kkJuC0wvsACVuvep/sds5\nIEX0e+/rq9UBj/V3rzsvbHzb6IVulSjEqcM32NA4SyqR1m5jAj/WVDXQcxzruBDO\nZbdNhDS1T4+HckTWzttAE4o83bRju+3BhR9CtrDtt+7CSei4MccSMEH7yxo1BGuL\nONfkhB6qAWh55T6tamTyjLg9R9xqBkG6x3ZmoOB9j/11P5awuUoE1DfbqQ3KMLSR\nr64unavECfBaBYvuxlWbxRJFAN8C95oE+Kbc1g1bEL9du6FIeHZ/eaBbkcl84Fm7\nswdHBnd7+tqfo4TGzvbEW4H2ZQLiuiGK23ao0wIBJQKCAQEAiYGvV4KVd81VDuFb\nzp0GOCypyrmSCKjVFowowdYgYRLnj5/5QRB0IxbcaKJbFgYm56e60qBNclOIC/sn\nuiasNm5uRLBclY7SoMbM1aq6tN3AxJakc70c4+8chip3mJMZStdYRZw6QOtrX5+o\n5JpFcI7yqNDS96nShTBnN/jMf3K6yjQjvTv/DJi9SHJ6dTtrY1AuUNEoiO3cwgeH\nFks169756L+fpweL4VjQl4UyClL0bwHWpe579XzjBV0AlGu1tHaE5zslTPtGw1lg\nnZhj/7skZKAIGQxIzfmGv4QEcvePKYzM8EUhOr/0O3BHjLC0lp5hMwmsPJfaHlMb\nBak0JQKBgQD0cRu65WNkCcRlpuUvp5/kiMvu7PmcFUsY6dMeV0bL4oQ+PCqfwXFj\nhkyS7V2DJnllYPwi6E68soie+IL1blmY7hWcoznJ48PWJ0bJmqBgzhpC623RtTKS\ny/O0IbrGKPpaRGfD/PAvqJOpwx7Im2k6/UVQ0OYSurC8CB3BDRTCXQKBgQDFWEq7\ny2SntPFA9zu+31bW57lb26l8nNmUXmRLnXyvqomAkCGSadiW/i5nBEBDV/zJ/rXp\n0QWrmrpfvjnMF6g26m4sj6Pfs5zoSV1+FEidqYDcytUPJnpR55Ulpshf57TGuFbx\n1SCnda1dmm3TzAzzKTc6MbSPV0krMyAgCP7E7wKBgFXijTPUDioRRQEe9pQz+eiD\nFzhFbHUcPPrqXu78EfSbsexaVCpK4qZtdNmtWDT/rhyzX4Hi6zthUpi4LgM0nAVM\nu3w5WX5JG0s+O3dEKoLgoXF1UBk/qfw50iqIZDfJNV38W889McuOQbgvzIusObrH\nsJIENSks1k/nLQx6N7npAoGAUAEzDdzVx3LeWJuUwwCY06oM4Azxrw8nxochvco5\nd6YAZI11ZN7NbaVRFQG5MA7p8QZlbKDYyQdgUFQJl+3qP8bSuB6Oix9Ncu1Panbt\nAaWVGz14+E3ej+hDYkqIlZVJSaStoE978NyuETDEvaXAD40/5yjoVclwsKYGGtM2\n2jcCgYA6v1tvd2QdDeijiSRnXAeJ1hDLB8Jj2WJqnDZ7dQ5+XTIKfY4POIpHCPx8\n6Uk4NCnyJGmBHog1M7Bjb/o0c1UTid6CNBI4ciVaRyXXcy6Czup2EhkiNGom2883\n8+9pdxShKf0pJCqdZxJdVmg1NHZnr20PwN7PASbVcRg3t+wt2g==\n-----END RSA PRIVATE KEY-----')
private_key_file.seek(0)
ki = paramiko.RSAKey.from_private_key(private_key_file)

Django ssh failed during sftp connection

This is the error am getting
ri#ri-desktop:~/workspace/ssh$ python ssh.py
Establishing SSH connection to: upload.partner.com 19321 ...
Failed loading ~/workspace/ssh/rsa_private_key
No handlers could be found for logger "paramiko.transport"
Trying ssh-agent key b747f6899b3a450e63bc6faab1625686 ... failed! key cannot be used for signing
*** Caught exception: <type 'exceptions.AttributeError'>: 'NoneType' object has no attribute 'get_fingerprint'
============================================================
Total files copied: 0
All operations complete!
============================================================
ri#ri-desktop:~/workspace/ssh$
This is my setup code. i took this setup code from activstate . I given correct path only and my doubt is firstly the error showing Failed loading ~/workspace/ssh/rsa_private_key. but its showing some ssh-agent key failed what is that?
hostname = 'upload.partner.com' # remote hostname where SSH server is running
port = 19321
username = 'music--test'
password = 'grg'
rsa_private_key = r"~/workspace/ssh/rsa_private_key"
dir_local='~/workspace/ssh/New/'
dir_remote = "remote_machine_folder/subfolder"
glob_pattern='*.*'
import os
import glob
import paramiko
import md5
rsa = None
def agent_auth(transport, username):
ki = None
#ppk = None
Attempt to authenticate to the given transport using any of the private
keys available from an SSH agent or from a local private RSA key file (assumes no pass phrase).
try:
ki = paramiko.RSAKey.from_private_key_file(rsa_private_key)
except Exception, e:
print 'Failed loading {0}'.format (rsa_private_key, e)
agent = paramiko.Agent()
agent_keys = agent.get_keys() + (ki,)
if len(agent_keys) == 0:
return
for key in agent_keys:
print 'Trying ssh-agent key {0}'.format(key.get_fingerprint().encode('hex')),
try:
transport.auth_publickey(username, key)
print '... success!'
return
except paramiko.SSHException, e:
print '... failed!', e
hostkeytype = None
hostkey = None
files_copied = 0
try:
host_keys = paramiko.util.load_host_keys(os.path.expanduser('~/.ssh/known_hosts'))
except IOError:
try:
# try ~/ssh/ too, e.g. on windows
host_keys = paramiko.util.load_host_keys(os.path.expanduser('~/ssh/known_hosts'))
except IOError:
print ' Unable to open host keys file'
host_keys = {hostname:'upload.partner.com'}
if hostname in host_keys:
hostkeytype = host_keys[hostname].keys()[0]
hostkey = host_keys[hostname][hostkeytype]
print 'Using host key of type {0}'.format(hostkeytype)
try:
print 'Establishing SSH connection to:', hostname, port, '...'
t = paramiko.Transport((hostname, port))
t.connect()
agent_auth(t, username)
if not t.is_authenticated():
print 'RSA key auth failed! Trying password login...'
t.connect(username=username, password=password, hostkey=hostkey)
else:
sftp = t.open_session()
sftp = paramiko.SFTPClient.from_transport(t)
try:
sftp.mkdir(dir_remote)
except IOError, e:
print '(assuming ', dir_remote, 'exists)', e
for fname in glob.glob(dir_local + os.sep + glob_pattern):
is_up_to_date = False
if fname.lower().endswith('xml'):
local_file = os.path.join(dir_local, fname)
remote_file = dir_remote + '/' + os.path.basename(fname)
try:
if sftp.stat(remote_file):
local_file_data = open(local_file, "rb").read()
remote_file_data = sftp.open(remote_file).read()
md1 = md5.new(local_file_data).digest()
md2 = md5.new(remote_file_data).digest()
if md1 == md2:
is_up_to_date = True
print "UNCHANGED:", os.path.basename(fname)
else:
print "MODIFIED:", os.path.basename(fname),
except:
print "NEW: ", os.path.basename(fname),
if not is_up_to_date:
print 'Copying', local_file, 'to ', remote_file
sftp.put(local_file, remote_file)
files_copied += 1
t.close()
except Exception, e:
print '*** Caught exception: %s: %s' % (e.__class__, e)
try:
t.close()
except:
pass
print '=' * 60
print 'Total files copied:',files_copied
print 'All operations complete!'
print '=' * 60
can you help me to solve this error
EDIT After much to and fro, the solution was simply to add user's private key to she ssh agent via ssh-add.
Actually, you might be trying to use your public key, not your private key.
The file name "id-rsa.pub" looks suspiciously like a public key. The corresponding private key is usually named "id-rsa". Public keys look something like this:
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCUmHZlySQqmZCGgE0NKWYyYlPHb3g1Bwdll2ztngUWNkrDSWGmLq6IqJP9EwwxungwJkdkR/U86gFv5MQfQ92+0ote7pUXOACwHfqvIoUXXFI3ZLo/C2cuqDIO7fcO50KKGFAuWbjTd3rugbpoMnNqT99wz/1lrCkTsJLd0YxtRo/QsJ8jiZXRuaEzbdXKwZJaP8G3eBHSMiEa1781yWklk50xxLk2qtpWVzen+Om6InbQ2cR6bBvfiA4B3LES53ccmMfzrCygjl8B6yaV3NI60Re5oNdyNrPZgH4VXf5p4VwrKpY4dCcJZyNmHlFhJlTgZu25uKAbp8Wk4u1ky0vJ mhawke#localhost.localdomain
Whereas unencypted private keys look something like this:
-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQCfqBLoK4Vec7r0df4a2CYNzmhJn74qIDqbJnkGasHcN5/GYuDv
.
.
xLCNrVMXYPd1I7L5NGzZalaTrS+DkgLwNvGhkVGKUGao
-----END RSA PRIVATE KEY-----
and encrypted private keys like this:
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,1ACD09B7F078AEB84B9A109979B77CBB
LDEuf08/xMGsHyesZxFGkRNHf8C78vpdDJyBBManOt/mRf/5fkjOel8RgoH4oYEz
.
.
tKjd+PlR4WLluMPFeHujdqhvyC4fQeFzWqak+rlUG5o3lm+TAcKqjypAEU4RVUuW
-----END RSA PRIVATE KEY-----
Check to see if you are using the correct key file. You can test it by:
openssl rsa -text -noout -in private_key_file
You shouldn't see any errors.
The first problem is occurring in this block of code
rsa_private_key = r"~/workspace/ssh/rsa_private_key"
.
.
.
try:
ki = paramiko.RSAKey.from_private_key_file(rsa_private_key)
except Exception, e:
print 'Failed loading {0}'.format (rsa_private_key, e)
I don't think that from_private_key_file() handles file expansion, so the ~ is interpreted literally as a tilde. Try changing rsa_private_key to the absolute file name.
Also the print statement in your except clause doesn't print out the exception. I suggest changing it to print 'Failed loading {0} : {1}'.format (rsa_private_key, e). You will probably then see the cause of the problem in the following error message:
Failed loading ~/workspace/ssh/rsa_private_key : [Errno 2] No such file or directory: '~/workspace/ssh/rsa_private_key'
Also, you may need to supply a password to from_private_key_file() if your key is encrypted.

AES encrypted File transfer over TCP socket; padding issue

I'm trying to do a file transfer using encrypted TCP socket with AES 256.
If I transfer a file without encryption, it works fine.
If I send small commands (such as 'ipconfig') to the client or server, the encryption works fine.
No matter the file size is, I keep receiving the following error message:
DecodeAES = lambda c, e: c.decrypt(base64.b64decode(e)).rstrip(PADDING)
File "/usr/lib/python2.6/base64.py", line 76, in b64decode
raise TypeError(msg)
TypeError: Incorrect padding
My function for encoding and decoding is the following (the hat variable is the message):
def AESENC(hat,typ):
BLOCK_SIZE = 32
PADDING = '{'
pad = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * PADDING
EncodeAES = lambda c, s: base64.b64encode(c.encrypt(pad(s)))
DecodeAES = lambda c, e: c.decrypt(base64.b64decode(e)).rstrip(PADDING)
secret = '01234567890123456789012345678912'
IV = 'wir&/>H54mgd9a";'
cipher = AES.new(secret,AES.MODE_CFB,IV)
if typ == 0:
encoded = EncodeAES(cipher, hat)
return encoded
else:
decoded = DecodeAES(cipher, hat)
return decoded
Client side
if os.path.exists(df):
print ' found the file '
f = open(df, 'rb')
packet = f.read(1024)
while packet != '':
s.send(AESENC(packet,0))
s.send( AESENC('123XXX',0) )
s.send('123XXX')
f.close()
Server side
f = open('/root/Desktop/Transfer.mp3','wb')
while True:
bits = AESENC ( conn.recv(1024) , 1 )
while (bits):
f.write(bits)
bits = AESENC ( conn.recv(1024) , 1 )
if bits.endswith('123XXX'):
print '[+] Transfer completed '
break
f.close()
break
return
Does anybody know how to fix this?
Apologies for the misunderstanding of your sample code, and thank you for adding the server and client calls!
Please note that the error message that you're getting is unrelated to the encryption. You can read the relevant part of the base64.py library: the error message indicates that the base64 data is not valid. In particular, it's not properly padded at the end.
With the additional call site information, I believe the problem is that you're encrypting and then separately encoding each 1024 byte block of the data on the client. Then, on the server, you're reading 1024 bytes from the network and trying to decode it. However, base64-encoding will increase the length of the original data, so what you'll read will only be the first 1024 bytes of the encoded form, which will be a truncated base64 message (hence improperly padded).
As for how to solve it, a base64 message needs to be decoded all as one piece. So you either need to calculate how much data to read from the network to get a complete encoded block (a calculation that you can do reliably except for the possibly short last packet, since the base64 length is always (length + 2) / 3 * 4), encode the entire data stream at once (which is likely a problem due to memory use if you want to handle arbitrary-sized files), or define your network protocol so that the server can tell that it's seen a complete block that can be decoded with base64.

Categories