-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdfs.c
More file actions
361 lines (316 loc) · 11.3 KB
/
dfs.c
File metadata and controls
361 lines (316 loc) · 11.3 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
/* ***********************************
* dfs.c - A Distributed File System Server
* usage: ./dfs <port>
* Program by Josh Miltier
*********************************** */
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h> /* for fgets */
#include <strings.h> /* for bzero, bcopy */
#include <unistd.h> /* for read, write */
#include <sys/socket.h> /* for socket use */
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <dirent.h> /* for directory */
#include <fcntl.h> /* for open */
#include <sys/types.h> /* for open (UNIX)*/
#include <sys/stat.h> /* for open (UNIX)*/
#include <signal.h> /* to gracefully stop */
#include <limits.h>
#include <time.h> /* for time keeping */
#define BUFSIZE 4096
#define MAXLINE 8192 /* max text line length */
#define MAXBUF 8192 /* max I/O buffer size */
#define LISTENQ 1024 /* second argument to listen() */
#define TYPELENGTH 7 /* number of file types */
#define CONF_FILE "dfs.conf" /* server config file */
#define USERS 2 /* static number of users */
int login_auth = 0; // login tracker
char server_name[10]; // file directory
char username[USERS][30], password[USERS][30];
char *file_types[TYPELENGTH] = {"html", "txt", "png", "gif", "jpg", "css", "js"};
char *content_types[TYPELENGTH] = {"text/html", "text/plain", "image/png", "image/gif",
"image/jpg", "text/css", "application/javascript"};
volatile sig_atomic_t done = 0;
/* function references to top */
int open_listenfd(int port);
void *thread(void *vargp);
void parse_config_file();
void error500(char buf[MAXLINE], int connfd);
void term(int signum);
void server_res(int n);
char *getcwd(char *buf, size_t size);
void error(char *msg);
int main(int argc, char **argv) {
socklen_t clientlen; // byte size of client's address
struct sockaddr_in clientaddr; // client addr
char buf[BUFSIZE]; // message buf
int optval; // flag value for setsockopt
int n, snd; // message byte size
struct stat st = {0}; // for creating user file structures
char cmd_in[10], filename_in[30]; // incoming command and filename
int listenfd, *connfdp, port;
pthread_t tid;
// check command line arguments
if (argc != 3) {
fprintf(stderr, "usage: %s <server> <port>\n", argv[0]);
exit(1);
}
strcpy(server_name, argv[1]);
port = atoi(argv[2]);
// create the server folder if it doesn't exist
char cwd[BUFSIZE];
getcwd(cwd, sizeof(cwd));
strcat(cwd, "/");
strcat(cwd, server_name);
if (stat(cwd, &st) == -1)
mkdir(cwd, 0700);
// gracefully exit
struct sigaction action;
memset(&action, 0, sizeof(struct sigaction));
action.sa_handler = term;
sigaction(SIGINT, &action, NULL);
// read configuration file (usernames, passwords)
parse_config_file();
// just to help clarify
// system("clear"); // clean console when initiated :)
printf("Server listening on port %i.\n", port);
printf("Graceful exit: escape character is 'Ctrl+C'.\n");
// continuous listening of server
if ((listenfd = open_listenfd(port)) < 0)
printf("Failure to open listening port.\n");
// main loop: wait for a datagram, then echo it
clientlen = sizeof(clientaddr);
while (!done) {
connfdp = malloc(sizeof(int));
if((*connfdp = accept(listenfd, (struct sockaddr *)&clientaddr, &clientlen)) < 0)
error("accept failure");
// printf("Connected to http://localhost:%i on socket %i\n", port, *connfdp);
pthread_create(&tid, NULL, thread, connfdp);
}
// once while loop gracefully exits, close connections
shutdown(*connfdp, 0);
close(*connfdp);
close(listenfd);
}
/*
* requests sent to server & server's response
*/
void server_res(int connfd) {
size_t n;
char buf[BUFSIZE], auth[MAXLINE], *filetype;
char cmd_in[10], filename_in[30]; // incoming command and filename
char user_in[30], pass_in[30], local_cwd[BUFSIZE];
int filedesc, socket_msg, filesize, filetype_index, user_auth = 0;
struct sockaddr_in clientaddr; // client addr
socklen_t clientlen; // byte size of client's address
struct stat st = {0}; // for creating user file structures
FILE file;
// set working directory
char cwd[MAXLINE];
getcwd(cwd, sizeof(cwd));
if (strncmp(server_name, "/", 1) != 0)
strcat(cwd, "/");
strcat(cwd, server_name);
// receive message from socket
socket_msg = recv(connfd, auth, MAXLINE, 0);
// receive and validate user; add user folder if validated
sscanf(auth, "%s %s[^\n]", user_in, pass_in);
if (strcmp(username[0], user_in) == 0 && strcmp(password[0], pass_in) == 0) {
printf("** %s authenticated. **\n", user_in);
login_auth = 1;
strcat(cwd, "/");
strcat(cwd, user_in);
if (stat(cwd, &st) == -1)
mkdir(cwd, 0700);
write(connfd, "authed", 6);
user_auth = 1;
} else printf("Invalid user '%s'.\n", user_in);
// idle response
time_t rawtime;
struct tm * timeinfo;
time ( &rawtime );
timeinfo = localtime ( &rawtime );
if (socket_msg == 0)
printf("Connection has idled at %s. Refresh page to continue.\n", asctime (timeinfo));
else if (socket_msg < 0)
printf("Socket error.\n");
// connected
else if (socket_msg > 0 && user_auth) {
printf("** Client connected. **\n");
while(1) {
buf[0] = '\0';
cmd_in[0] = '\0';
filename_in[0] = '\0';
local_cwd[0] = '\0';
// recvfrom: receive a UDP datagram from a client
bzero(buf, BUFSIZE);
n = recvfrom(connfd, buf, BUFSIZE, 0, (struct sockaddr *) &clientaddr, &clientlen);
if (n < 0)
error("ERROR in recvfrom");
write(connfd, "ready", 5);
sscanf(buf, "%s %s", cmd_in, filename_in);
printf("buf: %s\n", buf);
// format the filename_in to point to right spot
strcat(local_cwd, cwd);
if (strncmp(filename_in, "/", 1) != 0) strcat(local_cwd, "/");
strcat(local_cwd, filename_in);
// user has authenticated
if (login_auth) {
// format for server
/* ******* list command handling ******* */
if (!strcmp(cmd_in, "list")) {
DIR *folder;
struct dirent *namelist;
folder = opendir(local_cwd);
char file_list[BUFSIZE];
char files[BUFSIZE] = "";
while ((namelist = readdir(folder))) {
sprintf(file_list, "%s\n", namelist->d_name);
if (strncmp(file_list, ".", 1))
strcat(files, file_list);
}
// return file list to client
write(connfd, files, BUFSIZE);
free(namelist);
closedir(folder);
/* ******* get command handling ******* */
} else if (!strcmp(cmd_in, "get")) {
buf[0] = '\0';
if (access(local_cwd, F_OK) != -1) {
// open file to send
FILE *file = fopen(local_cwd, "rb");
fseek(file, 0L, SEEK_END);
long int file_size = ftell(file);
fseek(file, 0, SEEK_SET);
fread(buf, BUFSIZE, file_size, file);
fclose(file);
write(connfd, &(buf), file_size);
} else {
buf[0] = '\0';
write(connfd, &(buf), 0);
}
// /* ******* put command handling ******* */
} else if (!strcmp(cmd_in, "put")) {
// format the filename_in to point to right spot
local_cwd[0] = '\0';
strcat(local_cwd, cwd);
if (strncmp(filename_in, "/", 1) != 0) strcat(local_cwd, "/");
strcat(local_cwd, filename_in);
buf[0] = '\0';
printf("in put?\n");
read(connfd, buf, BUFSIZE);
FILE *file = fopen(local_cwd, "wb");
fwrite(buf, sizeof(char), strlen(buf), file);
fclose(file);
// recvfrom: receive a UDP datagram from a client
bzero(buf, BUFSIZE);
n = recvfrom(connfd, buf, BUFSIZE, 0, (struct sockaddr *) &clientaddr, &clientlen);
if (n < 0)
error("ERROR in recvfrom");
write(connfd, "ready", 5);
sscanf(buf, "%s %s", cmd_in, filename_in);
printf("buf: %s\n", buf);
// format the filename_in to point to right spot
local_cwd[0] = '\0';
strcat(local_cwd, cwd);
if (strncmp(filename_in, "/", 1) != 0) strcat(local_cwd, "/");
strcat(local_cwd, filename_in);
buf[0] = '\0';
read(connfd, buf, BUFSIZE);
FILE *file2 = fopen(local_cwd, "wb");
printf("in put2?\n");
fwrite(buf, sizeof(char), strlen(buf), file2);
fclose(file2);
/* ******* exit command handling ******* */
} else if (!strcmp(cmd_in,"exit")) {
printf("Client @ %d exited.\n", connfd);
login_auth = 0;
break;
// exit(0); // probably shouldn't allow from client side
/* ******* trash command handling ******* */
} else printf("Invalid command from client.\n");
} else write(connfd, "Not authorized.", 0);
} // end while loop
shutdown(connfd, 0);
close(connfd);
}
}
/* thread routine */
void *thread(void *vargp) {
int connfd = *((int *)vargp);
pthread_detach(pthread_self());
free(vargp);
server_res(connfd);
close(connfd);
return NULL;
}
/*
* open_listenfd - open and return a listening socket on port
* Returns -1 in case of failure
*/
int open_listenfd(int port) {
int listenfd, optval = 1;
struct sockaddr_in serveraddr;
/* Create a socket descriptor */
if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
error("listenfd, error opening socket\n");
return -1;
}
/* Eliminates "Address already in use" error from bind. */
if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (const void *)&optval, sizeof(int)) < 0) {
error("setsockopt fn\n");
return -1;
}
/* listenfd will be an endpoint for all requests to port
on any IP address for this host */
bzero((char *)&serveraddr, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
serveraddr.sin_port = htons((unsigned short)(port));
if (bind(listenfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0) {
error("unable to bind socket\n");
return -1;
}
/* Make it a listening socket ready to accept connection requests */
if (listen(listenfd, LISTENQ) < 0) {
error("listenfd, listen fn\n");
return -1;
}
return listenfd;
} /* end open_listenfd */
/* parse username and password from config file */
void parse_config_file() {
FILE *file = fopen(CONF_FILE, "r");
char *line = NULL;
size_t len = 0, cnt = 0;
ssize_t read;
if (file == NULL) { printf("Configuration file not found.\n"); EXIT_FAILURE; }
while ((read = getline(&line, &len, file)) != -1) {
if(sscanf(line, "%s %s[^\n]", username[cnt], password[cnt]))
cnt++;
else { printf("Bad configuration file.\n"); EXIT_FAILURE; }
}
// printf("Configuration file '%s' successfully parsed.\n", CONF_FILE);
if (line) free(line);
if (fclose(file) != 0) printf("Not able to close file '%s'.\n", CONF_FILE);
return;
}
/* Error 500 Internal Server Error handling */
void error500(char buf[MAXLINE], int connfd) {
strcpy(buf, "HTTP/1.1 500 Internal Server Error");
strcat(buf, "\n");
write(connfd, buf, strlen(buf));
}
//! error - wrapper for perror
void error(char *msg) {
perror(msg);
exit(1);
}
/* Gracefully exit program (while loop) */
void term(int signum){
done = 1;
}