// perform FTP TYPE command, switch binary mode on and off void command_type(CLIENT_INFO * client_info) { char line[BUFFER_SIZE] = { 0 }; client_read_line(client_info->fd, client_info->buf, &client_info->buffer_pos); extract_line(line, client_info->buf, &client_info->buffer_pos); int len = strlen(line); if (len < 6) { send_code(client_info->fd, 500); return; } char * param = line + 5; if (strcmp(param, "A") == 0) client_info->binary_flag = 0; else if (strcmp(param, "A N") == 0) client_info->binary_flag = 0; else if (strcmp(param, "I") == 0) client_info->binary_flag = 1; else if (strcmp(param, "L 8") == 0) client_info->binary_flag = 1; else { send_code(client_info->fd, 500); return; } send_code(client_info->fd, 200); }
// perform FTP CWD command, changes directory void command_cwd(CLIENT_INFO * client_info) { char line[BUFFER_SIZE] = { 0 }; client_read_line(client_info->fd, client_info->buf, &client_info->buffer_pos); extract_line(line, client_info->buf, &client_info->buffer_pos); int len = strlen(line); if (len < 5) { send_code(client_info->fd, 500); return; } char dirbackup[PATH_MAX + 1] = { 0 }; strncpy(dirbackup, client_info->dir, PATH_MAX); char * dir = line + 4; if (dir[0] == '/') { strncpy(client_info->dir, dir, PATH_MAX); } else { strncat(client_info->dir, dir, PATH_MAX); strncat(client_info->dir, "/", PATH_MAX); } char dirbuf[PATH_MAX + 1] = { 0 }; snprintf(dirbuf, PATH_MAX, "%s%s", basedir, client_info->dir); char pathbuf[PATH_MAX + 1] = { 0 }; if (! realpath(dirbuf, pathbuf)) { strncpy(client_info->dir, dirbackup, PATH_MAX); send_code(client_info->fd, 550); return; } strncpy(client_info->dir, pathbuf, PATH_MAX); if (strlen(client_info->dir) < strlen(basedir)) { strncpy(client_info->dir, dirbackup, PATH_MAX); send_code(client_info->fd, 550); return; } memmove(client_info->dir, client_info->dir + strlen(basedir), strlen(client_info->dir) - strlen(basedir) + 1); if (client_info->dir[strlen(client_info->dir) - 1] != '/') strncat(client_info->dir, "/", PATH_MAX); send_code(client_info->fd, 250); }
// perform FTP RETR command, sends a file to the client void command_retr(CLIENT_INFO * client_info) { char line[BUFFER_SIZE] = { 0 }; client_read_line(client_info->fd, client_info->buf, &client_info->buffer_pos); extract_line(line, client_info->buf, &client_info->buffer_pos); int len = strlen(line); if (len < 6) { send_code(client_info->fd, 500); return; } char * filename = line + 5; char filenamebuf[PATH_MAX + 1] = { 0 }; if (filename[0] == '/') { snprintf(filenamebuf, PATH_MAX, "%s%s", basedir, filename); } else { snprintf(filenamebuf, PATH_MAX, "%s%s%s", basedir, client_info->dir, filename); } int fd = open(filenamebuf, O_RDONLY); if (fd == -1) { // cannot open file send_code(client_info->fd, 550); return; } send_code(client_info->fd, 150); open_data_connection(client_info); while (1) { char buf[FILE_READ_BUFFER_SIZE]; int bytes_read = read(fd, buf, FILE_READ_BUFFER_SIZE); if (bytes_read == 0) break; if (bytes_read == -1) epicfail("read"); int res = data_connection_write_buffer(client_info, buf, bytes_read); if (res == -1) break; } close(fd); close_data_connection(client_info); send_code(client_info->fd, 226); }
// perform FTP PWD command, prints current directory void command_pwd(CLIENT_INFO * client_info) { char line[BUFFER_SIZE] = { 0 }; client_read_line(client_info->fd, client_info->buf, &client_info->buffer_pos); extract_line(line, client_info->buf, &client_info->buffer_pos); int len = strlen(line); if (len != 3) { send_code(client_info->fd, 500); return; } send_code_param(client_info->fd, 257, client_info->dir); }
// perform FTP SYST command, identify as a standard UNIX FTP server void command_syst(CLIENT_INFO * client_info) { char line[BUFFER_SIZE] = { 0 }; client_read_line(client_info->fd, client_info->buf, &client_info->buffer_pos); extract_line(line, client_info->buf, &client_info->buffer_pos); int len = strlen(line); if (len != 4) { send_code(client_info->fd, 500); return; } send_code_param(client_info->fd, 215, "UNIX Type: L8"); }
// perform FTP USER command, take any username as valid void command_user(CLIENT_INFO * client_info) { char line[BUFFER_SIZE] = { 0 }; client_read_line(client_info->fd, client_info->buf, &client_info->buffer_pos); extract_line(line, client_info->buf, &client_info->buffer_pos); int len = strlen(line); if (len < 6) { send_code(client_info->fd, 500); return; } send_code(client_info->fd, 331); }
// perform FTP PASV command, prepare for passive data connection void command_pasv(CLIENT_INFO * client_info) { char line[BUFFER_SIZE] = { 0 }; client_read_line(client_info->fd, client_info->buf, &client_info->buffer_pos); extract_line(line, client_info->buf, &client_info->buffer_pos); int len = strlen(line); if (len != 4) { send_code(client_info->fd, 500); return; } if (client_info->passive_fd != 0) { close(client_info->passive_fd); client_info->passive_fd = 0; } struct sockaddr_in s; socklen_t l; l = sizeof(s); getsockname(client_info->fd, (struct sockaddr *)&s, &l); char * ip = inet_ntoa(s.sin_addr); if (! ip) epicfail("inet_ntoa"); client_info->data_connection_mode = CONN_MODE_PASSIVE; client_info->passive_fd = create_tcp_server_socket(ip, 0); l = sizeof(s); getsockname(client_info->passive_fd, (struct sockaddr *)&s, &l); int port = ntohs(s.sin_port); int ip1, ip2, ip3, ip4, port1, port2; if (sscanf(ip, "%d.%d.%d.%d", &ip1, &ip2, &ip3, &ip4) != 4) epicfail("command_pasv"); port1 = port >> 8; port2 = port & 0xff; char p[64]; sprintf(p, "%d,%d,%d,%d,%d,%d", ip1, ip2, ip3, ip4, port1, port2); send_code_param(client_info->fd, 227, p); }
static int client_input_received(struct client *client, size_t bytesRead) { char *line; int ret; fifo_buffer_append(client->input, bytesRead); /* process all lines */ while ((line = client_read_line(client)) != NULL) { ret = client_process_line(client, line); g_free(line); if (ret == COMMAND_RETURN_KILL || ret == COMMAND_RETURN_CLOSE) return ret; if (client_is_expired(client)) return COMMAND_RETURN_CLOSE; } return 0; }
// perform FTP PORT command, prepare for active data connection void command_port(CLIENT_INFO * client_info) { char line[BUFFER_SIZE] = { 0 }; client_read_line(client_info->fd, client_info->buf, &client_info->buffer_pos); extract_line(line, client_info->buf, &client_info->buffer_pos); int ip1, ip2, ip3, ip4, port1, port2; if (sscanf(line + 5, "%d,%d,%d,%d,%d,%d", &ip1, &ip2, &ip3, &ip4, &port1, &port2) != 6) { send_code(client_info->fd, 500); return; } client_info->data_connection_mode = CONN_MODE_ACTIVE; memset(&(client_info->active_addr), 0, sizeof(client_info->active_addr)); client_info->active_addr.sin_family = AF_INET; char buf_addr[32]; sprintf(buf_addr, "%d.%d.%d.%d", ip1, ip2, ip3, ip4); inet_pton(AF_INET, buf_addr, &(client_info->active_addr.sin_addr)); client_info->active_addr.sin_port = htons((port1 << 8) + port2); send_code(client_info->fd, 200); }
// reads a line from the client buffer void get_line(int fd, char * line, char * buf, int * buffer_pos) { client_read_line(fd, buf, buffer_pos); extract_line(line, buf, buffer_pos); }
// perform FTP LIST command, sends a directory listing to the client void command_list(CLIENT_INFO * client_info) { char line[BUFFER_SIZE] = { 0 }; client_read_line(client_info->fd, client_info->buf, &client_info->buffer_pos); extract_line(line, client_info->buf, &client_info->buffer_pos); int len = strlen(line); char * path = NULL; if (len != 4) { char * p = line + 5; if (*p == '-') { while (*p && (*p != ' ')) p++; } while (*p && (*p == ' ')) p++; path = p; } char dirbuf[PATH_MAX + 1] = { 0 }; snprintf(dirbuf, PATH_MAX, "%s%s", basedir, client_info->dir); if (path) { if (path[0] == '/') { snprintf(dirbuf, PATH_MAX, "%s%s", basedir, path); } else { strcat(dirbuf, "/"); strcat(dirbuf, path); } } char realpathbuf[PATH_MAX + 1] = { 0 }; if (! realpath(dirbuf, realpathbuf)) { send_code(client_info->fd, 550); return; } strcpy(dirbuf, realpathbuf); if (dirbuf[strlen(dirbuf) - 1] != '/') strncat(dirbuf, "/", PATH_MAX); DIR * dirp = opendir(dirbuf); if (! dirp) { send_code(client_info->fd, 550); return; } send_code(client_info->fd, 150); open_data_connection(client_info); while (1) { struct dirent * entry = readdir(dirp); if (! entry) break; char filenamebuf[PATH_MAX + 1] = { 0 }; snprintf(filenamebuf, PATH_MAX, "%s%s", dirbuf, entry->d_name); struct stat s; if (stat(filenamebuf, &s) == -1) { // cannot stat continue; } char buf[PATH_MAX + 128 + 1] = { 0 }; strmode(s.st_mode, buf); unsigned int size = (unsigned int)s.st_size; char date[64]; struct tm * ts = localtime(&s.st_mtime); strftime(date, 64, "%b %e %Y", ts); sprintf(buf + 11, "%3d %-8d %-8d %8u %s %s\r\n", s.st_nlink, (int)s.st_uid, (int)s.st_gid, size, date, entry->d_name); data_connection_write_string(client_info, buf); } close_data_connection(client_info); send_code(client_info->fd, 226); }