#include "ftpd.h" #include "ftpd_vfs.h" #include #include #include #include #include #include #include #include #include ftpd_t server; static void ftpd_make_nonblocking(conn_t* conn) { unsigned long ul = 1; ioctlsocket(conn->s, FIONBIO, &ul); printf_d("ftpd_make_nonblocking %d\n",conn->s); } extern struct vfs_mod_s mod_root; extern struct vfs_mod_s mod_mnt; extern struct vfs_mod_s mod_home; extern struct vfs_mod_s mod_tmp; #ifdef MOD_FTPD extern struct vfs_mod_s mod_ftpd; #endif void ftpd_init() { vfs_add_mod(&mod_root); vfs_add_mod(&mod_mnt); vfs_add_mod(&mod_home); vfs_add_mod(&mod_tmp); #ifdef MOD_FTPD vfs_add_mod(&mod_ftpd); #endif } int ftpd_start_server(struct in_addr extip,int port) { int i,j; if((server.local.s = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP)) == INVALID_SOCKET) return 1; for(i = 0; i < MAX_USERS; i++) { server.userslots[i] = 0; for(j = 0; j < MAX_DATACHANNELS; j++) server.user[i].dataslots[j] = 0; } server.local.addr.sin_family = AF_INET; server.local.addr.sin_addr.s_addr = INADDR_ANY; server.local.addr.sin_port = htons(port); if(bind(server.local.s,(const struct sockaddr*)&server.local.addr, sizeof(struct sockaddr_in)) == SOCKET_ERROR) { closesocket(server.local.s); return 1; } listen(server.local.s,SOMAXCONN); ftpd_make_nonblocking(&server.local); server.extip = extip; return 0; } //User thread static void ftpd_prepare_conn(conn_t* conn) { conn->sendbuf = NULL; conn->sendbufsize = 0; conn->sendbufoff = 0; } //This function will automatically send buffer each call until buffer end static int ftpd_conn_do_send(conn_t* conn) { int sent = 0; if(conn->sendbufsize) { //printf_d("sending: %s\n",(const char*)conn->sendbuf); sent = send(conn->s,(const char*)conn->sendbuf+conn->sendbufoff, MIN(1024,conn->sendbufsize),0); if(sent < 0) conn->sendbufsize = 0; //On error we terminate transmission else { conn->sendbufsize -= sent; conn->sendbufoff += sent; printf_d("%d bytes sent to %p connection (remaining %d at off %d)\n",sent,conn, conn->sendbufsize,conn->sendbufoff); } } if(conn->sendbufsize <= 0) { free(conn->sendbuf); ftpd_prepare_conn(conn); printf_d("transmission for %p is over\n",conn); } return sent; } void ftpd_conn_send(conn_t* conn,const char* buf,size_t len) { if(!conn->sendbuf) conn->sendbuf = (char*)malloc(len); else conn->sendbuf = (char*)realloc(conn->sendbuf,conn->sendbufsize+len); memcpy((char*)conn->sendbuf+conn->sendbufsize,buf,len); conn->sendbufsize += len; } ftpd_user_t* ftpd_open_session(conn_t* conn) { int i; ftpd_user_t* user; //Find slot printf_d("ftpd_open_session for %d\n",conn->s); for(i = 0; i < MAX_USERS; i++) { if(BMP_GET(server.userslots,i) == 0) { user = &server.user[i]; //Alloc slot BMP_ON(server.userslots,i); printf_d("allocated slot %d (%p) for %d\n",i,user,conn->s); break; } } if(i == MAX_USERS) NULL; memset(user,'\0',sizeof(ftpd_user_t)); memcpy(&user->control,conn,sizeof(conn_t)); ftpd_prepare_conn(&user->control); ftpd_make_nonblocking(&user->control); user->command_cur = 0; user->disconnected = 0; user->dlcommand = 0; user->rest = 0; strcpy(user->wdir,"/"); return user; } //creates socket to connect (ACTIVE) static dataconn_t* ftpd_data_open_active(ftpd_user_t* user,conn_t* dstconn) { int i; dataconn_t* dc; for(i = 0; i < MAX_DATACHANNELS; i++) { if(BMP_GET(user->dataslots,i) == 0) { dc = &user->data[i]; break; } } if(i == MAX_DATACHANNELS) return NULL; memcpy(&dc->conn.addr,dstconn,sizeof(conn_t)); ftpd_prepare_conn(&dc->conn); dc->conn.s = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); dc->conn.addr.sin_family = AF_INET; dc->lconn.s = 0; //Should we use nonblocking connect? //ftpd_make_nonblocking(&dc->conn); dc->type = CONN_ACTIVE; dc->event = NULL; dc->close = NULL; user->lastdc = i; BMP_ON(user->dataslots,i); return dc; } //allocates and binds new socket (PASSIVE) static dataconn_t* ftpd_data_open_passive(ftpd_user_t* user) { int i,slot; dataconn_t* dc; for(i = 0; i < MAX_DATACHANNELS; i++) { if(BMP_GET(user->dataslots,i) == 0) { dc = &user->data[i]; break; } } if(i == MAX_DATACHANNELS) return NULL; slot = i; dc->conn.s = 0; dc->lconn.s = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); printf_d("datachannel passive socket %d\n",dc->lconn.s); dc->lconn.addr.sin_family = AF_INET; dc->lconn.addr.sin_addr.s_addr = INADDR_ANY; for(i = MIN_USRPORT; i < MAX_USRPORT; i++) { dc->lconn.addr.sin_port = htons(i); if(!bind(dc->lconn.s,(const struct sockaddr*) &dc->lconn.addr,sizeof(struct sockaddr_in))) { break; } } if(i == MAX_USRPORT) { closesocket(dc->lconn.s); return NULL; } listen(dc->lconn.s,1); ftpd_make_nonblocking(&dc->lconn); dc->type = CONN_PASSIVE; dc->event = NULL; dc->close = NULL; user->lastdc = slot; BMP_ON(user->dataslots,slot); return dc; } void ftpd_data_close(ftpd_user_t* user,dataconn_t* dc) { unsigned int slot; if(dc->close) dc->close(user,dc); slot = (unsigned int)((ptrdiff_t)dc-(ptrdiff_t)&user->data)/sizeof(dataconn_t); if(dc->hFile) CloseHandle(dc->hFile); if(dc->lconn.s) closesocket(dc->lconn.s); if(dc->conn.s) closesocket(dc->conn.s); memset(dc,'\0',sizeof(dataconn_t)); BMP_OFF(user->dataslots,slot); } void ftpd_close_session(ftpd_user_t* user) { unsigned int i,slot; slot = (unsigned int)((ptrdiff_t)user-(ptrdiff_t)&server.user)/sizeof(ftpd_user_t); closesocket(user->control.s); if(user->control.sendbuf) free(user->control.sendbuf); //Close data channels for(i = 0; i < MAX_DATACHANNELS; i++) { dataconn_t* dc; if(!BMP_GET(user->dataslots,i)) continue; dc = &user->data[i]; ftpd_data_close(user,dc); } memset(user,'\0',sizeof(ftpd_user_t)); BMP_OFF(server.userslots,slot); //Free slot } typedef enum { POLL_CONTROL = 0, POLL_DATA, } polltype_t; typedef struct { SOCKET s; int type; ftpd_user_t* user; dataconn_t* dc; } pollpair_t; static void pollpair_insert_user(pollpair_t* pair,int idx,ftpd_user_t* user) { pair[idx].s = user->control.s; pair[idx].type = POLL_CONTROL; pair[idx].user = user; } static void pollpair_insert_dataconn(pollpair_t* pair,int idx, ftpd_user_t* user,dataconn_t* dc) { pair[idx].s = dc->conn.s; pair[idx].type = POLL_DATA; pair[idx].user = user; pair[idx].dc = dc; } void ftpd_user_send_reply(ftpd_user_t* user,const char* text) { printf_d("send REPLY: %s\n",text); ftpd_conn_send(&user->control,text,strlen(text)); } void ftpd_dc_send_reply(dataconn_t* dc,const char* text) { ftpd_conn_send(&dc->conn,text,strlen(text)); } void ftpd_user_send_replyf(ftpd_user_t* user,const char* fmt,...) { char buffer[512]; va_list ap; va_start(ap,fmt); vsnprintf(buffer,sizeof(buffer),fmt,ap); va_end(ap); ftpd_user_send_reply(user,buffer); } void ftpd_dc_send_replyf(dataconn_t* dc,const char* fmt,...) { char buffer[512]; va_list ap; va_start(ap,fmt); vsnprintf(buffer,sizeof(buffer),fmt,ap); va_end(ap); ftpd_dc_send_reply(dc,buffer); } void ftpd_user_disconnect(ftpd_user_t* user,const char* reason) { ftpd_user_send_reply(user,reason); user->disconnected = 1; } static int is_private_ip(struct in_addr addr) { const int num = 4; const char* ips[] = {"127.0.0.0","10.0.0.0","172.16.0.0","192.168.0.0"}; const int lens[] = {8,8,12,16}; int i; for(i = 0; i < num; i++) { uint32_t mask; struct in_addr laddr; laddr.s_addr = inet_addr(ips[i]); mask = ~((uint32_t)pow(2,32-lens[i])-1); if(laddr.s_addr == addr.s_addr&mask) return 1; } return 0; } static int cmdcmp(const char* line,const char* cmd) { int i; for(i = 0; i < strlen(cmd); i++) if(tolower(line[i]) != tolower(cmd[i])) return 0; return 1; } int ftpd_user_make_dataconn(ftpd_user_t* user) { dataconn_t* dc; dc = &user->data[user->lastdc]; if(dc->type == CONN_ACTIVE) { //dc->conn.s is nonblocking!!!! FIX ME if(connect(dc->conn.s,(const struct sockaddr*) &dc->conn.addr,sizeof(struct sockaddr_in))) { ftpd_data_close(user,dc); return 1; } } else if(dc->type == CONN_PASSIVE) { WSAPOLLFD poll; poll.fd = dc->lconn.s; poll.events = POLLIN; printf_d("passive poll to %d\n",dc->lconn.s); if(WSAPoll(&poll,1,-1)) //3 seconds { if(poll.revents & POLLIN) { //Accept data conn int namelen; namelen = sizeof(struct sockaddr_in); dc->conn.s = accept(dc->lconn.s,(struct sockaddr*)&dc->conn.addr,&namelen); if(dc->conn.s == INVALID_SOCKET) { ftpd_data_close(user,dc); return 1; } ftpd_make_nonblocking(&dc->conn); } else { ftpd_data_close(user,dc); return 1; } } else { ftpd_data_close(user,dc); return 1; } } dc->type = CONN_CONNECTED; return 0; } dataconn_t* ftpd_user_get_cur_dataconn(ftpd_user_t* user) { return &user->data[user->lastdc]; } /* FTPd VIRTUAL FILESYSTEM / - root /mnt - drives /mnt/c - windows drive D: /mnt/d - windows drive D: /tmp - %TEMP% dir for current user /home - home dir for current user /home/user1/ /home/user2/ /ftpd - FTPd Virtual Dir screenshot.jpg - download to take screenshot execute - upload here exe to execute */ static int is_slash_at_end(const char* str,char slash) { return str[strlen(str)-1] == slash; } static char ftpd_user_cur_drive(ftpd_user_t* user) { return user->wdir[5]; } void ftpd_list_event_end(void* u,void* d) { ftpd_user_t* user; user = (ftpd_user_t*)u; ftpd_user_send_reply(user,"226 Transfer complete.\r\n"); } //Wrapper for protocol level. Pass execution to virtual level void ftpd_user_list_dir(ftpd_user_t* user,int mlsd) { dataconn_t* dc; if(user->dlcommand == 0) { puts_d("LIST first stage"); ftpd_user_send_reply(user,"150 Opening BINARY mode data connection for LIST.\r\n"); user->dlcommand++; } else if(user->dlcommand == 1) { if(ftpd_user_make_dataconn(user)) { ftpd_user_send_reply(user,"425 Can't open data connection.\r\n"); user->dlcommand = 0; return; } dc = ftpd_user_get_cur_dataconn(user); dc->mode = DATACONN_SEND; //Actual command vfs_list(user,mlsd); user->dlcommand = 0; dc->event = ftpd_list_event_end; } } static struct in_addr get_local_ip() { struct hostent* host; char hostname[256]; gethostname(hostname,sizeof(hostname)); host = gethostbyname(hostname); return *((struct in_addr*)host->h_addr_list[0]); } void ftpd_user_exec_command(ftpd_user_t* user) { printf_d("got command: %s\n",user->command); //ftpd_user_send_reply(user,"200 Hello WOorld!\r\n"); if(strlen(user->command) == 0) return; if(cmdcmp(user->command,"PWD")) ftpd_user_send_replyf(user,"257 \"%s\" is current directory.\r\n",user->wdir); else if(cmdcmp(user->command,"CWD")) { char buffer[MAX_PATH]; char* path,*c; strncpy(buffer,user->command,MAX_PATH-1); if(!(path = strchr(buffer,' '))) ftpd_user_send_reply(user,"501 Syntax error in parameters or arguments.\r\n"); else { path++; //if(vfs_retr(user,path)) // ftpd_user_send_reply(user,"550 Requested action not taken. File unavailable (e.g., file not found, no access).\r\n"); if(vfs_changepath(user,path)) ftpd_user_send_reply(user,"550 Requested action not taken. File unavailable (e.g., file not found, no access).\r\n"); else ftpd_user_send_replyf(user,"250 CWD successful. \"%s\" is current directory.\r\n",user->wdir); } } else if(cmdcmp(user->command,"CDUP")) { if(vfs_changepath(user,"..")) ftpd_user_send_reply(user,"550 Requested action not taken. File unavailable (e.g., file not found, no access).\r\n"); else ftpd_user_send_replyf(user,"200 CDUP successful. \"%s\" is current directory.\r\n",user->wdir); } else if(cmdcmp(user->command,"LIST")) { ftpd_user_list_dir(user,0); } else if(cmdcmp(user->command,"MLSD")) { ftpd_user_list_dir(user,1); } else if(cmdcmp(user->command,"RETR")) { char buffer[MAX_PATH]; char* path,*c; if(!BMP_GET(user->dataslots,user->lastdc)) ftpd_user_send_reply(user,"503 Bad sequence of commands.\r\n"); strncpy(buffer,user->command,MAX_PATH-1); if(!(path = strchr(buffer,' '))) ftpd_user_send_reply(user,"501 Syntax error in parameters or arguments.\r\n"); else { path++; if(vfs_retr(user,path)) ftpd_user_send_reply(user,"550 Requested action not taken. File unavailable (e.g., file not found, no access).\r\n"); } } else if(cmdcmp(user->command,"STOR")) { char buffer[MAX_PATH]; char* path,*c; if(!BMP_GET(user->dataslots,user->lastdc)) ftpd_user_send_reply(user,"503 Bad sequence of commands.\r\n"); strncpy(buffer,user->command,MAX_PATH-1); if(!(path = strchr(buffer,' '))) ftpd_user_send_reply(user,"501 Syntax error in parameters or arguments.\r\n"); else { path++; if(vfs_stor(user,path)) ftpd_user_send_reply(user,"550 Requested action not taken. File unavailable (e.g., file not found, no access).\r\n"); } } else if(cmdcmp(user->command,"APPE")) { char buffer[MAX_PATH]; char* path,*c; if(!BMP_GET(user->dataslots,user->lastdc)) ftpd_user_send_reply(user,"503 Bad sequence of commands.\r\n"); strncpy(buffer,user->command,MAX_PATH-1); if(!(path = strchr(buffer,' '))) ftpd_user_send_reply(user,"501 Syntax error in parameters or arguments.\r\n"); else { path++; if(vfs_appe(user,path)) ftpd_user_send_reply(user,"550 Requested action not taken. File unavailable (e.g., file not found, no access).\r\n"); } } else if(cmdcmp(user->command,"DELE") || cmdcmp(user->command,"RMD")) { char buffer[MAX_PATH]; char* path,*c; strncpy(buffer,user->command,MAX_PATH-1); if(!(path = strchr(buffer,' '))) ftpd_user_send_reply(user,"501 Syntax error in parameters or arguments.\r\n"); else { path++; if(vfs_dele(user,path)) ftpd_user_send_reply(user,"550 Requested action not taken. File unavailable (e.g., file not found, no access).\r\n"); } } else if(cmdcmp(user->command,"SIZE")) { char buffer[MAX_PATH]; char* path,*c; strncpy(buffer,user->command,MAX_PATH-1); if(!(path = strchr(buffer,' '))) ftpd_user_send_reply(user,"501 Syntax error in parameters or arguments.\r\n"); else { path++; if(vfs_size(user,path)) ftpd_user_send_reply(user,"550 Requested action not taken. File unavailable (e.g., file not found, no access).\r\n"); } } else if(cmdcmp(user->command,"MKD")) { char buffer[MAX_PATH]; char* path,*c; strncpy(buffer,user->command,MAX_PATH-1); if(!(path = strchr(buffer,' '))) ftpd_user_send_reply(user,"501 Syntax error in parameters or arguments.\r\n"); else { path++; if(vfs_mkd(user,path)) ftpd_user_send_reply(user,"550 Requested action not taken. File unavailable (e.g., file not found, no access).\r\n"); } } else if(cmdcmp(user->command,"PORT")) //PORT %d,%d,%d,%d,%d,%d { dataconn_t* dc; conn_t dstconn; int a,b,c,d,hp,lp; if(!sscanf(user->command,"PORT %d,%d,%d,%d,%d,%d",&a,&b,&c,&d,&hp,&lp)) ftpd_user_send_reply(user,"501 Syntax error in parameters or arguments.\r\n"); else { dstconn.addr.sin_family = AF_INET; dstconn.addr.sin_addr.s_addr = (a << 24) | (b << 16) | (c << 8) | d; dstconn.addr.sin_port = htons((hp << 8) | lp); if(!(dc = ftpd_data_open_active(user,&dstconn))) ftpd_user_send_reply(user,"425 Can't open data connection.\r\n"); else ftpd_user_send_reply(user,"200 PORT command successful.\r\n"); } } else if(cmdcmp(user->command,"PASV")) { dataconn_t* dc; struct in_addr addr; int port; if(!(dc = ftpd_data_open_passive(user))) ftpd_user_send_reply(user,"425 Can't open data connection.\r\n"); else { addr = is_private_ip(user->control.addr.sin_addr) ? server.extip : get_local_ip(); port = ntohs(dc->lconn.addr.sin_port); //227 Entering Passive Mode (192,168,150,90,195,149). ftpd_user_send_replyf(user,"227 Entering Passive Mode (%d,%d,%d,%d,%d,%d).\r\n", (addr.s_addr&0xFF),(addr.s_addr>>8)&0xFF,(addr.s_addr>>16)&0xFF, (addr.s_addr>>24)&0xFF,port>>8,port&0xFF); } } else if(cmdcmp(user->command,"REST")) { if(!sscanf(user->command,"REST %llu",&user->rest)) ftpd_user_send_reply(user,"501 Syntax error in parameters or arguments.\r\n"); else ftpd_user_send_replyf(user,"350 Rest supported. Restarting at %lu\r\n",user->rest); printf("REST %llu\n",user->rest); } else if(cmdcmp(user->command,"FEAT")) { ftpd_user_send_reply(user,"211-Features:\r\n" " REST STREAM\r\n" " SIZE\r\n" " MLSD modify*;perm*;size*;type*;unique*;UNIX.group*;UNIX.mode*;UNIX.owner*;\r\n" " UTF8\r\n" "211 End\r\n"); } else if(cmdcmp(user->command,"QUIT")) { user->disconnected = 1; ftpd_user_send_reply(user,"200 OK\r\n"); } else if(cmdcmp(user->command,"TYPE")) ftpd_user_send_reply(user,"200 OK\r\n"); else if(cmdcmp(user->command,"USER")) ftpd_user_send_reply(user,"200 OK\r\n"); else if(cmdcmp(user->command,"PASS")) ftpd_user_send_reply(user,"200 OK\r\n"); else if(cmdcmp(user->command,"NOOP")) ftpd_user_send_reply(user,"200 OK\r\n"); else if(cmdcmp(user->command,"OPTS")) ftpd_user_send_reply(user,"200 OK\r\n"); else if(cmdcmp(user->command,"AUTH")) ftpd_user_send_reply(user,"500 AUTH not understood.\r\n"); else if(cmdcmp(user->command,"SYST")) ftpd_user_send_reply(user,"215 UNIX Type: L8\r\n"); else ftpd_user_send_reply(user,"202 Command not implemented, superfluous at this site.\r\n"); } int ftpd_user_putc(ftpd_user_t* user,char ch) { if(user->command_cur >= MAX_COMMANDLEN) return 1; user->command[user->command_cur++] = ch; return 0; } void ftpd_user_recv(ftpd_user_t* user) { int rd; char ch; //While we have data while(recv(user->control.s,&ch,1,0) > 0) { if(ch == '\n') { if(user->command_cur <= 0) return; user->command_cur--; user->command[user->command_cur] = '\0'; ftpd_user_exec_command(user); user->command_cur = 0; } else { if(ftpd_user_putc(user,ch) == 1) { ftpd_user_disconnect(user,"501 Syntax error in parameters or arguments.\r\n"); return; } } } } void ftpd_user_send(ftpd_user_t* user) { ftpd_conn_do_send(&user->control); } //ftpd_data_recv(ppair[i].dc); void ftpd_data_recv(ftpd_user_t* user,dataconn_t* dc) { DWORD dwWrote; char buf[1024]; int rd; rd = recv(dc->conn.s,buf,1024,0); if(rd <= 0) ftpd_data_close(user,dc); //fwrite(buf,rd,1,dc->fp); WriteFile(dc->hFile,buf,rd,&dwWrote,NULL); } //ftpd_data_send(ppair[i].dc); void ftpd_data_send(ftpd_user_t* user,dataconn_t* dc) { printf_d("DATACONN MODE %d\n",dc->mode); if(dc->mode == DATACONN_SEND_FP) { char buf[1024]; DWORD dwRead; //rd = fread(buf,1,1024,dc->fp); ReadFile(dc->hFile,buf,1024,&dwRead,NULL); send(dc->conn.s,buf,dwRead,0); if(dwRead < 1024) //Last block of data in file { if(dc->event) dc->event(user,dc); ftpd_data_close(user,dc); } } else { printf_d("CONN BUFSIZE %d\n",dc->conn.sendbufsize); ftpd_conn_do_send(&dc->conn); if(dc->conn.sendbufsize == 0) //End { if(dc->event) dc->event(user,dc); ftpd_data_close(user,dc); } } } void ftpd_server_loop() { //WSAPOLLFD* polls; //pollpair_t* ppair; //32 reserved to special purposes static WSAPOLLFD polls[MAX_POLLSLOTS]; static pollpair_t ppair[MAX_POLLSLOTS]; size_t count; int i,j; count = 0; polls[0].fd = server.local.s; polls[0].events = POLLIN; count++; //polls = (WSAPOLLFD*)calloc(count,sizeof(WSAPOLLFD)); //ppair = (pollpair_t*)calloc(count,sizeof(pollpair_t)); for(i = 0; i < MAX_USERS; i++) { ftpd_user_t* user; if(!BMP_GET(server.userslots,i)) continue; user = &server.user[i]; printf_d("user %p\n",user); polls[count].fd = user->control.s; polls[count].events = POLLIN; pollpair_insert_user(ppair,count,user); //count++; //If we have data for user if(user->control.sendbuf) { puts_d("we have data for user"); //polls[count].fd = user->control.s; polls[count].events |= POLLOUT; //pollpair_insert_user(ppair,count,user); } count++; for(j = 0; j < MAX_DATACHANNELS; j++) { dataconn_t* dc; if(!BMP_GET(user->dataslots,j)) continue; dc = &user->data[j]; if(dc->type != CONN_CONNECTED) continue; if(dc->mode == DATACONN_SEND) { polls[count].fd = dc->conn.s; polls[count].events = POLLOUT; pollpair_insert_dataconn(ppair,count,user,dc); count++; } else if(dc->mode == DATACONN_SEND_FP) { polls[count].fd = dc->conn.s; polls[count].events = POLLOUT; pollpair_insert_dataconn(ppair,count,user,dc); count++; } else { polls[count].fd = dc->conn.s; polls[count].events = POLLIN; pollpair_insert_dataconn(ppair,count,user,dc); count++; } } } printf_d("prepoll count %d\n",count); for(i = 0; i < count; i++) { printf_d("%s %d user %p\n",polls[i].events == POLLIN ? "POLLIN" : (polls[i].events == POLLOUT ? "POLLOUT" : "POLLIN|POLLOUT"),polls[i].fd,ppair[i].user); } if(WSAPoll(polls,count,-1)) { for(i = 0; i < count; i++) { printf_d("%d\n",count); if(i != 0 && count > 1) { printf_d("Polling %s %d\n",ppair[i].type == POLL_CONTROL ? "POLL_CONTROL" : "POLL_DATA",i); if(ppair[i].type == POLL_CONTROL) { //Process control IO //Delayed mode requires immediately send if(ppair[i].user->dlcommand != 0) { if(polls[i].revents & POLLOUT) { puts_d("ftpd_user_send"); ftpd_user_send(ppair[i].user); } //Execute delayed command ftpd_user_exec_command(ppair[i].user); } else { if(polls[i].revents & POLLIN) { puts_d("ftpd_user_recv"); ftpd_user_recv(ppair[i].user); } if(polls[i].revents & POLLOUT) { puts_d("ftpd_user_send"); ftpd_user_send(ppair[i].user); } if((polls[i].revents & (POLLERR | POLLHUP)) || ppair[i].user->disconnected) { //Disconnect control channel ftpd_close_session(ppair[i].user); } } } else { //Process data IO if(polls[i].revents & POLLIN) { ftpd_data_recv(ppair[i].user,ppair[i].dc); } if(polls[i].revents & POLLOUT) { puts_d("ftpd_data_send"); ftpd_data_send(ppair[i].user,ppair[i].dc); } if(polls[i].revents & (POLLERR | POLLHUP)) { //Disconnect data channel ftpd_data_close(ppair[i].user,ppair[i].dc); } } } else { if(polls[0].revents & POLLIN) { conn_t conn; int namelen; ftpd_user_t* user; puts_d("accept"); namelen = sizeof(struct sockaddr_in); conn.s = accept(server.local.s,(struct sockaddr*)&conn.addr,&namelen); printf_d("accept fd %d\n",conn.s); if(!(user = ftpd_open_session(&conn))) { closesocket(conn.s); printf_d("%s failed to connected (no free slots)\n",inet_ntoa(conn.addr.sin_addr)); } else { printf_d("Connected %s\n",inet_ntoa(conn.addr.sin_addr)); ftpd_user_send_reply(user,"220 Welcome to ftpd.c\r\n"); } } } } } } void ftpd_main_loop() { while(1) { ftpd_server_loop(); } }