#include "scp.h"
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <sys/file.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <fcntl.h> // 主要头文件(包含 open() 的函数原型和标志常量)
/**************************************************************************************************/
/* GLOBAL_VARIABLES */
/**************************************************************************************************/
scp_context *scp_contextp = NULL;
/**************************************************************************************************/
/* DEFINES */
/**************************************************************************************************/
#if 1
//Archer
const char ISP_FILE_PATH[] = "/etc/3g4g.gz";
const char TM_FILTER_APPS_FILE_PATH[] = "/tmp/tm-shn/list_pc_filter_apps.db.gz";
const char TM_SEC_RULES_FILE_PATH[] = "/tmp/tm-shn/list_security_rules.db.gz";
const char TM_SEC_WRS_CATS_FILE_PATH[] = "/tmp/tm-shn/list_security_wrs_cats.db.gz";
const char TM_APPBLOCKLIST_FILE_PATH[] = "/tmp/appblock/avira_applist.gz";
const char GUEST_NETWORK_PORTAL_LOGO_PATH_READ[] = "/www/tmp/portal_logo.png";
const char GUEST_NETWORK_PORTAL_BACK_PATH_READ[] = "/www/tmp/portal_back.jpg";
const char GUEST_NETWORK_PORTAL_LOGO_PATH_WRITE[] = "/tmp/guest_portal/portal_logo.png";
const char GUEST_NETWORK_PORTAL_BACK_PATH_WRITE[] = "/tmp/guest_portal/portal_back.jpg";
const char TESTFILE1[] = "/tmp/testfile1";
const char TESTFILE2[] = "/tmp/testfile2";
#else
//Aginet
const char USBDISK_VOICEMAIL_RECORD_PATH[] = "/var/usbdisk/usbvm/voicemail/record/";
const char USBDISK_VOICEMAIL_NOTIFY_PATH[] = "/var/usbdisk/usbvm/voicemail/voiceNotify/";
const char LARGEDATA_VOICEMAIL_RECORD_PATH[] = "/large_data/usbvm/voicemail/record/";
const char REGION_JSON_GZ_FILE[] = "/web/js/region.json.gz";
const char REGION_JSON_GZ_MD5_FILE[] = "/web/js/region.json.gz.MD5";
const char ISPPROFILE_GZ_JSON_FILE[] = "/web/js/ispprofile.json.gz";
const char ISPPROFILE_GZ_JSON_MD5_FILE[] = "/web/js/ispprofile.json.gz.MD5";
const char ISPLOGO_PNG_GZ_FILE[] = "/web/img/isplogo.png.gz";
const char ISPLOGO_PNG_GZ_MD5_FILE[] = "/web/img/isplogo.png.gz.MD5";
const char AGC_DATABUFF_ISPLOGO_PNG_GZ_FILE[] = "/var/tmp/AginetConfig/databuff/isplogo.png.gz";
const char AGC_DATABUFF_ISPLOGO_PNG_GZ_MD5_FILE[] = "/var/tmp/AginetConfig/databuff/isplogo.png.gz.MD5";
//test
const char TESTFILE1[] = "/tmp/testfile1";
const char TESTFILE2[] = "/tmp/testfile2";
#endif
typedef struct {
const char *file_path;
int options;
} SCP_FILE_PERMISSION;
#define OPT_READ (0x01)
#define OPT_WRITE (0x02)
#define OPT_DATE (0x04)
#define OPT_INCLUDE(x, opt) (opt == ((x) & opt))
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
#if 1
//Archer
static SCP_FILE_PERMISSION file_permissions[] = {
{ISP_FILE_PATH, OPT_READ | OPT_DATE},
{TM_FILTER_APPS_FILE_PATH, OPT_READ | OPT_DATE},
{TM_SEC_RULES_FILE_PATH, OPT_READ | OPT_DATE},
{TM_SEC_WRS_CATS_FILE_PATH, OPT_READ | OPT_DATE},
{TM_APPBLOCKLIST_FILE_PATH, OPT_READ | OPT_DATE},
{GUEST_NETWORK_PORTAL_LOGO_PATH_READ, OPT_READ | OPT_DATE},
{GUEST_NETWORK_PORTAL_BACK_PATH_READ, OPT_READ | OPT_DATE},
{GUEST_NETWORK_PORTAL_LOGO_PATH_WRITE, OPT_WRITE | OPT_DATE},
{GUEST_NETWORK_PORTAL_BACK_PATH_WRITE, OPT_WRITE | OPT_DATE},
{TESTFILE1, OPT_READ | OPT_WRITE | OPT_DATE},
{TESTFILE2, OPT_READ | OPT_WRITE | OPT_DATE},
};
#else
//Aginet
static SCP_FILE_PERMISSION file_permissions[] = {
{USBDISK_VOICEMAIL_RECORD_PATH, OPT_READ | OPT_DATE},
{USBDISK_VOICEMAIL_NOTIFY_PATH, OPT_READ | OPT_DATE},
{LARGEDATA_VOICEMAIL_RECORD_PATH, OPT_READ | OPT_DATE},
{REGION_JSON_GZ_FILE, OPT_READ | OPT_DATE},
{REGION_JSON_GZ_MD5_FILE, OPT_READ | OPT_DATE},
{ISPPROFILE_GZ_JSON_FILE, OPT_READ | OPT_DATE},
{ISPPROFILE_GZ_JSON_MD5_FILE, OPT_READ | OPT_DATE},
{ISPLOGO_PNG_GZ_FILE, OPT_READ | OPT_DATE},
{ISPLOGO_PNG_GZ_MD5_FILE, OPT_READ | OPT_DATE},
{AGC_DATABUFF_ISPLOGO_PNG_GZ_FILE, OPT_READ | OPT_DATE},
{AGC_DATABUFF_ISPLOGO_PNG_GZ_MD5_FILE, OPT_READ | OPT_DATE},
{TESTFILE1, OPT_READ | OPT_WRITE | OPT_DATE},
{TESTFILE2, OPT_READ | OPT_WRITE | OPT_DATE},
};
#endif
/**************************************************************************************************/
/* LOCAL_FUNCTIONS */
/**************************************************************************************************/
static int check_file_permission(const char *fn, int type, int need_date) {
int i = 0;
if (fn == NULL) {
return -1;
}
for (i = 0; i < ARRAY_SIZE(file_permissions); i++) {
if (0 == strcmp(fn, file_permissions[i].file_path)) {
if ((SESSION_TYPE_SOURCE == type)
&& (!OPT_INCLUDE(file_permissions[i].options, OPT_READ))) {
return -1;
}
if ((SESSION_TYPE_TARGET == type)
&& (!OPT_INCLUDE(file_permissions[i].options, OPT_WRITE))) {
return -1;
}
if (need_date
&& (!OPT_INCLUDE(file_permissions[i].options, OPT_DATE))) {
return -1;
}
return 0;
}
}
return -1;
}
static void set_file_time(int fd, int atime, int mtime) {
if (fd < 0) return;
struct timespec tv[2] = {
{0, UTIME_NOW}, //0: access
{0, UTIME_NOW} //1: modify
};
if (atime >= 0) {
tv[0].tv_sec = atime;
tv[0].tv_nsec = 0;
}
if (mtime >= 0) {
tv[1].tv_sec = mtime;
tv[1].tv_nsec = 0;
}
futimens(fd, tv);
}
static void hex2bin(const char *hex, unsigned char *bin, int hex_len) {
int i = 0;
int val = 0;
if (hex_len & 1) hex_len--;
memset(bin, 0, hex_len >> 1);
for (i = 0; i < hex_len; i++) {
switch (hex[i]) {
case '0'...'9':
val = hex[i] - '0';
break;
case 'a'...'z':
val = hex[i] - 'a' + 10;
break;
case 'A'...'Z':
val = hex[i] - 'A' + 10;
break;
}
bin[i >> 1] = (bin[i >> 1] << 4) + val;
}
}
static void bin2hex(const unsigned char *bin, char *hex, int bin_len) {
int i = 0;
int val = 0;
for (i = 0; i < bin_len; i++) {
val = (bin[i] >> 4) & 0x0f;
hex[i << 1] = val + ((val >= 10) ? ('A' - 10) : '0');
val = (bin[i]) & 0x0f;
hex[(i << 1) + 1] = val + ((val >= 10) ? ('A' - 10) : '0');
}
}
static int calc_checksum(int fd, unsigned char *output, int size) {
if (fd < 0 || NULL == output || size <= 0) return -1;
unsigned char buf[SCP_PACKET_MAX_SIZE];
int read_len = SCP_PACKET_MAX_SIZE;
int len = 0;
off_t offset = lseek(fd, 0, SEEK_CUR);
lseek(fd, 0, SEEK_SET);
SHA256_CTX ctx;
SHA256_Init(&ctx);
do {
if (size < read_len) read_len = size;
len = scp_fd_read_direct(fd, (char *)buf, read_len);
if (len <= 0) return -1;
SHA256_Update(&ctx, buf, len);
size -= len;
} while (size > 0);
SHA256_Final(output, &ctx);
lseek(fd, offset, SEEK_SET);
return 0;
}
/**************************************************************************************************/
/* PUBLIC_FUNCTIONS */
/**************************************************************************************************/
int scp_fd_write_direct(int fd, const char* buf, size_t size)
{
int len;
int ret = size;
if (fd < 0) {
return 0;
}
for (;;)
{
len = write(fd, buf, size);
if (len < 0)
{
if (EINTR == errno)
{
continue;
}
ret = -1;
break;
}
else if (len == 0)
{
ret = 0;
break;
}
else if ((size_t)len >= size)
{
break;
}
else
{
buf += len;
size -= len;
}
}
return ret;
}
int scp_fd_read_direct(int fd, char* buf, size_t size)
{
if (fd < 0) {
return 0;
}
int len;
int ret = size;
fd_set fds;
struct timeval tv;
int retval;
for (;;)
{
FD_ZERO(&fds);
FD_SET(fd, &fds);
tv.tv_sec = 1;
tv.tv_usec = 0;
retval = select(fd + 1, &fds, NULL, NULL, &tv);
if (retval < 0)
{
if (EINTR == errno)
{
continue;
}
ret = -1;
break;
}
else if (retval == 0)
{
ret = -1;
break;
}
len = read(fd, buf, size);
if (len < 0)
{
if (EINTR == errno)
{
continue;
}
ret = -1;
break;
}
else if (len == 0)
{
ret = 0;
break;
}
else if ((size_t)len >= size)
{
break;
}
else
{
buf += len;
size -= len;
}
}
return ret;
}
int scp_make_packet(unsigned char *buf, cJSON *control) {
SCP_HEADER *scp_hdr = (SCP_HEADER *)buf;
int ctrl_len = 0;
char *control_str = NULL;
control_str = cJSON_PrintUnformatted(control);
SCPSDebug("scp_make_packet: %s", control_str);
ctrl_len = strlen(control_str);
scp_hdr->ctrl_len = htons(ctrl_len);
memcpy(SCP_HEADER_GET_PAYLOAD(scp_hdr, 0), control_str, ctrl_len);
free(control_str);
SCPSDebug("make packet ctrl %d.", ctrl_len);
for (; (ctrl_len & 0x03) != 0; ctrl_len++)
*SCP_HEADER_GET_PAYLOAD(scp_hdr, ctrl_len) = '\0';
SCPSDebug("packet ctrl padding length %d.", ctrl_len);
scp_hdr->data_len = 0;
return ctrl_len + sizeof(SCP_HEADER);
}
#define CHECK_PARAM(param, obj_type) do { \
if (NULL == param) return SCP_ERROR_CODE_CONTROL_SECTION_NOT_FOUND; \
if (param->type != obj_type) { \
SCPSDebug("Get param %s %d, %d expected.", param->string, param->type, obj_type); \
return SCP_ERROR_CODE_CONTROL_VALUE_ILLEGAL; \
} \
} while(0)
int scp_get_method(const unsigned char *buf, cJSON **control, int *id, int *type, int *val, cJSON **param_ret) {
cJSON *root = NULL;
cJSON *param = NULL;
SCP_HEADER *scp_hdr = (SCP_HEADER *)buf;
SCPSDebug("scp get json1: %s\n", SCP_HEADER_GET_PAYLOAD(scp_hdr, 0));
root = cJSON_Parse((const char *)SCP_HEADER_GET_PAYLOAD(scp_hdr, 0));
if (NULL == root) return SCP_ERROR_CODE_CONTROL_DECRYPT_FAILED;
if (NULL != control) *control = root; //调用者自行删除root
CHECK_PARAM(root, cJSON_Object);
param = cJSON_GetObjectItem(root, "id");
CHECK_PARAM(param, cJSON_Number);
if (0 >= param->valueint || SCP_MAX_ID < param->valueint)
return SCP_ERROR_CODE_CONTROL_VALUE_ILLEGAL;
*id = param->valueint;
param = cJSON_GetObjectItem(root, "error_code");
if (param != NULL) {
CHECK_PARAM(param, cJSON_Number);
*val = param->valueint;
*type = SCP_PACKET_TYPE_RESPONSE;
if (NULL != param_ret) *param_ret = cJSON_GetObjectItem(root, "result");
return 0;
}
param = cJSON_GetObjectItem(root, "method");
if (param != NULL) {
CHECK_PARAM(param, cJSON_String);
if (0 == strncmp(param->valuestring, "read", SCP_METHOD_STR_LEN)) *val = SESSION_TYPE_SOURCE;
else if (0 == strncmp(param->valuestring, "write", SCP_METHOD_STR_LEN)) *val = SESSION_TYPE_TARGET;
else return SCP_ERROR_CODE_CONTROL_VALUE_ILLEGAL;
*type = SCP_PACKET_TYPE_REQUEST;
if (NULL != param_ret) *param_ret = cJSON_GetObjectItem(root, "params");
return 0;
}
param = cJSON_GetObjectItem(root, "data_offset");
if (param != NULL) {
CHECK_PARAM(param, cJSON_Number);
*val = param->valueint;
*type = SCP_PACKET_TYPE_DATA;
return 0;
}
return SCP_ERROR_CODE_CONTROL_SECTION_NOT_FOUND;
}
int send_file_data(scp_session *psession) {
int ret = 0;
int fd = -1;
int file_size = 0;
int state = 0;
cJSON* control = NULL;
cJSON* control_offset = NULL;
//assert(psession != NULL);
control = cJSON_CreateObject();
if (NULL == control) {
ret = SCP_ERROR_CODE_SERVER_ERROR;
goto send_data_session_end;
}
cJSON_AddNumberToObject(control, "id", psession->id);
cJSON_AddNumberToObject(control, "data_offset", 0);
control_offset = cJSON_GetObjectItem(control, "data_offset");
if (NULL == control) {
ret = SCP_ERROR_CODE_SERVER_ERROR;
goto send_data_session_end;
}
ctx_rdlock_session(SESSION_TYPE_SOURCE);
fd = psession->fd;
file_size = psession->file_size;
ctx_unlock_session(SESSION_TYPE_SOURCE);
//assert(fd >= 0);
//assert(file_size >= 0);
unsigned char buf[SCP_PACKET_MAX_SIZE];
SCP_HEADER *scp_hdr = (SCP_HEADER *)buf;
unsigned char *buf_ctrl = SCP_HEADER_GET_PAYLOAD(scp_hdr, 0);
int data_len = 0;
int pkt_len = 0;
int len = 0;
int offset = 0;
SCPSDebug("send_file_data start.");
while (offset < file_size) {
//设置数据报文控制信息
cJSON_SetIntValue(control_offset, offset);
pkt_len = scp_make_packet(buf, control);
//读取文件数据
data_len = (SCP_PACKET_MAX_SIZE - pkt_len);
if (file_size - offset < data_len) data_len = file_size - offset;
scp_hdr->data_len = htons(data_len);
len = scp_fd_read_direct(fd, (char *)buf + pkt_len, data_len);
if (len < data_len) {
ret = SCP_ERROR_CODE_SERVER_ERROR;
break;
}
SCPSDebug("fd = %d, make data pack %d %d.", fd, offset, data_len);
//检查任务是否已被停止
ctx_lock_data_send();
while (scp_contextp->data_pack_num >= scp_contextp->data_speed_limit) {
ctx_wait_data_send();
}
scp_contextp->data_pack_num++;
ctx_unlock_data_send();
//SCPSDebug("get condition.");
ctx_rdlock_session(SESSION_TYPE_SOURCE);
state = psession->state;
ctx_unlock_session(SESSION_TYPE_SOURCE);
if (SESSION_STATE_HALT == state) {
SCPSDebug("get halt state.");
break;
}
//SCPSDebug("send data.");
if (ctx_send_request_func != NULL) {
ctx_send_request_func(buf, pkt_len + data_len);
}
offset += data_len;
}
SCPSDebug("send data finish.");
ctx_wrlock_session(SESSION_TYPE_SOURCE);
clean_session(SESSION_TYPE_SOURCE, psession);
ctx_unlock_session(SESSION_TYPE_SOURCE);
send_data_session_end:
if (NULL != control) {
cJSON_Delete(control);
}
if (NULL != ctx_send_callback_func) {
ctx_send_callback_func();
}
return ret;
}
int recv_file_data(int id, const unsigned char *buf, int offset) {
int ret = 0;
int ses_index = -1;
scp_session *psession = NULL;
scp_target_session *pext = NULL;
int data_len = 0;
int len = 0;
const unsigned char *buf_data;
const SCP_HEADER *hdr = (SCP_HEADER *)buf;
//assert(control != NULL)
//assert(buf != NULL)
SCPSDebug("recv_file_data start.");
ctx_wrlock_session(SESSION_TYPE_TARGET);
ses_index = match_session(SESSION_TYPE_TARGET, id);
if (-1 == ses_index) goto error_recv_data; //匹配失败,不回复
psession = ctx_get_session(SESSION_TYPE_TARGET, ses_index);
pext = ctx_get_target_extention(ses_index);
if (offset != pext->offset) {
ret = SCP_ERROR_CODE_FILE_CHECK_DATA_LOST;
goto error_recv_data;
}
//SCPSDebug("offset check.");
psession->expire = 0;
data_len = ntohs(hdr->data_len);
len = ntohs(hdr->ctrl_len);
buf_data = SCP_HEADER_GET_PAYLOAD(hdr, SCP_PADDING_LENGTH(len));
if (data_len > 0) {
//SCPSDebug("write data to file %d.", data_len);
len = scp_fd_write_direct(psession->fd, (const char *)buf_data, data_len);
if (len < data_len) {
ret = SCP_ERROR_CODE_SERVER_ERROR;
goto error_recv_data;
}
SHA256_Update(&(pext->ctx), buf_data, data_len);
}
pext->offset += data_len;
if (pext->offset >= psession->file_size)
{
SCPSDebug("get all data.");
ret = SCP_ERROR_CODE_FILE_CHECK_PASS;
if (pext->offset > 0) {
unsigned char dgest[SCP_SHA_CHECKSUM_LEN] = {0};
SHA256_Final(dgest, &(pext->ctx));
if (memcmp(pext->checksum, dgest, SCP_SHA_CHECKSUM_LEN)) {
//SCPSDebug("wrong checksum.");
ret = SCP_ERROR_CODE_FILE_CHECK_CHECKSUM_FAIL;
}
}
set_file_time(psession->fd, pext->time[0], pext->time[1]);
}
error_recv_data:
if (ret != 0 && ses_index != -1) { //接收数据完成或失败,关闭session
//接收文件失败时是否需要删除文件?
clean_session(SESSION_TYPE_TARGET, psession);
}
ctx_unlock_session(SESSION_TYPE_TARGET);
return ret;
}
int stop_send_file_data(int id) {
int ses_index = -1;
scp_session *psession = NULL;
SCPSDebug("stop_send_file_data start.");
ctx_wrlock_session(SESSION_TYPE_SOURCE);
do {
ses_index = match_session(SESSION_TYPE_SOURCE, id);
if (-1 == ses_index) {
break;
}
SCPSDebug("stop session %d.", ses_index);
psession = ctx_get_session(SESSION_TYPE_SOURCE, ses_index);
psession->state = SESSION_STATE_HALT;
} while (1);
ctx_unlock_session(SESSION_TYPE_SOURCE);
return SCP_ERROR_CODE_ACCEPT;
}
int send_file_request(int id, cJSON *control, int *new_session, cJSON **new_status) {
int ret = 0;
int ses_index = -1;
scp_session *psession = NULL;
int fd = -1;
cJSON *param = NULL, *param_options = NULL;
char *filename = NULL;
int max_size = 0;
int set_date = 0;
struct stat stat_i;
param = cJSON_GetObjectItem(control, "filename");
CHECK_PARAM(param, cJSON_String);
filename = param->valuestring;
if (strlen(filename) > SCP_MAX_FILEPATH_LEN) {
return SCP_ERROR_CODE_CONTROL_VALUE_ILLEGAL;
}
param_options = cJSON_GetObjectItem(control, "option");
if (NULL != param_options) {
CHECK_PARAM(param_options, cJSON_Object);
param = cJSON_GetObjectItem(param_options, "max_size");
if (NULL != param) {
CHECK_PARAM(param, cJSON_Number);
max_size = param->valueint;
}
param = cJSON_GetObjectItem(param_options, "send_date");
if (NULL != param) {
CHECK_PARAM(param, cJSON_Number);
set_date = 1;
}
}
if (0 != check_file_permission(filename, SESSION_TYPE_SOURCE, set_date)) {
return SCP_ERROR_CODE_OPERATION_NO_PERMISSION;
}
if (0 != stat(filename, &stat_i)) {
if (ENOENT == errno)
return SCP_ERROR_CODE_OPERATION_FILE_NOT_FOUND;
else
return SCP_ERROR_CODE_SERVER_ERROR;
}
if (max_size > 0 && stat_i.st_size > max_size) {
return SCP_ERROR_CODE_OPERATION_FILE_OVER_SIZE;
}
SCPSDebug("generate file status.");
//生成文件基本信息
char mode[5];
sprintf(mode, "%04o", stat_i.st_mode & 0x0fff);
cJSON* status = cJSON_CreateObject();
cJSON_AddNumberToObject(status, "size", stat_i.st_size);
cJSON_AddStringToObject(status, "permissions", mode);
if (set_date) {
cJSON* date = cJSON_CreateObject();
cJSON_AddNumberToObject(date, "change_time", stat_i.st_mtime);
cJSON_AddNumberToObject(date, "access_time", stat_i.st_atime);
cJSON_AddItemToObject(status, "date", date);
}
SCPSDebug("send_file_request start.");
if (stat_i.st_size <= 0) {
SCPSDebug("filesize 0.");
cJSON_AddStringToObject(status, "checksum", "0000000000000000000000000000000000000000000000000000000000000000");
ses_index = -1;
goto skip_session;
}
ctx_wrlock_session(SESSION_TYPE_SOURCE);
ret = scp_open_file_read(&fd, filename);
if (ret < 0) {
goto error_send_request;
}
ret = create_session(SESSION_TYPE_SOURCE, id, fd, &ses_index);
if (ret) {
goto error_send_request;
}
psession = ctx_get_session(SESSION_TYPE_SOURCE, ses_index);
if (stat_i.st_size > 0) {
unsigned char checksum[SCP_SHA_CHECKSUM_LEN];
char checksum_HEX[SCP_SHA_CHECKSUM_STR_LEN + 1];
SCPSDebug("calculate checksum.");
if (calc_checksum(fd, checksum, stat_i.st_size) < 0) {
ret = SCP_ERROR_CODE_SERVER_ERROR;
goto error_send_request;
}
bin2hex(checksum, checksum_HEX, SCP_SHA_CHECKSUM_LEN);
checksum_HEX[SCP_SHA_CHECKSUM_STR_LEN] = '\0';
cJSON_AddStringToObject(status, "checksum", checksum_HEX);
}
psession->file_size = stat_i.st_size;
SCPSDebug("file %s, size = %d\n", param->valuestring, psession->file_size);
skip_session:
//数据发送任务需要等发送完response包后再创建
*new_session = ses_index;
*new_status = status;
ctx_unlock_session(SESSION_TYPE_SOURCE);
return SCP_ERROR_CODE_ACCEPT;
error_send_request:
if (fd >= 0) {
scp_close_file(&fd);
}
if (0 != ses_index) {
clean_session(SESSION_TYPE_SOURCE, psession);
}
ctx_unlock_session(SESSION_TYPE_SOURCE);
return ret;
}
int recv_file_request(int id, cJSON* control) {
int ret = 0;
int ses_index = -1;
scp_session *psession = NULL;
scp_target_session *pext = NULL;
int fd = -1;
cJSON *param = NULL, *param_status = NULL, *param2 = NULL;
char *filename = NULL;
mode_t mode = 0;
int set_date = 0;
int size = 0;
char *checksum = NULL;
int atime = -1;
int mtime = -1;
//assert(control != NULL)
param = cJSON_GetObjectItem(control, "filename");
CHECK_PARAM(param, cJSON_String);
filename = param->valuestring;
if (strlen(filename) > SCP_MAX_FILEPATH_LEN) {
return SCP_ERROR_CODE_CONTROL_VALUE_ILLEGAL;
}
param_status = cJSON_GetObjectItem(control, "file_status");
CHECK_PARAM(param_status, cJSON_Object);
param = cJSON_GetObjectItem(param_status, "date");
if (NULL != param) {
//SCPSDebug("find date param.");
CHECK_PARAM(param, cJSON_Object);
param2 = cJSON_GetObjectItem(param, "access_time");
if (NULL != param2) {
CHECK_PARAM(param2, cJSON_Number);
atime = param2->valueint;
}
param2 = cJSON_GetObjectItem(param, "change_time");
if (NULL != param2) {
CHECK_PARAM(param2, cJSON_Number);
mtime = param2->valueint;
}
set_date = 1;
}
param = cJSON_GetObjectItem(param_status, "permissions");
if (NULL != param) {
CHECK_PARAM(param, cJSON_String);
sscanf(param->valuestring, "%o", &mode);
}
param = cJSON_GetObjectItem(param_status, "size");
CHECK_PARAM(param, cJSON_Number);
size = param->valueint;
// 文件大小为0时checksum无意义
if (size > 0) {
param = cJSON_GetObjectItem(param_status, "checksum");
CHECK_PARAM(param, cJSON_String);
checksum = param->valuestring;
if (strlen(checksum) != SCP_SHA_CHECKSUM_STR_LEN) {
return SCP_ERROR_CODE_CONTROL_VALUE_ILLEGAL;
}
}
if (0 != check_file_permission(filename, SESSION_TYPE_TARGET, set_date)) {
return SCP_ERROR_CODE_OPERATION_NO_PERMISSION;
}
SCPSDebug("recv_file_request start.");
ctx_wrlock_session(SESSION_TYPE_TARGET);
ret = scp_open_file_write(&fd, filename);
if (ret) {
goto error_recv_request;
}
if (0 != mode) fchmod(fd, mode);
if (size <= 0) {
SCPSDebug("filesize 0.");
set_file_time(fd, atime, mtime);
scp_close_file(&fd);
goto skip_session;
}
ret = create_session(SESSION_TYPE_TARGET, id, fd, &ses_index);
if (ret) {
goto error_recv_request;
}
init_session_target(ses_index);
// 设置文件基本信息
psession = ctx_get_session(SESSION_TYPE_TARGET, ses_index);
pext = ctx_get_target_extention(ses_index);
SCPSDebug("set file stat.");
pext->time[0] = atime;
pext->time[1] = mtime;
psession->file_size = size;
if (size > 0) {
SCPSDebug("cache checksum.");
hex2bin(checksum, (unsigned char *)pext->checksum, SCP_SHA_CHECKSUM_STR_LEN);
}
skip_session:
ctx_unlock_session(SESSION_TYPE_TARGET);
return SCP_ERROR_CODE_ACCEPT;
error_recv_request:
if (fd >= 0) {
scp_close_file(&fd);
}
if (0 != ses_index) {
clean_session(SESSION_TYPE_TARGET, psession);
}
ctx_unlock_session(SESSION_TYPE_TARGET);
return ret;
}
int scp_open_file_read(int *fd, char *filename) {
//assert(*fd < 0);
SCPSDebug("open file %s read", filename);
int new_fd = open(filename, O_RDONLY);
if (new_fd < 0) return SCP_ERROR_CODE_OPERATION_FILE_NOT_FOUND;
if (flock(new_fd, LOCK_SH | LOCK_NB)) {
SCPSDebug("lock file %s failed.", filename);
close(new_fd);
return SCP_ERROR_CODE_OPERATION_FILE_IN_USE;
}
*fd = new_fd;
return 0;
}
int scp_open_file_write(int *fd, char *filename) {
//assert(*fd < 0);
SCPSDebug("open file %s write", filename);
int new_fd = -1;
if (access(filename, F_OK) != -1)
new_fd = open(filename, O_WRONLY | O_TRUNC);
else
new_fd = open(filename, O_WRONLY | O_CREAT);
if (new_fd < 0) return SCP_ERROR_CODE_SERVER_ERROR;
if (flock(new_fd, LOCK_EX | LOCK_NB)) {
SCPSDebug("lock file %s failed.", filename);
close(new_fd);
return SCP_ERROR_CODE_OPERATION_FILE_IN_USE;
}
*fd = new_fd;
return 0;
}
int scp_close_file(int *fd) {
if (*fd < 0) return 0;
flock(*fd, LOCK_UN);
close(*fd);
*fd = -1;
return 0;
}
int init_session_target(int ses_index) {
scp_contextp->target_ext[ses_index].offset = 0;
SHA256_Init(&(scp_contextp->target_ext[ses_index].ctx));
return 0;
}
int create_session(int type, int id, int fd, int *res) {
SCPSDebug("create session %d %d.", type, id);
int len;
int i = 0;
int tar = -1;
scp_session *psession;
ctx_session_foreach(type, i, psession) {
if (psession->state == SESSION_STATE_STOP && -1 == tar) tar = i;
if (SESSION_TYPE_TARGET == type && psession->id == id) { //只有接收模式的id对重复是敏感的
return SCP_ERROR_CODE_CONTROL_VALUE_ILLEGAL;
}
}
if (-1 == tar && i >= scp_contextp->session_max[type]) {
if (scp_contextp->session_max[type] < SCP_MAX_SESSION_NUM) {
scp_contextp->session_max[type]++;
} else {
SCPSDebug("session %d full.", type);
return SCP_ERROR_CODE_SERVER_ERROR;
}
tar = i;
}
scp_contextp->session[type][tar] = (scp_session) {
.id = id,
.fd = fd,
.file_size = 0,
.state = SESSION_STATE_RUNNING,
.expire = 0
};
SCPSDebug("new session %d %d.", type, tar);
*res = tar;
return 0;
}
int match_session(int type, int id) {
int i = 0;
scp_session *psession;
ctx_session_foreach(type, i, psession) {
if (psession->state != SESSION_STATE_RUNNING) continue;
if (psession->id == id) {
SCPSDebug("match session %d %d %d.", type, id, i);
return i;
}
}
SCPSDebug("session %d %d not found.", type, id);
return -1;
}
int clean_session(int type, scp_session* psession) {
//SCPSDebug("clean session %d %d.", type, psession->id);
psession->state = SESSION_STATE_STOP;
scp_close_file(&(psession->fd));
//target扩展信息没有动态申请内存,无需处理
while ((scp_contextp->session_max[type] > 0) &&
(SESSION_STATE_STOP == scp_contextp->session[type][scp_contextp->session_max[type] - 1].state))
scp_contextp->session_max[type]--;
SCPSDebug("session %d %d cleaned, session max %d.", type, psession->id, scp_contextp->session_max[type]);
return 0;
}
void init_session() {
int i = 0;
for (i = 0; i < SCP_MAX_SESSION_NUM; i++) {
scp_contextp->session[SESSION_TYPE_SOURCE][i].state = SESSION_STATE_STOP;
scp_contextp->session[SESSION_TYPE_TARGET][i].state = SESSION_STATE_STOP;
}
scp_contextp->session_max[SESSION_TYPE_SOURCE] = 0;
scp_contextp->session_max[SESSION_TYPE_TARGET] = 0;
pthread_rwlock_init(&(scp_contextp->sessionlock[SESSION_TYPE_SOURCE]), NULL);
pthread_rwlock_init(&(scp_contextp->sessionlock[SESSION_TYPE_TARGET]), NULL);
}
void close_all_session() {
int i = 0;
scp_session *psession;
SCPSDebug("clean all sessions.");
ctx_wrlock_session(SESSION_TYPE_TARGET);
ctx_session_foreach(SESSION_TYPE_TARGET, i, psession) {
if (psession->state == SESSION_STATE_RUNNING)
clean_session(SESSION_TYPE_TARGET, psession);
}
ctx_unlock_session(SESSION_TYPE_TARGET);
// 发送会话需等任务自行结束
ctx_wrlock_session(SESSION_TYPE_SOURCE);
ctx_session_foreach(SESSION_TYPE_SOURCE, i, psession) {
if (psession->state == SESSION_STATE_RUNNING)
psession->state = SESSION_STATE_HALT;
}
ctx_unlock_session(SESSION_TYPE_SOURCE);
// 防止有任务卡在发包限速的位置
ctx_lock_data_send();
scp_contextp->data_speed_limit = SCP_MAX_SESSION_NUM;
scp_contextp->data_pack_num = 0;
ctx_active_data_send();
ctx_unlock_data_send();
usleep(100000);
pthread_rwlock_destroy(&(scp_contextp->sessionlock[SESSION_TYPE_SOURCE]));
pthread_rwlock_destroy(&(scp_contextp->sessionlock[SESSION_TYPE_TARGET]));
}
void scp_data_flush() {
// 清空发包计数
ctx_lock_data_send();
scp_contextp->data_pack_num = 0;
ctx_active_data_send();
ctx_unlock_data_send();
// 检查收包超时
int i = 0;
scp_session *psession;
ctx_wrlock_session(SESSION_TYPE_TARGET);
ctx_session_foreach(SESSION_TYPE_TARGET, i, psession) {
if (psession->state == SESSION_STATE_RUNNING) {
psession->expire++;
if (psession->expire >= SCP_DEFAULT_TIMEOUT) {
SCPSDebug("target session %d timeout, id: %d.", psession->id);
clean_session(SESSION_TYPE_TARGET, psession);
}
}
}
ctx_unlock_session(SESSION_TYPE_TARGET);
}
void scp_set_data_speed(int speed_limit) {
ctx_lock_data_send();
scp_contextp->data_speed_limit = speed_limit;
ctx_unlock_data_send();
}
void scp_init(
scp_context *context,
p_scp_send_request sdp,
p_scp_send_data_end_callback sdep)
{
if (NULL == context) {
return;
}
scp_contextp = context;
// 设置数据发送任务的回调函数,尤其是发包接口一定要统一
scp_contextp->send_func = sdp;
scp_contextp->finish_func = sdep;
// 数据发送限速模块
pthread_mutex_init(&(scp_contextp->speedlock), NULL);
pthread_cond_init(&(scp_contextp->speedcond), NULL);
scp_contextp->data_pack_num = 0;
scp_contextp->data_speed_limit = SCP_DEFAULT_SPEED_LIMIT;
}
void scp_close() {
close_all_session();
pthread_mutex_destroy(&(scp_contextp->speedlock));
pthread_cond_destroy(&(scp_contextp->speedcond));
}解析这个文件,怎么实现scp
最新发布