├── .gitignore ├── Makefile ├── README.md ├── client ├── LeapFTP.exe ├── miniftpd └── miniftpd.conf ├── command_map.c ├── command_map.h ├── common.h ├── configure.c ├── configure.h ├── ftp_assist.c ├── ftp_assist.h ├── ftp_codes.h ├── ftp_nobody.c ├── ftp_nobody.h ├── ftp_proto.c ├── ftp_proto.h ├── ftpserver.conf ├── hash.c ├── hash.h ├── main.c ├── parse_conf.c ├── parse_conf.h ├── priv_command.c ├── priv_command.h ├── priv_sock.c ├── priv_sock.h ├── session.c ├── session.h ├── strutil.c ├── strutil.h ├── sysutil.c ├── sysutil.h ├── test_conf.c ├── test_hash.c ├── test_ls.c ├── trans_ctrl.c ├── trans_ctrl.h ├── trans_data.c └── trans_data.h /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | ftpServer 3 | a.out 4 | .*.swp 5 | *.exe 6 | 7 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY:clean 2 | CC=gcc 3 | CFLAGS=-Wall -g 4 | BIN=ftpServer 5 | OBJS=main.o sysutil.o session.o strutil.o ftp_nobody.o ftp_proto.o configure.o parse_conf.o command_map.o trans_data.o priv_sock.o priv_command.o trans_ctrl.o hash.o ftp_assist.o 6 | LIB=-lcrypt 7 | $(BIN):$(OBJS) 8 | $(CC) $(CFLAGS) $^ -o $@ $(LIB) 9 | %.o:%.c 10 | $(CC) $(CFLAGS) -c $< -o $@ 11 | clean: 12 | rm -f *.o $(BIN) 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #功能:实现一个简易的FTP服务器,支持文件的上传、下载,以及断点续传 2 | #采用多线程模型完成 3 | #有port和pasv两种工作模式 4 | 5 | #下面是个函数功能简单介绍 6 | 1, 基本模块: 7 | main.c sysutil.c strutil.c session.c ftp_nobody.c ftp_proto.c common.h 8 | 其中,session模块是核心, 9 | ftp_nobody模块实现nobody功能,主要实现控制功能, 10 | ftp_proto模块负责一些具体的实现,实现数据传输功能 11 | sysutil模块是系统函数模块、strutil模块是字符串处理模块,common.h用来存放公共头文件; 12 | 2,configure.c parse_conf.c ftp_codes.h 13 | 分别是配置文件模块,配置文件解析模块、FTP应答的宏模块; 14 | 3, 具体实现模块 15 | command_map.c priv_sock.c trans_data.c trans_ctrl.c 16 | 分别对应的是命令映射控制模块、命令请求和应答模块、数据传输模块、控制连接和数据连接模块(上传和下载限速等) 17 | 4, 其他 18 | hash.c ftp_assist.c 19 | 哈希模块(用来控制客户端和ip连接数)、main函数实现模块 20 | -------------------------------------------------------------------------------- /client/LeapFTP.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dasima/ftpServer/183872e9adaa3cf28401817f8f50cfdf92335e75/client/LeapFTP.exe -------------------------------------------------------------------------------- /client/miniftpd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dasima/ftpServer/183872e9adaa3cf28401817f8f50cfdf92335e75/client/miniftpd -------------------------------------------------------------------------------- /client/miniftpd.conf: -------------------------------------------------------------------------------- 1 | #test 2 | pasv_enable=YES 3 | port_enable=YES 4 | listen_port=8989 5 | max_clients=1000 6 | max_per_ip=30 7 | accept_timeout=60 8 | connect_timeout=60 9 | idle_session_timeout=10 10 | data_connection_timeout=900 11 | local_umask=077 12 | upload_max_rate=10000000 13 | download_max_rate=0 14 | listen_address=192.168.17.135 15 | -------------------------------------------------------------------------------- /command_map.c: -------------------------------------------------------------------------------- 1 | #include "command_map.h" 2 | #include "common.h" 3 | #include "session.h" 4 | #include "sysutil.h" 5 | #include "ftp_codes.h" 6 | #include "configure.h" 7 | #include "trans_data.h" 8 | #include "priv_sock.h" 9 | #include "strutil.h" 10 | #include "trans_ctrl.h" 11 | 12 | 13 | typedef struct Ftpcmd 14 | { 15 | const char *cmd; //FTP指令 16 | void (*cmd_handler)(Session_t *sess);//该指令所对应的执行函数 17 | } ftpcmd_t; 18 | 19 | static ftpcmd_t ctrl_cmds[] = { 20 | /* 访问控制命令 */ 21 | {"USER", do_user }, 22 | {"PASS", do_pass }, 23 | {"CWD", do_cwd }, 24 | {"XCWD", do_cwd }, 25 | {"CDUP", do_cdup }, 26 | {"XCUP", do_cdup }, 27 | {"QUIT", do_quit }, 28 | {"ACCT", NULL }, 29 | {"SMNT", NULL }, 30 | {"REIN", NULL }, 31 | /* 传输参数命令 */ 32 | {"PORT", do_port }, 33 | {"PASV", do_pasv }, 34 | {"TYPE", do_type }, 35 | {"STRU", do_stru }, 36 | {"MODE", do_mode }, 37 | 38 | /* 服务命令 */ 39 | {"RETR", do_retr }, 40 | {"STOR", do_stor }, 41 | {"APPE", do_appe }, 42 | {"LIST", do_list }, 43 | {"NLST", do_nlst }, 44 | {"REST", do_rest }, 45 | {"ABOR", do_abor }, 46 | {"\377\364\377\362ABOR", do_abor}, 47 | {"PWD", do_pwd }, 48 | {"XPWD", do_pwd }, 49 | {"MKD", do_mkd }, 50 | {"XMKD", do_mkd }, 51 | {"RMD", do_rmd }, 52 | {"XRMD", do_rmd }, 53 | {"DELE", do_dele }, 54 | {"RNFR", do_rnfr }, 55 | {"RNTO", do_rnto }, 56 | {"SITE", do_site }, 57 | {"SYST", do_syst }, 58 | {"FEAT", do_feat }, 59 | {"SIZE", do_size }, 60 | {"STAT", do_stat }, 61 | {"NOOP", do_noop }, 62 | {"HELP", do_help }, 63 | {"STOU", NULL }, 64 | {"ALLO", NULL } 65 | }; 66 | 67 | //进行命令映射 68 | void do_command_map(Session_t *sess) 69 | { 70 | int i; 71 | int size = sizeof(ctrl_cmds) / sizeof(ctrl_cmds[0]); //数组大小 72 | for (i=0; icom) == 0) 75 | { 76 | if (ctrl_cmds[i].cmd_handler != NULL) 77 | { 78 | ctrl_cmds[i].cmd_handler(sess); 79 | } 80 | else 81 | { 82 | //该命令没有实现 83 | ftp_reply(sess, FTP_COMMANDNOTIMPL, "Unimplement command."); 84 | } 85 | 86 | break; 87 | } 88 | } 89 | 90 | if (i == size) 91 | { 92 | //未识别的命令 93 | ftp_reply(sess, FTP_BADCMD, "Unknown command."); 94 | } 95 | } 96 | 97 | void ftp_reply(Session_t *sess, int status, const char *text) 98 | { 99 | char tmp[1024] = { 0 }; 100 | snprintf(tmp, sizeof tmp, "%d %s\r\n", status, text); 101 | writen(sess->peer_fd, tmp, strlen(tmp)); 102 | } 103 | 104 | void ftp_lreply(Session_t *sess, int status, const char *text) 105 | { 106 | char tmp[1024] = { 0 }; 107 | snprintf(tmp, sizeof tmp, "%d-%s\r\n", status, text); 108 | writen(sess->peer_fd, tmp, strlen(tmp)); 109 | } 110 | 111 | void do_user(Session_t *sess) 112 | { 113 | struct passwd *pw; 114 | if((pw = getpwnam(sess->args)) == NULL) 115 | { 116 | ftp_reply(sess, FTP_LOGINERR, "Login incorrect."); 117 | return; 118 | } 119 | 120 | sess->user_uid = pw->pw_uid; 121 | ftp_reply(sess, FTP_GIVEPWORD, "Please specify the password."); 122 | } 123 | 124 | void do_pass(Session_t *sess) 125 | { 126 | //struct passwd *getpwuid(uid_t uid) 127 | struct passwd *pw; 128 | if((pw = getpwuid(sess->user_uid)) == NULL) 129 | { 130 | ftp_reply(sess, FTP_LOGINERR, "Login incorrect."); 131 | return; 132 | } 133 | 134 | //struct spwd *getspnam(const char *name); 135 | struct spwd *spw; 136 | if((spw = getspnam(pw->pw_name)) == NULL) 137 | { 138 | ftp_reply(sess, FTP_LOGINERR, "Login incorrect."); 139 | return; 140 | } 141 | 142 | //char *crypt(const char *key, const char *salt); 143 | char *encrypted_password = crypt(sess->args, spw->sp_pwdp); 144 | if(strcmp(encrypted_password, spw->sp_pwdp) != 0) 145 | { 146 | ftp_reply(sess, FTP_LOGINERR, "Login incorrect."); 147 | return; 148 | } 149 | 150 | if(setegid(pw->pw_gid) == -1) 151 | ERR_EXIT("setegid"); 152 | if(seteuid(pw->pw_uid) == -1) 153 | ERR_EXIT("seteuid"); 154 | 155 | //home---切换到主目录 156 | if(chdir(pw->pw_dir) == -1) 157 | ERR_EXIT("chdir"); 158 | //umask 159 | umask(tunable_local_umask); 160 | 161 | strcpy(sess->username, pw->pw_name); 162 | 163 | ftp_reply(sess, FTP_LOGINOK, "Login successful."); 164 | } 165 | 166 | 167 | void do_cwd(Session_t *sess) 168 | { 169 | if(chdir(sess->args) == -1) 170 | { 171 | //550 172 | ftp_reply(sess, FTP_FILEFAIL, "Failed to change directory."); 173 | return; 174 | } 175 | 176 | //250 Directory successfully changed. 177 | ftp_reply(sess, FTP_CWDOK, "Directory successfully changed."); 178 | } 179 | 180 | void do_cdup(Session_t *sess) 181 | { 182 | if(chdir("..") == -1) 183 | { 184 | //550 185 | ftp_reply(sess, FTP_FILEFAIL, "Failed to change directory."); 186 | return; 187 | } 188 | 189 | //250 Directory successfully changed. 190 | ftp_reply(sess, FTP_CWDOK, "Directory successfully changed."); 191 | } 192 | 193 | void do_quit(Session_t *sess) 194 | { 195 | ftp_reply(sess, FTP_GOODBYE, "Good Bye!"); 196 | exit(EXIT_SUCCESS); 197 | } 198 | 199 | void do_port(Session_t *sess) 200 | { 201 | //设置主动工作模式 202 | //PORT 192,168,44,1,200,174 203 | unsigned int v[6] = {0}; 204 | sscanf(sess->args, "%u,%u,%u,%u,%u,%u", &v[0], &v[1], &v[2], &v[3], &v[4], &v[5]); 205 | 206 | sess->p_addr = (struct sockaddr_in *)malloc(sizeof (struct sockaddr_in)); 207 | memset(sess->p_addr, 0, sizeof(struct sockaddr_in)); 208 | sess->p_addr->sin_family = AF_INET; 209 | 210 | char *p = (char*)&sess->p_addr->sin_port; 211 | p[0] = v[4]; 212 | p[1] = v[5]; 213 | 214 | p = (char*)&sess->p_addr->sin_addr.s_addr; 215 | p[0] = v[0]; 216 | p[1] = v[1]; 217 | p[2] = v[2]; 218 | p[3] = v[3]; 219 | 220 | ftp_reply(sess, FTP_PORTOK, "PORT command successful. Consider using PASV."); 221 | } 222 | 223 | void do_pasv(Session_t *sess) 224 | { 225 | char ip[16] = {0}; 226 | get_local_ip(ip); 227 | 228 | //给nobody发送命令 229 | priv_sock_send_cmd(sess->proto_fd, PRIV_SOCK_PASV_LISTEN); 230 | //接收nobody的应答 231 | char res = priv_sock_recv_result(sess->proto_fd); 232 | if(res == PRIV_SOCK_RESULT_BAD) 233 | { 234 | ftp_reply(sess, FTP_BADCMD, "get listenfd error"); 235 | return; 236 | } 237 | //接收port 238 | uint16_t port = priv_sock_recv_int(sess->proto_fd); 239 | 240 | 241 | //227 Entering Passive Mode (192,168,44,136,194,6). 242 | unsigned int v[6]; 243 | sscanf(ip, "%u.%u.%u.%u", &v[0], &v[1], &v[2], &v[3]); 244 | uint16_t net_endian_port = htons(port); //网络字节序 245 | unsigned char *p = (unsigned char*)&net_endian_port; 246 | v[4] = p[0]; 247 | v[5] = p[1]; 248 | 249 | char text[1024] = {0}; 250 | snprintf(text, sizeof text, "Entering Passive Mode (%u,%u,%u,%u,%u,%u).", v[0], v[1], v[2], v[3], v[4], v[5]); 251 | 252 | ftp_reply(sess, FTP_PASVOK, text); 253 | } 254 | 255 | void do_type(Session_t *sess) 256 | { 257 | //指定FTP的传输模式 258 | if (strcmp(sess->args, "A") == 0) 259 | { 260 | sess->ascii_mode = 1; 261 | ftp_reply(sess, FTP_TYPEOK, "Switching to ASCII mode."); 262 | } 263 | else if (strcmp(sess->args, "I") == 0) 264 | { 265 | sess->ascii_mode = 0; 266 | ftp_reply(sess, FTP_TYPEOK, "Switching to Binary mode."); 267 | } 268 | else 269 | { 270 | ftp_reply(sess, FTP_BADCMD, "Unrecognised TYPE command."); 271 | } 272 | } 273 | 274 | void do_stru(Session_t *sess) 275 | { 276 | 277 | } 278 | 279 | void do_mode(Session_t *sess) 280 | { 281 | 282 | } 283 | 284 | void do_retr(Session_t *sess) 285 | { 286 | download_file(sess); 287 | } 288 | 289 | void do_stor(Session_t *sess) 290 | { 291 | upload_file(sess, 0); 292 | } 293 | 294 | void do_appe(Session_t *sess) 295 | { 296 | upload_file(sess, 1); 297 | } 298 | 299 | void do_list(Session_t *sess) 300 | { 301 | trans_list(sess, 1); 302 | } 303 | 304 | //实现简单的目录传输 305 | void do_nlst(Session_t *sess) 306 | { 307 | trans_list(sess, 0); 308 | } 309 | 310 | void do_rest(Session_t *sess) 311 | { 312 | sess->restart_pos = atoll(sess->args); 313 | //Restart position accepted (344545). 314 | char text[1024] = {0}; 315 | snprintf(text, sizeof text, "Restart position accepted (%lld).", sess->restart_pos); 316 | ftp_reply(sess, FTP_RESTOK, text); 317 | } 318 | 319 | void do_abor(Session_t *sess) 320 | { 321 | //225 No transfer to ABOR 322 | ftp_reply(sess, FTP_ABOR_NOCONN, "No transfer to ABOR."); 323 | } 324 | 325 | void do_pwd(Session_t *sess) 326 | { 327 | char tmp[1024] = {0}; 328 | if(getcwd(tmp, sizeof tmp) == NULL) 329 | { 330 | //return值为-1/0,函数进入系统内核, 331 | //返回值判断用perror 332 | //返回值为NULL,不用perror,fprintf(stderr, "a"); 333 | fprintf(stderr, "get cwd error\n"); 334 | ftp_reply(sess, FTP_BADMODE, "error"); 335 | return; 336 | } 337 | char text[1024] = {0}; 338 | snprintf(text, sizeof text, "\"%s\"", tmp); 339 | ftp_reply(sess, FTP_PWDOK, text); 340 | } 341 | 342 | void do_mkd(Session_t *sess) 343 | { 344 | if(mkdir(sess->args, 0777) == -1) 345 | { 346 | ftp_reply(sess, FTP_FILEFAIL, "Create directory operation failed."); 347 | return; 348 | } 349 | 350 | char text[1024] = {0}; 351 | if(sess->args[0] == '/') //绝对路径 352 | snprintf(text, sizeof text, "%s created.", sess->args); 353 | else 354 | { 355 | //char *getcwd(char *buf, size_t size); 356 | char tmp[1024] = {0}; 357 | if(getcwd(tmp, sizeof tmp) == NULL) 358 | { 359 | ERR_EXIT("getcwd"); 360 | } 361 | snprintf(text, sizeof text, "%s/%s created.", tmp, sess->args); 362 | } 363 | 364 | ftp_reply(sess, FTP_MKDIROK, text); 365 | } 366 | 367 | void do_rmd(Session_t *sess) 368 | { 369 | if(rmdir(sess->args) == -1) 370 | { 371 | //550 Remove directory operation failed. 372 | ftp_reply(sess, FTP_FILEFAIL, "Remove directory operation failed."); 373 | return; 374 | } 375 | //250 Remove directory operation successful. 376 | ftp_reply(sess, FTP_RMDIROK, "Remove directory operation successful."); 377 | } 378 | 379 | void do_dele(Session_t *sess) 380 | { 381 | if(unlink(sess->args) == -1) 382 | { 383 | //550 Delete operation failed. 384 | ftp_reply(sess, FTP_FILEFAIL, "Delete operation failed."); 385 | return; 386 | } 387 | //250 Delete operation successful. 388 | ftp_reply(sess, FTP_DELEOK, "Delete operation successful."); 389 | } 390 | 391 | void do_rnfr(Session_t *sess) 392 | { 393 | if(sess->rnfr_name) //防止内存泄露 394 | { 395 | free(sess->rnfr_name); 396 | sess->rnfr_name = NULL; 397 | } 398 | sess->rnfr_name = (char*)malloc(strlen(sess->args)+1); 399 | strcpy(sess->rnfr_name, sess->args); 400 | //350 Ready for RNTO. 401 | ftp_reply(sess, FTP_RNFROK, "Ready for RNTO."); 402 | } 403 | 404 | void do_rnto(Session_t *sess) 405 | { 406 | if(sess->rnfr_name == NULL) 407 | { 408 | //503 RNFR required first. 409 | ftp_reply(sess, FTP_NEEDRNFR, "RNFR required first."); 410 | return; 411 | } 412 | 413 | if(rename(sess->rnfr_name, sess->args) == -1) 414 | { 415 | ftp_reply(sess, FTP_FILEFAIL, "Rename failed."); 416 | return; 417 | } 418 | free(sess->rnfr_name); 419 | sess->rnfr_name = NULL; 420 | 421 | //250 Rename successful. 422 | ftp_reply(sess, FTP_RENAMEOK, "Rename successful."); 423 | } 424 | 425 | void do_site(Session_t *sess) 426 | { 427 | 428 | char cmd[1024] = {0}; 429 | char args[1024] = {0}; 430 | str_split(sess->args, cmd, args, ' '); 431 | str_upper(cmd); 432 | 433 | if(strcmp("CHMOD", cmd)) 434 | do_site_chmod(sess, args); 435 | else if(strcmp("UMASK", cmd)) 436 | do_site_umask(sess, args); 437 | else if(strcmp("HELP", cmd)) 438 | do_site_help(sess); 439 | else 440 | ftp_reply(sess, FTP_BADCMD, "Unknown SITE command."); 441 | } 442 | 443 | void do_syst(Session_t *sess) 444 | { 445 | ftp_reply(sess, FTP_SYSTOK, "UNIX Type: L8"); 446 | } 447 | 448 | void do_feat(Session_t *sess) 449 | { 450 | //211-Features: 451 | ftp_lreply(sess, FTP_FEAT, "Features:"); 452 | 453 | //EPRT 454 | writen(sess->peer_fd, " EPRT\r\n", strlen(" EPRT\r\n")); 455 | writen(sess->peer_fd, " EPSV\r\n", strlen(" EPSV\r\n")); 456 | writen(sess->peer_fd, " MDTM\r\n", strlen(" MDTM\r\n")); 457 | writen(sess->peer_fd, " PASV\r\n", strlen(" PASV\r\n")); 458 | writen(sess->peer_fd, " REST STREAM\r\n", strlen(" REST STREAM\r\n")); 459 | writen(sess->peer_fd, " SIZE\r\n", strlen(" SIZE\r\n")); 460 | writen(sess->peer_fd, " TVFS\r\n", strlen(" TVFS\r\n")); 461 | writen(sess->peer_fd, " UTF8\r\n", strlen(" UTF8\r\n")); 462 | 463 | //211 End 464 | ftp_reply(sess, FTP_FEAT, "End"); 465 | } 466 | 467 | void do_size(Session_t *sess) 468 | { 469 | struct stat sbuf; 470 | if(lstat(sess->args, &sbuf) == -1) 471 | { 472 | ftp_reply(sess, FTP_FILEFAIL, "SIZE operation failed."); 473 | return; 474 | } 475 | 476 | //只能求普通文件size 477 | if(!S_ISREG(sbuf.st_mode)) 478 | { 479 | ftp_reply(sess, FTP_FILEFAIL, "SIZE operation failed."); 480 | return; 481 | } 482 | 483 | //213 6 484 | char text[1024] = {0}; 485 | snprintf(text, sizeof text, "%lu", sbuf.st_size); 486 | ftp_reply(sess, FTP_SIZEOK, text); 487 | } 488 | 489 | void do_stat(Session_t *sess) 490 | { 491 | ftp_lreply(sess, FTP_STATOK, "FTP server status:"); 492 | 493 | char text[1024] = {0}; 494 | struct in_addr in; 495 | in.s_addr = sess->ip; 496 | snprintf(text, sizeof text, " Connected to %s\r\n", inet_ntoa(in)); 497 | writen(sess->peer_fd, text, strlen(text)); 498 | 499 | snprintf(text, sizeof text, " Logged in as %s\r\n", sess->username); 500 | writen(sess->peer_fd, text, strlen(text)); 501 | ftp_reply(sess, FTP_STATOK, "End of status"); 502 | } 503 | 504 | void do_noop(Session_t *sess) 505 | { 506 | ftp_reply(sess, FTP_GREET, "(FtpServer 1.0)"); 507 | } 508 | 509 | void do_help(Session_t *sess) 510 | { 511 | ftp_lreply(sess, FTP_HELP, "The following commands are recognized."); 512 | writen(sess->peer_fd, " ABOR ACCT ALLO APPE CDUP CWD DELE EPRT EPSV FEAT HELP LIST MDTM MKD\r\n", strlen(" ABOR ACCT ALLO APPE CDUP CWD DELE EPRT EPSV FEAT HELP LIST MDTM MKD\r\n")); 513 | writen(sess->peer_fd, " MODE NLST NOOP OPTS PASS PASV PORT PWD QUIT REIN REST RETR RMD RNFR\r\n", strlen(" MODE NLST NOOP OPTS PASS PASV PORT PWD QUIT REIN REST RETR RMD RNFR\r\n")); 514 | writen(sess->peer_fd, " RNTO SITE SIZE SMNT STAT STOR STOU STRU SYST TYPE USER XCUP XCWD XMKD\r\n", strlen(" RNTO SITE SIZE SMNT STAT STOR STOU STRU SYST TYPE USER XCUP XCWD XMKD\r\n")); 515 | writen(sess->peer_fd, " XPWD XRMD\r\n", strlen(" XPWD XRMD\r\n")); 516 | ftp_reply(sess, FTP_HELP, "Help OK."); 517 | } 518 | -------------------------------------------------------------------------------- /command_map.h: -------------------------------------------------------------------------------- 1 | #ifndef _COMMAND_MAP_H_ 2 | #define _COMMAND_MAP_H_ 3 | 4 | #include "session.h" 5 | 6 | //命令映射相关函数 7 | 8 | void do_command_map(Session_t *); 9 | void ftp_reply(Session_t *, int, const char *); 10 | void ftp_lreply(Session_t *, int, const char *); 11 | 12 | void do_user(Session_t *); 13 | void do_pass(Session_t *); 14 | void do_cwd(Session_t *); 15 | void do_cdup(Session_t *); 16 | void do_quit(Session_t *); 17 | void do_port(Session_t *); 18 | void do_pasv(Session_t *); 19 | void do_type(Session_t *); 20 | void do_stru(Session_t *); 21 | void do_mode(Session_t *); 22 | void do_retr(Session_t *); 23 | void do_stor(Session_t *); 24 | void do_appe(Session_t *); 25 | void do_list(Session_t *); 26 | void do_nlst(Session_t *); 27 | void do_rest(Session_t *); 28 | void do_abor(Session_t *); 29 | void do_pwd(Session_t *); 30 | void do_mkd(Session_t *); 31 | void do_rmd(Session_t *); 32 | void do_dele(Session_t *); 33 | void do_rnfr(Session_t *); 34 | void do_rnto(Session_t *); 35 | void do_site(Session_t *); 36 | void do_syst(Session_t *); 37 | void do_feat(Session_t *); 38 | void do_size(Session_t *); 39 | void do_stat(Session_t *); 40 | void do_noop(Session_t *); 41 | void do_help(Session_t *); 42 | 43 | #endif /*_COMMAND_MAP_H_*/ 44 | -------------------------------------------------------------------------------- /common.h: -------------------------------------------------------------------------------- 1 | #ifndef _COMMON_H_ 2 | #define _COMMON_H_ 3 | //c 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | //linux 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include "crypt.h" 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | #define ERR_EXIT(m) \ 36 | do { \ 37 | perror(m);\ 38 | exit(EXIT_FAILURE);\ 39 | }while(0) 40 | 41 | #endif /*_COMMON_H_*/ 42 | -------------------------------------------------------------------------------- /configure.c: -------------------------------------------------------------------------------- 1 | #include "configure.h" 2 | 3 | int tunable_pasv_enable = 1; 4 | int tunable_port_enable = 1; 5 | unsigned int tunable_listen_port = 21; 6 | unsigned int tunable_max_clients = 2000; 7 | unsigned int tunable_max_per_ip = 50; 8 | unsigned int tunable_accept_timeout = 60; 9 | unsigned int tunable_connect_timeout = 60; 10 | unsigned int tunable_idle_session_timeout = 300; 11 | unsigned int tunable_data_connection_timeout = 300; 12 | unsigned int tunable_local_umask = 077; 13 | unsigned int tunable_upload_max_rate = 0; 14 | unsigned int tunable_download_max_rate = 0; 15 | const char *tunable_listen_address; 16 | 17 | -------------------------------------------------------------------------------- /configure.h: -------------------------------------------------------------------------------- 1 | #ifndef _CONFIGURE_H_ 2 | #define _CONFIGURE_H_ 3 | 4 | extern int tunable_pasv_enable; 5 | extern int tunable_port_enable; 6 | extern unsigned int tunable_listen_port; 7 | extern unsigned int tunable_max_clients; 8 | extern unsigned int tunable_max_per_ip; 9 | extern unsigned int tunable_accept_timeout; 10 | extern unsigned int tunable_connect_timeout; 11 | extern unsigned int tunable_idle_session_timeout; 12 | extern unsigned int tunable_data_connection_timeout; 13 | extern unsigned int tunable_local_umask; 14 | extern unsigned int tunable_upload_max_rate; 15 | extern unsigned int tunable_download_max_rate; 16 | extern const char *tunable_listen_address; 17 | 18 | #endif /*_CONFIGURE_H_*/ 19 | -------------------------------------------------------------------------------- /ftp_assist.c: -------------------------------------------------------------------------------- 1 | #include "ftp_assist.h" 2 | #include "common.h" 3 | #include "sysutil.h" 4 | #include "session.h" 5 | #include "configure.h" 6 | #include "parse_conf.h" 7 | #include "ftp_codes.h" 8 | #include "command_map.h" 9 | #include "hash.h" 10 | 11 | unsigned int num_of_clients = 0; //表示连接数目 12 | 13 | Hash_t *ip_to_clients; 14 | Hash_t *pid_to_ip; 15 | 16 | static void handle_sigchld(int sig); 17 | static unsigned int hash_func(unsigned int buckets, void *key); 18 | //获取当前ip的数目,并且+1 19 | static unsigned int add_ip_to_hash(uint32_t ip); 20 | 21 | 22 | void check_permission() 23 | { 24 | //root 的uid为0 25 | if(getuid()) 26 | { 27 | fprintf(stderr, "FtpServer must be started by root\n"); 28 | exit(EXIT_FAILURE); 29 | } 30 | } 31 | 32 | void setup_signal_chld() 33 | { 34 | //signal第二个参数是handler,则signal阻塞,执行handler函数 35 | if(signal(SIGCHLD, handle_sigchld) == SIG_ERR) 36 | ERR_EXIT("signal"); 37 | } 38 | 39 | void print_conf() 40 | { 41 | printf("tunable_pasv_enable=%d\n", tunable_pasv_enable); 42 | printf("tunable_port_enable=%d\n", tunable_port_enable); 43 | 44 | printf("tunable_listen_port=%u\n", tunable_listen_port); 45 | printf("tunable_max_clients=%u\n", tunable_max_clients); 46 | printf("tunable_max_per_ip=%u\n", tunable_max_per_ip); 47 | printf("tunable_accept_timeout=%u\n", tunable_accept_timeout); 48 | printf("tunable_connect_timeout=%u\n", tunable_connect_timeout); 49 | printf("tunable_idle_session_timeout=%u\n", tunable_idle_session_timeout); 50 | printf("tunable_data_connection_timeout=%u\n", tunable_data_connection_timeout); 51 | printf("tunable_local_umask=0%o\n", tunable_local_umask); 52 | printf("tunable_upload_max_rate=%u\n", tunable_upload_max_rate); 53 | printf("tunable_download_max_rate=%u\n", tunable_download_max_rate); 54 | 55 | if (tunable_listen_address == NULL) 56 | printf("tunable_listen_address=NULL\n"); 57 | else 58 | printf("tunable_listen_address=%s\n", tunable_listen_address); 59 | } 60 | 61 | void limit_num_clients(Session_t *sess) 62 | { 63 | if(tunable_max_clients > 0 && sess->curr_clients > tunable_max_clients) 64 | { 65 | //421 There are too many connected users, please try later. 66 | ftp_reply(sess, FTP_TOO_MANY_USERS, "There are too many connected users, please try later."); 67 | exit(EXIT_FAILURE); 68 | } 69 | 70 | if(tunable_max_per_ip > 0 && sess->curr_ip_clients > tunable_max_per_ip) 71 | { 72 | //421 There are too many connections from your internet address. 73 | ftp_reply(sess, FTP_IP_LIMIT, "There are too many connections from your internet address."); 74 | exit(EXIT_FAILURE); 75 | } 76 | } 77 | 78 | static void handle_sigchld(int sig) 79 | { 80 | pid_t pid; 81 | //waitpid挂起当前进程的执行,直到孩子状态发生改变 82 | //孩子状态发生变化,伴随着孩子进程相关资源的释放,这可以解决僵尸进程 83 | while((pid = waitpid(-1, NULL, WNOHANG)) > 0) 84 | { 85 | --num_of_clients; 86 | //pid -> ip 87 | uint32_t *p_ip = hash_lookup_value_by_key(pid_to_ip, &pid, sizeof(pid)); 88 | assert(p_ip != NULL); //ip必须能找到 89 | uint32_t ip = *p_ip; 90 | //ip -> clients 91 | unsigned int *p_value = (unsigned int *)hash_lookup_value_by_key(ip_to_clients, &ip, sizeof(ip)); 92 | assert(p_value != NULL && *p_value > 0); 93 | --*p_value; 94 | 95 | //释放hash表的结点 96 | hash_free_entry(pid_to_ip, &pid, sizeof(pid)); 97 | } 98 | } 99 | 100 | //hash函数 101 | static unsigned int hash_func(unsigned int buckets, void *key) 102 | { 103 | unsigned int *number = (unsigned int*)key; 104 | 105 | return (*number) % buckets; 106 | } 107 | 108 | 109 | //在对应的ip记录中+1,返回当前ip的客户数量 110 | static unsigned int add_ip_to_hash(uint32_t ip) 111 | { 112 | unsigned int *p_value = (unsigned int *)hash_lookup_value_by_key(ip_to_clients, &ip, sizeof(ip)); 113 | if(p_value == NULL) 114 | { 115 | unsigned int value = 1; 116 | hash_add_entry(ip_to_clients, &ip, sizeof(ip), &value, sizeof(value)); 117 | return 1; 118 | } 119 | else 120 | { 121 | unsigned int value = *p_value; 122 | ++value; 123 | *p_value = value; 124 | 125 | return value; 126 | } 127 | } 128 | 129 | 130 | void init_hash() 131 | { 132 | ip_to_clients = hash_alloc(256, hash_func); 133 | pid_to_ip = hash_alloc(256, hash_func); 134 | } 135 | 136 | void add_clients_to_hash(Session_t *sess, uint32_t ip) 137 | { 138 | ++num_of_clients; //连接数目+1 139 | sess->curr_clients = num_of_clients; 140 | sess->curr_ip_clients = add_ip_to_hash(ip); 141 | } 142 | 143 | void add_pid_ip_to_hash(pid_t pid, uint32_t ip) 144 | { 145 | hash_add_entry(pid_to_ip, &pid, sizeof(pid), &ip, sizeof(ip)); 146 | } 147 | 148 | -------------------------------------------------------------------------------- /ftp_assist.h: -------------------------------------------------------------------------------- 1 | #ifndef _FTP_ASSIST_H_ 2 | #define _FTP_ASSIST_H_ 3 | 4 | #include "session.h" 5 | #include "hash.h" 6 | 7 | extern Session_t *p_sess; 8 | extern unsigned int num_of_clients; 9 | extern Hash_t *ip_to_clients; 10 | extern Hash_t *pid_to_ip; 11 | 12 | void init_hash(); 13 | void check_permission(); 14 | void setup_signal_chld(); 15 | void print_conf(); 16 | 17 | 18 | void limit_num_clients(Session_t *sess); 19 | void add_clients_to_hash(Session_t *sess, uint32_t ip); 20 | void add_pid_ip_to_hash(pid_t pid, uint32_t ip); 21 | 22 | #endif /*_FTP_ASSIST_H_*/ 23 | -------------------------------------------------------------------------------- /ftp_codes.h: -------------------------------------------------------------------------------- 1 | #ifndef _FTP_CODES_H_ 2 | #define _FTP_CODES_H_ 3 | 4 | //FTP应答 5 | #define FTP_DATACONN 150 6 | 7 | #define FTP_NOOPOK 200 8 | #define FTP_TYPEOK 200 9 | #define FTP_PORTOK 200 10 | #define FTP_EPRTOK 200 11 | #define FTP_UMASKOK 200 12 | #define FTP_CHMODOK 200 13 | #define FTP_EPSVALLOK 200 14 | #define FTP_STRUOK 200 15 | #define FTP_MODEOK 200 16 | #define FTP_PBSZOK 200 17 | #define FTP_PROTOK 200 18 | #define FTP_OPTSOK 200 19 | #define FTP_ALLOOK 202 20 | #define FTP_FEAT 211 21 | #define FTP_STATOK 211 22 | #define FTP_SIZEOK 213 23 | #define FTP_MDTMOK 213 24 | #define FTP_STATFILE_OK 213 25 | #define FTP_SITEHELP 214 26 | #define FTP_HELP 214 27 | #define FTP_SYSTOK 215 28 | #define FTP_GREET 220 29 | #define FTP_GOODBYE 221 30 | #define FTP_ABOR_NOCONN 225 31 | #define FTP_TRANSFEROK 226 32 | #define FTP_ABOROK 226 33 | #define FTP_PASVOK 227 34 | #define FTP_EPSVOK 229 35 | #define FTP_LOGINOK 230 36 | #define FTP_AUTHOK 234 37 | #define FTP_CWDOK 250 38 | #define FTP_RMDIROK 250 39 | #define FTP_DELEOK 250 40 | #define FTP_RENAMEOK 250 41 | #define FTP_PWDOK 257 42 | #define FTP_MKDIROK 257 43 | 44 | #define FTP_GIVEPWORD 331 45 | #define FTP_RESTOK 350 46 | #define FTP_RNFROK 350 47 | 48 | #define FTP_IDLE_TIMEOUT 421 49 | #define FTP_DATA_TIMEOUT 421 50 | #define FTP_TOO_MANY_USERS 421 51 | #define FTP_IP_LIMIT 421 52 | #define FTP_IP_DENY 421 53 | #define FTP_TLS_FAIL 421 54 | #define FTP_BADSENDCONN 425 55 | #define FTP_BADSENDNET 426 56 | #define FTP_BADSENDFILE 451 57 | 58 | #define FTP_BADCMD 500 59 | #define FTP_BADOPTS 501 60 | #define FTP_COMMANDNOTIMPL 502 61 | #define FTP_NEEDUSER 503 62 | #define FTP_NEEDRNFR 503 63 | #define FTP_BADPBSZ 503 64 | #define FTP_BADPROT 503 65 | #define FTP_BADSTRU 504 66 | #define FTP_BADMODE 504 67 | #define FTP_BADAUTH 504 68 | #define FTP_NOSUCHPROT 504 69 | #define FTP_NEEDENCRYPT 522 70 | #define FTP_EPSVBAD 522 71 | #define FTP_DATATLSBAD 522 72 | #define FTP_LOGINERR 530 73 | #define FTP_NOHANDLEPROT 536 74 | #define FTP_FILEFAIL 550 75 | #define FTP_NOPERM 550 76 | #define FTP_UPLOADFAIL 553 77 | 78 | #endif /*_FTP_CODES_H_*/ 79 | -------------------------------------------------------------------------------- /ftp_nobody.c: -------------------------------------------------------------------------------- 1 | #include "ftp_nobody.h" 2 | #include "common.h" 3 | #include "sysutil.h" 4 | #include "priv_command.h" 5 | #include "priv_sock.h" 6 | 7 | void set_nobody(); 8 | void set_bind_capabilities(); 9 | int capset(cap_user_header_t hdrp, const cap_user_data_t datap); 10 | 11 | //nobody时刻准备从子进程接受命令 12 | void handle_nobody(Session_t *sess) 13 | { 14 | //设置为nobody进程 15 | set_nobody(); 16 | //添加绑定20端口的特权 17 | set_bind_capabilities(); 18 | 19 | char cmd; 20 | while(1) 21 | { 22 | cmd = priv_sock_recv_cmd(sess->nobody_fd); 23 | switch (cmd) 24 | { 25 | case PRIV_SOCK_GET_DATA_SOCK: 26 | privop_pasv_get_data_sock(sess); 27 | break; 28 | case PRIV_SOCK_PASV_ACTIVE: 29 | privop_pasv_active(sess); 30 | break; 31 | case PRIV_SOCK_PASV_LISTEN: 32 | privop_pasv_listen(sess); 33 | break; 34 | case PRIV_SOCK_PASV_ACCEPT: 35 | privop_pasv_accept(sess); 36 | break; 37 | default: 38 | fprintf(stderr, "Unkown command\n"); 39 | exit(EXIT_FAILURE); 40 | } 41 | } 42 | } 43 | 44 | void set_nobody() 45 | { 46 | //基本思路 47 | //1. 首先获取nobody的uid、gid 48 | //2. 然后逐项进行设置 49 | struct passwd *pw; 50 | if((pw = getpwnam("nobody")) == NULL) 51 | ERR_EXIT("getpwnam"); 52 | 53 | //先获取gid 54 | if(setegid(pw->pw_gid) == -1) 55 | ERR_EXIT("setegid"); 56 | 57 | //euid---有效的用户ID 58 | if(seteuid(pw->pw_uid) == -1) 59 | ERR_EXIT("seteuid"); 60 | } 61 | 62 | 63 | void set_bind_capabilities() 64 | { 65 | struct __user_cap_header_struct cap_user_header; 66 | cap_user_header.version = _LINUX_CAPABILITY_VERSION_1; 67 | cap_user_header.pid = getpid(); 68 | 69 | struct __user_cap_data_struct cap_user_data; 70 | __u32 cap_mask = 0; //类似于权限的集合 71 | cap_mask |= (1 << CAP_NET_BIND_SERVICE); //0001000000 72 | cap_user_data.effective = cap_mask; 73 | cap_user_data.permitted = cap_mask; 74 | cap_user_data.inheritable = 0; //子进程不继承特权 75 | 76 | if(capset(&cap_user_header, &cap_user_data) == -1) 77 | ERR_EXIT("capset"); 78 | } 79 | 80 | int capset(cap_user_header_t hdrp, const cap_user_data_t datap) 81 | { 82 | return syscall(SYS_capset, hdrp, datap); 83 | } 84 | 85 | -------------------------------------------------------------------------------- /ftp_nobody.h: -------------------------------------------------------------------------------- 1 | #ifndef _FTP_NOBODY_H_ 2 | #define _FTP_NOBODY_H_ 3 | 4 | #include "session.h" 5 | 6 | void handle_nobody(Session_t *sess); 7 | 8 | #endif /*_FTP_NOBODY_H_*/ 9 | -------------------------------------------------------------------------------- /ftp_proto.c: -------------------------------------------------------------------------------- 1 | #include "ftp_proto.h" 2 | #include "common.h" 3 | #include "sysutil.h" 4 | #include "strutil.h" 5 | #include "ftp_codes.h" 6 | #include "command_map.h" 7 | #include "trans_ctrl.h" 8 | 9 | //子进程不断的从FTP客户端接收FTP指令,并给与回应 10 | void handle_proto(Session_t *sess) 11 | { 12 | //往客户端写 13 | ftp_reply(sess, FTP_GREET, "(FtpServer 1.0)"); 14 | while(1) 15 | { 16 | session_reset_command(sess); //清空状态 17 | 18 | //开始计时 19 | start_signal_alarm_ctrl_fd(); 20 | 21 | //接受命令 22 | int ret = readline(sess->peer_fd, sess->command, MAX_COMMAND); 23 | if(ret == -1) 24 | { 25 | if(errno == EINTR) 26 | continue; 27 | ERR_EXIT("readline"); 28 | } 29 | else if(ret == 0) 30 | { 31 | exit(EXIT_SUCCESS); 32 | } 33 | str_trim_crlf(sess->command); 34 | str_split(sess->command, sess->com, sess->args, ' '); 35 | str_upper(sess->com); 36 | printf("COMMD=[%s], ARGS=[%s]\n", sess->com, sess->args); 37 | 38 | do_command_map(sess); //执行命令映射 39 | } 40 | } 41 | 42 | -------------------------------------------------------------------------------- /ftp_proto.h: -------------------------------------------------------------------------------- 1 | #ifndef _FTP_PROTO_H_ 2 | #define _FTP_PROTO_H_ 3 | 4 | /* 5 | * 子进程 6 | * 7 | */ 8 | #include "session.h" 9 | 10 | void handle_proto(Session_t *sess); 11 | 12 | #endif /*_FTP_PROTO_H_*/ 13 | -------------------------------------------------------------------------------- /ftpserver.conf: -------------------------------------------------------------------------------- 1 | pasv_enable=YES 2 | port_enable=YES 3 | listen_port=8989 4 | max_clients=100 5 | max_per_ip=2 6 | accept_timeout=60 7 | connect_timeout=60 8 | idle_session_timeout=500 9 | data_connection_timeout=900 10 | local_umask=077 11 | upload_max_rate=0 12 | download_max_rate=0 13 | listen_address=192.168.17.140 14 | 15 | -------------------------------------------------------------------------------- /hash.c: -------------------------------------------------------------------------------- 1 | #include "hash.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | static Hash_node_t ** hash_get_bucket(Hash_t *hash, void *key); 8 | static Hash_node_t *hash_get_node_by_key(Hash_t *hash, void *key, unsigned int key_size); 9 | static void hash_clear_bucket(Hash_t *hash, unsigned int index); 10 | 11 | //建立一个hash表,然后找回指针返回 12 | Hash_t *hash_alloc(unsigned int buckets, hashfunc_t hash_func) 13 | { 14 | Hash_t *hash = (Hash_t *)malloc(sizeof(Hash_t)); 15 | hash->buckets = buckets; //桶的个数 16 | hash->hash_func = hash_func; //hash函数 17 | int len = sizeof(Hash_node_t *) * buckets; //数组占用字节数 18 | hash->nodes = (Hash_node_t **)malloc(len); 19 | memset(hash->nodes, 0, len); 20 | return hash; 21 | } 22 | 23 | //根据key查找value 24 | void *hash_lookup_value_by_key(Hash_t *hash, 25 | void *key, 26 | unsigned int key_size) 27 | { 28 | //先查找结点 29 | Hash_node_t *node = hash_get_node_by_key(hash, key, key_size); 30 | if(node == NULL) 31 | { 32 | return NULL; 33 | } 34 | else 35 | { 36 | return node->value; 37 | } 38 | } 39 | 40 | //向hash中添加结点 41 | void hash_add_entry(Hash_t *hash, 42 | void *key, 43 | unsigned int key_size, 44 | void *value, 45 | unsigned int value_size) 46 | { 47 | //1. 查找bucket 48 | Hash_node_t **buck = hash_get_bucket(hash, key); 49 | assert(buck != NULL); 50 | 51 | //2.生成新的结点 52 | Hash_node_t *node = (Hash_node_t *)malloc(sizeof(Hash_node_t)); 53 | memset(node, 0, sizeof(Hash_node_t)); 54 | node->key = malloc(key_size); 55 | node->value = malloc(value_size); 56 | memcpy(node->key, key, key_size); 57 | memcpy(node->value, value, value_size); 58 | 59 | //3.插入相应的链表, 头插法 60 | if(*buck != NULL) 61 | { 62 | Hash_node_t *head = *buck; //head是链表第一个结点 63 | 64 | node->next = head; 65 | head->pre = node; 66 | *buck = node; // 这里要用指针操作 67 | } 68 | else 69 | { 70 | *buck = node; 71 | } 72 | } 73 | 74 | //hash中释放结点 75 | void hash_free_entry(Hash_t *hash, 76 | void *key, 77 | unsigned int key_size) 78 | { 79 | //查找结点 80 | Hash_node_t *node = hash_get_node_by_key(hash, key, key_size); 81 | assert(node != NULL); 82 | 83 | free(node->key); 84 | free(node->value); 85 | 86 | //删除结点 87 | if(node->pre != NULL) //不是第一个结点 88 | { 89 | node->pre->next = node->next; 90 | } 91 | else 92 | { 93 | Hash_node_t **buck = hash_get_bucket(hash, key); 94 | *buck = node->next; 95 | } 96 | if(node->next != NULL) //还有下一个结点 97 | node->next->pre = node->pre; 98 | free(node); 99 | } 100 | 101 | //清空hash表 102 | void hash_clear_entry(Hash_t *hash) 103 | { 104 | unsigned int i; 105 | for(i = 0; i < hash->buckets; ++i) 106 | { 107 | hash_clear_bucket(hash, i); 108 | hash->nodes[i] = NULL; 109 | } 110 | } 111 | 112 | //销毁hash表 113 | void hash_destroy(Hash_t *hash) 114 | { 115 | hash_clear_entry(hash); 116 | free(hash->nodes); 117 | free(hash); 118 | } 119 | 120 | //根据key获取bucket 121 | static Hash_node_t **hash_get_bucket(Hash_t *hash, void *key) 122 | { 123 | unsigned int pos = hash->hash_func(hash->buckets, key); //根据key获取key所在buckets的位置 124 | assert(pos < hash->buckets); 125 | 126 | return &(hash->nodes[pos]); //返回key所在链表地址 127 | } 128 | 129 | //根据key获取node结点 130 | static Hash_node_t *hash_get_node_by_key(Hash_t *hash, 131 | void *key, 132 | unsigned int key_size) 133 | { 134 | //获取bucket 135 | Hash_node_t **buck = hash_get_bucket(hash, key); 136 | assert(buck != NULL); 137 | 138 | //查找元素 139 | Hash_node_t *node = *buck; //指向(key所在链表的)第一个元素 140 | while(node != NULL && memcmp(node->key, key, key_size) != 0) 141 | { 142 | node = node->next; 143 | } 144 | 145 | return node; //包含三种情况 NULL、??? 146 | } 147 | 148 | //清空bucket 149 | static void hash_clear_bucket(Hash_t *hash, unsigned int index) 150 | { 151 | assert(index < hash->buckets); //防止越界 152 | Hash_node_t *node = hash->nodes[index]; //获得key所在桶第一个元素 153 | Hash_node_t *tmp = node; 154 | while(node) 155 | { 156 | tmp = node->next; 157 | free(node->key); 158 | free(node->value); 159 | free(node); 160 | node = tmp; 161 | } 162 | hash->nodes[index] = NULL; 163 | } 164 | 165 | -------------------------------------------------------------------------------- /hash.h: -------------------------------------------------------------------------------- 1 | #ifndef _HASH_H_ 2 | #define _HASH_H_ 3 | 4 | //hash函数 5 | typedef unsigned int (*hashfunc_t)(unsigned int, void*); 6 | 7 | //hash结点定义 8 | typedef struct Hash_node 9 | { 10 | void *key; //关键字 11 | void *value; //要的存储信息 12 | struct Hash_node *pre; 13 | struct Hash_node *next; 14 | }Hash_node_t; 15 | 16 | //hash表定义 17 | typedef struct Hash 18 | { 19 | unsigned int buckets; //bucket个数 20 | hashfunc_t hash_func; //hash函数 21 | Hash_node_t **nodes; //链表数组 22 | }Hash_t; 23 | 24 | // 25 | Hash_t *hash_alloc(unsigned int buckets, hashfunc_t hash_func); 26 | void *hash_lookup_value_by_key(Hash_t *hash, void *key, unsigned int key_size); 27 | void hash_add_entry(Hash_t *hash, void *key, unsigned int key_size, void *value, unsigned int value_size); 28 | void hash_free_entry(Hash_t *hash, void *key, unsigned int key_size); 29 | void hash_clear_entry(Hash_t *hash); 30 | void hash_destroy(Hash_t *hash); 31 | 32 | #endif /*_HASH_H_*/ 33 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include "sysutil.h" 3 | #include "session.h" 4 | #include "configure.h" 5 | #include "parse_conf.h" 6 | #include "ftp_assist.h" 7 | 8 | int main(int argc, const char *argv[]) 9 | { 10 | //使用root权限运行 11 | check_permission(); 12 | 13 | //处理僵尸进程 14 | setup_signal_chld(); 15 | 16 | //解析配置文件 17 | parseconf_load_file("ftpserver.conf"); 18 | print_conf(); 19 | 20 | init_hash(); 21 | 22 | //创建一个监听fd 23 | int listenfd = tcp_server(tunable_listen_address, tunable_listen_port); 24 | 25 | pid_t pid; 26 | Session_t sess; 27 | session_init(&sess); 28 | p_sess = &sess; //配置全局变量 29 | 30 | while(1) 31 | { 32 | //每当用户连接上,就fork一个子进程 33 | 34 | struct sockaddr_in addr; 35 | int peerfd = accept_timeout(listenfd, &addr, tunable_accept_timeout); 36 | if(peerfd == -1 && errno == ETIMEDOUT) 37 | continue; 38 | else if(peerfd == -1) 39 | ERR_EXIT("accept_timeout"); 40 | 41 | //获取ip地址,并在hash中添加一条记录 42 | uint32_t ip = addr.sin_addr.s_addr; 43 | sess.ip = ip; 44 | add_clients_to_hash(&sess, ip); 45 | 46 | if((pid = fork()) == -1) 47 | ERR_EXIT("fork"); 48 | else if(pid == 0) 49 | { 50 | close(listenfd); 51 | 52 | sess.peer_fd = peerfd; 53 | limit_num_clients(&sess); 54 | session_begin(&sess); 55 | //这里保证每次成功执行后退出循环 56 | exit(EXIT_SUCCESS); 57 | } 58 | else 59 | { 60 | //pid_to_ip 61 | add_pid_ip_to_hash(pid, ip); 62 | close(peerfd); 63 | } 64 | } 65 | 66 | return 0; 67 | } 68 | 69 | -------------------------------------------------------------------------------- /parse_conf.c: -------------------------------------------------------------------------------- 1 | #include "parse_conf.h" 2 | #include "common.h" 3 | #include "strutil.h" 4 | #include "configure.h" 5 | 6 | /* 7 | * parseconf_load_setting()函数只是 8 | * 由parseconf_load_file()函数调用, 9 | * 并不希望让用户调用, 10 | * 所以这里parseconf_load_settting() 11 | * 函数的声明和实现都在源文件中 12 | */ 13 | static void parseconf_load_setting(const char *setting); 14 | 15 | //bool类型的配置变量 16 | //这种struct a b;的形式值得借鉴 17 | static struct parseconf_bool_setting 18 | { 19 | const char *p_setting_name; 20 | int *p_variable; 21 | } 22 | parseconf_bool_array[] = 23 | { 24 | { "pasv_enable", &tunable_pasv_enable }, 25 | { "port_enable", &tunable_port_enable }, 26 | { NULL, NULL } 27 | }; 28 | 29 | static struct parseconf_uint_setting 30 | { 31 | const char *p_setting_name; 32 | unsigned int *p_variable; 33 | } 34 | parseconf_uint_array[] = 35 | { 36 | { "listen_port", &tunable_listen_port }, 37 | { "max_clients", &tunable_max_clients }, 38 | { "max_per_ip", &tunable_max_per_ip }, 39 | { "accept_timeout", &tunable_accept_timeout }, 40 | { "connect_timeout", &tunable_connect_timeout }, 41 | { "idle_session_timeout", &tunable_idle_session_timeout }, 42 | { "data_connection_timeout", &tunable_data_connection_timeout }, 43 | { "local_umask", &tunable_local_umask }, 44 | { "upload_max_rate", &tunable_upload_max_rate }, 45 | { "download_max_rate", &tunable_download_max_rate }, 46 | { NULL, NULL } 47 | }; 48 | 49 | static struct parseconf_str_setting 50 | { 51 | const char *p_setting_name; 52 | const char **p_variable; 53 | } 54 | parseconf_str_array[] = 55 | { 56 | { "listen_address", &tunable_listen_address }, 57 | { NULL, NULL } 58 | }; 59 | 60 | //parseconf_load_file() 61 | void parseconf_load_file(const char *path) 62 | { 63 | FILE *fp = fopen(path, "r"); 64 | if (fp == NULL) 65 | ERR_EXIT("fopen"); 66 | 67 | char setting_line[1024] = {0}; 68 | while (fgets(setting_line, sizeof(setting_line), fp) != NULL) 69 | { 70 | if (strlen(setting_line) == 0 71 | || setting_line[0] == '#' 72 | || str_all_space(setting_line)) 73 | continue; 74 | 75 | str_trim_crlf(setting_line); 76 | parseconf_load_setting(setting_line); 77 | memset(setting_line, 0, sizeof(setting_line)); 78 | } 79 | 80 | fclose(fp); 81 | } 82 | 83 | static void parseconf_load_setting(const char *setting) 84 | { 85 | // 去除左空格 86 | while (isspace(*setting)) 87 | ++setting; 88 | 89 | char key[128] ={0}; 90 | char value[128] = {0}; 91 | str_split(setting, key, value, '='); 92 | if (strlen(value) == 0) 93 | { 94 | fprintf(stderr, "mising value in config file for: %s\n", key); 95 | exit(EXIT_FAILURE); 96 | } 97 | 98 | 99 | { 100 | const struct parseconf_str_setting *p_str_setting = parseconf_str_array; 101 | while (p_str_setting->p_setting_name != NULL) 102 | { 103 | if (strcmp(key, p_str_setting->p_setting_name) == 0) 104 | { 105 | const char **p_cur_setting = p_str_setting->p_variable; 106 | if (*p_cur_setting) 107 | free((char*)*p_cur_setting); 108 | 109 | *p_cur_setting = strdup(value); 110 | return; 111 | } 112 | 113 | ++p_str_setting; 114 | } 115 | } 116 | 117 | { 118 | const struct parseconf_bool_setting *p_bool_setting = parseconf_bool_array; 119 | while (p_bool_setting->p_setting_name != NULL) 120 | { 121 | if (strcmp(key, p_bool_setting->p_setting_name) == 0) 122 | { 123 | str_upper(value); 124 | if (strcmp(value, "YES") == 0 125 | || strcmp(value, "TRUE") == 0 126 | || strcmp(value, "1") == 0) 127 | *(p_bool_setting->p_variable) = 1; 128 | else if (strcmp(value, "NO") == 0 129 | || strcmp(value, "FALSE") == 0 130 | || strcmp(value, "0") == 0) 131 | *(p_bool_setting->p_variable) = 0; 132 | else 133 | { 134 | fprintf(stderr, "bad bool value in config file for: %s\n", key); 135 | exit(EXIT_FAILURE); 136 | } 137 | 138 | return; 139 | } 140 | 141 | ++p_bool_setting; 142 | } 143 | } 144 | 145 | { 146 | const struct parseconf_uint_setting *p_uint_setting = parseconf_uint_array; 147 | while (p_uint_setting->p_setting_name != NULL) 148 | { 149 | if (strcmp(key, p_uint_setting->p_setting_name) == 0) 150 | { 151 | if (value[0] == '0') 152 | *(p_uint_setting->p_variable) = str_octal_to_uint(value); 153 | else 154 | *(p_uint_setting->p_variable) = atoi(value); 155 | 156 | return; 157 | } 158 | 159 | ++p_uint_setting; 160 | } 161 | } 162 | } 163 | 164 | -------------------------------------------------------------------------------- /parse_conf.h: -------------------------------------------------------------------------------- 1 | #ifndef _PARSE_CONF_H_ 2 | #define _PARSE_CONF_H_ 3 | 4 | void parseconf_load_file(const char*); 5 | 6 | #endif /*_PARSE_CONF_H_*/ 7 | -------------------------------------------------------------------------------- /priv_command.c: -------------------------------------------------------------------------------- 1 | #include "priv_command.h" 2 | #include "common.h" 3 | #include "sysutil.h" 4 | #include "priv_sock.h" 5 | #include "configure.h" 6 | 7 | //获取数据套接字 8 | void privop_pasv_get_data_sock(Session_t *sess) 9 | { 10 | char ip[16] = {0}; 11 | priv_sock_recv_str(sess->nobody_fd, ip, sizeof ip); 12 | uint16_t port = priv_sock_recv_int(sess->nobody_fd); 13 | //创建fd 14 | int data_fd = tcp_client(20); 15 | 16 | struct sockaddr_in addr; 17 | memset(&addr, 0, sizeof addr); 18 | addr.sin_family = AF_INET; 19 | addr.sin_addr.s_addr = inet_addr(ip); 20 | addr.sin_port = htons(port); 21 | int ret = connect_timeout(data_fd, &addr, tunable_connect_timeout); 22 | if(ret == -1) 23 | ERR_EXIT("connect_timeout"); 24 | priv_sock_send_result(sess->nobody_fd, PRIV_SOCK_RESULT_OK); 25 | priv_sock_send_fd(sess->nobody_fd, data_fd); 26 | close(data_fd); 27 | } 28 | 29 | //判断pasv模式是否开启 30 | void privop_pasv_active(Session_t *sess) 31 | { 32 | //发给proto结果 33 | priv_sock_send_int(sess->nobody_fd, (sess->listen_fd != -1)); 34 | } 35 | 36 | //获取监听fd 37 | void privop_pasv_listen(Session_t *sess) 38 | { 39 | //创建listen fd 40 | char ip[16] = {0}; 41 | get_local_ip(ip); 42 | int listenfd = tcp_server(ip, 20); 43 | sess->listen_fd = listenfd; 44 | 45 | struct sockaddr_in addr; 46 | socklen_t len = sizeof addr; 47 | if(getsockname(listenfd, (struct sockaddr*)&addr, &len) == -1) 48 | ERR_EXIT("getsockname"); 49 | 50 | //发送应答 51 | priv_sock_send_result(sess->nobody_fd, PRIV_SOCK_RESULT_OK); 52 | //发送port 53 | uint16_t net_endian_port = ntohs(addr.sin_port); 54 | priv_sock_send_int(sess->nobody_fd, net_endian_port); 55 | } 56 | 57 | //accept一个新的连接 58 | void privop_pasv_accept(Session_t *sess) 59 | { 60 | //接受新连接 61 | int peerfd = accept_timeout(sess->listen_fd, NULL, tunable_accept_timeout); 62 | //清除状态 63 | close(sess->listen_fd); 64 | sess->listen_fd = -1; 65 | if(peerfd == -1) 66 | { 67 | priv_sock_send_result(sess->nobody_fd, PRIV_SOCK_RESULT_BAD); 68 | ERR_EXIT("accept_timeout"); 69 | } 70 | 71 | //给对方回应 72 | priv_sock_send_result(sess->nobody_fd, PRIV_SOCK_RESULT_OK); 73 | //将data fd传给对方 74 | priv_sock_send_fd(sess->nobody_fd, peerfd); 75 | close(peerfd); 76 | } 77 | 78 | -------------------------------------------------------------------------------- /priv_command.h: -------------------------------------------------------------------------------- 1 | #ifndef _PRIV_COMMAND_H_ 2 | #define _PRIV_COMMAND_H_ 3 | 4 | #include "session.h" 5 | 6 | void privop_pasv_get_data_sock(Session_t *sess); 7 | void privop_pasv_active(Session_t *sess); 8 | void privop_pasv_listen(Session_t *sess); 9 | void privop_pasv_accept(Session_t *sess); 10 | 11 | #endif /*_PRIV_COMMAND_H_*/ 12 | -------------------------------------------------------------------------------- /priv_sock.c: -------------------------------------------------------------------------------- 1 | #include "priv_sock.h" 2 | #include "common.h" 3 | #include "sysutil.h" 4 | #include "trans_ctrl.h" 5 | 6 | 7 | void priv_sock_init(Session_t *sess) 8 | { 9 | int fds[2]; 10 | if(socketpair(PF_UNIX, SOCK_STREAM, 0, fds) == -1) 11 | ERR_EXIT("socketpair"); 12 | 13 | sess->nobody_fd = fds[0]; 14 | sess->proto_fd = fds[1]; 15 | } 16 | 17 | void priv_sock_close(Session_t *sess) 18 | { 19 | if(sess->nobody_fd != -1) 20 | { 21 | close(sess->nobody_fd); 22 | sess->nobody_fd = -1; 23 | } 24 | 25 | if(sess->proto_fd != -1) 26 | { 27 | close(sess->proto_fd); 28 | sess->proto_fd = -1; 29 | } 30 | } 31 | void priv_sock_set_nobody_context(Session_t *sess) 32 | { 33 | close(sess->peer_fd); //关闭控制连接 34 | 35 | if(sess->proto_fd != -1) 36 | { 37 | close(sess->proto_fd); 38 | sess->proto_fd = -1; 39 | } 40 | } 41 | 42 | void priv_sock_set_proto_context(Session_t *sess) 43 | { 44 | if(sess->nobody_fd != -1) 45 | { 46 | close(sess->nobody_fd); 47 | sess->nobody_fd = -1; 48 | } 49 | 50 | setup_signal_alarm_ctrl_fd(); //安装定时器 51 | setup_signal_sigurg(); //安装定时器 52 | activate_oobinline(sess->peer_fd); //开启带外数据 53 | activate_signal_sigurg(sess->peer_fd); //开启sigurg信号 54 | } 55 | 56 | void priv_sock_send_cmd(int fd, char cmd) 57 | { 58 | int ret = writen(fd, &cmd, sizeof cmd); 59 | if(ret != sizeof(cmd)) 60 | { 61 | fprintf(stderr, "priv_sock_send_cmd error\n"); 62 | exit(EXIT_FAILURE); 63 | } 64 | } 65 | 66 | char priv_sock_recv_cmd(int fd) 67 | { 68 | char res; 69 | int ret = readn(fd, &res, sizeof res); 70 | //子进程关闭 71 | if(ret == 0) 72 | { 73 | printf("Proto close!\n"); 74 | exit(EXIT_SUCCESS); 75 | } 76 | if(ret != sizeof(res)) 77 | { 78 | fprintf(stderr, "priv_sock_recv_cmd error\n"); 79 | exit(EXIT_FAILURE); 80 | } 81 | 82 | return res; 83 | } 84 | 85 | void priv_sock_send_result(int fd, char res) 86 | { 87 | int ret = writen(fd, &res, sizeof res); 88 | if(ret != sizeof(res)) 89 | { 90 | fprintf(stderr, "priv_sock_send_result\n"); 91 | exit(EXIT_FAILURE); 92 | } 93 | } 94 | 95 | char priv_sock_recv_result(int fd) 96 | { 97 | char res; 98 | int ret = readn(fd, &res, sizeof res); 99 | if(ret != sizeof(res)) 100 | { 101 | fprintf(stderr, "priv_sock_recv_result error\n"); 102 | exit(EXIT_FAILURE); 103 | } 104 | 105 | return res; 106 | } 107 | 108 | void priv_sock_send_int(int fd, int the_int) 109 | { 110 | int ret = writen(fd, &the_int, sizeof(int)); 111 | if(ret != sizeof(the_int)) 112 | { 113 | fprintf(stderr, "priv_sock_send_int error\n"); 114 | exit(EXIT_FAILURE); 115 | } 116 | } 117 | 118 | int priv_sock_recv_int(int fd) 119 | { 120 | int res; 121 | int ret = readn(fd, &res, sizeof(res)); 122 | if(ret != sizeof(res)) 123 | { 124 | fprintf(stderr, "priv_sock_recv_int error\n"); 125 | exit(EXIT_FAILURE); 126 | } 127 | 128 | return res; 129 | } 130 | 131 | void priv_sock_send_str(int fd, const char *buf, unsigned int len) 132 | { 133 | priv_sock_send_int(fd, (int)len); 134 | int ret = writen(fd, buf, len); 135 | if(ret != (int)len) 136 | { 137 | fprintf(stderr, "priv_sock_send_str error\n"); 138 | exit(EXIT_FAILURE); 139 | } 140 | } 141 | 142 | 143 | void priv_sock_recv_str(int fd, char *buf, unsigned int len) 144 | { 145 | unsigned int recv_len = (unsigned int)priv_sock_recv_int(fd); 146 | if (recv_len > len) 147 | { 148 | fprintf(stderr, "priv_sock_recv_str error\n"); 149 | exit(EXIT_FAILURE); 150 | } 151 | 152 | int ret = readn(fd, buf, recv_len); 153 | if (ret != (int)recv_len) 154 | { 155 | fprintf(stderr, "priv_sock_recv_str error\n"); 156 | exit(EXIT_FAILURE); 157 | } 158 | } 159 | 160 | void priv_sock_send_fd(int sock_fd, int fd) 161 | { 162 | send_fd(sock_fd, fd); 163 | } 164 | 165 | int priv_sock_recv_fd(int sock_fd) 166 | { 167 | return recv_fd(sock_fd); 168 | } 169 | 170 | -------------------------------------------------------------------------------- /priv_sock.h: -------------------------------------------------------------------------------- 1 | #ifndef _PRIV_SOCK_H_ 2 | #define _PRIV_SOCK_H_ 3 | 4 | #include "session.h" 5 | 6 | // FTP服务进程向nobody进程请求的命令 7 | #define PRIV_SOCK_GET_DATA_SOCK 1 8 | #define PRIV_SOCK_PASV_ACTIVE 2 9 | #define PRIV_SOCK_PASV_LISTEN 3 10 | #define PRIV_SOCK_PASV_ACCEPT 4 11 | 12 | // nobody进程对FTP服务进程的应答 13 | #define PRIV_SOCK_RESULT_OK 1 14 | #define PRIV_SOCK_RESULT_BAD 2 15 | 16 | void priv_sock_init(Session_t *sess); 17 | void priv_sock_close(Session_t *sess); 18 | void priv_sock_set_nobody_context(Session_t *sess); 19 | void priv_sock_set_proto_context(Session_t *sess); 20 | void priv_sock_send_cmd(int fd, char cmd); 21 | char priv_sock_recv_cmd(int fd); 22 | void priv_sock_send_result(int fd, char res); 23 | char priv_sock_recv_result(int fd); 24 | void priv_sock_send_int(int fd, int the_int); 25 | int priv_sock_recv_int(int fd); 26 | void priv_sock_send_str(int fd, const char *buf, unsigned int len); 27 | void priv_sock_recv_str(int fd, char *buf, unsigned int len); 28 | void priv_sock_send_fd(int sock_fd, int fd); 29 | int priv_sock_recv_fd(int sock_fd); 30 | 31 | #endif /*_PRIV_SOCK_H_*/ 32 | -------------------------------------------------------------------------------- /session.c: -------------------------------------------------------------------------------- 1 | #include "session.h" 2 | #include "common.h" 3 | #include "ftp_proto.h" 4 | #include "ftp_nobody.h" 5 | #include "priv_sock.h" 6 | #include "configure.h" 7 | 8 | void session_init(Session_t *sess) 9 | { 10 | memset(sess->command, 0, sizeof (sess->command)); 11 | memset(sess->com, 0, sizeof (sess->com)); 12 | memset(sess->args, 0, sizeof (sess->args)); 13 | 14 | sess->ip = 0; 15 | memset(sess->username, 0, sizeof(sess->username)); 16 | 17 | sess->peer_fd = -1; 18 | sess->nobody_fd = -1; 19 | sess->proto_fd = -1; 20 | 21 | sess->user_uid = 0; 22 | sess->ascii_mode = 0; 23 | 24 | sess->p_addr = NULL; 25 | sess->data_fd = -1; 26 | sess->listen_fd = -1; 27 | 28 | sess->restart_pos = 0; 29 | sess->rnfr_name = NULL; 30 | 31 | sess->limits_max_upload = tunable_upload_max_rate * 1024; 32 | sess->limits_max_download = tunable_download_max_rate * 1024; 33 | sess->start_time_sec = 0; 34 | sess->start_time_usec = 0; 35 | 36 | sess->is_translating_data = 0; 37 | sess->is_receive_abor = 0; 38 | 39 | sess->curr_clients = 0; 40 | sess->curr_ip_clients = 0; 41 | } 42 | 43 | void session_reset_command(Session_t *sess) 44 | { 45 | memset(sess->command, 0, sizeof (sess->command)); 46 | memset(sess->com, 0, sizeof (sess->com)); 47 | memset(sess->args, 0, sizeof (sess->args)); 48 | } 49 | 50 | void session_begin(Session_t *sess) 51 | { 52 | priv_sock_init(sess); 53 | 54 | pid_t pid; 55 | if((pid = fork()) == -1) 56 | ERR_EXIT("fork"); 57 | else if(pid == 0) 58 | { 59 | priv_sock_set_proto_context(sess); 60 | handle_proto(sess); 61 | } 62 | else 63 | { 64 | priv_sock_set_nobody_context(sess); 65 | handle_nobody(sess); 66 | } 67 | } 68 | 69 | -------------------------------------------------------------------------------- /session.h: -------------------------------------------------------------------------------- 1 | #ifndef _SESSION_H_ 2 | #define _SESSION_H_ 3 | 4 | #include "common.h" 5 | #define MAX_COMMAND 1024 6 | 7 | /* 8 | * 核心模块 9 | */ 10 | 11 | typedef struct 12 | { 13 | char command[MAX_COMMAND];//client发来的FTP指令 14 | char com[MAX_COMMAND];//FTP指令 15 | char args[MAX_COMMAND];//FTP指令的参数 16 | 17 | uint32_t ip; //客户端ip地址 18 | char username[100]; //用户名 19 | 20 | int peer_fd;//客户连接的fd 21 | 22 | int nobody_fd;//nobody进程所使用的fd 23 | int proto_fd;//proto进程所使用的fd 24 | 25 | uid_t user_uid; //用户的uid 26 | int ascii_mode; //是否为ascii传输模式 27 | 28 | struct sockaddr_in *p_addr; //port模式下对方的ip和port 29 | int data_fd; //数据传输fd 30 | int listen_fd; //监听fd,用于PASV模式 31 | 32 | long long restart_pos; //文件传输断点 33 | char *rnfr_name; //文件重命名 RNTR RNTO 34 | 35 | 36 | int limits_max_upload; //限定的最大上传速度 37 | int limits_max_download; //限定的最大下载速度 38 | int start_time_sec; //开始的秒数 39 | int start_time_usec; //开始的微秒数 40 | 41 | int is_translating_data; //是否在传输数据 42 | int is_receive_abor; //是否受到了abor 43 | 44 | unsigned int curr_clients; //当前的客户数量 45 | unsigned int curr_ip_clients; //当前ip的客户数量 46 | }Session_t; 47 | 48 | //初始化session 49 | void session_init(Session_t *sess); 50 | 51 | //将三个字符数组置位 52 | void session_reset_command(Session_t *sess); 53 | 54 | //处理会话,这里主要是创建nobody与ftp子进程 55 | void session_begin(Session_t *sess); 56 | 57 | #endif /*_SESSION_H_*/ 58 | -------------------------------------------------------------------------------- /strutil.c: -------------------------------------------------------------------------------- 1 | #include "strutil.h" 2 | #include "common.h" 3 | 4 | void str_trim_crlf(char *str) 5 | { 6 | char *p = &str[strlen(str)-1]; 7 | while(*p == '\r' || *p == '\n') 8 | *p-- = '\0'; 9 | } 10 | 11 | void str_split(const char *str , char *left, char *right, char c) 12 | { 13 | //strchr返回字符串str中第一次出现字符c的位置 14 | char *p = strchr(str, c); 15 | if (p == NULL) 16 | strcpy(left, str); 17 | else 18 | { 19 | //strncpy最多拷贝p-str个字符到字符串left中 20 | strncpy(left, str, p-str); 21 | //strcpy从p+1指向的位置拷贝字符串到right指向的开始位置 22 | strcpy(right, p+1); 23 | } 24 | } 25 | 26 | int str_all_space(const char *str) 27 | { 28 | while (*str) 29 | { 30 | if (!isspace(*str)) 31 | return 0; 32 | ++str; 33 | } 34 | return 1; 35 | } 36 | 37 | void str_upper(char *str) 38 | { 39 | while (*str) 40 | { 41 | *str = toupper(*str); 42 | ++str; 43 | } 44 | } 45 | 46 | 47 | unsigned int str_octal_to_uint(const char *str) 48 | { 49 | unsigned int result = 0; 50 | int seen_non_zero_digit = 0; 51 | 52 | while (*str) 53 | { 54 | int digit = *str; 55 | if (!isdigit(digit) || digit > '7') 56 | break; 57 | 58 | if (digit != '0') 59 | seen_non_zero_digit = 1; 60 | 61 | if (seen_non_zero_digit) 62 | { 63 | result <<= 3; 64 | result += (digit - '0'); 65 | } 66 | ++str; 67 | } 68 | return result; 69 | } 70 | 71 | -------------------------------------------------------------------------------- /strutil.h: -------------------------------------------------------------------------------- 1 | #ifndef _STRUTIL_H_ 2 | #define _STRUTIL_H_ 3 | 4 | //去除右边的\r\n 5 | void str_trim_crlf(char *str); 6 | //分割字符串,根据c分割为两部分 7 | void str_split(const char *str , char *left, char *right, char c); 8 | //判断字符串是否全部为空格 9 | int str_all_space(const char *str); 10 | //把字符串转化为大写 11 | void str_upper(char *str); 12 | //把八进制字符串转化为无符号整数 13 | unsigned int str_octal_to_uint(const char *str); 14 | 15 | #endif /*_STRUTIL_H_*/ 16 | -------------------------------------------------------------------------------- /sysutil.c: -------------------------------------------------------------------------------- 1 | #include "sysutil.h" 2 | #include "common.h" 3 | 4 | static ssize_t recv_peek(int sockfd, void *buf, size_t len); 5 | 6 | static int lock_file(int fd, int type); 7 | 8 | static struct timeval tv = {0, 0}; //全局变量 9 | 10 | //设置socket的选项,使其接收带外数据 11 | void activate_oobinline(int sockfd) 12 | { 13 | int oob_inline = 1; 14 | if(setsockopt(sockfd, SOL_SOCKET, SO_OOBINLINE, &oob_inline, sizeof(oob_inline)) == -1) 15 | ERR_EXIT("setsockopt oobinline"); 16 | } 17 | 18 | //开启SIGURG信号,当有带外数据到来时,发出该信号 19 | void activate_signal_sigurg(int sockfd) 20 | { 21 | if(fcntl(sockfd, F_SETOWN, getpid()) == -1) 22 | ERR_EXIT("fcntl sigurg"); 23 | } 24 | 25 | int get_curr_time_sec() 26 | { 27 | if(gettimeofday(&tv, NULL) == -1) 28 | ERR_EXIT("gettimeofday"); 29 | return tv.tv_sec; 30 | } 31 | 32 | int get_curr_time_usec() 33 | { 34 | return tv.tv_usec; 35 | } 36 | 37 | int nano_sleep(double t) 38 | { 39 | int sec = (time_t)t;//取整数部分 40 | int nsec = (t - sec) * 1000000000; 41 | //int nanosleep(const struct timespec *req, struct timespec *rem); 42 | struct timespec ts; 43 | ts.tv_sec = sec; 44 | ts.tv_nsec = nsec; 45 | 46 | int ret; 47 | do 48 | {//当睡眠被打断时,剩余时间放到ts里面 49 | ret = nanosleep(&ts, &ts); 50 | } 51 | while(ret == -1 && errno == EINTR) 52 | ; 53 | 54 | return ret; 55 | } 56 | 57 | int lock_file_read(int fd) 58 | { 59 | return lock_file(fd, F_RDLCK); 60 | } 61 | 62 | int lock_file_write(int fd) 63 | { 64 | return lock_file(fd, F_WRLCK); 65 | } 66 | 67 | int unlock_file(int fd) 68 | { 69 | struct flock lock; 70 | memset(&lock, 0, sizeof lock); 71 | lock.l_type = F_UNLCK; 72 | lock.l_whence = SEEK_SET; 73 | lock.l_start = 0; 74 | lock.l_len = 0; 75 | lock.l_pid = getpid(); 76 | 77 | return fcntl(fd, F_SETLK, &lock); 78 | } 79 | 80 | static int lock_file(int fd, int type) 81 | { 82 | struct flock lock; 83 | memset(&lock, 0, sizeof lock); 84 | lock.l_type = type; 85 | lock.l_whence = SEEK_SET; 86 | lock.l_start = 0; 87 | lock.l_len = 0; 88 | lock.l_pid = getpid(); 89 | 90 | int ret; 91 | do 92 | { 93 | ret = fcntl(fd, F_SETLKW, &lock); 94 | } 95 | while(ret == -1 && errno == EINTR) 96 | ; 97 | 98 | return ret; 99 | } 100 | 101 | /* 102 | *函数功能:创建客户套接字 103 | *参数port: 端口号 104 | *函数返回值:返回客户端套接字 105 | */ 106 | int tcp_client(unsigned int port) 107 | { 108 | int sockfd; 109 | //设置端口复用 110 | if((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) 111 | ERR_EXIT("socket"); 112 | 113 | if(port > 0) 114 | { 115 | int on = 1; 116 | if((setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char *)&on, sizeof(on))) == -1) 117 | ERR_EXIT("setsockopt"); 118 | 119 | struct sockaddr_in addr; 120 | memset(&addr, 0, sizeof addr); 121 | addr.sin_family = AF_INET; 122 | addr.sin_port = htons(port); 123 | char ip[16] = {0}; 124 | get_local_ip(ip); 125 | addr.sin_addr.s_addr = inet_addr(ip); 126 | 127 | if(bind(sockfd, (struct sockaddr *)&addr, sizeof addr) == -1) 128 | ERR_EXIT("bind"); 129 | } 130 | 131 | return sockfd; 132 | } 133 | 134 | /* 135 | *函数功能:启动服务器 136 | *参数host:服务器IP地址或者服务器主机名 137 | *参数port:服务器端口 138 | *函数返回值:成功则返回监听套接字 139 | */ 140 | int tcp_server(const char *host, unsigned short port) 141 | { 142 | //建立套接字 143 | int listenfd; 144 | if((listenfd = socket(PF_INET, SOCK_STREAM, 0)) < 0) 145 | ERR_EXIT("tcp_server"); 146 | struct sockaddr_in seraddr; 147 | memset(&seraddr, 0, sizeof(seraddr)); 148 | seraddr.sin_family = AF_INET; 149 | if(host != NULL) 150 | { 151 | /* 152 | *inet_aton函数将const char * 类型的host 153 | *转化为二进制形式 154 | *并存放到in_addr结构体中,sin_addr是in_addr类型的结构体 155 | */ 156 | if(inet_aton(host, &seraddr.sin_addr) == 0) 157 | { 158 | /* 159 | *gethostbyname返回一个hostent结构体类型 160 | *并将host拷贝到hostent结构体下的h_name中, 161 | *以及host的in_addr结构体类型内容拷贝到返回值中hostent结构体类型的h_addr_list[0]中 162 | *其中#define h_addr h_addr_list[0] 163 | */ 164 | struct hostent *hp; 165 | hp = gethostbyname(host); 166 | if(hp == NULL) 167 | ERR_EXIT("gethostbyname"); 168 | seraddr.sin_addr = *(struct in_addr*)hp->h_addr; 169 | } 170 | } 171 | else 172 | seraddr.sin_addr.s_addr = htonl(INADDR_ANY); 173 | 174 | seraddr.sin_port = htons(port); 175 | 176 | //设置端口复用 177 | int on = 1; 178 | if((setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on))) < 0) 179 | ERR_EXIT("setsockopt"); 180 | 181 | //绑定 182 | if(bind(listenfd, (struct sockaddr*)&seraddr, sizeof(seraddr)) < 0) 183 | ERR_EXIT("bind"); 184 | 185 | //监听 186 | if(listen(listenfd, SOMAXCONN) < 0) 187 | ERR_EXIT("listen"); 188 | 189 | return listenfd; 190 | } 191 | 192 | /* 193 | *功能:获取当前主机的IP 194 | *ip: 195 | *返回值: 196 | */ 197 | int get_local_ip(char *ip) 198 | { 199 | int sockfd; 200 | if((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) 201 | { 202 | ERR_EXIT("socket"); 203 | } 204 | 205 | struct ifreq req; 206 | 207 | bzero(&req, sizeof(struct ifreq)); 208 | strcpy(req.ifr_name, "eth0"); 209 | 210 | if(ioctl(sockfd, SIOCGIFADDR, &req) == -1) 211 | ERR_EXIT("ioctl"); 212 | 213 | struct sockaddr_in *host = (struct sockaddr_in*)&req.ifr_addr; 214 | strcpy(ip, inet_ntoa(host->sin_addr)); 215 | close(sockfd); 216 | return 1; 217 | } 218 | 219 | /* 220 | *功能:设置I/O为非阻塞模式 221 | *fd:文件描述符 222 | */ 223 | void activate_nonblock(int fd) 224 | { 225 | int ret; 226 | int flags = fcntl(fd, F_GETFL); 227 | if(flags == -1) 228 | ERR_EXIT("fcntl"); 229 | 230 | flags |= O_NONBLOCK;// 231 | ret = fcntl(fd, F_SETFL, flags); 232 | if(ret == -1) 233 | ERR_EXIT("fcntl"); 234 | } 235 | 236 | /* 237 | *功能:设置I/O为阻塞模式 238 | *fd:文件描述符 239 | */ 240 | void deactivate_nonblock(int fd) 241 | { 242 | int ret; 243 | int flags = fcntl(fd, F_GETFL); 244 | if(flags == -1) 245 | ERR_EXIT("fcntl"); 246 | 247 | flags &= ~O_NONBLOCK; 248 | ret = fcntl(fd, F_SETFL, flags); 249 | if(ret == -1) 250 | ERR_EXIT("fcntl"); 251 | } 252 | 253 | /* 254 | *功能:读操作检测函数,不包含读操作 255 | *fd:文件描述符 256 | *wait_seconds:等待超时时间(s),如果为0表示不检测超时 257 | *返回值:成功返回0,失败返回-1,超时返回-1,且errno = ETIMEDOUT 258 | */ 259 | int read_timeout(int fd, unsigned int wait_seconds) 260 | { 261 | int ret; 262 | if(wait_seconds > 0) 263 | { 264 | fd_set read_fd; 265 | FD_ZERO(&read_fd); 266 | FD_SET(fd, &read_fd); 267 | 268 | struct timeval timeout; 269 | timeout.tv_sec = wait_seconds; 270 | timeout.tv_usec = 0; 271 | 272 | do 273 | { 274 | ret = select(fd + 1, &read_fd, NULL, NULL, &timeout); 275 | }while(ret < 0 && errno == EINTR); 276 | 277 | if(ret == 0) 278 | { 279 | ret = -1; 280 | errno = ETIMEDOUT;//ETIMEDOUT--connection timed out(POSIX.1) 281 | } 282 | else if(ret == 1) 283 | ret = 0; 284 | } 285 | return ret; 286 | } 287 | 288 | /* 289 | *功能:写超时检测函数,不包含写操作 290 | *fd:文件描述符 291 | *wait_seconds:等待超时时间(秒),如果为0表示不检测超时 292 | *返回值:成功返回0,失败返回-1,超时返回-1,且errno = ETIMEDOUT 293 | */ 294 | int write_timeout(int fd, unsigned int wait_seconds) 295 | { 296 | int ret; 297 | if(wait_seconds > 0) 298 | { 299 | fd_set write_fd; 300 | FD_ZERO(&write_fd); 301 | FD_SET(fd, &write_fd); 302 | 303 | struct timeval timeout; 304 | timeout.tv_sec = wait_seconds; 305 | timeout.tv_usec = 0; 306 | 307 | do 308 | { 309 | ret = select(fd + 1, NULL, &write_fd, NULL, &timeout); 310 | }while(ret < 0 && errno == EINTR); 311 | 312 | if(ret == 0) 313 | { 314 | ret = -1; 315 | errno = ETIMEDOUT;//ETIMEDOUT--connection timed out(POSIX.1) 316 | } 317 | else if(ret == 1) 318 | ret = 0; 319 | } 320 | return ret; 321 | } 322 | 323 | /* 324 | *功能:有超时功能的accept 325 | *fd:套接字 326 | *addr:输出参数,返回对方地址 327 | *wait_seconds:等待超时时间,0表示正常不带有超时功能 328 | *返回值:成功(未超时)返回已连接套接字,超时返回-1,并且errno = ETIMEDOUT 329 | */ 330 | int accept_timeout(int fd, struct sockaddr_in *addr, unsigned int wait_seconds) 331 | { 332 | int ret; 333 | socklen_t addrlen = sizeof(struct sockaddr_in); 334 | if(wait_seconds > 0) 335 | { 336 | fd_set accept_fd; 337 | FD_ZERO(&accept_fd); 338 | FD_SET(fd, &accept_fd); 339 | 340 | struct timeval timeout; 341 | timeout.tv_sec = wait_seconds; 342 | timeout.tv_usec = 0; 343 | 344 | do 345 | { 346 | ret = select(fd + 1, &accept_fd, NULL, NULL, &timeout); 347 | }while(ret < 0 && errno == EINTR); 348 | if(ret == -1) 349 | return -1; 350 | else if(ret == 0) 351 | { 352 | errno = ETIMEDOUT; 353 | return -1; 354 | } 355 | } 356 | 357 | if(addr != NULL) 358 | ret = accept(fd, (struct sockaddr*)addr, &addrlen); 359 | else 360 | ret = accept(fd, NULL, NULL); 361 | 362 | return ret; 363 | } 364 | 365 | /* 366 | *功能:有超时功能的connect 367 | *fd: 套接字 368 | *addr: 要连接的对方地址 369 | *wait_seconds: 等待的超时时间(秒), 0表示为正常没有超时设置 370 | *返回值:成功(未超时)返回0, 失败返回-1, 超时返回-1并且errno = ETIMEDOUT 371 | */ 372 | //??????????????? 373 | int connect_timeout(int fd, struct sockaddr_in *addr, unsigned int wait_seconds) 374 | { 375 | int ret; 376 | socklen_t addrlen = sizeof(struct sockaddr_in); 377 | 378 | if(wait_seconds > 0) 379 | activate_nonblock(fd); 380 | 381 | ret = connect(fd, (struct sockaddr*)addr, addrlen); 382 | printf("ret %d\n", ret); 383 | if(ret < 0 && errno == EINPROGRESS) 384 | { 385 | fd_set connect_fd; 386 | FD_ZERO(&connect_fd); 387 | FD_SET(fd, &connect_fd); 388 | 389 | struct timeval timeout; 390 | timeout.tv_sec = wait_seconds; 391 | timeout.tv_usec = 0; 392 | 393 | do 394 | { 395 | printf("a\n"); 396 | /*一旦连接建立,套接字就可写*/ 397 | ret = select(fd + 1, NULL, &connect_fd, NULL, &timeout); 398 | printf("ret - %d\n", ret); 399 | }while(ret < 0 && errno == EINTR); 400 | if(ret == 0) 401 | { 402 | ret = -1; 403 | errno = ETIMEDOUT; 404 | } 405 | else if(ret < 0) 406 | return -1; 407 | else if(ret == 1) 408 | { 409 | /* 410 | *ret返回为1 411 | *可能有两种情况:一种是连接建立成功,一种是套接字产生错误 412 | *此时错误信息不会保存至errno变量中,因此,需要调用getsockopt来获取 413 | */ 414 | int err; 415 | socklen_t socklen = sizeof(err); 416 | int sockoptret = getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &socklen); 417 | if(sockoptret == -1) 418 | { 419 | return -1; 420 | } 421 | //else if(err == 0) 422 | if(err == 0)//这里 423 | { 424 | ret = 0; 425 | } 426 | else 427 | { 428 | errno = err; 429 | ret = -1; 430 | } 431 | } 432 | } 433 | if(wait_seconds > 0) 434 | { 435 | deactivate_nonblock(fd); 436 | } 437 | return ret; 438 | } 439 | 440 | /* 441 | *功能:读取固定的字节数 442 | *fd:文件描述符 443 | *buf:接受数据的缓冲区 444 | *n:要读取的字节数 445 | *返回值:成功返回n,失败返回-1,读到EOF返回小于n的值 446 | */ 447 | ssize_t readn(int fd, void *buf, size_t n) 448 | { 449 | size_t nleft = n; 450 | ssize_t nread; 451 | char *bufp = (char*)buf; 452 | 453 | while(nleft > 0) 454 | { 455 | if((nread = read(fd, bufp, nleft)) < 0)//这里 456 | { 457 | if(errno == EINTR) 458 | continue; 459 | return -1; 460 | } 461 | else if(nread == 0) 462 | return n - nleft; 463 | 464 | bufp += nread; 465 | nleft -= nread; 466 | } 467 | return n; 468 | } 469 | 470 | /* 471 | *功能:发送固定的字节数 472 | *fd:文件描述符 473 | *buf:发送数据的缓冲区 474 | *n:要发送的字节数 475 | *返回值:成功返回n,失败返回-1 476 | */ 477 | ssize_t writen(int fd, const void *buf, size_t n) 478 | { 479 | size_t nleft = n; 480 | ssize_t nwrite; 481 | char *bufp = (char*)buf; 482 | 483 | while(nleft > 0) 484 | { 485 | if((nwrite = write(fd, bufp, nleft)) < 0)//这里 486 | { 487 | if (errno == EINTR) 488 | continue; 489 | return -1; 490 | } 491 | else if (nwrite == 0) 492 | continue; 493 | 494 | bufp += nwrite; 495 | nleft -= nwrite; 496 | } 497 | return n; 498 | } 499 | 500 | /* 501 | *功能:查看套接字缓冲区数据,但不移除数据 502 | *sockfd:套接字 503 | *buf:接受数据缓冲区 504 | *len: 接受字节数长度 505 | *返回值:成功一个返回大于零(即从recv获得的字节数)的值,失败返回-1 506 | */ 507 | static ssize_t recv_peek(int sockfd, void *buf, size_t len) 508 | { 509 | int nread; 510 | while (1) 511 | { 512 | //这个过程只成功调用一次 513 | nread = recv(sockfd, buf, len, MSG_PEEK); 514 | if (nread < 0 && errno == EINTR)//这里 515 | { 516 | //被中断则继续读取 517 | continue; 518 | } 519 | if(nread < 0) 520 | { 521 | return -1; 522 | } 523 | break; 524 | } 525 | return nread; 526 | } 527 | 528 | /* 529 | *功能:按行读取数据 530 | *sockfd:套接字 531 | *buf:接受数据的缓冲区 532 | *maxsize: 一行的最大长度 533 | *返回值:成功返回大于等于零的值, 失败返回-1 534 | */ 535 | ssize_t readline(int sockfd, void *buf, size_t maxsize) 536 | { 537 | int nread; //一次IO读取的数量 538 | int nleft; //还剩余的字节数 539 | char *ptr; //存放数据的指针的位置 540 | int ret; //readn的返回值 541 | int total = 0; //目前总共读取的字节数 542 | 543 | nleft = maxsize-1; 544 | ptr = buf; 545 | 546 | while (nleft > 0) { 547 | //这一次调用仅仅是预览数据 548 | //并没有真的把数据从缓冲区中取走 549 | ret = recv_peek(sockfd, ptr, nleft); 550 | //注意这里读取的字节不够,绝对不是错> 误!!! 551 | if(ret <= 0) 552 | { 553 | return ret; 554 | } 555 | 556 | nread = ret; 557 | int i; 558 | for(i = 0; i < nread; ++i) 559 | { 560 | if (ptr[i] == '\n') 561 | { 562 | //这里才是真正的读取过程 563 | ret = readn(sockfd, ptr, i + 1); 564 | if (ret != i + 1) 565 | { 566 | return -1; 567 | } 568 | total += ret; 569 | ptr += ret; 570 | *ptr = 0; 571 | return total; //返回此行的> 长度 '\n'包含在其中 572 | } 573 | } 574 | //如果没有发现\n,这些数据应全部接收 575 | ret = readn(sockfd, ptr, nread); 576 | if(ret != nread) 577 | { 578 | return -1; 579 | } 580 | nleft -= nread; 581 | total += nread; 582 | ptr += nread; 583 | } 584 | *ptr = 0; 585 | return maxsize - 1; 586 | } 587 | 588 | /* 589 | *功能: 590 | *sockfd: 591 | *fd: 592 | */ 593 | void send_fd(int sockfd, int fd) 594 | { 595 | int ret; 596 | struct msghdr msg; 597 | struct cmsghdr *p_cmsg; 598 | struct iovec vec; 599 | char cmsgbuf[CMSG_SPACE(sizeof(fd))];//这里用到宏 600 | int *p_fds; 601 | char sendchar = 0; 602 | msg.msg_control = cmsgbuf;//这里用到cmsgbuf 603 | msg.msg_controllen = sizeof(cmsgbuf); 604 | p_cmsg = CMSG_FIRSTHDR(&msg); 605 | p_cmsg->cmsg_level = SOL_SOCKET; 606 | p_cmsg->cmsg_type = SCM_RIGHTS; 607 | p_cmsg->cmsg_len = CMSG_LEN(sizeof(fd)); 608 | p_fds = (int*)CMSG_DATA(p_cmsg); 609 | *p_fds = fd; 610 | 611 | msg.msg_name = NULL; 612 | msg.msg_namelen = 0; 613 | msg.msg_iov = &vec;//这里用到vec 614 | msg.msg_iovlen = 1; 615 | msg.msg_flags = 0; 616 | 617 | vec.iov_base = &sendchar; 618 | vec.iov_len = sizeof(sendchar); 619 | ret = sendmsg(sockfd, &msg, 0);//这里用到msg 620 | if(ret != 1) 621 | ERR_EXIT("sendmsg"); 622 | } 623 | 624 | /* 625 | *功能: 626 | *sockfd: 627 | *返回值: 628 | */ 629 | int recv_fd(const int sockfd) 630 | { 631 | int ret; 632 | struct msghdr msg; 633 | char recvchar; 634 | struct iovec vec; 635 | int recvfd; 636 | char cmsgbuf[CMSG_SPACE(sizeof(recvfd))]; 637 | struct cmsghdr *p_cmsg; 638 | int *p_fd; 639 | vec.iov_base = &recvchar; 640 | vec.iov_len = sizeof(recvchar); 641 | msg.msg_name = NULL; 642 | msg.msg_namelen = 0; 643 | msg.msg_iov = &vec; 644 | msg.msg_iovlen = 1; 645 | msg.msg_control = cmsgbuf; 646 | msg.msg_controllen = sizeof(cmsgbuf); 647 | msg.msg_flags = 0; 648 | 649 | p_fd = (int*)CMSG_DATA(CMSG_FIRSTHDR(&msg)); 650 | *p_fd = -1; 651 | ret = recvmsg(sockfd, &msg, 0); 652 | if(ret != 1) 653 | ERR_EXIT("recvmsg"); 654 | 655 | p_cmsg = CMSG_FIRSTHDR(&msg); 656 | if(p_cmsg == NULL) 657 | ERR_EXIT("no passed fd"); 658 | 659 | p_fd = (int *)CMSG_DATA(p_cmsg); 660 | recvfd = *p_fd; 661 | if(recvfd == -1) 662 | ERR_EXIT("no [assed fd"); 663 | 664 | return recvfd; 665 | } 666 | 667 | -------------------------------------------------------------------------------- /sysutil.h: -------------------------------------------------------------------------------- 1 | #ifndef _SYS_UTIL_H_ 2 | #define _SYS_UTIL_H_ 3 | 4 | #include "common.h" 5 | 6 | void activate_oobinline(int sockfd); 7 | void activate_signal_sigurg(int sockfd); 8 | 9 | int get_curr_time_sec(); 10 | int get_curr_time_usec(); 11 | int nano_sleep(double t); 12 | 13 | int lock_file_read(int fd); 14 | int lock_file_write(int fd); 15 | int unlock_file(int fd); 16 | 17 | //创建客户fd 18 | int tcp_client(unsigned int port); 19 | //创建监听fd 20 | int tcp_server(const char *host, unsigned short port); 21 | 22 | int get_local_ip(char *ip); 23 | 24 | //设置fd为阻塞或者非阻塞 25 | void activate_nonblock(int fd); 26 | void deactivate_nonblock(int fd); 27 | 28 | //超时IO函数 29 | int read_timeout(int fd, unsigned int wait_seconds); 30 | int write_timeout(int fd, unsigned int wait_seconds); 31 | int accept_timeout(int fd, struct sockaddr_in *addr, unsigned int wait_seconds); 32 | int connect_timeout(int fd, struct sockaddr_in *addr, unsigned int wait_seconds); 33 | 34 | //处理TCP粘包问题 35 | ssize_t readn(int fd, void *buf, size_t count); 36 | ssize_t writen(int fd, const void *buf, size_t n); 37 | ssize_t readline(int sockfd, void *buf, size_t maxline); 38 | 39 | //发送与接收文件描述符 40 | void send_fd(int sock_fd, int fd); 41 | int recv_fd(const int sock_fd); 42 | 43 | #endif /*_SYS_UTIL_H_*/ 44 | -------------------------------------------------------------------------------- /test_conf.c: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include "parse_conf.h" 3 | #include "configure.h" 4 | 5 | int main(int argc, const char *argv[]) 6 | { 7 | parseconf_load_file("ftpserver.conf"); 8 | 9 | printf("tunable_pasv_enable=%d\n", tunable_pasv_enable); 10 | printf("tunable_port_enable=%d\n", tunable_port_enable); 11 | 12 | printf("tunable_listen_port=%u\n", tunable_listen_port); 13 | printf("tunable_max_clients=%u\n", tunable_max_clients); 14 | printf("tunable_max_per_ip=%u\n", tunable_max_per_ip); 15 | printf("tunable_accept_timeout=%u\n", tunable_accept_timeout); 16 | printf("tunable_connect_timeout=%u\n", tunable_connect_timeout); 17 | printf("tunable_idle_session_timeout=%u\n", tunable_idle_session_timeout); 18 | printf("tunable_data_connection_timeout=%u\n", tunable_data_connection_timeout); 19 | printf("tunable_local_umask=0%o\n", tunable_local_umask); 20 | printf("tunable_upload_max_rate=%u\n", tunable_upload_max_rate); 21 | printf("tunable_download_max_rate=%u\n", tunable_download_max_rate); 22 | 23 | if (tunable_listen_address == NULL) 24 | printf("tunable_listen_address=NULL\n"); 25 | else 26 | printf("tunable_listen_address=%s\n", tunable_listen_address); 27 | return 0; 28 | } 29 | 30 | -------------------------------------------------------------------------------- /test_hash.c: -------------------------------------------------------------------------------- 1 | #include "hash.h" 2 | #include 3 | #include 4 | #include 5 | 6 | typedef struct Student 7 | { 8 | char sno[5]; 9 | char name[32]; 10 | int age; 11 | }Stu_t; 12 | 13 | unsigned int hash_str(unsigned int buckets, void *key) 14 | { 15 | char *sno = (char *)key; 16 | unsigned int index = 0; 17 | 18 | while(*sno) 19 | { 20 | index = *sno + 4 * index; 21 | ++sno; 22 | } 23 | 24 | return index % buckets; 25 | } 26 | 27 | int main(int argc, char* argv[]) 28 | { 29 | Stu_t stu[] = 30 | { 31 | { "1011", "aaaa", 23}, 32 | { "1012", "bbbb", 25}, 33 | { "1013", "cccc", 22} 34 | }; 35 | 36 | Hash_t *hash = hash_alloc(20, hash_str); 37 | 38 | //添加元素 39 | int size = sizeof(stu) / sizeof(stu[0]); 40 | int i; 41 | for(i = 0; i < size; ++i) 42 | { 43 | hash_add_entry(hash, stu[i].sno, strlen(stu[i].sno), &stu[i], sizeof(stu[i])); 44 | } 45 | printf("------\n"); 46 | for(i = 0; i < size; ++i) 47 | { 48 | printf("%s %s %d\n", stu[i].sno, stu[i].name, stu[i].age); 49 | } 50 | printf("\nsearch: ----\n"); 51 | 52 | //查找元素 53 | Stu_t *s = (Stu_t *)hash_lookup_value_by_key(hash, "1012", strlen("1012")); 54 | if(s) 55 | { 56 | printf("%s %s %d\n", s->sno, s->name, s->age); 57 | } 58 | else 59 | { 60 | printf("not found\n"); 61 | } 62 | 63 | //删除结点 64 | printf("\nsearch after free:\n"); 65 | hash_free_entry(hash, "1012", strlen("1012")); 66 | s = (Stu_t *)hash_lookup_value_by_key(hash, "1012", strlen("1012")); 67 | if(s) 68 | { 69 | printf("%s %s %d\n", s->sno, s->name, s->age); 70 | } 71 | else 72 | { 73 | printf("not found\n"); 74 | } 75 | 76 | //清空结点 77 | hash_clear_entry(hash); 78 | 79 | //销毁hash 80 | hash_destroy(hash); 81 | 82 | return 0; 83 | } 84 | 85 | -------------------------------------------------------------------------------- /test_ls.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #define ERR_EXIT(m) \ 12 | do { \ 13 | perror(m);\ 14 | exit(EXIT_FAILURE);\ 15 | }while(0) 16 | 17 | const char *statbuf_get_perms(struct stat *sbuf); 18 | const char *statbuf_get_date(struct stat *sbuf); 19 | const char *statbuf_get_filename(struct stat *sbuf, const char *name); 20 | const char *statbuf_get_user_info(struct stat *sbuf); 21 | const char *statbuf_get_size(struct stat *sbuf); 22 | 23 | int main(int argc, const char *argv[]) 24 | { 25 | DIR *dir = opendir("."); 26 | if(dir == NULL) 27 | ERR_EXIT("opendir"); 28 | 29 | struct dirent *dr; 30 | while((dr = readdir(dir))) 31 | { 32 | const char *filename = dr->d_name; 33 | if(filename[0] == '.') 34 | continue; 35 | 36 | char buf[1024] = {0}; 37 | struct stat sbuf; 38 | if(lstat(filename, &sbuf) == -1) 39 | ERR_EXIT("lstat"); 40 | 41 | strcpy(buf, statbuf_get_perms(&sbuf)); 42 | strcat(buf, " "); 43 | strcat(buf, statbuf_get_user_info(&sbuf)); 44 | strcat(buf, " "); 45 | strcat(buf, statbuf_get_size(&sbuf)); 46 | strcat(buf, " "); 47 | strcat(buf, statbuf_get_date(&sbuf)); 48 | strcat(buf, " "); 49 | strcat(buf, statbuf_get_filename(&sbuf, filename)); 50 | 51 | printf("%s\n", buf); 52 | } 53 | 54 | closedir(dir); 55 | return 0; 56 | } 57 | 58 | const char *statbuf_get_perms(struct stat *sbuf) 59 | { 60 | static char perms[] = "----------"; 61 | mode_t mode = sbuf->st_mode; 62 | 63 | //文件类型 64 | switch(mode & S_IFMT) 65 | { 66 | case S_IFSOCK: 67 | perms[0] = 's'; 68 | break; 69 | case S_IFLNK: 70 | perms[0] = 'l'; 71 | break; 72 | case S_IFREG: 73 | perms[0] = '-'; 74 | break; 75 | case S_IFBLK: 76 | perms[0] = 'b'; 77 | break; 78 | case S_IFDIR: 79 | perms[0] = 'd'; 80 | break; 81 | case S_IFCHR: 82 | perms[0] = 'c'; 83 | break; 84 | case S_IFIFO: 85 | perms[0] = 'p'; 86 | break; 87 | } 88 | 89 | //权限 90 | if(mode & S_IRUSR) 91 | perms[1] = 'r'; 92 | if(mode & S_IWUSR) 93 | perms[2] = 'w'; 94 | if(mode & S_IXUSR) 95 | perms[3] = 'x'; 96 | if(mode & S_IRGRP) 97 | perms[4] = 'r'; 98 | if(mode & S_IWGRP) 99 | perms[5] = 'w'; 100 | if(mode & S_IXGRP) 101 | perms[6] = 'x'; 102 | if(mode & S_IROTH) 103 | perms[7] = 'r'; 104 | if(mode & S_IWOTH) 105 | perms[8] = 'w'; 106 | if(mode & S_IXOTH) 107 | perms[9] = 'x'; 108 | 109 | if(mode & S_ISUID) 110 | perms[3] = (perms[3] == 'x') ? 's' : 'S'; 111 | if(mode & S_ISGID) 112 | perms[6] = (perms[6] == 'x') ? 's' : 'S'; 113 | if(mode & S_ISVTX) 114 | perms[9] = (perms[9] == 'x') ? 't' : 'T'; 115 | 116 | return perms; 117 | } 118 | 119 | const char *statbuf_get_date(struct stat *sbuf) 120 | { 121 | static char datebuf[1024] = {0}; 122 | struct tm *ptm; 123 | time_t ct = sbuf->st_ctime; 124 | if((ptm = localtime(&ct)) == NULL) 125 | ERR_EXIT("localtime"); 126 | 127 | const char *format = "%b %e %H:%M"; //时间格式 128 | 129 | if(strftime(datebuf, sizeof datebuf, format, ptm) == 0) 130 | { 131 | fprintf(stderr, "strftime error\n"); 132 | exit(EXIT_FAILURE); 133 | } 134 | 135 | return datebuf; 136 | } 137 | 138 | const char *statbuf_get_filename(struct stat *sbuf, const char *name) 139 | { 140 | static char filename[1024] = {0}; 141 | //name 处理链接名字 142 | if(S_ISLNK(sbuf->st_mode)) 143 | { 144 | char linkfile[1024] = {0}; 145 | if(readlink(name, linkfile, sizeof linkfile) == -1) 146 | ERR_EXIT("readlink"); 147 | snprintf(filename, sizeof filename, " %s -> %s", name, linkfile); 148 | }else 149 | { 150 | strcpy(filename, name); 151 | } 152 | 153 | return filename; 154 | } 155 | 156 | const char *statbuf_get_user_info(struct stat *sbuf) 157 | { 158 | static char info[1024] = {0}; 159 | snprintf(info, sizeof info, " %3d %8d %8d", sbuf->st_nlink, sbuf->st_uid, sbuf->st_gid); 160 | 161 | return info; 162 | } 163 | 164 | const char *statbuf_get_size(struct stat *sbuf) 165 | { 166 | static char buf[100] = {0}; 167 | snprintf(buf, sizeof buf, "%8lu", (unsigned long)sbuf->st_size); 168 | return buf; 169 | } 170 | 171 | -------------------------------------------------------------------------------- /trans_ctrl.c: -------------------------------------------------------------------------------- 1 | #include "trans_ctrl.h" 2 | #include "common.h" 3 | #include "sysutil.h" 4 | #include "configure.h" 5 | #include "command_map.h" 6 | #include "ftp_codes.h" 7 | #include "strutil.h" 8 | Session_t *p_sess = NULL; 9 | 10 | static void handle_signal_alarm_ctrl_fd(int sig); 11 | static void handle_signal_alarm_data_fd(int sig); 12 | static void handle_signal_sigurg(int sig); 13 | 14 | //限速功能 15 | void limit_curr_rate(Session_t *sess, int nbytes, int is_upload) 16 | { 17 | //获取当前时间 18 | int curr_time_sec = get_curr_time_sec(); 19 | int curr_time_usec = get_curr_time_usec(); 20 | 21 | //求时间差 22 | double elapsed = 0.0; 23 | elapsed += (curr_time_sec - sess->start_time_sec); 24 | elapsed += (curr_time_usec - sess->start_time_usec) / (double)1000000; 25 | if(elapsed < 0.000001) //double和0不能用== 26 | elapsed = 0.001; 27 | 28 | //求速度 29 | double curr_rate = nbytes / elapsed; 30 | 31 | //求比率 32 | double rate_radio = 0.0; 33 | if(is_upload) 34 | { 35 | //如果用户配置了限速,并且当前速度已经超过了限定速度 36 | if(sess->limits_max_upload > 0 && curr_rate > sess->limits_max_upload) 37 | { 38 | rate_radio = curr_rate / (sess->limits_max_upload); 39 | } 40 | else 41 | { 42 | //如果不限速,必须更新时间 43 | sess->start_time_sec = get_curr_time_sec(); 44 | sess->start_time_usec = get_curr_time_usec(); 45 | return; 46 | } 47 | }else 48 | { 49 | if(sess->limits_max_download > 0 && curr_rate > sess->limits_max_download) 50 | { 51 | rate_radio = curr_rate / (sess->limits_max_download); 52 | } 53 | else 54 | { 55 | //如果不限速,必须更新时间 56 | sess->start_time_sec = get_curr_time_sec(); 57 | sess->start_time_usec = get_curr_time_usec(); 58 | return; 59 | } 60 | } 61 | 62 | //求出限速时间 63 | double sleep_time = (rate_radio - 1) * elapsed; 64 | 65 | //限速睡眠 66 | if(nano_sleep(sleep_time) == -1) 67 | ERR_EXIT("nano_sleep"); 68 | 69 | //注意更新当前时间 70 | sess->start_time_sec = get_curr_time_sec(); 71 | sess->start_time_usec = get_curr_time_usec(); 72 | } 73 | 74 | //开启控制连接的定时器 75 | void setup_signal_alarm_ctrl_fd() 76 | { 77 | if(signal(SIGALRM, handle_signal_alarm_ctrl_fd) == SIG_ERR) 78 | ERR_EXIT("signal"); 79 | } 80 | 81 | //开始计时 82 | void start_signal_alarm_ctrl_fd() 83 | { 84 | alarm(tunable_idle_session_timeout); 85 | } 86 | 87 | //信号处理程序用于控制连接 88 | static void handle_signal_alarm_ctrl_fd(int sig) 89 | { 90 | if(tunable_idle_session_timeout > 0) //用户配置了此选项 91 | { 92 | //直接关闭控制连接,然后退出 93 | shutdown(p_sess->peer_fd, SHUT_RD); 94 | //421 95 | ftp_reply(p_sess, FTP_IDLE_TIMEOUT, "Timeout."); 96 | shutdown(p_sess->peer_fd, SHUT_WR); 97 | exit(EXIT_SUCCESS); 98 | } 99 | } 100 | 101 | //安装数据连接的定时器 102 | void setup_signal_alarm_data_fd() 103 | { 104 | if(signal(SIGALRM, handle_signal_alarm_data_fd) == SIG_ERR) 105 | ERR_EXIT("signal"); 106 | } 107 | 108 | //数据连接开始计时 109 | void start_signal_alarm_data_fd() 110 | { 111 | alarm(tunable_data_connection_timeout); 112 | } 113 | 114 | //信号处理,用户数据连接 115 | static void handle_signal_alarm_data_fd(int sig) 116 | { 117 | if(tunable_data_connection_timeout > 0) 118 | { 119 | if(p_sess->is_translating_data == 1) 120 | { 121 | //有数据传输则重新启动定时器 122 | start_signal_alarm_data_fd(); 123 | } 124 | else 125 | { 126 | //没有数据则给421,并且退出 127 | close(p_sess->data_fd); 128 | shutdown(p_sess->peer_fd, SHUT_RD); 129 | ftp_reply(p_sess, FTP_DATA_TIMEOUT, "Timeout."); 130 | shutdown(p_sess->peer_fd, SHUT_WR); 131 | exit(EXIT_SUCCESS); 132 | } 133 | } 134 | } 135 | 136 | //取消定时器 137 | void cancel_signal_alarm() 138 | { 139 | alarm(0); 140 | } 141 | 142 | //处理SIGURG信号,实质是处理带外数据 143 | static void handle_signal_sigurg(int sig) 144 | { 145 | char cmdline[1024] = {0}; 146 | int ret = readline(p_sess->peer_fd, cmdline, sizeof cmdline); 147 | if(ret <= 0) //存在带外数据,不可能为0 148 | ERR_EXIT("readline"); 149 | 150 | str_trim_crlf(cmdline); 151 | str_upper(cmdline); 152 | 153 | if(strcmp("ABOR", cmdline) == 0 || strcmp("\377\364\377\362ABOR", cmdline) == 0) 154 | { 155 | //处理abor指令 156 | p_sess->is_receive_abor = 1; 157 | close(p_sess->data_fd); 158 | p_sess->data_fd = -1; 159 | } 160 | else 161 | { 162 | //未识别的命令 163 | ftp_reply(p_sess, FTP_BADCMD, "Unknown command."); 164 | } 165 | } 166 | 167 | //安装sigurg信号 168 | void setup_signal_sigurg() 169 | { 170 | if(signal(SIGURG, handle_signal_sigurg) == SIG_ERR) 171 | ERR_EXIT("signal"); 172 | } 173 | 174 | void do_site_chmod(Session_t *sess, char *args) 175 | { 176 | if (strlen(args) == 0) 177 | { 178 | ftp_reply(sess, FTP_BADCMD, "SITE CHMOD needs 2 arguments."); 179 | return; 180 | } 181 | 182 | char perm[100] = {0}; 183 | char file[100] = {0}; 184 | str_split(args , perm, file, ' '); 185 | if (strlen(file) == 0) 186 | { 187 | ftp_reply(sess, FTP_BADCMD, "SITE CHMOD needs 2 arguments."); 188 | return; 189 | } 190 | 191 | unsigned int mode = str_octal_to_uint(perm); 192 | if (chmod(file, mode) < 0) 193 | { 194 | ftp_reply(sess, FTP_CHMODOK, "SITE CHMOD command failed."); 195 | } 196 | else 197 | { 198 | ftp_reply(sess, FTP_CHMODOK, "SITE CHMOD command ok."); 199 | } 200 | } 201 | 202 | void do_site_umask(Session_t *sess, char *args) 203 | { 204 | // SITE UMASK [umask] 205 | if (strlen(args) == 0) 206 | { 207 | char text[1024] = {0}; 208 | sprintf(text, "Your current UMASK is 0%o", tunable_local_umask); 209 | ftp_reply(sess, FTP_UMASKOK, text); 210 | } 211 | else 212 | { 213 | unsigned int um = str_octal_to_uint(args); 214 | umask(um); 215 | char text[1024] = {0}; 216 | sprintf(text, "UMASK set to 0%o", um); 217 | ftp_reply(sess, FTP_UMASKOK, text); 218 | } 219 | } 220 | 221 | void do_site_help(Session_t *sess) 222 | { 223 | //214 CHMOD UMASK HELP 224 | ftp_reply(sess, FTP_HELP, "CHMOD UMASK HELP"); 225 | } 226 | 227 | -------------------------------------------------------------------------------- /trans_ctrl.h: -------------------------------------------------------------------------------- 1 | #ifndef _TRANS_CTRL_H_ 2 | #define _TRANS_CTRL_H_ 3 | 4 | /* 5 | *限速模块 6 | */ 7 | #include "session.h" 8 | 9 | void limit_curr_rate(Session_t *sess, int nbytes, int is_upload); 10 | 11 | //控制连接 12 | void setup_signal_alarm_ctrl_fd(); 13 | void start_signal_alarm_ctrl_fd(); 14 | 15 | //数据连接 16 | void setup_signal_alarm_data_fd(); 17 | void start_signal_alarm_data_fd(); 18 | 19 | void cancel_signal_alarm(); 20 | 21 | void setup_signal_sigurg(); 22 | 23 | void do_site_chmod(Session_t *sess, char *args); 24 | void do_site_umask(Session_t *sess, char *args); 25 | void do_site_help(Session_t *sess); 26 | 27 | #endif /*_TRANS_CTRL_H_*/ 28 | -------------------------------------------------------------------------------- /trans_data.c: -------------------------------------------------------------------------------- 1 | #include "trans_data.h" 2 | #include "common.h" 3 | #include "sysutil.h" 4 | #include "ftp_codes.h" 5 | #include "command_map.h" 6 | #include "configure.h" 7 | #include "priv_sock.h" 8 | #include "trans_ctrl.h" 9 | 10 | //这5个函数为静态函数, 他们只能在本文件中使用 11 | static const char *statbuf_get_perms(struct stat *sbuf); 12 | static const char *statbuf_get_date(struct stat *sbuf); 13 | static const char *statbuf_get_filename(struct stat *sbuf, const char *name); 14 | static const char *statbuf_get_user_info(struct stat *sbuf); 15 | static const char *statbuf_get_size(struct stat *sbuf); 16 | 17 | //判断主动模式是否开启 18 | static int is_port_active(Session_t *sess); 19 | //判断被动模式是否开启 20 | static int is_pasv_active(Session_t *sess); 21 | 22 | static void get_port_data_fd(Session_t *sess); 23 | static void get_pasv_data_fd(Session_t *sess); 24 | 25 | static void trans_list_common(Session_t *sess, int list); 26 | static int get_trans_data_fd(Session_t *sess); 27 | 28 | void download_file(Session_t *sess) 29 | { 30 | //进入数据传输阶段 31 | sess->is_translating_data = 1; 32 | 33 | //获取data_fd 34 | if(get_trans_data_fd(sess) == 0) 35 | { 36 | ftp_reply(sess, FTP_FILEFAIL, "Failed to open file."); 37 | return; 38 | } 39 | 40 | //open 文件 41 | int fd = open(sess->args, O_RDONLY); 42 | if(fd == -1) 43 | { 44 | ftp_reply(sess, FTP_FILEFAIL, "Failed to open file."); 45 | return; 46 | } 47 | 48 | //对文件加锁 49 | if(lock_file_read(fd) == -1) 50 | { 51 | ftp_reply(sess, FTP_FILEFAIL, "Failed to open file."); 52 | return; 53 | } 54 | 55 | //判断是否是普通文件 56 | struct stat sbuf; 57 | if(fstat(fd, &sbuf) == -1) 58 | ERR_EXIT("fstat"); 59 | if(!S_ISREG(sbuf.st_mode)) 60 | { 61 | ftp_reply(sess, FTP_FILEFAIL, "Can only download regular file."); 62 | return; 63 | } 64 | 65 | //判断断点续传 66 | unsigned long filesize = sbuf.st_size;//剩余的文件字节 67 | int offset = sess->restart_pos; 68 | if(offset != 0) 69 | { 70 | filesize -= offset; 71 | } 72 | 73 | if(lseek(fd, offset, SEEK_SET) == -1) 74 | ERR_EXIT("lseek"); 75 | 76 | //150 ascii 77 | //150 Opening ASCII mode data connection for /home/wing/redis-stable.tar.gz (1251318 bytes). 78 | char text[1024] = {0}; 79 | if(sess->ascii_mode == 1) 80 | snprintf(text, sizeof text, "Opening ASCII mode data connection for %s (%lu bytes).", sess->args, filesize); 81 | else 82 | snprintf(text, sizeof text, "Opening Binary mode data connection for %s (%lu bytes).", sess->args, filesize); 83 | ftp_reply(sess, FTP_DATACONN, text); 84 | 85 | //记录时间 86 | sess->start_time_sec = get_curr_time_sec(); 87 | sess->start_time_usec = get_curr_time_usec(); 88 | 89 | //传输 90 | int flag = 0; //记录下载的结果 91 | int nleft = filesize; //剩余字节数 92 | int block_size = 0; //一次传输的字节数 93 | const int kSize = 65536; 94 | while(nleft > 0) 95 | { 96 | block_size = (nleft > kSize) ? kSize : nleft;//读取字节数 97 | //sendfile发生在内核,更加高效 98 | int nwrite = sendfile(sess->data_fd, fd, NULL, block_size); 99 | 100 | if(sess->is_receive_abor == 1) 101 | { 102 | flag = 2; //ABOR 103 | //426 104 | ftp_reply(sess, FTP_BADSENDNET, "Interupt downloading file."); 105 | sess->is_receive_abor = 0; 106 | break; 107 | } 108 | 109 | if(nwrite == -1) 110 | { 111 | flag = 1; //错误 112 | break; 113 | } 114 | nleft -= nwrite; 115 | 116 | //实行限速 117 | limit_curr_rate(sess, nwrite, 0); 118 | } 119 | if(nleft == 0) 120 | flag = 0; //正确退出 121 | 122 | //清理 关闭fd 文件解锁 123 | if(unlock_file(fd) == -1) 124 | ERR_EXIT("unlock_file"); 125 | close(fd); 126 | close(sess->data_fd); 127 | sess->data_fd = -1; 128 | 129 | //226 130 | if(flag == 0) 131 | ftp_reply(sess, FTP_TRANSFEROK, "Transfer complete."); 132 | else if(flag == 1) 133 | ftp_reply(sess, FTP_BADSENDFILE, "Sendfile failed."); 134 | else if(flag == 2) 135 | ftp_reply(sess, FTP_ABOROK, "ABOR successful."); 136 | 137 | //先恢复控制连接的信号 138 | setup_signal_alarm_ctrl_fd(); 139 | sess->is_translating_data = 0; 140 | } 141 | 142 | void upload_file(Session_t *sess, int is_appe) 143 | { 144 | //进入数据传输阶段 145 | sess->is_translating_data = 1; 146 | //获取data fd 147 | if(get_trans_data_fd(sess) == 0) 148 | { 149 | ftp_reply(sess, FTP_UPLOADFAIL, "Failed to get data fd."); 150 | return; 151 | } 152 | 153 | //open 文件 154 | int fd = open(sess->args, O_WRONLY | O_CREAT, 0666); 155 | if(fd == -1) 156 | { 157 | ftp_reply(sess, FTP_UPLOADFAIL, "Failed to open file."); 158 | return; 159 | } 160 | 161 | //对文件加锁 162 | if(lock_file_write(fd) == -1) 163 | { 164 | ftp_reply(sess, FTP_UPLOADFAIL, "Failed to lock file."); 165 | return; 166 | } 167 | 168 | //判断是否是普通文件 169 | struct stat sbuf; 170 | if(fstat(fd, &sbuf) == -1) 171 | ERR_EXIT("fstat"); 172 | if(!S_ISREG(sbuf.st_mode)) 173 | { 174 | ftp_reply(sess, FTP_UPLOADFAIL, "Can only upload regular file."); 175 | return; 176 | } 177 | 178 | //区分模式 179 | long long offset = sess->restart_pos; 180 | if(!is_appe && offset == 0) //STOR 181 | { 182 | //创建新的文件 183 | ftruncate(fd, 0); //如果源文件存在则直接覆盖 184 | } 185 | else if(!is_appe && offset != 0) // REST + STOR 186 | { 187 | //lseek进行偏移 188 | ftruncate(fd, offset); //截断后面的内容 189 | if(lseek(fd, offset, SEEK_SET) == -1) 190 | ERR_EXIT("lseek"); 191 | } 192 | else //APPE 193 | { 194 | //对文件进行扩展 偏移到末尾进行追加 195 | if(lseek(fd, 0, SEEK_END) == -1) 196 | ERR_EXIT("lseek"); 197 | } 198 | 199 | //150 ascii 200 | ftp_reply(sess, FTP_DATACONN, "OK to send."); 201 | 202 | //更新当前时间 203 | sess->start_time_sec = get_curr_time_sec(); 204 | sess->start_time_usec = get_curr_time_usec(); 205 | 206 | //上传 207 | char buf[65535] = {0}; 208 | int flag = 0; 209 | while(1) 210 | { 211 | int nread = read(sess->data_fd, buf, sizeof buf); 212 | 213 | if(sess->is_receive_abor == 1) 214 | { 215 | flag = 3; //ABOR 216 | //426 217 | ftp_reply(sess, FTP_BADSENDNET, "Interupt uploading file."); 218 | sess->is_receive_abor = 0; 219 | break; 220 | } 221 | 222 | if(nread == -1) 223 | { 224 | if(errno == EINTR) 225 | continue; 226 | flag = 1; 227 | break; 228 | } 229 | else if(nread == 0) 230 | { 231 | flag = 0; 232 | break; 233 | } 234 | 235 | if(writen(fd, buf, nread) != nread) 236 | { 237 | flag = 2; 238 | break; 239 | } 240 | 241 | //实行限速 242 | limit_curr_rate(sess, nread, 1); 243 | } 244 | 245 | //清理 关闭fd 文件解锁 246 | if(unlock_file(fd) == -1) 247 | ERR_EXIT("unlock_file"); 248 | close(fd); 249 | close(sess->data_fd); 250 | sess->data_fd = -1; 251 | 252 | 253 | //226 254 | if(flag == 0) 255 | ftp_reply(sess, FTP_TRANSFEROK, "Transfer complete."); 256 | else if(flag == 1) 257 | ftp_reply(sess, FTP_BADSENDNET, "Reading from Network Failed."); 258 | else if(flag == 2) 259 | ftp_reply(sess, FTP_BADSENDFILE, "Writing to File Failed."); 260 | else 261 | ftp_reply(sess, FTP_ABOROK, "ABOR successful."); 262 | 263 | //先恢复控制连接的信号 264 | setup_signal_alarm_ctrl_fd(); 265 | sess->is_translating_data = 0; 266 | } 267 | 268 | void trans_list(Session_t *sess, int list) 269 | { 270 | //发起数据连接 271 | if(get_trans_data_fd(sess) == 0) 272 | return ; 273 | 274 | //给出150 Here comes the directory listing. 275 | ftp_reply(sess, FTP_DATACONN, "Here comes the directory listing."); 276 | 277 | //传输目录列表 278 | if(list == 1) 279 | trans_list_common(sess, 1); 280 | else 281 | trans_list_common(sess, 0); 282 | close(sess->data_fd); //传输结束记得关闭 283 | sess->data_fd = -1; 284 | 285 | //给出226 Directory send OK. 286 | ftp_reply(sess, FTP_TRANSFEROK, "Directory send OK."); 287 | } 288 | 289 | //返回值表示成功与否 290 | static int get_trans_data_fd(Session_t *sess) 291 | { 292 | int is_port = is_port_active(sess); 293 | int is_pasv = is_pasv_active(sess); 294 | 295 | 296 | //两者都未开启,应返回425 297 | if(!is_port && !is_pasv) 298 | { 299 | ftp_reply(sess, FTP_BADSENDCONN, "Use PORT or PASV first."); 300 | return 0; 301 | } 302 | 303 | if(is_port && is_pasv) 304 | { 305 | fprintf(stderr, "both of PORT and PASV are active\n"); 306 | exit(EXIT_FAILURE); 307 | } 308 | 309 | //主动模式 310 | if(is_port) 311 | { 312 | get_port_data_fd(sess); 313 | } 314 | 315 | if(is_pasv) 316 | { 317 | get_pasv_data_fd(sess); 318 | } 319 | 320 | //这里获取data fd成功 321 | //安装信号 322 | setup_signal_alarm_data_fd(); 323 | //开始计时 324 | start_signal_alarm_data_fd(); 325 | 326 | return 1; 327 | } 328 | 329 | static const char *statbuf_get_perms(struct stat *sbuf) 330 | { 331 | //这里使用static返回perms 332 | static char perms[] = "----------"; 333 | mode_t mode = sbuf->st_mode; 334 | 335 | //文件类型 336 | switch(mode & S_IFMT) 337 | { 338 | case S_IFSOCK: 339 | perms[0] = 's'; 340 | break; 341 | case S_IFLNK: 342 | perms[0] = 'l'; 343 | break; 344 | case S_IFREG: 345 | perms[0] = '-'; 346 | break; 347 | case S_IFBLK: 348 | perms[0] = 'b'; 349 | break; 350 | case S_IFDIR: 351 | perms[0] = 'd'; 352 | break; 353 | case S_IFCHR: 354 | perms[0] = 'c'; 355 | break; 356 | case S_IFIFO: 357 | perms[0] = 'p'; 358 | break; 359 | } 360 | //权限 361 | if(mode & S_IRUSR) 362 | perms[1] = 'r'; 363 | if(mode & S_IWUSR) 364 | perms[2] = 'w'; 365 | if(mode & S_IXUSR) 366 | perms[3] = 'x'; 367 | if(mode & S_IRGRP) 368 | perms[4] = 'r'; 369 | if(mode & S_IWGRP) 370 | perms[5] = 'w'; 371 | if(mode & S_IXGRP) 372 | perms[6] = 'x'; 373 | if(mode & S_IROTH) 374 | perms[7] = 'r'; 375 | if(mode & S_IWOTH) 376 | perms[8] = 'w'; 377 | if(mode & S_IXOTH) 378 | perms[9] = 'x'; 379 | 380 | if(mode & S_ISUID) 381 | perms[3] = (perms[3] == 'x') ? 's' : 'S'; 382 | if(mode & S_ISGID) 383 | perms[6] = (perms[6] == 'x') ? 's' : 'S'; 384 | if(mode & S_ISVTX) 385 | perms[9] = (perms[9] == 'x') ? 't' : 'T'; 386 | 387 | return perms; 388 | } 389 | 390 | //获取文件最近更改日期 391 | static const char *statbuf_get_date(struct stat *sbuf) 392 | { 393 | static char datebuf[1024] = {0}; 394 | struct tm *ptm; 395 | time_t ct = sbuf->st_ctime; 396 | if((ptm = localtime(&ct)) == NULL) 397 | ERR_EXIT("localtime"); 398 | 399 | const char *format = "%b %e %H:%M"; //时间格式 400 | 401 | if(strftime(datebuf, sizeof datebuf, format, ptm) == 0) 402 | { 403 | fprintf(stderr, "strftime error\n"); 404 | exit(EXIT_FAILURE); 405 | } 406 | 407 | return datebuf; 408 | } 409 | 410 | //获取文件名字 411 | static const char *statbuf_get_filename(struct stat *sbuf, const char *name) 412 | { 413 | static char filename[1024] = {0}; 414 | //name 处理链接名字 415 | if(S_ISLNK(sbuf->st_mode)) 416 | { 417 | char linkfile[1024] = {0}; 418 | if(readlink(name, linkfile, sizeof linkfile) == -1) 419 | ERR_EXIT("readlink"); 420 | snprintf(filename, sizeof filename, "%s -> %s", name, linkfile); 421 | }else 422 | { 423 | strcpy(filename, name); 424 | } 425 | 426 | return filename; 427 | } 428 | 429 | //获取用户信息 430 | static const char *statbuf_get_user_info(struct stat *sbuf) 431 | { 432 | static char info[1024] = {0}; 433 | snprintf(info, sizeof info, " %3d %8d %8d", sbuf->st_nlink, sbuf->st_uid, sbuf->st_gid); 434 | 435 | return info; 436 | } 437 | 438 | //获取文件大小 439 | static const char *statbuf_get_size(struct stat *sbuf) 440 | { 441 | static char buf[100] = {0}; 442 | snprintf(buf, sizeof buf, "%8lu", (unsigned long)sbuf->st_size); 443 | return buf; 444 | } 445 | 446 | static int is_port_active(Session_t *sess) 447 | { 448 | return (sess->p_addr != NULL); 449 | } 450 | 451 | static int is_pasv_active(Session_t *sess) 452 | { 453 | //首先给nobody发命令 454 | priv_sock_send_cmd(sess->proto_fd, PRIV_SOCK_PASV_ACTIVE); 455 | //接收结果 456 | return priv_sock_recv_int(sess->proto_fd); 457 | } 458 | 459 | static void get_port_data_fd(Session_t *sess) 460 | { 461 | //发送cmd 462 | priv_sock_send_cmd(sess->proto_fd, PRIV_SOCK_GET_DATA_SOCK); 463 | //发送ip port 464 | char *ip = inet_ntoa(sess->p_addr->sin_addr); 465 | uint16_t port = ntohs(sess->p_addr->sin_port); 466 | priv_sock_send_str(sess->proto_fd, ip, strlen(ip)); 467 | priv_sock_send_int(sess->proto_fd, port); 468 | //接收应答 469 | char result = priv_sock_recv_result(sess->proto_fd); 470 | if(result == PRIV_SOCK_RESULT_BAD) 471 | { 472 | ftp_reply(sess, FTP_BADCMD, "get pasv data_fd error"); 473 | fprintf(stderr, "get data fd error\n"); 474 | exit(EXIT_FAILURE); 475 | } 476 | //接收fd 477 | sess->data_fd = priv_sock_recv_fd(sess->proto_fd); 478 | 479 | //释放port模式 480 | free(sess->p_addr); 481 | sess->p_addr = NULL; 482 | } 483 | 484 | static void get_pasv_data_fd(Session_t *sess) 485 | { 486 | //先给nobody发命令 487 | priv_sock_send_cmd(sess->proto_fd, PRIV_SOCK_PASV_ACCEPT); 488 | 489 | //接收结果 490 | char res = priv_sock_recv_result(sess->proto_fd); 491 | if(res == PRIV_SOCK_RESULT_BAD) 492 | { 493 | ftp_reply(sess, FTP_BADCMD, "get pasv data_fd error"); 494 | fprintf(stderr, "get data fd error\n"); 495 | exit(EXIT_FAILURE); 496 | } 497 | 498 | //接收fd 499 | sess->data_fd = priv_sock_recv_fd(sess->proto_fd); 500 | } 501 | 502 | 503 | static void trans_list_common(Session_t *sess, int list) 504 | { 505 | DIR *dir = opendir("."); 506 | if(dir == NULL) 507 | ERR_EXIT("opendir"); 508 | 509 | struct dirent *dr; 510 | while((dr = readdir(dir))) 511 | { 512 | const char *filename = dr->d_name; 513 | if(filename[0] == '.') 514 | continue; 515 | 516 | char buf[1024] = {0}; 517 | struct stat sbuf; 518 | if(lstat(filename, &sbuf) == -1) 519 | ERR_EXIT("lstat"); 520 | 521 | if(list == 1) // LIST 522 | { 523 | strcpy(buf, statbuf_get_perms(&sbuf)); 524 | strcat(buf, " "); 525 | strcat(buf, statbuf_get_user_info(&sbuf)); 526 | strcat(buf, " "); 527 | strcat(buf, statbuf_get_size(&sbuf)); 528 | strcat(buf, " "); 529 | strcat(buf, statbuf_get_date(&sbuf)); 530 | strcat(buf, " "); 531 | strcat(buf, statbuf_get_filename(&sbuf, filename)); 532 | } 533 | else //NLST 534 | { 535 | strcpy(buf, statbuf_get_filename(&sbuf, filename)); 536 | } 537 | 538 | strcat(buf, "\r\n"); 539 | writen(sess->data_fd, buf, strlen(buf)); 540 | } 541 | 542 | closedir(dir); 543 | } 544 | 545 | -------------------------------------------------------------------------------- /trans_data.h: -------------------------------------------------------------------------------- 1 | #ifndef _TRANS_DATA_H_ 2 | #define _TRANS_DATA_H_ 3 | 4 | #include "session.h" 5 | 6 | void download_file(Session_t *sess); 7 | void upload_file(Session_t *sess, int is_appe); 8 | void trans_list(Session_t *sess, int list); 9 | 10 | #endif /*_TRANS_DATA_H_*/ 11 | --------------------------------------------------------------------------------