This repository has been archived by the owner on Mar 20, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdec_server.c
242 lines (214 loc) · 8.22 KB
/
dec_server.c
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
/**
* @file dec_server.c
* @brief Client program that connects to the dec_client and recieves a plaintext and key to be decrypted.
*
* This program connects to the dec_client on a specified port and recieves a plaintext and key to be decrypted. The program validates that the client it is connected to is the dec_client before recieving data.
*
* @author: Nils Streedain
* @date [3/3/2023]
*/
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#define BUFFER_SIZE 1000
/**
* @brief Reports an error message to the standard error output and exits the program.
*
* @param exitCode The exit code to exit the program with.
* @param format The format string for the error message.
* @param ... Additional arguments to be included in the error message.
*
* @return Does not return; exits the program.
*/
int error(int exitCode, const char *format, ...) {
// Retrieve additional arguments
va_list args;
va_start(args, format);
// Print error to stderr
fprintf(stderr, "Client error: ");
vfprintf(stderr, format, args);
fprintf(stderr, "\n");
// End var arg list & exit
va_end(args);
exit(exitCode);
}
/**
* @brief Sets up a sockaddr_in struct with the given port number and hostname.
*
* This function clears out the given address struct and sets its sin_family to AF_INET, indicating that it is network capable. It then stores the given port number in the sin_port field of the address struct. Next, it uses the gethostbyname() function to retrieve information about the given hostname, and allows a client at any address to connect to the server.
*
* @param address A pointer to the sockaddr_in struct to be set up.
* @param portNumber The port number to be stored in the sin_port field of the address struct.
*/
void setupAddressStruct(struct sockaddr_in* address, int portNumber){
// Clear out the address struct
memset((char*) address, '\0', sizeof(*address));
// The address should be network capable
address->sin_family = AF_INET;
// Store the port number
address->sin_port = htons(portNumber);
// Allow a client at any address to connect to this server
address->sin_addr.s_addr = INADDR_ANY;
}
/**
* @brief Sends data over a socket in multiple smaller chunks to prevent exceeding the buffer size.
*
* First, the function sends the length of the data as an integer, then it sends the data in smaller chunks of size BUFFER_SIZE or less. If an error occurs during sending, the function will exit with an error code of 1.
*
* @param sock The socket to send data over
* @param data The data to send
* @pre The socket is connected and able to send data
* @post The entire data will be sent over the socket in multiple smaller
*/
void sendData(int sock, char* data) {
// Get length of data
int len = (int)strlen(data);
if (send(sock, &len, sizeof(len), 0) < 0)
error(1, "Unable to write to socket");
// Loop over send() for len amount of data
int charsSent;
for (int i = 0; i < len; i += charsSent) {
int remaining = len - i;
charsSent = remaining < BUFFER_SIZE ? remaining : BUFFER_SIZE;
if (send(sock, data + i, charsSent, 0) < 0)
error(1, "Unable to write to socket");
}
}
/**
* @brief Receives data over a socket in multiple smaller chunks to prevent exceeding the buffer size.
*
* First, the function receives the length of the data as an integer, then it receives the data in smaller chunks of size BUFFER_SIZE - 1 or less. If an error occurs during receiving or memory allocation, the function will exit with an error code of 1.
*
* @param sock The socket to receive data from
* @return A pointer to a string of received data. The string must be freed by the caller when no longer needed.
* @pre The socket is connected and able to receive data
* @post The entire data will be received over the socket in multiple smaller chunks of size BUFFER_SIZE - 1 or less, and returned as a string
*/
char* receive(int sock) {
// Get length of data
int len;
if (recv(sock, &len, sizeof(len), 0) < 0)
error(1, "Unable to read from socket");
// Init output
char* result = malloc(len + 1);
if (!result)
error(1, "Unable to allocate memory");
// Loop over recv() for len amount of data
int charsRead;
for (int i = 0; i < len; i += charsRead) {
int size = len - i > BUFFER_SIZE - 1 ? BUFFER_SIZE - 1 : len - i;
charsRead = (int)recv(sock, result + i, size, 0);
if (charsRead < 0)
error(1, "Unable to read from socket");
}
result[len] = '\0';
return result;
}
/**
* @brief Validates whether the given socket is connected to an enc_client
*
* Recieves a "dec" message from the socket and sends a response to the client. If the response is not "dec", the function will close the socket and exit with an error code of 1.
*
* @param sock The socket to validate
* @pre The socket is connected and able to send/receive data
* @post The socket will be closed if the server's response is not "enc"
*/
void validate(int sock) {
// Init client/server validation vars
char client[4], server[4] = "dec";
memset(client, '\0', sizeof(client));
// Recieve validation from client
if (recv(sock, client, sizeof(client), 0) < 0)
error(1, "Unable to read from socket");
// Send validation to client
if (send(sock, server, sizeof(server), 0) < 0)
error(1, "Unable to write to socket");
// Check client validation
if (strcmp(client, server)) {
close(sock);
error(2, "Client not dec_client");
}
}
/**
* @brief Handles a single one-time pad communication.
*
* This function receives plaintext and key from the given socket, decodes the plaintext using the one-time pad encryption algorithm, sends the resulting ciphertext back to the client through the socket, and closes the socket.
*
* @param sock The socket to use for communication.
*/
void handleOtpComm(int sock) {
// Init dec vars
char* enc = receive(sock);
char* key = receive(sock);
int len = (int)strlen(enc);
char* result = (char*) malloc(len + 1);
// Perform decryption
for (int i = 0; i < len; i++) {
int encVal = enc[i] == ' ' ? 26 : enc[i] - 'A';
int keyVal = key[i] == ' ' ? 26 : key[i] - 'A';
int txtVal = abs(encVal - keyVal + 27) % 27;
result[i] = txtVal == 26 ? ' ' : txtVal + 'A';
}
result[len] = '\0';
// Send decryted text back, free data & close socket
sendData(sock, result);
free(result);
free(enc);
free(key);
close(sock);
}
/**
* @brief The main function for the decryption server.
*
* The function starts by validating the client connection using the validate() function, and then enters an infinite loop, where it receives data from the client using the receive() function, performs decryption on the data, and then sends the decrypted data back to the client using the sendData() function. The server will exit the loop and close the connection if it receives a termination message from the client.
*
* @param argc The number of command-line arguments.
* @param argv An array of strings containing the command-line arguments.
* @return 0 if the program exits normally, and a non-zero integer if an error occurs.
*/
int main(int argc, const char * argv[]) {
// Check usage & args
if (argc < 2)
error(1, "USAGE: %s port\n", argv[0]);
// Create the socket that will listen for connections
int listenSock = socket(AF_INET, SOCK_STREAM, 0);
if (listenSock < 0)
error(1, "Unable to open socket");
// Set up the address struct for the server socket
struct sockaddr_in server, client;
socklen_t clientSize = sizeof(client);
setupAddressStruct(&server, atoi(argv[1]));
// Associate the socket to the port
if (bind(listenSock, (struct sockaddr *) &server, sizeof(server)) < 0)
error(1, "Unable to bind socket");
// Start listening for connetions. Allow up to 5 connections to queue up
listen(listenSock, 5);
while (1) {
// Accept the connection request which creates a connection socket
int sock = accept(listenSock, (struct sockaddr *)&client, &clientSize);
if (sock < 0)
error(1, "Unable to accept connection");
// Fork children to handle client connections
int pid = fork();
switch (pid) {
case -1:
// Fork error
error(1, "Unable to fork child");
break;
case 0:
// Child case
validate(sock);
handleOtpComm(sock);
exit(0);
default:
// Parent case
close(sock);
}
}
// Close the listening socket
close(listenSock);
return 0;
}