├── README.md ├── client ├── ftclient.c ├── ftclient.h └── makefile ├── common ├── common.c └── common.h └── server ├── .auth ├── .auth~ ├── ftserve.c ├── ftserve.h └── makefile /README.md: -------------------------------------------------------------------------------- 1 | FTP Client-Server Implementation 2 | =========== 3 | Simple implementation of a file transfer program. It includes custom client and server programs that provide functionality to authenticate a user, list remote files, and retrieve remote files. 4 | 5 | ### Directory layout: 6 | ftp/ 7 | client/ 8 | ftclient.c 9 | ftclient.h 10 | makefile 11 | common/ 12 | common.c 13 | common.h 14 | server/ 15 | ftserve.c 16 | ftserve.h 17 | makefile 18 | .auth 19 | 20 | ###Usage 21 | To compile and link ftserve: 22 | ``` 23 | $ cd server/ 24 | $ make 25 | ``` 26 | 27 | To compile and link ftclient: 28 | ``` 29 | $ cd client/ 30 | $ make 31 | ``` 32 | 33 | To run ftserve: 34 | ``` 35 | $ server/ftserve PORTNO 36 | ``` 37 | 38 | To run ftclient: 39 | ``` 40 | $ client/ftclient HOSTNAME PORTNO 41 | 42 | Commands: 43 | list 44 | get 45 | quit 46 | ``` 47 | 48 | Available commands: 49 | ``` 50 | list - retrieve list of files in the current remote directory 51 | get - get the specified file 52 | quit - end the ftp session 53 | ``` 54 | 55 | Logging In: 56 | ``` 57 | Name: anonymous 58 | Password: [empty] 59 | ``` 60 | -------------------------------------------------------------------------------- /client/ftclient.c: -------------------------------------------------------------------------------- 1 | #include "ftclient.h" 2 | 3 | 4 | int sock_control; 5 | 6 | 7 | /** 8 | * Receive a response from server 9 | * Returns -1 on error, return code on success 10 | */ 11 | int read_reply(){ 12 | int retcode = 0; 13 | if (recv(sock_control, &retcode, sizeof retcode, 0) < 0) { 14 | perror("client: error reading message from server\n"); 15 | return -1; 16 | } 17 | return ntohl(retcode); 18 | } 19 | 20 | 21 | 22 | /** 23 | * Print response message 24 | */ 25 | void print_reply(int rc) 26 | { 27 | switch (rc) { 28 | case 220: 29 | printf("220 Welcome, server ready.\n"); 30 | break; 31 | case 221: 32 | printf("221 Goodbye!\n"); 33 | break; 34 | case 226: 35 | printf("226 Closing data connection. Requested file action successful.\n"); 36 | break; 37 | case 550: 38 | printf("550 Requested action not taken. File unavailable.\n"); 39 | break; 40 | } 41 | 42 | } 43 | 44 | 45 | /** 46 | * Parse command in cstruct 47 | */ 48 | int ftclient_read_command(char* buf, int size, struct command *cstruct) 49 | { 50 | memset(cstruct->code, 0, sizeof(cstruct->code)); 51 | memset(cstruct->arg, 0, sizeof(cstruct->arg)); 52 | 53 | printf("ftclient> "); // prompt for input 54 | fflush(stdout); 55 | 56 | // wait for user to enter a command 57 | read_input(buf, size); 58 | 59 | char *arg = NULL; 60 | arg = strtok (buf," "); 61 | arg = strtok (NULL, " "); 62 | 63 | if (arg != NULL){ 64 | // store the argument if there is one 65 | strncpy(cstruct->arg, arg, strlen(arg)); 66 | } 67 | 68 | // buf = command 69 | if (strcmp(buf, "list") == 0) { 70 | strcpy(cstruct->code, "LIST"); 71 | } 72 | else if (strcmp(buf, "get") == 0) { 73 | strcpy(cstruct->code, "RETR"); 74 | } 75 | else if (strcmp(buf, "quit") == 0) { 76 | strcpy(cstruct->code, "QUIT"); 77 | } 78 | else {//invalid 79 | return -1; 80 | } 81 | 82 | // store code in beginning of buffer 83 | memset(buf, 0, 400); 84 | strcpy(buf, cstruct->code); 85 | 86 | // if there's an arg, append it to the buffer 87 | if (arg != NULL) { 88 | strcat(buf, " "); 89 | strncat(buf, cstruct->arg, strlen(cstruct->arg)); 90 | } 91 | 92 | return 0; 93 | } 94 | 95 | 96 | 97 | /** 98 | * Do get command 99 | */ 100 | int ftclient_get(int data_sock, int sock_control, char* arg) 101 | { 102 | char data[MAXSIZE]; 103 | int size; 104 | FILE* fd = fopen(arg, "w"); 105 | 106 | while ((size = recv(data_sock, data, MAXSIZE, 0)) > 0) { 107 | fwrite(data, 1, size, fd); 108 | } 109 | 110 | if (size < 0) { 111 | perror("error\n"); 112 | } 113 | 114 | fclose(fd); 115 | return 0; 116 | } 117 | 118 | 119 | /** 120 | * Open data connection 121 | */ 122 | int ftclient_open_conn(int sock_con) 123 | { 124 | int sock_listen = socket_create(CLIENT_PORT_ID); 125 | 126 | // send an ACK on control conn 127 | int ack = 1; 128 | if ((send(sock_con, (char*) &ack, sizeof(ack), 0)) < 0) { 129 | printf("client: ack write error :%d\n", errno); 130 | exit(1); 131 | } 132 | 133 | int sock_conn = socket_accept(sock_listen); 134 | close(sock_listen); 135 | return sock_conn; 136 | } 137 | 138 | 139 | 140 | 141 | /** 142 | * Do list commmand 143 | */ 144 | int ftclient_list(int sock_data, int sock_con) 145 | { 146 | size_t num_recvd; // number of bytes received with recv() 147 | char buf[MAXSIZE]; // hold a filename received from server 148 | int tmp = 0; 149 | 150 | // Wait for server starting message 151 | if (recv(sock_con, &tmp, sizeof tmp, 0) < 0) { 152 | perror("client: error reading message from server\n"); 153 | return -1; 154 | } 155 | 156 | memset(buf, 0, sizeof(buf)); 157 | while ((num_recvd = recv(sock_data, buf, MAXSIZE, 0)) > 0) { 158 | printf("%s", buf); 159 | memset(buf, 0, sizeof(buf)); 160 | } 161 | 162 | if (num_recvd < 0) { 163 | perror("error"); 164 | } 165 | 166 | // Wait for server done message 167 | if (recv(sock_con, &tmp, sizeof tmp, 0) < 0) { 168 | perror("client: error reading message from server\n"); 169 | return -1; 170 | } 171 | return 0; 172 | } 173 | 174 | 175 | 176 | /** 177 | * Input: cmd struct with an a code and an arg 178 | * Concats code + arg into a string and sends to server 179 | */ 180 | int ftclient_send_cmd(struct command *cmd) 181 | { 182 | char buffer[MAXSIZE]; 183 | int rc; 184 | 185 | sprintf(buffer, "%s %s", cmd->code, cmd->arg); 186 | 187 | // Send command string to server 188 | rc = send(sock_control, buffer, (int)strlen(buffer), 0); 189 | if (rc < 0) { 190 | perror("Error sending command to server"); 191 | return -1; 192 | } 193 | 194 | return 0; 195 | } 196 | 197 | 198 | 199 | /** 200 | * Get login details from user and 201 | * send to server for authentication 202 | */ 203 | void ftclient_login() 204 | { 205 | struct command cmd; 206 | char user[256]; 207 | memset(user, 0, 256); 208 | 209 | // Get username from user 210 | printf("Name: "); 211 | fflush(stdout); 212 | read_input(user, 256); 213 | 214 | // Send USER command to server 215 | strcpy(cmd.code, "USER"); 216 | strcpy(cmd.arg, user); 217 | ftclient_send_cmd(&cmd); 218 | 219 | // Wait for go-ahead to send password 220 | int wait; 221 | recv(sock_control, &wait, sizeof wait, 0); 222 | 223 | // Get password from user 224 | fflush(stdout); 225 | char *pass = getpass("Password: "); 226 | 227 | // Send PASS command to server 228 | strcpy(cmd.code, "PASS"); 229 | strcpy(cmd.arg, pass); 230 | ftclient_send_cmd(&cmd); 231 | 232 | // wait for response 233 | int retcode = read_reply(); 234 | switch (retcode) { 235 | case 430: 236 | printf("Invalid username/password.\n"); 237 | exit(0); 238 | case 230: 239 | printf("Successful login.\n"); 240 | break; 241 | default: 242 | perror("error reading message from server"); 243 | exit(1); 244 | break; 245 | } 246 | } 247 | 248 | 249 | 250 | 251 | 252 | int main(int argc, char* argv[]) 253 | { 254 | int data_sock, retcode, s; 255 | char buffer[MAXSIZE]; 256 | struct command cmd; 257 | struct addrinfo hints, *res, *rp; 258 | 259 | if (argc != 3) { 260 | printf("usage: ./ftclient hostname port\n"); 261 | exit(0); 262 | } 263 | 264 | char *host = argv[1]; 265 | char *port = argv[2]; 266 | 267 | // Get matching addresses 268 | memset(&hints, 0, sizeof(struct addrinfo)); 269 | hints.ai_family = AF_UNSPEC; 270 | hints.ai_socktype = SOCK_STREAM; 271 | 272 | s = getaddrinfo(host, port, &hints, &res); 273 | if (s != 0) { 274 | printf("getaddrinfo() error %s", gai_strerror(s)); 275 | exit(1); 276 | } 277 | 278 | // Find an address to connect to & connect 279 | for (rp = res; rp != NULL; rp = rp->ai_next) { 280 | sock_control = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); 281 | 282 | if (sock_control < 0) 283 | continue; 284 | 285 | if(connect(sock_control, res->ai_addr, res->ai_addrlen)==0) { 286 | break; 287 | } else { 288 | perror("connecting stream socket"); 289 | exit(1); 290 | } 291 | close(sock_control); 292 | } 293 | freeaddrinfo(rp); 294 | 295 | 296 | // Get connection, welcome messages 297 | printf("Connected to %s.\n", host); 298 | print_reply(read_reply()); 299 | 300 | 301 | /* Get name and password and send to server */ 302 | ftclient_login(); 303 | 304 | while (1) { // loop until user types quit 305 | 306 | // Get a command from user 307 | if ( ftclient_read_command(buffer, sizeof buffer, &cmd) < 0) { 308 | printf("Invalid command\n"); 309 | continue; // loop back for another command 310 | } 311 | 312 | // Send command to server 313 | if (send(sock_control, buffer, (int)strlen(buffer), 0) < 0 ) { 314 | close(sock_control); 315 | exit(1); 316 | } 317 | 318 | retcode = read_reply(); 319 | if (retcode == 221) { 320 | /* If command was quit, just exit */ 321 | print_reply(221); 322 | break; 323 | } 324 | 325 | if (retcode == 502) { 326 | // If invalid command, show error message 327 | printf("%d Invalid command.\n", retcode); 328 | } else { 329 | // Command is valid (RC = 200), process command 330 | 331 | // open data connection 332 | if ((data_sock = ftclient_open_conn(sock_control)) < 0) { 333 | perror("Error opening socket for data connection"); 334 | exit(1); 335 | } 336 | 337 | // execute command 338 | if (strcmp(cmd.code, "LIST") == 0) { 339 | ftclient_list(data_sock, sock_control); 340 | } 341 | else if (strcmp(cmd.code, "RETR") == 0) { 342 | // wait for reply (is file valid) 343 | if (read_reply() == 550) { 344 | print_reply(550); 345 | close(data_sock); 346 | continue; 347 | } 348 | ftclient_get(data_sock, sock_control, cmd.arg); 349 | print_reply(read_reply()); 350 | } 351 | close(data_sock); 352 | } 353 | 354 | } // loop back to get more user input 355 | 356 | // Close the socket (control connection) 357 | close(sock_control); 358 | return 0; 359 | } 360 | -------------------------------------------------------------------------------- /client/ftclient.h: -------------------------------------------------------------------------------- 1 | /* ftclient.h 2 | * 3 | * Rebecca Sagalyn 4 | * 11/15/13 5 | * 6 | * Client side of TCP file transfer implementation, runs with custom server, 7 | * ftserve.c. Receives commands from input, and retreives list of files in current 8 | * and files. 9 | * 10 | * Valid commands: 11 | * get 12 | * list 13 | * quit 14 | * 15 | * Usage: 16 | * ./ftclient SERVER_HOSTNAME PORT# 17 | */ 18 | 19 | #ifndef FTCLIENT_H 20 | #define FTCLIENT_H 21 | 22 | #include "../common/common.h" 23 | 24 | 25 | /** 26 | * Receive a response from server 27 | * Returns -1 on error, return code on success 28 | */ 29 | int read_reply(); 30 | 31 | 32 | /** 33 | * Print response message 34 | */ 35 | void print_reply(int rc); 36 | 37 | 38 | /** 39 | * Parse command in cstruct 40 | */ 41 | int ftclient_read_command(char* buf, int size, struct command *cstruct); 42 | 43 | 44 | /** 45 | * Do get command 46 | */ 47 | int ftclient_get(int data_sock, int sock_control, char* arg); 48 | 49 | 50 | /** 51 | * Open data connection 52 | */ 53 | int ftclient_open_conn(int sock_con); 54 | 55 | 56 | /** 57 | * Do list commmand 58 | */ 59 | int ftclient_list(int sock_data, int sock_con); 60 | 61 | 62 | /** 63 | * Input: cmd struct with an a code and an arg 64 | * Concats code + arg into a string and sends to server 65 | */ 66 | int ftclient_send_cmd(struct command *cmd); 67 | 68 | 69 | /** 70 | * Get login details from user and 71 | * send to server for authentication 72 | */ 73 | void ftclient_login(); 74 | 75 | 76 | #endif 77 | -------------------------------------------------------------------------------- /client/makefile: -------------------------------------------------------------------------------- 1 | CC := gcc 2 | CFLAGS := -Wall -g -Os 3 | 4 | SHDIR := ../common 5 | 6 | OBJS = ftclient.o $(SHDIR)/common.o 7 | 8 | all: ftclient 9 | 10 | ftclient: $(OBJS) 11 | @$(CC) -o ftclient $(CFLAGS) $(OBJS) 12 | 13 | $(OBJS) : %.o: %.c 14 | @$(CC) -c $(CFLAGS) $< -o $@ 15 | 16 | .PHONY: 17 | clean: 18 | @rm -f *.o ftclient 19 | @rm -f ../common/*.o 20 | @echo Done cleaning 21 | -------------------------------------------------------------------------------- /common/common.c: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | 3 | 4 | /** 5 | * Create listening socket on remote host 6 | * Returns -1 on error, socket fd on success 7 | */ 8 | int socket_create(int port) 9 | { 10 | int sockfd; 11 | int yes = 1; 12 | struct sockaddr_in sock_addr; 13 | 14 | // create new socket 15 | if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { 16 | perror("socket() error"); 17 | return -1; 18 | } 19 | 20 | // set local address info 21 | sock_addr.sin_family = AF_INET; 22 | sock_addr.sin_port = htons(port); 23 | sock_addr.sin_addr.s_addr = htonl(INADDR_ANY); 24 | 25 | if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) { 26 | close(sockfd); 27 | perror("setsockopt() error"); 28 | return -1; 29 | } 30 | 31 | // bind 32 | if (bind(sockfd, (struct sockaddr *) &sock_addr, sizeof(sock_addr)) < 0) { 33 | close(sockfd); 34 | perror("bind() error"); 35 | return -1; 36 | } 37 | 38 | // begin listening for incoming TCP requests 39 | if (listen(sockfd, 5) < 0) { 40 | close(sockfd); 41 | perror("listen() error"); 42 | return -1; 43 | } 44 | return sockfd; 45 | } 46 | 47 | 48 | 49 | 50 | 51 | /** 52 | * Create new socket for incoming client connection request 53 | * Returns -1 on error, or fd of newly created socket 54 | */ 55 | int socket_accept(int sock_listen) 56 | { 57 | int sockfd; 58 | struct sockaddr_in client_addr; 59 | socklen_t len = sizeof(client_addr); 60 | 61 | // Wait for incoming request, store client info in client_addr 62 | sockfd = accept(sock_listen, (struct sockaddr *) &client_addr, &len); 63 | 64 | if (sockfd < 0) { 65 | perror("accept() error"); 66 | return -1; 67 | } 68 | return sockfd; 69 | } 70 | 71 | 72 | 73 | 74 | /** 75 | * Connect to remote host at given port 76 | * Returns: socket fd on success, -1 on error 77 | */ 78 | int socket_connect(int port, char*host) 79 | { 80 | int sockfd; 81 | struct sockaddr_in dest_addr; 82 | 83 | // create socket 84 | if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { 85 | perror("error creating socket"); 86 | return -1; 87 | } 88 | 89 | // create server address 90 | memset(&dest_addr, 0, sizeof(dest_addr)); 91 | dest_addr.sin_family = AF_INET; 92 | dest_addr.sin_port = htons(port); 93 | dest_addr.sin_addr.s_addr = inet_addr(host); 94 | 95 | // Connect on socket 96 | if(connect(sockfd, (struct sockaddr *)&dest_addr, sizeof(dest_addr)) < 0 ) { 97 | perror("error connecting to server"); 98 | return -1; 99 | } 100 | return sockfd; 101 | } 102 | 103 | 104 | 105 | /** 106 | * Receive data on sockfd 107 | * Returns -1 on error, number of bytes received 108 | * on success 109 | */ 110 | int recv_data(int sockfd, char* buf, int bufsize){ 111 | size_t num_bytes; 112 | memset(buf, 0, bufsize); 113 | num_bytes = recv(sockfd, buf, bufsize, 0); 114 | if (num_bytes < 0) { 115 | return -1; 116 | } 117 | return num_bytes; 118 | } 119 | 120 | 121 | 122 | 123 | /** 124 | * Trim whiteshpace and line ending 125 | * characters from a string 126 | */ 127 | void trimstr(char *str, int n) 128 | { 129 | int i; 130 | for (i = 0; i < n; i++) { 131 | if (isspace(str[i])) str[i] = 0; 132 | if (str[i] == '\n') str[i] = 0; 133 | } 134 | } 135 | 136 | 137 | /** 138 | * Send resposne code on sockfd 139 | * Returns -1 on error, 0 on success 140 | */ 141 | int send_response(int sockfd, int rc) 142 | { 143 | int conv = htonl(rc); 144 | if (send(sockfd, &conv, sizeof conv, 0) < 0 ) { 145 | perror("error sending...\n"); 146 | return -1; 147 | } 148 | return 0; 149 | } 150 | 151 | 152 | 153 | 154 | /** 155 | * Read input from command line 156 | */ 157 | void read_input(char* buffer, int size) 158 | { 159 | char *nl = NULL; 160 | memset(buffer, 0, size); 161 | 162 | if ( fgets(buffer, size, stdin) != NULL ) { 163 | nl = strchr(buffer, '\n'); 164 | if (nl) *nl = '\0'; // truncate, ovewriting newline 165 | } 166 | } 167 | 168 | 169 | -------------------------------------------------------------------------------- /common/common.h: -------------------------------------------------------------------------------- 1 | #ifndef COMMON_H 2 | #define COMMON_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include // getaddrinfo() 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | 20 | 21 | /* constants */ 22 | #define DEBUG 1 23 | #define MAXSIZE 512 // max buffer size 24 | #define CLIENT_PORT_ID 30020 25 | 26 | 27 | /* Holds command code and argument */ 28 | struct command { 29 | char arg[255]; 30 | char code[5]; 31 | }; 32 | 33 | 34 | 35 | 36 | /** 37 | * Create listening socket on remote host 38 | * Returns -1 on error, socket fd on success 39 | */ 40 | int socket_create(int port); 41 | 42 | 43 | /** 44 | * Create new socket for incoming client connection request 45 | * Returns -1 on error, or fd of newly created socket 46 | */ 47 | int socket_accept(int sock_listen); 48 | 49 | 50 | /** 51 | * Connect to remote host at given port 52 | * Returns socket fd on success, -1 on error 53 | */ 54 | int socket_connect(int port, char *host); 55 | 56 | 57 | 58 | /** 59 | * Receive data on sockfd 60 | * Returns -1 on error, number of bytes received 61 | * on success 62 | */ 63 | int recv_data(int sockfd, char* buf, int bufsize); 64 | 65 | 66 | /** 67 | * Send resposne code on sockfd 68 | * Returns -1 on error, 0 on success 69 | */ 70 | int send_response(int sockfd, int rc); 71 | 72 | 73 | 74 | //------------------- UTILITY FUNCTIONS-------------------// 75 | 76 | /** 77 | * Trim whiteshpace and line ending 78 | * characters from a string 79 | */ 80 | void trimstr(char *str, int n); 81 | 82 | 83 | 84 | /** 85 | * Read input from command line 86 | */ 87 | void read_input(char* buffer, int size); 88 | 89 | 90 | #endif 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /server/.auth: -------------------------------------------------------------------------------- 1 | user pass 2 | test testpass 3 | anonymous 4 | -------------------------------------------------------------------------------- /server/.auth~: -------------------------------------------------------------------------------- 1 | user pass 2 | test testpass 3 | -------------------------------------------------------------------------------- /server/ftserve.c: -------------------------------------------------------------------------------- 1 | #include "ftserve.h" 2 | 3 | 4 | int main(int argc, char *argv[]) 5 | { 6 | int sock_listen, sock_control, port, pid; 7 | 8 | if (argc != 2) { 9 | printf("usage: ./ftserve port\n"); 10 | exit(0); 11 | } 12 | 13 | port = atoi(argv[1]); 14 | 15 | // create socket 16 | if ((sock_listen = socket_create(port)) < 0 ) { 17 | perror("Error creating socket"); 18 | exit(1); 19 | } 20 | 21 | while(1) { // wait for client request 22 | 23 | // create new socket for control connection 24 | if ((sock_control = socket_accept(sock_listen)) < 0 ) 25 | break; 26 | 27 | // create child process to do actual file transfer 28 | if ((pid = fork()) < 0) { 29 | perror("Error forking child process"); 30 | } else if (pid == 0) { 31 | close(sock_listen); 32 | ftserve_process(sock_control); 33 | close(sock_control); 34 | exit(0); 35 | } 36 | 37 | close(sock_control); 38 | } 39 | 40 | close(sock_listen); 41 | 42 | return 0; 43 | } 44 | 45 | 46 | 47 | /** 48 | * Send file specified in filename over data connection, sending 49 | * control message over control connection 50 | * Handles case of null or invalid filename 51 | */ 52 | void ftserve_retr(int sock_control, int sock_data, char* filename) 53 | { 54 | FILE* fd = NULL; 55 | char data[MAXSIZE]; 56 | size_t num_read; 57 | 58 | fd = fopen(filename, "r"); 59 | 60 | if (!fd) { 61 | // send error code (550 Requested action not taken) 62 | send_response(sock_control, 550); 63 | 64 | } else { 65 | // send okay (150 File status okay) 66 | send_response(sock_control, 150); 67 | 68 | do { 69 | num_read = fread(data, 1, MAXSIZE, fd); 70 | 71 | if (num_read < 0) { 72 | printf("error in fread()\n"); 73 | } 74 | 75 | // send block 76 | if (send(sock_data, data, num_read, 0) < 0) 77 | perror("error sending file\n"); 78 | 79 | } while (num_read > 0); 80 | 81 | // send message: 226: closing conn, file transfer successful 82 | send_response(sock_control, 226); 83 | 84 | fclose(fd); 85 | } 86 | } 87 | 88 | 89 | 90 | 91 | 92 | /** 93 | * Send list of files in current directory 94 | * over data connection 95 | * Return -1 on error, 0 on success 96 | */ 97 | int ftserve_list(int sock_data, int sock_control) 98 | { 99 | char data[MAXSIZE]; 100 | size_t num_read; 101 | FILE* fd; 102 | 103 | int rs = system("ls -l | tail -n+2 > tmp.txt"); 104 | if ( rs < 0) { 105 | exit(1); 106 | } 107 | 108 | fd = fopen("tmp.txt", "r"); 109 | if (!fd) { 110 | exit(1); 111 | } 112 | 113 | /* Seek to the beginning of the file */ 114 | fseek(fd, SEEK_SET, 0); 115 | 116 | send_response(sock_control, 1); //starting 117 | 118 | memset(data, 0, MAXSIZE); 119 | while ((num_read = fread(data, 1, MAXSIZE, fd)) > 0) { 120 | if (send(sock_data, data, num_read, 0) < 0) { 121 | perror("err"); 122 | } 123 | memset(data, 0, MAXSIZE); 124 | } 125 | 126 | fclose(fd); 127 | 128 | send_response(sock_control, 226); // send 226 129 | 130 | return 0; 131 | } 132 | 133 | 134 | 135 | 136 | 137 | 138 | /** 139 | * Open data connection to client 140 | * Returns: socket for data connection 141 | * or -1 on error 142 | */ 143 | int ftserve_start_data_conn(int sock_control) 144 | { 145 | char buf[1024]; 146 | int wait, sock_data; 147 | 148 | // Wait for go-ahead on control conn 149 | if (recv(sock_control, &wait, sizeof wait, 0) < 0 ) { 150 | perror("Error while waiting"); 151 | return -1; 152 | } 153 | 154 | // Get client address 155 | struct sockaddr_in client_addr; 156 | socklen_t len = sizeof client_addr; 157 | getpeername(sock_control, (struct sockaddr*)&client_addr, &len); 158 | inet_ntop(AF_INET, &client_addr.sin_addr, buf, sizeof(buf)); 159 | 160 | // Initiate data connection with client 161 | if ((sock_data = socket_connect(CLIENT_PORT_ID, buf)) < 0) 162 | return -1; 163 | 164 | return sock_data; 165 | } 166 | 167 | 168 | 169 | 170 | 171 | /** 172 | * Authenticate a user's credentials 173 | * Return 1 if authenticated, 0 if not 174 | */ 175 | int ftserve_check_user(char*user, char*pass) 176 | { 177 | char username[MAXSIZE]; 178 | char password[MAXSIZE]; 179 | char *pch; 180 | char buf[MAXSIZE]; 181 | char *line = NULL; 182 | size_t num_read; 183 | size_t len = 0; 184 | FILE* fd; 185 | int auth = 0; 186 | 187 | fd = fopen(".auth", "r"); 188 | if (fd == NULL) { 189 | perror("file not found"); 190 | exit(1); 191 | } 192 | 193 | while ((num_read = getline(&line, &len, fd)) != -1) { 194 | memset(buf, 0, MAXSIZE); 195 | strcpy(buf, line); 196 | 197 | pch = strtok (buf," "); 198 | strcpy(username, pch); 199 | 200 | if (pch != NULL) { 201 | pch = strtok (NULL, " "); 202 | strcpy(password, pch); 203 | } 204 | 205 | // remove end of line and whitespace 206 | trimstr(password, (int)strlen(password)); 207 | 208 | if ((strcmp(user,username)==0) && (strcmp(pass,password)==0)) { 209 | auth = 1; 210 | break; 211 | } 212 | } 213 | free(line); 214 | fclose(fd); 215 | return auth; 216 | } 217 | 218 | 219 | 220 | 221 | 222 | /** 223 | * Log in connected client 224 | */ 225 | int ftserve_login(int sock_control) 226 | { 227 | char buf[MAXSIZE]; 228 | char user[MAXSIZE]; 229 | char pass[MAXSIZE]; 230 | memset(user, 0, MAXSIZE); 231 | memset(pass, 0, MAXSIZE); 232 | memset(buf, 0, MAXSIZE); 233 | 234 | // Wait to recieve username 235 | if ( (recv_data(sock_control, buf, sizeof(buf)) ) == -1) { 236 | perror("recv error\n"); 237 | exit(1); 238 | } 239 | 240 | int i = 5; 241 | int n = 0; 242 | while (buf[i] != 0) 243 | user[n++] = buf[i++]; 244 | 245 | // tell client we're ready for password 246 | send_response(sock_control, 331); 247 | 248 | // Wait to recieve password 249 | memset(buf, 0, MAXSIZE); 250 | if ( (recv_data(sock_control, buf, sizeof(buf)) ) == -1) { 251 | perror("recv error\n"); 252 | exit(1); 253 | } 254 | 255 | i = 5; 256 | n = 0; 257 | while (buf[i] != 0) { 258 | pass[n++] = buf[i++]; 259 | } 260 | 261 | return (ftserve_check_user(user, pass)); 262 | } 263 | 264 | 265 | 266 | 267 | 268 | /** 269 | * Wait for command from client and 270 | * send response 271 | * Returns response code 272 | */ 273 | int ftserve_recv_cmd(int sock_control, char*cmd, char*arg) 274 | { 275 | int rc = 200; 276 | char buffer[MAXSIZE]; 277 | 278 | memset(buffer, 0, MAXSIZE); 279 | memset(cmd, 0, 5); 280 | memset(arg, 0, MAXSIZE); 281 | 282 | // Wait to recieve command 283 | if ((recv_data(sock_control, buffer, sizeof(buffer)) ) == -1) { 284 | perror("recv error\n"); 285 | return -1; 286 | } 287 | 288 | strncpy(cmd, buffer, 4); 289 | char *tmp = buffer + 5; 290 | strcpy(arg, tmp); 291 | 292 | if (strcmp(cmd, "QUIT")==0) { 293 | rc = 221; 294 | } else if((strcmp(cmd, "USER")==0) || (strcmp(cmd, "PASS")==0) || 295 | (strcmp(cmd, "LIST")==0) || (strcmp(cmd, "RETR")==0)) { 296 | rc = 200; 297 | } else { //invalid command 298 | rc = 502; 299 | } 300 | 301 | send_response(sock_control, rc); 302 | return rc; 303 | } 304 | 305 | 306 | 307 | 308 | 309 | 310 | /** 311 | * Child process handles connection to client 312 | */ 313 | void ftserve_process(int sock_control) 314 | { 315 | int sock_data; 316 | char cmd[5]; 317 | char arg[MAXSIZE]; 318 | 319 | // Send welcome message 320 | send_response(sock_control, 220); 321 | 322 | // Authenticate user 323 | if (ftserve_login(sock_control) == 1) { 324 | send_response(sock_control, 230); 325 | } else { 326 | send_response(sock_control, 430); 327 | exit(0); 328 | } 329 | 330 | while (1) { 331 | // Wait for command 332 | int rc = ftserve_recv_cmd(sock_control, cmd, arg); 333 | 334 | if ((rc < 0) || (rc == 221)) { 335 | break; 336 | } 337 | 338 | if (rc == 200 ) { 339 | // Open data connection with client 340 | if ((sock_data = ftserve_start_data_conn(sock_control)) < 0) { 341 | close(sock_control); 342 | exit(1); 343 | } 344 | 345 | // Execute command 346 | if (strcmp(cmd, "LIST")==0) { // Do list 347 | ftserve_list(sock_data, sock_control); 348 | } else if (strcmp(cmd, "RETR")==0) { // Do get 349 | ftserve_retr(sock_control, sock_data, arg); 350 | } 351 | 352 | // Close data connection 353 | close(sock_data); 354 | } 355 | } 356 | } 357 | 358 | 359 | -------------------------------------------------------------------------------- /server/ftserve.h: -------------------------------------------------------------------------------- 1 | /* ftserve.h 2 | * 3 | * Rebecca Sagalyn 4 | * CS372, Program 1 5 | * 11/15/13 6 | * 7 | * Server side of TCP file transfer implementation, runs with custom client, 8 | * ftclient.c. Sends list of files in current directory and files to ftclient. 9 | * Requires user login. 10 | * 11 | * Usage: 12 | * ./ftserve PORT# 13 | */ 14 | 15 | #ifndef FTSERVE_H 16 | #define FTSERVE_H 17 | 18 | #include "../common/common.h" 19 | 20 | 21 | /** 22 | * Send file specified in filename over data connection, sending 23 | * control message over control connection 24 | * Handles case of null or invalid filename 25 | */ 26 | void ftserve_retr(int sock_control, int sock_data, char* filename); 27 | 28 | 29 | 30 | /** 31 | * Send list of files in current directory 32 | * over data connection 33 | * Return -1 on error, 0 on success 34 | */ 35 | int ftserve_list(int sock_data, int sock_control); 36 | 37 | 38 | 39 | 40 | /** 41 | * Open data connection to client 42 | * Returns: socket for data connection 43 | * or -1 on error 44 | */ 45 | int ftserve_start_data_conn(int sock_control); 46 | 47 | 48 | 49 | /** 50 | * Authenticate a user's credentials 51 | * Return 1 if authenticated, 0 if not 52 | */ 53 | int ftserve_check_user(char*user, char*pass); 54 | 55 | 56 | 57 | /** 58 | * Log in connected client 59 | */ 60 | int ftserve_login(int sock_control); 61 | 62 | 63 | /** 64 | * Wait for command from client and send response 65 | * Returns response code 66 | */ 67 | int ftserve_recv_cmd(int sock_control, char*cmd, char*arg); 68 | 69 | 70 | 71 | /** 72 | * Child process handles connection to client 73 | */ 74 | void ftserve_process(int sock_control); 75 | 76 | 77 | #endif 78 | -------------------------------------------------------------------------------- /server/makefile: -------------------------------------------------------------------------------- 1 | CC := gcc 2 | CFLAGS := -Wall -g -Os 3 | 4 | SHDIR := ../common 5 | 6 | OBJS = ftserve.o $(SHDIR)/common.o 7 | 8 | all: ftserve 9 | 10 | ftserve: $(OBJS) 11 | @$(CC) -o ftserve $(CFLAGS) $(OBJS) 12 | 13 | $(OBJS) : %.o: %.c 14 | @$(CC) -c $(CFLAGS) $< -o $@ 15 | 16 | .PHONY: 17 | clean: 18 | @rm -f *.o ftserve 19 | @rm -f ../common/*.o 20 | @echo Done cleaning 21 | --------------------------------------------------------------------------------