Introduction to Network Security – Part 7

NOTIFICATION: These examples are provided for educational purposes. The use of this code and/or information is under your own responsibility and risk. The information and/or code is given ‘as is’. I do not take responsibilities of how they are used.

Transposition Ciphers

The main idea of transposition ciphers is to rearrange the order of the letters used in the plaintext. This prevent the attacker to be able to recognise the message by using the frequency of distributions.

Rail Fence Cipher

Encryption

The basic concept of encryption on Rail Fence cipher is the follow:

  1. Select a number of rows greater or equal to two. For this example, we will pick three:
  2. Place each letter of the message in each row, one letter at a time, on one row at a time, from the top to the bottom
    1. Lets assume the plaintext is “SUPERSECRETMESSAGE”
    2. Rearrange the letters on the rows:
  3. After finished, we append one row after another in order, forming the ciphertext.

Decryption

The decryption of a rail fence cipher is almost the reverse process of the encryption.

  1. You will need the ciphertext and the number of rows:
    1. The ciphertext is “SEEEEAURCTSGPSRMSE”
    2. The number of rows is:
      |rows| = 3
  2. Computer the length of the ciphertext. In this case, the ciphertext “SEEEEAURCTSGPSRMSE” is:
    |ciphertext| = 18
  3. Lets calculate the columns that we will have:
    Number of Columns = ( |ciphertext| ÷ |rows| ) + ( |ciphertext| mod |rows| )
    = ( 18 ÷ 3 ) + (18 mod 3 )
    = 6 + 0
    = 6 columns
  4. Now, we have a table of 3 rows by 6 columns:
  5. Let fill up this table with the ciphertext, one letter at a time, from top to down and left to right:
  6. Now recreate the plaintext from this table:
Share

Introduction to Network Security – Part 6

NOTIFICATION: These examples are provided for educational purposes. The use of this code and/or information is under your own responsibility and risk. The information and/or code is given ‘as is’. I do not take responsibilities of how they are used.

Poly-alphabetic Cipher

In the previous posting, we say that the mono-alphabetic cipher instead of shifting the alphabet a number of letters (Caesar cipher), its substitute each letter arbitrarily by mapping the plaintext letter map to a random arranged ciphertext. The only requirement for the ciphertext was that the letters must not be repeated. Now we are going to see a cipher that uses a set of related mono-alphabetic rules plus a key to determine which rule will be use to perform a transformation.

Vigenère Cipher

Encryption:

This cipher is similar to the Caesar cipher for the use of the 26 letters alphabet with the only different that we create a table in which:

  1. The columns represent the plain text
  2. The rows represent the key
  3. The alphabet inside the table is shifted to the right one letter one time for each letter of the alphabet key.

To be more clear, let take a quick look of the Caesar cipher table:
In this example, We started the alphabet on the letter ‘E’ because the key was 5.

Now, the Vigenère Cipher will apply this shifting 26 times, one time per row, for each letter of the alphabet that correspond to the key as follow:

Lets say that you have the following key “THIS  MESSAGE WAS FOR YOU”, and your key is “HELLO” then using the table:

We would obtain:

