-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathftserver.c
More file actions
385 lines (335 loc) · 11.1 KB
/
Copy pathftserver.c
File metadata and controls
385 lines (335 loc) · 11.1 KB
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
/* ftserver
* CS372 Spring 2018
* -----------------
* Name: Zachary Thomas
* Email: thomasza@oregonstate.edu
* Date: 5/19/2018
* -----------------
* Starts up a server that listens
* for control connections on a control port.
* After receiving a connection will communicate with
* client and take request. If a file is requested
* that file is sent over a new connection on the data port.
* after request has been handled the server closes the
* control and data port connections and waits for a new
* request.
* -----------------------
* Cited references:
*
* Reviewed my code from my
* CS344 OTP socket programming
* assignment.
* https://github.com/silverware13/OTP/blob/master/otp_enc_d.c
*
* Reviewed my code from our last assignment (project1)
* https://github.com/silverware13/Chat/blob/master/chatclient.c
*
* Found a refrence to how to use dirent.h to list the content of directories.
* https://www.geeksforgeeks.org/c-program-list-files-sub-directories-directory/
*/
#define MAX_CHARS_MESSAGE 900000000
#define MAX_CHARS_FILE_NAME 10000
#define MAX_FIELD_SIZE 100
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdbool.h>
#include <dirent.h>
//function prototypes
bool checkArgs(int argc, char *argv[]);
void startup(int controlPort);
void handleRequest(int controlConnection, int controlPort, char *clientName);
void fileTransfer(char *buffer, char type, int dataPort, char *fileName);
int main(int argc, char *argv[])
{
//make sure the user passed valid arguments
if(!checkArgs(argc, argv)){
return 0;
}
//set port number
int controlPort = strtol(argv[1], NULL, 10);
//setup the server and handle all incoming commands
startup(controlPort);
return 0;
}
/* Function: checkArgs
* --------------------
* Checks to make sure there are two arguments.
* Makes sure the portnumber is an unsigned int.
*
* argc: The number of command line arguments.
* *argv[]: Array of command line arguments.
*
* returns: Returns true if correct number of
* valid arguments, otherwise returns false.
*/
bool checkArgs(int argc, char *argv[])
{
//make sure the user entered correct number of arguments.
if(argc != 2){
printf("Useage: %s [port number]\n", argv[0]);
return false;
}
//make sure the user has entered the port number as digits
if(!isdigit(*argv[1])){
printf("Please enter port number as an unsigned integer.\n");
return false;
}
return true;
}
/* Function: startup
* --------------------------
* Starts up the server and listens
* for control connections on the given port
* number.
*
* controlPort: The port number given on command line.
*/
void startup(int controlPort)
{
//setup variables
int socketFD, listenSocket, controlConnection;
struct sockaddr_in serverAddress, clientAddress;
struct hostent* server_host_info;
socklen_t size_client_info;
//set up the server address struct
memset((char*)&serverAddress, '\0', sizeof(serverAddress)); //clear address struct
serverAddress.sin_family = AF_INET; //set address family
serverAddress.sin_port = htons(controlPort); //save port number
serverAddress.sin_addr.s_addr = INADDR_ANY; //we allow controlConnection from any address
//set up socket
listenSocket = socket(AF_INET, SOCK_STREAM, 0); //create the socket
//start listening with current socket for any incoming controlConnections
bind(listenSocket, (struct sockaddr *)&serverAddress, sizeof(serverAddress)); //connect socket to port
listen(listenSocket, 10); //socket is now listening for a controlConnection
printf("Server open on %d\n\n", controlPort);
//we will keep looking for control connections until we are terminated
while(1){
//accept the next control connection
size_client_info = sizeof(clientAddress); //get the size of the address for the client that will connect
controlConnection = accept(listenSocket, (struct sockaddr *)&clientAddress, &size_client_info); //accept
//fork the accepted connection
pid_t spawnPID = -5;
spawnPID = fork();
//check for error, child, parent
switch(spawnPID){
//fork failed
case -1:{
fprintf(stderr, "Error trying to fork.\n");
exit(2);
}
//the child handles the request
case 0:{
//show the connecting client
char clientName[INET_ADDRSTRLEN];
memset(clientName, '\0', INET_ADDRSTRLEN);
inet_ntop(AF_INET, &clientAddress.sin_addr.s_addr, clientName, sizeof(clientName));
printf("Connection from %s\n", clientName);
//react to clients command
handleRequest(controlConnection, controlPort, clientName);
//close the connection
close(controlConnection);
return;
}
}
}
close(listenSocket);
}
/* Function: handleRequest
* --------------------------
* Receive a request from the client,
* interprets the request, and then send an
* appropriate response.
*
* controlConnection: The connection number.
* controlPort: The connection port.
* clientName: A string of the clients address.
*/
void handleRequest(int controlConnection, int controlPort, char *clientName)
{
//setup variables
int bufLen, charsRead;
char portBuffer[MAX_FIELD_SIZE] = {0};
char serverName[MAX_FIELD_SIZE] = {0};
int bufSum = 0; //the number of chars we have writen to our buffer
char *fileName;
fileName = (char *)malloc(MAX_FIELD_SIZE * sizeof(char));
memset(fileName, '\0', MAX_FIELD_SIZE);
char *buffer;
buffer = (char *)malloc(MAX_CHARS_MESSAGE * sizeof(char));
memset(buffer, '\0', MAX_CHARS_MESSAGE);
//read message from the client
do{
charsRead = recv(controlConnection, &buffer[bufSum], 100, 0); //read from socket
bufSum += charsRead;
bufLen = strlen(buffer);
if(charsRead < 0){
fprintf(stderr, "Error reading from socket.\n");
exit(2);
}
} while(buffer[bufLen - 1] != '\n');
//get the data port
int i = 0;
do{
portBuffer[i] = buffer[i];
bufLen = strlen(portBuffer);
i++;
if(i == MAX_FIELD_SIZE) break;
} while(portBuffer[bufLen - 2] != '-');
//get the type of request
char type = portBuffer[bufLen - 1];
//remove the - from the portBuffer
portBuffer[bufLen - 2] = '\0';
int dataPort = strtol(portBuffer, NULL, 10);
//get the server name
int ii = 0;
do{
serverName[ii] = buffer[i];
bufLen = strlen(serverName);
i++;
ii++;
if(ii == MAX_FIELD_SIZE) break;
} while(serverName[bufLen - 1] != '@');
serverName[bufLen - 1] = '\0';
//get requested file name if not list mode
ii = 0;
if(type == 'g'){
do{
fileName[ii] = buffer[i];
bufLen = strlen(fileName);
i++;
ii++;
if(ii == MAX_FIELD_SIZE) break;
} while(fileName[bufLen - 1] != '\n');
fileName[bufLen - 1] = '\0';
}
//calculate how request should be handled
if(type == 'l'){
printf("List directory requested on port %d\n", dataPort);
printf("Sending directory contents to %s:%d\n\n", clientName, dataPort);
memset(buffer, '\0', MAX_CHARS_MESSAGE);
sprintf(buffer, "Receiving directory structure from %s:%d", serverName, dataPort);
} else {
printf("File \"%s\" requested on port %d\n", fileName, dataPort);
//confirm that the file exists
if(access(fileName, F_OK) != -1){
printf("Sending \"%s'\" to %s:%d\n\n", fileName, clientName, dataPort);
memset(buffer, '\0', MAX_CHARS_MESSAGE);
sprintf(buffer, "Receiving \"%s\" from %s:%d", fileName, serverName, dataPort);
} else {
type = 'n';
printf("File not found. Sending error message to %s:%d\n\n", clientName, dataPort);
memset(buffer, '\0', MAX_CHARS_MESSAGE);
sprintf(buffer, "%s:%d says FILE NOT FOUND", serverName, controlPort);
}
}
//add a special end of file string
sprintf(buffer + strlen(buffer), "!$@$!");
//send message to client on control connection
int charsWritten = 0;
do{
charsWritten += send(controlConnection, buffer, strlen(buffer), 0); //write to socket
if(charsWritten < 0){
fprintf(stderr, "Error writing to socket.\n");
exit(2);
}
} while(charsWritten < strlen(buffer));
//transfer the file
fileTransfer(buffer, type, dataPort, fileName);
}
/* Function: fileTransfer
* --------------------------
* Creates a data connection.
* Sends a file to the client and
* then closes the data connection.
*
* buffer: A buffer that holds file info or listing info to be sent to the client.
* type: The type of request sent.
* dataPort: The port number for the data port.
* fileName: The name of the requested file.
*/
void fileTransfer(char *buffer, char type, int dataPort, char *fileName)
{
//setup variables
int socketFD, listenSocket, dataConnection;
struct sockaddr_in serverAddress, clientAddress;
struct hostent* server_host_info;
socklen_t size_client_info;
//set up the server address struct
memset((char*)&serverAddress, '\0', sizeof(serverAddress)); //clear address struct
serverAddress.sin_family = AF_INET; //set address family
serverAddress.sin_port = htons(dataPort); //save port number
serverAddress.sin_addr.s_addr = INADDR_ANY; //we allow connection from any address
//set up socket
listenSocket = socket(AF_INET, SOCK_STREAM, 0); //create the socket
if(listenSocket < 0){
fprintf(stderr, "Error on data socket\n");
return;
}
//allow reusing the socket
int set = 1;
setsockopt(listenSocket, SOL_SOCKET, SO_REUSEADDR, &set, sizeof(int));
//start listening with current socket for any incoming controlConnections
bind(listenSocket, (struct sockaddr *)&serverAddress, sizeof(serverAddress)); //connect socket to port
listen(listenSocket, 10); //socket is now listening for a dataConnection
//accept the next connection
size_client_info = sizeof(clientAddress); //get the size of the address for the client that will connect
dataConnection = accept(listenSocket, (struct sockaddr *)&clientAddress, &size_client_info); //accept
if(dataConnection < 0){
fprintf(stderr, "Error on data accept\n");
return;
}
//get data that needs to be sent
if(type == 'l'){
//get the contents of current directory and put it into the buffer
char *bufferPtr = buffer;
struct dirent *dir_entry;
DIR *dir = opendir(".");
while((dir_entry = readdir(dir)) != NULL) {
bufferPtr += sprintf(bufferPtr, "%s ", dir_entry->d_name);
}
//add a special end of file string
sprintf(buffer + strlen(buffer), "!$@$!");
} else if (type == 'g'){
//get the contents of file and put it into the buffer
memset(buffer, '\0', MAX_CHARS_MESSAGE);
//open the file
FILE *fp = fopen(fileName, "r");
if(!fp) fprintf(stderr, "Error could not open file");
//get the size of the file
int fileSize;
fseek(fp, 0L, SEEK_END);
fileSize = ftell(fp);
fseek(fp, 0L, SEEK_SET);
//read the file into buffer
fread(buffer, sizeof(char), fileSize, fp);
//add a special end of file string
sprintf(buffer + strlen(buffer), "!$@$!");
//close the file
fclose(fp);
} else {
//no valid file to send
close(listenSocket);
close(dataConnection);
return;
}
//send file to client
int charsWritten = 0;
do{
charsWritten += send(dataConnection, buffer, strlen(buffer), 0); //write to socket
if(charsWritten < 0){
fprintf(stderr, "Error writing to socket.\n");
exit(2);
}
} while(charsWritten < strlen(buffer));
close(listenSocket);
close(dataConnection);
//free memory
free(fileName);
free(buffer);
}