#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "socket_wrapper.h" /*======== *Operation *========*/ /** * send user error message * 80 character limit * thread safe */ int send_fail(int sock,const char * msg){ struct TransferResult res; res.res = RES_USR_ERR; res.file_size = 0; res.err_number = 0; res.error_msg_size = strlen(msg); //os will be combining if tcp_autocorking emabled. if(send(sock,&res,sizeof(res),0) < 0){ perror("error msg send"); return -1; } if (send(sock,msg,res.error_msg_size,0) < 0){ perror("error msg send"); return -1; } return 0; } /** * send errno to client * thread safe */ int send_errno(int sock){ struct TransferResult r; r.res = RES_ERR; r.err_number = errno; r.file_size = 0; r.error_msg_size = 0; if(send(sock,&r,sizeof(r),0)){ perror("errno send"); return -1; } return 0; } /** * return fd, if success. otherwise, return -1. * thread safe */ int read_request(int sock,uint8_t * buf,size_t bufsize){ struct ReadOp p; int fd; ssize_t n = recv_until_byte(sock,&p,sizeof(p),TIMEOUT); if (n < 0){ if (n == -2) fprintf(stderr,"timeout!"); else perror("receive fail"); return -1; } if(bufsize <= ((size_t)p.file_url_size) + sizeof(p) + 1){ send_fail(sock,"buffer overflow"); return -1; } else if(p.file_url_size + 1 > MAX_PATH_SIZE){ send_fail(sock,"max path fail"); return -1; } else if(p.file_url_size == 0){ send_fail(sock,"filename zero fail"); return -1; } n = recv_until_byte(sock,buf,p.file_url_size,TIMEOUT); buf[p.file_url_size] = '\0'; //truncate fprintf(stdout,"str size: %d, request %s\n",p.file_url_size,buf); if(strchr((char *)buf,'/') != NULL){ send_fail(sock,"Illegal character /"); return -1; } fd = open((char *)buf,O_RDONLY); if(fd < 0){ send_errno(sock); close(fd); return -1; } return fd; } /** * send response to client * thread safe */ int send_response(int sock,int fd, uint8_t * buf, size_t bufsize){ struct TransferResult r; struct stat st; off_t offset = 0; ssize_t readed = 0; r.res = RES_OK; r.err_number = 0; r.error_msg_size = 0; if(fstat(fd,&st) < 0){ return send_errno(sock); } if(S_ISDIR(st.st_mode)){ return send_fail(sock,"is a directory"); } r.file_size = st.st_size; if(send(sock,&r,sizeof(r),0)<0){ perror("send fail"); return -1; } #ifdef USE_SENDFILE while (r.file_size != offset) { size_t count = SEND_FILE_CHUNK_SIZE < (r.file_size - offset) ? SEND_FILE_CHUNK_SIZE : (r.file_size - offset); if((readed = sendfile(sock,fd,&offset,count)) < 0){ perror("send file fail"); return -1; } } #else while (offset < r.file_size) { readed = bufsize < (r.file_size - offset) ? bufsize : r.file_size - offset; if(read(fd,buf,readed)<0){ perror("send response read fail"); return -1; } if(send(sock,buf,readed,0)<0){ perror("send response send fail"); return -1; } offset += readed; } #endif return 0; } static int sock; void safe_exit(){ close(sock); } int main(int argc, const char *argv[]){ uint8_t * buf; struct sockaddr_in addr; struct sockaddr_in client_addr; socklen_t client_addr_len = sizeof(client_addr); int csock; int bufsize; sock = socket(AF_INET,SOCK_STREAM,0); atexit(safe_exit); if(sock < 0){ perror("sock create fail"); return 1; } bufsize = getBufferSizeFrom(sock); buf = malloc(bufsize * sizeof(*buf)); if (buf == NULL){ fprintf(stderr,"lack of memory"); return 1; } addr.sin_addr.s_addr = htonl(INADDR_ANY); /*0.0.0.0 모든 네트워크 인터페이스에 묶임.*/ addr.sin_family = AF_INET; addr.sin_port = htons(SERVER_PORT); if(bind(sock, (struct sockaddr *)&addr,sizeof(addr)) < 0){ perror("bind failed"); return 1; } fprintf(stderr,"server bind on %s:%d\n",inet_ntoa(addr.sin_addr),SERVER_PORT); if(listen(sock,1) < 0){ perror("listen failed"); return 1; } while ((csock = accept(sock, (struct sockaddr *)&client_addr,&client_addr_len)) >= 0) { int fd; /*inet_ntoa is not reentrant function. do not use it in signal handler.*/ printf("Connect : %s\n",(inet_ntoa(client_addr.sin_addr))); if((fd = read_request(csock,buf,bufsize)) > 0){ send_response(csock,fd,buf,bufsize); close(fd); } if(close(csock) < 0) perror("csock close error"); } free(buf); perror("accept error"); return 1; }