From a mathematical point of view we have:

  1. Lets assume that we take the letters of the alphabet from A to Z and be replace them with number starting from 0, for example: A = 0, B = 1, …, Z = 25.
  2. Since we have 26 letters in the alphabet, lets perform module of 26 on this equation.
  3. If ‘i’ is the letter position, P indicate the plaintext, K indicate the key, and C indicate the ciphertext then:
    C_i \equiv (P_i + K_i) \pmod {26}
    (For more information about the algebra involved in the Vigenère cipher: http://en.wikipedia.org/wiki/Vigen%C3%A8re_cipher)

Decryption

For decryption we only need to use a letter of the key to identify the row and the letter of the ciphertext in the row to identify the column, the letter designated to the column give us the plaintext letter.

From a mathematical point of view we have:

  1. Lets assume that we take the letters of the alphabet from A to Z and be replace them with number starting from 0, for example: A = 0, B = 1, …, Z = 25.
  2. Since we have 26 letters in the alphabet, lets perform module of 26 on this equation.
  3. If ‘i’ is the letter position, P indicate the plaintext, K indicate the key, and C indicate the ciphertext then:
    P_i \equiv (C_i - K_i) \pmod {26}
    (For more information about the algebra involved in the Vigenère cipher: http://en.wikipedia.org/wiki/Vigen%C3%A8re_cipher)

Security

This cipher is not secure. If two or more sequences are identical inside the plaintext, we run the risk that identical ciphertext sequence will be generated. The attacker can use these repetition in the ciphertext to make a deduction about what is the plaintext. The more plaintext is needed to encrypt, the more chances that the ciphertext can be broken or the key found.

As an example, lets assume we have the following:
Plaintext:   WE RUN WHEN WE WERE DISCOVER BY THEM
Key:             RUNNING NO RUNNING NO RUNNING NO

This would give us a ciphertext in which we can spot the repetitions:

The only way around this problem is by using the Autokey cipher.

Autokey Cipher

An auto-key cipher is the concept of generating a key that does not have a repetition cycle.

Instead of having a plaintext and a key such as this example:
Plaintext:   WE RUN WHEN WE WERE DISCOVER BY THEM
Key:             RUNNING NO RUNNING NO RUNNING NO

We could have the following key:
Plaintext:   WE RUN WHEN WE WERE DISCOVER BY THEM
Key:             RUNNING IS NOT THE SOLUTION THIS

This would give us a ciphertext with no repetitions:

One-Time Pad Cipher

The One-Time Pad cipher use a similar concept as the Auto-Key Cipher; however, the difference is the generation of a random key which is as long as the message. Also, it is required that at the end of the transmission, the random key generated must be destroyed.

The only problem is to find a secure way to distribute the random generated key between the principals.

Share

Introduction to Network Security – Part 5

NOTIFICATION: These examples are provided for educational purposes. The use of this code and/or information is under your own responsibility and risk. The information and/or code is given ‘as is’. I do not take responsibilities of how they are used.

Symmetric Encryption

In the symmetric encryption, the same key (normally a single-key) is used to perform the encryption and decryption of the ciphertext.

Symmetric Cipher Model: This model is performed by performing transformations and substitutions on the plaintext. A secret key, independent from the plaintext and the algorithm, is used to cipher the plaintext. After, the ciphertext plus the secret key is used with the decryption algorithm to obtain the original plaintext.

Symmetric Encryption is the opposite to the concept of public key distribution which will be explained in future postings.

Requirements:

  1. The cipher model must be mathematical expression:
    (E: Encryption, D: Decryption, X: plaintext, Y: ciphertext, K: secret key)

    Y = E(K, X)
    X = D(K, Y)
    
  2. Assumption that the encryption algorithm is known to the attacker.
  3. A strong encryption algorithm which in case the attacker would obtain or know some examples of the ciphertext and the plaintext produced from the ciphertext, the attacker would still be not able to obtain the key. This means that if the attacker would obtain the ciphertext, the attacker would not be able to obtain the secret key or the plain text.
  4. Secret key should be known only by the sender and the receiver of the ciphertext.
  5. The distribution of the secret key must be done in a secure fashion. For example, the use of a third party that would generate and provide in a secure way the key to the sender and the receiver.

Substitution Ciphers

In classical substitution ciphers, all the letters in the plaintext will be replaced by another letter, number, and/or symbol.

Caesar Cipher

History explains that Julius Caesar <http://www.roman-empire.net/republic/caesar-index.html> came up with a substitution cipher that he used in his campaigns for military affairs.

The cipher works in the following way:

  1. We use the alphabet of 26 letters:
    
    
  2. Under this alphabet, we will rewrite the alphabet by picking a letter as a starting point.
    Lets say our key indicate the starting point such as K = 4 so we begin with the letter ‘E’ then:
  3. This means that if we wish to send a plaintext (P) that says HELLO, the ciphertext (C) would be LIPPS, and the key (K) would be 4
  4. The mathematical way to represent this cipher will be the follows:
    1. Give each letter of the alphabet a number:
      A = 1, B = 2, C = 3, D = 4, E = 5,F = 6, G = 7, H = 8, J = 9, K = 10, L = 11, M = 12, N = 13, O = 14,P = 15, Q = 16, R = 17, S = 18, T = 19, U = 20, V = 21, W = 22, X = 23, Y = 24, Z = 25.
    2. Encryption Algorithm:
      E: Encryption, Ct: Ciphertext, Pt: Plaintext, K: secret key

      Ct = E(Pt)
         = (Pt + K) mod 26
    3. Decryption Algorithm:
      D: Decryptor, Ct: Ciphertext, Pt: Plaintext, K: secret key

      Pt = D(Ct)
         = (26 + (Ct - K)) mod 26
  5. The weakness of this cipher is that it can be broken by brute force. We just need to test the 25 combinations of different keys  until we find the key that reveals the message.

Monoalphabetic Cipher

The mono-alphabetic cipher instead of shifting the alphabet a number of letters, its substitute each letter arbitrarily by mapping the plaintext letter map to a random arranged ciphertext. The only requirement for the ciphertext is that the letters must not be repeated.

Since we are using 26 letters of the alphabet the arrangement of the cipher can permute a total of 26! permutations.

If we wish to encode the word “HELLO”, we would obtain “NERRS”

Lets assume we wish to cipher a plaintext:

Plaintext = “THIS IS A SECRET MESSAGE ENCODED IN MONOALPHABETIC”

Ciphertext = “XNMW MW E WIGBIX OIWWEJI IPGSCIC MP OSPSERUEDIXMG”

The following website let you play a little with monoalphabetic cipher by randomizing for you the ciphertext:
<http://www.simonsingh.net/The_Black_Chamber/generalsubstitutionWithMenu.html>

The only problem is that this cipher can be exploited by doing regularities analysis over the frequency of the letters. Base on the language rules some letters are used more than others. For example, in English, the letter ‘E’ is the most common used in words, followed by A, I, O, N, R, S, T. Others letters such as K, J, Q, X, Z are less used than the rest.

The largest is the message, the most chances that the attacker can decrypt the message.
Just in this message “XNMW MW E WIGBIX OIWWEJI IPGSCIC MP OSPSERUEDIXMG” we have:

  • W = 6 letters
  • E = 4 letters
  • M = 4 letters
  • S = 3 letters
  • P = 2 letters
  • ….

And continue counting.

As you may notice the letter ‘W’ of the encrypted message have the most counts, so we could  assume that this is the letter E of the plaintext.

If you are interested to know the frequency of letters in English you can go to the following website:
<http://www.cryptograms.org/letter-frequencies.php>

For more information about attacking mono-alphabetic cipher, there is a good example on this website:
<http://unsecure.co.uk/attackingmonoalphabeticciphers.asp>

Playfair Cipher

Playfair is one way to improve the security of mono-alphabetic cipher by encrypting multiple letters.

Playfair Encryption

  1. Create a playfair key matrix:
    1. Create a matrix of letters based on a keyword. For this example, the matrix should be 5 by 5
    2. Fill in the letters of the keyword from left to right and from top to bottom. Make sure that there are not duplicate letters
    3. Fill the rest of the matrix with the other letters that are not in the keyboard, making sure to not duplicate letters.
    4. As a rule, the letter I and J count as one letter.
      • I am not sure the reason for this rule, except the following:
        • First, it make it harder to decrypt the message since one letter is missing.
        • Second, in some languages, the J and I would have the same pronunciation.
          For example, my last name Carlstein was originally written as Karlštejn.
      • In case you know the real reason, please let me know and give me a reference to verify (thanks).
    5. Example of playfair key matrix:
      1. Let use the keyword: “EDUCATOR”
      2. The table should looks like this:
      3. Notice that I and J are counted as one letter
  2. The next step is to encrypt the plaintext taking two letters at the time.
    1. In case a two letters are the same (repeated), we must insert a filler letter (use the letter X as the filler). For example:
      HELLO → HE LX LO
    2. In case two letters are in the same row, replace each letter with the letter to the right. In case the letter is at the last column, pick the letter of the first row (the table is considerate to be circular). For example, lets say we have the letters D and A:

      1. D → U and A → E
      2. Therefore DA became UE
    3. In case two letters are in the same column, replace each letter with the letter below. In case the letter is at the last row, pick the letter of the first row (the table is considerate to be circular). For example, lets say we have the letters T and V:

      1. T → G and V → E
      2. Therefore TV became GE
    4. In case two letter are in different row and column, the first letter will be replaced with another letter of the same row on the column of the second letter. The second letter will be replaced with another letter of the same row on the column of the first letter. For example lets say we have the letters O and Q:

      1. To replace the letter O:
        1. This means that O → B
      2. To replace the letter Q:
        1. This means that Q → N
      3. Therefore OQ became BN

Playfair Decryption:

  1. Decrypt two letters at a time:
    1. In case two letters are in the same row, replace each letter with the letter to the left. In case the letter is at the last column, pick the letter of the first row (the table is considerate to be circular). For example, lets say we have the letters U and E:

      1. U → D and E → A
      2. Therefore UE became DA
    2. In case two letters are in the same column, replace each letter with the letter above. In case the letter is at the last row, pick the letter of the first row (the table is considerate to be circular). For example, lets say we have the letters G and E:

      1. G → T and E → V
      2. Therefore GE became TV
    3. In case two letter are in different row and column, the first letter will be replaced with another letter of the same row on the column of the second letter. The second letter will be replaced with another letter of the same row on the column of the first letter. For example lets say we have the letters B and N:

      1. To replace the letter B:
        1. This means that O → B
      2. To replace the letter Q:
        1. This means that Q → N
      3. Therefore OQ became BN
  2. After you will finish with the final message. You must remove any extra X that do not make sense in the message:
    HE LX LO → HELLO
Share

Introduction to Network Security – Part 4

NOTIFICATION: These examples are provided for educational purposes. The use of this code and/or information is under your own responsibility and risk. The information and/or code is given ‘as is’. I do not take responsibilities of how they are used.

Before we begin talking about encryption, decryption, and ciphers related topic, let go over some terminologies to have in account:

  • Cipher: An algorithm used for encryption.
    Link reference: <http://www.merriam-webster.com/dictionary/cipher>
  • Ciphertext: The encrypted(coded) message.
    Link reference: <http://cryptnet.net/fdp/crypto/crypto-dict/en/crypto-dict.html>
  • Cryptanalysis: Study of the principles and methods of deciphering a ciphertext without having the required key.
    Link reference: <http://en.wikipedia.org/wiki/Cryptanalysis>
  • Cryptography: Study of the principles and methods of encryption.
    Link reference: <http://en.wikipedia.org/wiki/Cryptography>
  • Cryptology: The study of cryptanalysis and cryptography.
    Link reference: <http://www.britannica.com/EBchecked/topic/145058/cryptology>
  • Deciphering: Also known as decryption. The act of transforming a ciphertext to the original plaintext.
    Link reference: <http://www.merriam-webster.com/dictionary/deciphering>
  • Decryption: Also known as deciphering. The act of transforming a ciphertext to the original plaintext.
  • Enciphering: Also known as encryption. The act of transforming a plaintext to a ciphertext.
    Link reference: <http://www.merriam-webster.com/dictionary/enciphering>
  • Encryption: Also know as enciphering. The act of transforming a plaintext to a ciphertext.
  • Plaintext: the original message to be encrypted.
    Link reference: <http://en.wikipedia.org/wiki/Plaintext>
  • Product: stages of transposition and substitutions performed.
    Link reference: <http://www.britannica.com/EBchecked/topic/477942/product-cipher>
  • Secret key: An input required for the encryption and/or decryption algorithms.
  • Substitution: Map each element in a plain text to another element.
    Link reference: <http://substitution.webmasters.sk/>
  • Transposition: Rearrange the elements in the plaintext
    Link reference: <http://mw1.meriam-webster.com/dictionary/transposition%20cipher>

Cryptography

A cryptographic system is characterized by the use of encryption operations, number of keys used for encryption and decryption, and the way in which the plain text is processed.

Encryption Operations: In order to encrypt a plaintext to a chipertext is required to perform multiple stages of transposition and substitution, also known as product.

  • Substitution: We take each element from the plaintext and mapped them to another element
  • Transposition: We  take each element in the plaintext and rearrange its order in such a way that it differ from the original plaintext.

To perform encryption and decryption, we use a key reference. We can categorize the encryption techniques as  symmetric, single, asymmetric, double, and/or public.

The plaintext can be processed by using a method of streams or blocks:

  • Stream: The plaintext is processed as a continuous set of elements in which each element is encrypted one at a time.
  • Blocks: The plaintext is divided in a set of blocks in which each block is encrypted one at a time.

Cryptanalysis

As explained in the terminology list, Cryptanalysis is purpose of decrypt an encrypted ciphertext without the knowledge of the key used for the encryption. One way is to attack the encryption system and recover the key used for the encryption instead of recovering the plaintext from a single ciphertext.
Cryptanalysis attacks are divided in two categories:

  1. Brute-force Attack: Every combination of a possible key is tested on the chipertext until the plaintext is obtained.
  2. Cryptanalytic Attack: The use of knowing some characteristic of the original plaintext such as some used keywords, language, format, plaintext to ciphertext pairs examples, and  knowledge of the possible algorithm used to decrypt the ciphertext.

Unconditional Security

We call unconditional security when a cipher cannot be broken by using a ciphertext and the plaintext that produced the ciphertext regardless of the computational power and time available. Up to day, there are no encryption algorithm that can be unconditional secure with the exception of the one-time pad encryption algorithm <http://www.ibm.com/developerworks/library/s-pads.html> which will be explained in the following postings.

Computational Security

Base on the cost-benefit of braking a cipher, a cipher may not be broker due:

  1. The cost of braking the cipher is greater than the value of the plaintext encrypted
  2. The time required to breaking the cipher exceed the usefulness lifetime of the plaintext encrypted
  3. Depending of the complexity of the cipher, there would be a limitation of computing resources and time.

Brute Force Search

As explained before, we call brute force to try every key combination possible to decrypt the ciphertext into plaintext. Before obtaining success, the attacker must try at least 50 percent of the possible keys; therefore, the probability of success may be proportional to the size of the key.

Lets assume we wish to have to option of using:

  1. DES encoding (56-bit) <http://groups.csail.mit.edu/cag/raw/benchmark/suites/des/README.html>.
  2. Triple DES (168-bit) <http://en.wikipedia.org/wiki/Triple_DES>
  3. AES (Greater than 128 bits) <http://www.aescrypt.com/>

Depending of which encryption we use, the time required to find the right key by brute force could be:

Share

File Transfer Client and Server using Vigenere Cipher

[Disclaimer: This code is provided for educational purposes, you are responsible of how you use this code and this code is provided ‘as is’. Meaning that I do not take responsibility of  any harm that this code may or not produce]

The following is an example of a client and server that let you transfer a file from the client to the server by encrypting and then decrypting the packages using Vigenere Cipher.

Test file content example:

Note: This program is designed only to send files that are alphabetic in lower case, without spaces, and  only one line.

abcdefghijklmnopqrstuvwxyzaabcdefghijklmnopqrstuvwxyzaabcdefghijklmnopqrstuvwxyza

Makefile:

As a good policy, always add your name, short description, and any other information that could help a user that doesn’t know as you the content of your code and its behaviour.

Note: In the server, there must be a subdirectory named cli_serv in which ssh and scp can access after loading.



#########################################################################
# Author: Alejandro G. Carlstein Ramos Mejia
# Description: Client/Server File Transfer using Vergene Cipher
#
#
# make <= compile all files
# make build <= compile all files
# make all <= clean and compile all files
# make ssh <= Connect to server and go to folder cli_serv
# make upload <= Upload files on SRC_FILES list to server
# make uploadssh <= Upload files on SRC_FILES list to server and connect to server
# make submit <= Tar files on SRC_FILES list
# make zipsubmit <= Tar files on SRC_FILES list and gzip them
# make clean <= Clean all executable and *.o files
# make debug_server <= Debug server using dbg
# make debug_client <= Debug client using dbg
# make ldebug_server <= Debug for leaks on server
# make ldebug_client <= Debug for leaks on client
##########################################################################

##########################################################################
# Variables
##########################################################################

# PROJECT is the name used when preparing for sumit/
# The tar and/or zip file will be using this name
PROJECT = server_client_vergene

# SRC_FILES are list of files in the project.
# This list is used when uploading files to the server.
# Also, this list is used when tar/zipped for submit
SRC_FILES = \
	cli.c \
	serv.c\
	default.h \
	default.c \
	text.txt\
	Makefile \
	README

# OpenSSH SSH client (remote login program)
SSH = ssh

# secure copy (remote file copy program)
# SCP is used for uploading files to the server in secure mode
SCP = scp

# FOLDER_SERVER is the folder that SSH and SCP will try to access after login
FOLDER_SERVER = server_client_vergene

# SSH_SERVER is the hostname of the server
SSH_SERVER = bingsuns2.cc.binghamton.edu

# SSH_OPTION
# -t opens a pseudo-tty with in the current session.
# This flag is required to execute the commands on SSH_CD_FOLDER
SSH_OPTION = -t

# SSH_CD_FOLDER executes cd
# Then change the prompt to show the number of bash in the server
SSH_CD_FOLDER = 'cd $(FOLDER_SERVER); bash; echo $PS1'

# CC indicates which compiler is going to be used
CC = gcc

# Files required for compiling the server
CODE_SERVER_FILE = serv.c default.c

# Files required for compiling the client
CODE_CLIENT_FILE = cli.c default.c

# Name of the executable file for the server
EXEC_SERVER_FILE = serv

# Name for the executable file for the client
EXEC_CLIENT_FILE = cli

# Flags for the compiler
# -g indicate to provide debuggin information
# -Wall activates the warnings.
# -lm indicate the compiler to add basic mathematics libraries
CFLAGS = -g -Wall -lm

# COMPILE is the combination of the compiler with the flags
COMPILE = $(CC) $(CFLAGS)

# MFLAGS are flags that require to be added at the end
MFLAGS =

# USERNAME is used later when required to do an upload followed with ssh
USERNAME =

# Detect if the computer is SunOS to add flags needed for compiling
UNAME := $(shell uname)
ifeq ($(UNAME), SunOS)
 MFLAGS := -lsocket -lnsl
endif

##########################################################################
# 'make' options
##########################################################################

# Clean all files and compile client and server
all: clean compile_server compile_client

# Just build server and client
build: compile_server compile_client 

# Compile server
compile_server: $(CODE_SERVER_FILE)
	$(CC) $(CFLAGS) -o $(EXEC_SERVER_FILE) $(CODE_SERVER_FILE) $(MFLAGS)

# Compile client
compile_client: $(CODE_CLIENT_FILE)
	$(CC) $(CFLAGS) -o $(EXEC_CLIENT_FILE) $(CODE_CLIENT_FILE) $(MFLAGS)

# Debug server
debug_server:
	gdb $(EXEC_SERVER_FILE) 

# Debug client
debug_client:
	gdb $(EXEC_CLIENT_FILE) 

# Leak debug server
ldebug_server:
	valgrind --leak-check=full --show-reachable=yes -v $(EXEC_SERVER_FILE)

# Leak debug client
ldebug_client:
	valgrind --leak-check=full --show-reachable=yes -v $(EXEC_CLIENT_FILE)

# Upload files and connect to server
uploadssh:
	@echo -n 'Upload and connect to $(SSH_SERVER)- USERNAME: ';\
	read user_name;\
	$(SCP) $(SRC_FILES) $$user_name@$(SSH_SERVER):./$(FOLDER_SERVER);\
	$(SSH) $(SSH_OPTION) $$user_name@$(SSH_SERVER) $(SSH_CD_FOLDER);\

# Upload files to server
upload:
	@echo -n 'Upload to $(SSH_SERVER)- USERNAME: ';\
	read user_name;\
	$(SCP) $(SRC_FILES) $$user_name@$(SSH_SERVER):./$(FOLDER_SERVER);\

# Connect to server
ssh:
	@echo -n 'Connect to $(SSH_SERVER)- USERNAME: ';\
	read user_name;\
	$(SSH) $(SSH_OPTION) $$user_name@$(SSH_SERVER) $(SSH_CD_FOLDER);\

# Makes a archive containing all the project source files for submission.
submit:	$(SRC_FILES)
	tar -cvf $(PROJECT).tar $(SRC_FILES)

# Makes a archive containing all the project source files for submission
# and zip them
zipsubmit:	$(SRC_FILES)
	tar cvfz $(PROJECT).tar.gz $(SRC_FILES)

# Clean files
.PHONY: clean
clean:
	rm -f *~ *.o $(EXEC_SERVER_FILE) $(EXEC_CLIENT_FILE)

Default file is required for both the server and the client. It holds the libraries and definitions shared by both programs.

Default.h file:

/**
 * Author: Alejandro G. Carlstein
 * Description: File Transfer Client/Server
 */

#ifndef ACARLS_DEFAULT_H
#define ACARLS_DEFAULT_H

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <errno.h>
#include <unistd.h>
#include <dirent.h>

/* Required for linux */
#include <string.h>

/* Required for SunOS */
#include <strings.h>

#include <ctype.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netdb.h>

#define DEFAULT_PORT 					8414
#define DATA_PACKET_SIZE 			1024
#define NUM_BYTES							8
#define ENCRYPTION_KEY		    'security'
#define CHAR_KEY_A						97
#define CHAR_PLAIN_A					97
#define CHAR_CIPHER_A					65

#define DBG_LV0 0
#define DBG_LV1 0
#define DBG_LV2 0
#define DBG_LV3 0

#define TRUE									1
#define FALSE									0

#define QUIT_TRUE							0
#define QUIT_FALSE						1

#define CODE_FAIL							'0FAIL'
#define CODE_OK								'1OK'

#define CODE_HELLO						'100HELLO'
#define CODE_WELCOME 					'101WELCOME'
#define CODE_REQ_SERVER_NAME 	'102REQ_SERVER_NAME'

#define CODE_MSG 							'200MSG'

#define CODE_DATA							'300DATA'
#define CODE_EOF							'301EOF'
#define CODE_PUT						  '302PUT'
#define CODE_REQUEST_FILENAME	'303REQ_FILENAME'
#define CODE_REQUEST_ENCRYPT  '304REQ_ENCRYPT'
#define CODE_ENCRYPT					'305ENCRYPT'
#define CODE_REQUEST_FILE     '306REQUEST_FILE'

#define CODE_CMD							'400CMD'
#define CODE_LS								'401LS'

#define CODE_ERROR            '500ERROR'
#define CODE_ERROR_LS					'501ERROR_LS'
#define CODE_ERROR_CREAT_FILE '502ERROR_CREAT_FILE'

#define CODE_EXIT							'600EXIT'

#define MSG_ERR_WRONG_PROTOCOL  'Wrong protocol!'
#define MSG_ERROR_CREAT_FILE 		'Couldn't create file'
#define MSG_PORT_NUMBER_ONLY 		'Need port number only! \n%s <Port Number>'
#define MSG_ERR_COULDNT_SOCKET  'Couldn't obtain socket - %d'
#define MSG_ERR_COULDNT_CONNECT 'Couldn't connect!'
#define MSG_ERR_SENDING_DATA 		'Couldn't send data!'
#define MSG_ERR_RECEIVING_DATA	'Couln't recieve data!'
#define MSG_ERR_CONNECTION_FAIL 'Connection failed.'
#define MSG_ERR_NO_DIR_STREAM 	'Could not obtain the directory stream'

void debug(int debugLevel, char *fmt, ...);
void errorDoExit(char *fmt, ...);

#endif

Default.c:

/**
 * Author: Alejandro G. Carlstein
 * Description: File Transfer Client/Server
 */

#include 'default.h'

void debug(int debugLevel, char *fmt, ...){
	if (debugLevel == 1){
		va_list argp;
		fprintf(stdout, '[DBG] ');
		va_start(argp, fmt);
		vfprintf(stdout, fmt, argp);
		va_end(argp);
		fprintf(stdout, '\n');
	}
}

void errorDoExit(char *fmt, ...){
	va_list argp;
	fprintf(stderr, '[Error] ');
	va_start(argp, fmt);
	vfprintf(stderr, fmt, argp);
	va_end(argp);
	if (errno){
		fprintf(stderr, '=> %s\n', strerror(errno));
	}else{
		fprintf(stderr, '\n');
	}
	exit(1);
}

The following is the code for the server.
serv.c:

/**
 * Assignment: 1
 * Course: CS458
 * Author: Alejandro G. Carlstein
 * Description: FTP Server
 */

#include 'default.h'

/*max. length queue of pending connections may grow. */
#define MAX_BACKLOG							5
#define SETSOCKOPT_VAL  				1

#define MSG_SERVER_NAME 				'Server Name: ACARLSTEIN Server Version 1.0'
#define MSG_WAIT_CLIENT					'Waiting client...\n'
#define MSG_WAIT_CLIENT_ON_PORT	'\nTCPServer Waiting for client on port %d\n'

#define MSG_ERR_NO_SOCKET_OPT		'Couldn't set and/or get socket options'
#define MSG_ERR_UNABLE_BIND    	'Unable to bind'
#define MSG_ERR_UNABLE_LISTEN  	'Unable to Listen'
#define MSG_ERR_CANT_SEND_LIST  'Can't send server list\n'

struct Connection{
	int sock;
	int bytes_recieved;
	int port_number;
	int socket_descriptor;
	char send_data[DATA_PACKET_SIZE];
	char recv_data[DATA_PACKET_SIZE];
  struct sockaddr_in server_addr;
  struct sockaddr_in client_addr;
};

void setUpConnection(struct Connection *new_connection,
										 int port_number);
int menuDriver(struct Connection *new_connection);
void sendData(struct Connection *new_connection,
							const char* data);
int recieveData(struct Connection *new_connection);
void handShake(struct Connection* new_connection);
void sendListDirectoryContents(struct Connection* new_connection);
int receiveFileFromClient(struct Connection* new_connection);
int strdecrypt(const char* str_in,
							 char* str_out);

/**
 * MAIN
 */
int main(int argc, char *argv[]){
	debug(DBG_LV0, 'argc: %d', argc); 	

	short DO_QUIT_PROGRAM;
	int i;
	int port_number;
	socklen_t sin_size = sizeof(struct sockaddr_in);
	struct Connection connection;

	for(i = 0; DBG_LV1 && i < argc; ++i)
		debug(DBG_LV1, 'argv[%d]: %s', i, argv[i]); 		

	if (argc > 2)	errorDoExit(MSG_PORT_NUMBER_ONLY, argv[0]);

	port_number = (argc == 2) ? atoi(argv[1]) : DEFAULT_PORT;

	DO_QUIT_PROGRAM = QUIT_FALSE;
	while(DO_QUIT_PROGRAM){

		fflush(stdout);

		setUpConnection(&connection, port_number);

		printf(MSG_WAIT_CLIENT_ON_PORT, port_number);

		connection.socket_descriptor = accept(connection.sock,
													  						  (struct sockaddr *)&connection.client_addr,
																				  &sin_size);

		if (connection.socket_descriptor  == -1)
			errorDoExit(MSG_ERR_COULDNT_CONNECT);

		handShake(&connection);

 		menuDriver(&connection);

		close(connection.sock); 		

	}

  return 0;
}

/**
 * Menu driver waiting for instructions
 */
int menuDriver(struct Connection *new_connection){

	short DO_QUIT_CONNECTION;
	int bytes_received = 1;

	DO_QUIT_CONNECTION = QUIT_FALSE;
 	while (DO_QUIT_CONNECTION){  

		if(bytes_received == 0){
			DO_QUIT_CONNECTION = QUIT_TRUE;
		}else{

			printf(MSG_WAIT_CLIENT);

			bytes_received = recieveData(new_connection);
			if (strcmp(new_connection->recv_data, CODE_LS) == 0){
				sendListDirectoryContents(new_connection);
			}else
			if (strcmp(new_connection->recv_data, CODE_PUT) == 0){
				bytes_received = receiveFileFromClient(new_connection);
			}else{
				close(new_connection->sock);
				fprintf(stderr, MSG_ERR_WRONG_PROTOCOL ' - PUT\n');
				DO_QUIT_CONNECTION = QUIT_TRUE;
			}
		}
	}
	return 0;
}

/**
 * Set up connection with the client
 */
void setUpConnection(struct Connection *new_connection,
										 int port_number){
	debug(DBG_LV0, 'setUpConnection(port_number: %d)', port_number);

	int opt_val = SETSOCKOPT_VAL;
	new_connection->port_number = port_number;

  if ((new_connection->sock = socket(AF_INET, SOCK_STREAM, 0)) == -1)
		errorDoExit(MSG_ERR_COULDNT_SOCKET, new_connection->sock);

	if (setsockopt(new_connection->sock, SOL_SOCKET, SO_REUSEADDR, &opt_val, sizeof(int)) == -1)
		errorDoExit(MSG_ERR_NO_SOCKET_OPT);

	debug(DBG_LV1, 'Got socket!');

  new_connection->server_addr.sin_family = AF_INET;
 	debug(DBG_LV1,'sin_family');      

  new_connection->server_addr.sin_port = htons(port_number);
	debug(DBG_LV1,'sin_port');

  new_connection->server_addr.sin_addr.s_addr = INADDR_ANY;
	debug(DBG_LV1, 'sin_addr');

  bzero(&(new_connection->server_addr.sin_zero), NUM_BYTES);
	debug(DBG_LV1,'sin_zero');

	// bind a name to a socket
  if (bind(new_connection->sock,
  				(struct sockaddr *)&new_connection->server_addr,
  				sizeof(struct sockaddr)) == -1){
		close(new_connection->sock);
		errorDoExit(MSG_ERR_UNABLE_BIND);
	}

	// Listen for connection on the socket
  if (listen(new_connection->sock, MAX_BACKLOG) == -1){
		close(new_connection->sock);
   	errorDoExit(MSG_ERR_UNABLE_LISTEN);
  }
	debug(DBG_LV1, 'Listening...');	

}

/**
 * Send data to client
 */
void sendData(struct Connection *new_connection,
							const char* data){
	debug(DBG_LV0, 'sendData(data: %s)', data);

 	int data_length;

	bzero(new_connection->send_data, DATA_PACKET_SIZE);
 	strcpy(new_connection->send_data, data);
 	data_length = strlen(new_connection->send_data);

 	debug(DBG_LV1, 'new_connection->send_data: %s',
 	      new_connection->send_data);

	if (send(new_connection->socket_descriptor,
					 new_connection->send_data,
					 data_length, 0) != data_length){
		close(new_connection->sock);
		fprintf(stderr, MSG_ERR_SENDING_DATA '\n');
	}
}

/**
 * Receive data from client
 */
int recieveData(struct Connection *new_connection){
	debug(DBG_LV0, 'recieveData()');

	bzero(new_connection->recv_data, DATA_PACKET_SIZE);
	int bytes_recieved = recv(new_connection->socket_descriptor,
														new_connection->recv_data,
														DATA_PACKET_SIZE,
														0);		

	debug(DBG_LV1, 'Check bytes received');

	if(bytes_recieved < 1){

		close(new_connection->sock);

		if(bytes_recieved == 0){
			fprintf(stderr, MSG_ERR_CONNECTION_FAIL '\n');
		}else{
			fprintf(stderr,MSG_ERR_RECEIVING_DATA '\n');
		}

	}else{

		debug(DBG_LV1, 'Received: %s', new_connection->recv_data);

		new_connection->bytes_recieved = bytes_recieved;
  	new_connection->recv_data[bytes_recieved] = '\0';
  }

  return bytes_recieved;
}

/**
 * Hand shake with client to test own protocol
 */
void handShake(struct Connection* new_connection){
	debug(DBG_LV0, 'handShake()');

	printf('Connection from (%s , %d)...\n',
         inet_ntoa(new_connection->client_addr.sin_addr),
         ntohs(new_connection->client_addr.sin_port));

	debug(DBG_LV1,' If client say HELLO, Greed client with WELCOME');
 	recieveData(new_connection);
	if (strcmp(new_connection->recv_data, CODE_HELLO) == 0){
		debug(DBG_LV1, 'Client handshake with server');
		sendData(new_connection, CODE_WELCOME);
	}else{
		close(new_connection->sock);
		fprintf(stderr, MSG_ERR_WRONG_PROTOCOL ' - HELLO/WELCOME \n');
	}

	debug(DBG_LV0,'If client ask for server name send server name to client');
 	recieveData(new_connection);
	if (strcmp(new_connection->recv_data, CODE_REQ_SERVER_NAME) == 0){
		debug(DBG_LV1, 'Client asking for server name');
		sendData(new_connection, MSG_SERVER_NAME);
	}else{
		close(new_connection->sock);
		fprintf(stderr, MSG_ERR_WRONG_PROTOCOL ' - SERVER NAME\n');
	}
}

/**
 * Send list of directory contents to client
 */
void sendListDirectoryContents(struct Connection* new_connection){
	debug(DBG_LV0, 'void displayLocalListDirectoryContents()');
	int bytes_received;
	struct dirent *dirent_struct_ptr;
	DIR *directory_stream_ptr;

	if ((directory_stream_ptr = opendir('./')) != NULL){

  	while ((dirent_struct_ptr = readdir(directory_stream_ptr))){
			sendData(new_connection, dirent_struct_ptr->d_name);				

			bytes_received = recieveData(new_connection);
			if (strcmp(new_connection->recv_data, CODE_OK) != 0){
				fprintf(stderr, MSG_ERR_CANT_SEND_LIST);
			}
   	}
		sendData(new_connection, CODE_EOF);			  		

	}else{
		sendData(new_connection, CODE_ERROR_LS);
		errorDoExit(MSG_ERR_NO_DIR_STREAM);
	}

}

/**
 * Receive encrypted file from client
 */
int receiveFileFromClient(struct Connection* new_connection){
	debug(DBG_LV0, 'void receiveFileFromClient(is_encrypted: %s)');											

	int bytes_received;

	char filename[DATA_PACKET_SIZE];
	char filename_se[DATA_PACKET_SIZE];
	char filename_sd[DATA_PACKET_SIZE];
	char str_unencrypted[DATA_PACKET_SIZE];
	FILE *fp_se, *fp_sd;

	/* Request filename */
	debug(DBG_LV1, 'Request filename from client');
	sendData(new_connection, CODE_REQUEST_FILENAME);

	/* receive filename */
	bytes_received = recieveData(new_connection);
	strcpy(filename, new_connection->recv_data);
	sprintf(filename_se, '%s_se', filename);
	sprintf(filename_sd, '%s_sd', filename);

	debug(DBG_LV1, 'filename: %s', filename);
	debug(DBG_LV1, 'filename_se: %s', filename_se);
	debug(DBG_LV1, 'filename_sd: %s', filename_sd);	

	fp_se = fopen(filename_se, 'w');
	fp_sd = fopen(filename_sd, 'w');
	debug(DBG_LV1, 'Files open');

	if (fp_se == NULL || fp_sd == NULL){
		debug(DBG_LV1, 'Error, closing files');
		fclose(fp_se);
		fclose(fp_sd);
		errorDoExit(MSG_ERROR_CREAT_FILE);
	}else{

		debug(DBG_LV1, 'Download file...');
		/* Send code request file */
		sendData(new_connection, CODE_REQUEST_FILE);				

		bytes_received = recieveData(new_connection);

  	/* while loop until eof */
  	while (strcmp(new_connection->recv_data, CODE_EOF) != 0){

			debug(DBG_LV1, 'Saving: %s', new_connection->recv_data);
	  	/* save encrypted package */
			fprintf(fp_se, '%s', new_connection->recv_data);

			/* Decencrypt package */
			bzero(str_unencrypted, DATA_PACKET_SIZE);
			strdecrypt(new_connection->recv_data, str_unencrypted);
			debug(DBG_LV1, 'Decrypt: %s\n', new_connection->recv_data);

			/* Save unecrypted package */
			debug(DBG_LV1, 'DECRYPT: %s', str_unencrypted);

			fprintf(fp_sd, '%s', str_unencrypted);

			debug(DBG_LV1, 'Sending OK...');
	  	/*send ok */
			sendData(new_connection, CODE_OK);
			bytes_received = recieveData(new_connection);
  	}

	}	

	fclose(fp_se);
	fclose(fp_sd);
	return 0;
}

/**
 * Decrypt string
 */
int strdecrypt(const char* str_in,
							 char* str_out){
	debug(DBG_LV0, 'strdencrypt(str_in: %s)', str_in);

	int i, j;
	int i_cipher, i_key, i_temp;
	char str_key[DATA_PACKET_SIZE];
	strcpy(str_key, ENCRYPTION_KEY);
	char char_temp;
	for (i = 0, j = 0; i < strlen(str_in); ++i, ++j){

		i_cipher = str_in[i] - CHAR_CIPHER_A;
		i_key = str_key[j] - CHAR_KEY_A;
		i_temp = i_cipher - i_key;

		debug(DBG_LV1, 'Ci([%c]%d): %d, Ki([%c]%d): %d, Ci - Ki: %d',
					str_in[i], i, i_cipher, str_key[j], j, i_key, i_temp);

		if (i_temp > -1){
			//COMMON KNOWN DECRYPT ALGORIGHTM
			i_temp = ((i_temp) % 26);
		}else{
			//DECRYPT ALGORITHM FOR UNCOMMON CASES WHERE I_TEMP IS NEGATIVE
			i_temp = ((i_temp + 26) % 26);
		}

		char_temp = i_temp + CHAR_PLAIN_A;
		if (isalpha(char_temp)){
			str_out[i] = char_temp;
		}else{
			str_out[i] = '\0';
		}

		if ( (j + 1) >= strlen(str_key)) j = -1;
	}

	return 0;
}

The following is the code for the client.

cli.c

/**
 * Assignment: 1
 * Course: CS458
 * Author: Alejandro G. Carlstein
 * Description: Transfer Encrypted File Client
 */

#include 'default.h'

#define DEFAULT_HOST 									'localhost'
#define EXIT_PROGRAM 									0
#define DONT_EXIT_PROGRAM 						1

#define CMD_QUIT											'quit'
#define CMD_HELP											'help'
#define CMD_LS												'ls'
#define CMD_LLS												'lls'
#define CMD_LPWD											'lpwd'
#define CMD_PUT												'put'

#define MSG_DISPLAY_MENU 							'User help:\n'\
																			'help - Display help option\n'\
																		  'lls  - Display local directory list contents\n'\
                    				          'lpwd - Display local current directory\n'\
                    				          'put  - Transfer local file to server\n'\
                    				          '  put <file to transfer>\n'\
                    				          'quit - Quit program\n'

#define MSG_ERROR_UNKNOWN_COMMAND			'[X] Command not recognized: %s\n'
#define MSG_ERROR_GETTING_HOSTNAME 		'Couln't get hostname!'
#define MSG_ERROR_SERVER_DISCONNECTED 'Server disconnected...\n'

enum TOKENS{
	TOKEN_COMMAND,
	TOKEN_FILENAME,
	MAX_TOKENS
};

struct Connection{
	int sock;
	int bytes_received;
	int port_number;
	char send_data[DATA_PACKET_SIZE];
	char recv_data[DATA_PACKET_SIZE];
	struct hostent *host;
  struct sockaddr_in server_addr;
};

void getConnection(struct Connection *new_connection,
									 char *hostname,
									 int port_number);
void menuDriver(struct Connection *new_connection);
int receiveData(struct Connection *new_connection);
int sendData(struct Connection* new_connection, const char* data);
void handShake(struct Connection* new_connection);
void promptUser(char* tokens[]);
void displayHelp(void);
void displayLocalListDirectoryContents(void);
void printNameCurrentDirectory(void);
void receiveListDirectoryContents(struct Connection *new_connection);
void putFileOnServer(struct Connection *new_connection,
										 const char* filename);
int strencrypt(const char* str_in,
               char* str_out);
/**
 * MAIN
 */
int main(int argc, char* argv[]){
	debug(DBG_LV0, 'argc: %d', argc); 	

	int i;
	int port_number = (argc >= 3 ) ? atoi(argv[2]) : DEFAULT_PORT;
	char* host_name = (argc >= 2) ? argv[1] : DEFAULT_HOST;

	struct Connection connection;

	for(i = 0; i < argc; ++i)
		debug(DBG_LV0, 'argv[%d]: %s', i, argv[i]); 		

	if (argc > 3)
		errorDoExit(MSG_PORT_NUMBER_ONLY, argv[0]);

	debug(DBG_LV1, 'host: %s', host_name);

	getConnection(&connection, host_name, port_number);

	handShake(&connection);

	menuDriver(&connection);

	return 0;

}

/**
 * Driver menu
 */
void menuDriver(struct Connection *new_connection){
	debug(DBG_LV0, 'menuDriver()');

	short doExit = DONT_EXIT_PROGRAM;
	int i;
	char* tokens[MAX_TOKENS] = {NULL, NULL};
	char command[DATA_PACKET_SIZE];
	char filename[DATA_PACKET_SIZE];

  while(doExit){

		promptUser(tokens);				

		for (i = 0; DBG_LV1 && i < MAX_TOKENS; ++i)
			debug(DBG_LV1, 'TOKEN[%d]: %s', i, tokens[i]);

		/* Must copy string to work on SunOS */
		if (tokens[TOKEN_COMMAND] != NULL){
			strcpy(command, tokens[TOKEN_COMMAND]);
   		debug(DBG_LV1, '[Command: %s]', command);
		}

		if (tokens[TOKEN_FILENAME] != NULL){
    	strcpy(filename, tokens[TOKEN_FILENAME]);
	    debug(DBG_LV1, '[Filename: %s]', filename);
    }

		/* MENU */

		if (strcmp(command, CMD_QUIT) == 0){
			debug(DBG_LV1, 'COMMAND> %s: ', CMD_QUIT);
			close(new_connection->sock);
			printf('Bye Bye\n');
			doExit = EXIT_PROGRAM;

		}else
		if (strcmp(command, 'help') == 0){
			debug(DBG_LV1, 'COMMAND> %s ', CMD_HELP);
			displayHelp();

		}else
		if (strcmp(command, CMD_LLS) == 0){
			debug(DBG_LV1, 'COMMAND> %s ', CMD_LLS);
			displayLocalListDirectoryContents();

		}else
		if (strcmp(command, CMD_LLS) == 0){
			debug(DBG_LV1, 'COMMAND> %s ', CMD_LLS);
			displayLocalListDirectoryContents();

		}else
		if (strcmp(command, CMD_LS) == 0){
			debug(DBG_LV1, 'COMMAND> %s ', CMD_LS);
			receiveListDirectoryContents(new_connection);

		}else
		if (strcmp(command, CMD_LPWD) == 0){
			debug(DBG_LV1, 'COMMAND> %s ', CMD_LPWD);
			printNameCurrentDirectory();

		}else
		if (strcmp(command, CMD_PUT) == 0){
			debug(DBG_LV1, 'COMMAND> %s ', CMD_PUT);
			putFileOnServer(new_connection, filename);

		}else{
			printf(MSG_ERROR_UNKNOWN_COMMAND, command);
		}

	}

}

/**
 * Obtain connection with server
 */
void getConnection(struct Connection *new_connection,
									 char *hostname,
									 int port_number){
	debug(DBG_LV0, 'getConnection(hostname: %s, port_number: %d)', hostname, port_number);

	if ((new_connection->host = gethostbyname(hostname)) == NULL)
		errorDoExit(MSG_ERROR_GETTING_HOSTNAME);

	new_connection->port_number = port_number;	

	if ((new_connection->sock = socket(AF_INET, SOCK_STREAM, 0)) == -1)
		errorDoExit(MSG_ERR_COULDNT_SOCKET, new_connection->sock);

	debug(DBG_LV1, 'Got socket!');

  new_connection->server_addr.sin_family = AF_INET;
	debug(DBG_LV1,'sin_family');

  new_connection->server_addr.sin_port = htons(port_number);
	debug(DBG_LV1,'sin_port');

  new_connection->server_addr.sin_addr = *((struct in_addr *)new_connection->host->h_addr);
	debug(DBG_LV1, 'sin_addr');

	// bzero() is used only for setting the values to zero
	bzero(&(new_connection->server_addr.sin_zero), NUM_BYTES);
	debug(DBG_LV1,'sin_zero');

  if (connect(new_connection->sock,
  					  (struct sockaddr *)&new_connection->server_addr,
  					  sizeof(struct sockaddr)) == -1)
		errorDoExit(MSG_ERR_COULDNT_CONNECT);

	debug(DBG_LV1, 'Got connection!');
}

/**
 * Receive data
 * @Return: Number of bytes received
 */
int receiveData(struct Connection *new_connection){
	debug(DBG_LV0, 'receiveData()');

	bzero(new_connection->recv_data, DATA_PACKET_SIZE);
	int bytes_received = recv(new_connection->sock,
														new_connection->recv_data,
														DATA_PACKET_SIZE,
														0);		

	if(bytes_received < 1){
		close(new_connection->sock);
		if(bytes_received == 0){
			close(new_connection->sock);
			printf(MSG_ERROR_SERVER_DISCONNECTED);
			errorDoExit(MSG_ERR_CONNECTION_FAIL);		

		}else{
			errorDoExit(MSG_ERR_RECEIVING_DATA);
		}
	}

	debug(DBG_LV1, 'Received (%d): %s',
				strlen(new_connection->recv_data), new_connection->recv_data);	

	new_connection->bytes_received = bytes_received;
  new_connection->recv_data[bytes_received] = '\0'; 

  return bytes_received;
}

/**
 * Send data
 * @Return: Number of bytes sended
 */
int sendData(struct Connection* new_connection,
						const char* data){
	debug(DBG_LV0, 'sendData(%s)', data);
	int data_length;

	bzero(new_connection->send_data, DATA_PACKET_SIZE);
	strcpy(new_connection->send_data, data);
	data_length = strlen(new_connection->send_data);

	if (send(new_connection->sock, new_connection->send_data,
					 data_length, 0) != data_length){
		close(new_connection->sock);
		errorDoExit(MSG_ERR_SENDING_DATA);
	}

	return data_length;
}

/**
 * Hand shake with the server to verify own protocol
 */
void handShake(struct Connection* new_connection){
	debug(DBG_LV0, 'handShake()');

	debug(DBG_LV1, 'Sending HELLO message...');
	sendData(new_connection, CODE_HELLO);

	debug(DBG_LV1, 'Getting WELCOME message...');
	receiveData(new_connection);  		

	if (strcmp(new_connection->recv_data, CODE_WELCOME) > -1){
		debug(DBG_LV1, 'Got WELCOME!');

		debug(DBG_LV1, 'Request server name');
		sendData(new_connection, CODE_REQ_SERVER_NAME);

		receiveData(new_connection);
		printf('Server Name: %s\n', new_connection->recv_data);
	}else{
		errorDoExit(MSG_ERR_WRONG_PROTOCOL);
	}
}

/**
 * Prompt user for input
 */
void promptUser(char* tokens[]){
	char str_input[DATA_PACKET_SIZE];
	char *p;
	int i;

	printf('\nftp> ');
	fgets(str_input, DATA_PACKET_SIZE, stdin);		

	p = strtok(str_input, ' \n');
  for (i = 0; p != NULL; (p = strtok(NULL, ' \n')), i++)
		tokens[i] = p;
}

/**
 * Display help message
 */
void displayHelp(void){
	debug(DBG_LV0, 'displayHelp()');
	printf(MSG_DISPLAY_MENU);
}

/**
 * Display local list directory contents
 */
void displayLocalListDirectoryContents(void){
	debug(DBG_LV0, 'void displayLocalListDirectoryContents()');

	struct dirent *dirent_struct_ptr;
	DIR *directory_stream_ptr;

	if ((directory_stream_ptr = opendir('./')) != NULL){
		printNameCurrentDirectory();

  	while ((dirent_struct_ptr = readdir(directory_stream_ptr))){
	  	printf(' %s\n',dirent_struct_ptr->d_name);
   	}
	}else{
		errorDoExit(MSG_ERR_NO_DIR_STREAM);
	}

}

/**
 * Print the name of the current directory
 */
void printNameCurrentDirectory(void){
	debug(DBG_LV0, 'void printNameCurrentDirectory()');

	long size;
	char *buf;
	char *ptr;
	size = pathconf('.', _PC_PATH_MAX);
	if ((buf = (char *)malloc((size_t)size)) != NULL){
		ptr = getcwd(buf, (size_t)size);
	}else{
		errorDoExit('Could not obtain the current directory');
	}
	printf('Current Directory: %s\n', ptr);
}

/**
 * Receive the list of directory contents from the server
 */
void receiveListDirectoryContents(struct Connection *new_connection){
	debug(DBG_LV0, 'receiveListDirectoryContents()');

	sendData(new_connection, CODE_LS);

	receiveData(new_connection);
	while (strcmp(new_connection->recv_data, CODE_EOF) != 0){

		if (strcmp(new_connection->recv_data, CODE_ERROR_LS) == 0){
			fprintf(stderr, 'Couldn't read list directory contents!\n');
			break;
		}else{
			printf('%s\n', new_connection->recv_data);
			sendData(new_connection, CODE_OK);
		}
		receiveData(new_connection);
	}

}

/**
 * Put an encrypted file on the server
 */
void putFileOnServer(struct Connection *new_connection,
										 const char* filename){
	debug(DBG_LV0, 'void putFileOnServer(filename: %s)', filename);

	char str_filename[DATA_PACKET_SIZE];
	char filename_ce[DATA_PACKET_SIZE];
	char str_in_file[DATA_PACKET_SIZE];
	char str_in_file_encrypt[DATA_PACKET_SIZE];
	FILE *fp, *fp_ce;

	strcpy(str_filename, filename);

	debug(DBG_LV1, 'str_filename: %s', str_filename);

	sprintf(filename_ce, '%s_ce', str_filename);

	debug(DBG_LV1, 'filename_ce: %s', filename_ce);

	debug(DBG_LV1, 'OPEN FILE TO READ');
	fp = fopen(str_filename, 'r');

	debug(DBG_LV1, 'OPEN FILE TO WRITE');
	fp_ce = fopen(filename_ce, 'w');

	if (fp == NULL || fp_ce == NULL){
		fclose(fp_ce);
		fclose(fp);
		errorDoExit('Couldn't open file');
	}else{

		/* Send PUT code to server */
		sendData(new_connection, CODE_PUT);

		debug(DBG_LV0, 'Waiting for CODE_REQUEST_FILENAME');
		/* Receive filename request form server */
		receiveData(new_connection);
		if (strcmp(new_connection->recv_data, CODE_REQUEST_FILENAME) == 0){

			debug(DBG_LV0, 'Send filename');
			/* Send filename to server */
			sendData(new_connection, str_filename);

			/* Receive request to send file */
			receiveData(new_connection);
			if (strcmp(new_connection->recv_data, CODE_REQUEST_FILE) == 0){
				printf('Sending file: %s.\n', filename);
					/* Encrypt file */
				while(fgets(str_in_file, DATA_PACKET_SIZE, fp) != NULL){
					debug(DBG_LV2, 'Reading (Len: %d): %s', strlen(str_in_file), str_in_file);						

					/* encrypt package */
					strencrypt(str_in_file, str_in_file_encrypt);

					/* Save it in <filename>_ce */
					fprintf(fp_ce, '%s', str_in_file_encrypt);					

					/* send packages */
					sendData(new_connection, str_in_file_encrypt);

					/* Waiting for ok */
					receiveData(new_connection);
					if (strcmp(new_connection->recv_data, CODE_OK) == 0){
						printf('OK');
					}else{
						errorDoExit('Wrong protocol - Waiting OK');
					}	

				}
				sendData(new_connection, CODE_EOF);			

			}else{
				errorDoExit('Wrong protocol - Waiting request for file ');
			}

		}else{
			errorDoExit('Wrong protocol - Waiting for request of filename');
		}

	}

	fclose(fp);
	fclose(fp_ce);

}

/**
 * Encrypt the string using key
 * @Return: Number of bytes encrypted
 */
int strencrypt(const char* str_in,
               char* str_out){
	debug(DBG_LV0, 'strencrypt(str_in: %s)', str_in);

	int i, j;
	int i_plain, i_key;

	char str_key[DATA_PACKET_SIZE];

	strcpy(str_key, ENCRYPTION_KEY);

	for (i = 0, j = 0; i < strlen(str_in) - 1; ++i, ++j){

		i_plain = (unsigned int)str_in[i] - CHAR_PLAIN_A;
		i_key = (unsigned int)str_key[j] - CHAR_KEY_A;

		str_out[i] = (char)((i_plain + i_key) % 26 + CHAR_CIPHER_A);

		debug(DBG_LV1, '(str_out: %d) [%c:%d] = (str_in:%d)[%c:%d], (key:%d)[%c:%d]',
					i, str_out[i], (int)str_out[i],
					i, str_in[i], (int) str_in[i],
					j, str_key[j], (int)str_key[j]);

		if ( (j + 1) >= strlen(str_key)) j = -1;
	}

	return strlen(str_in);
}

When I wrote the code, I was following certain guidelines. As you may think the code could be written better. I agree.

In case you have any questions about the code please post them. I will try to answer you as soon as possible.

Share