Exemple #1
0
static int update_pid_file(struct runtime_config *rtc)
{
	FILE *fd;
	char *pidfile;

	if (access(rtc->statedir, W_OK))
		if (mkdir(rtc->statedir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH))
			return 1;
	pidfile = construct_pidfile(rtc);
	if (!(fd = fopen(pidfile, "w"))) {
#ifdef USE_SYSTEMD
		sd_journal_send("MESSAGE=could not open file",
				"MESSAGE_ID=%s", SD_ID128_CONST_STR(MESSAGE_ERROR),
				"PID_FILE=%s", pidfile, "STRERROR=%s", strerror(errno), "PRIORITY=3", NULL);
#else
		syslog(LOG_ERR, "could not open file: %s: %s", pidfile, strerror(errno));
#endif
		return 1;
	}
	fprintf(fd, "%u %d", getpid(), rtc->msg_type);
	if (close_stream(fd))
#ifdef USE_SYSTEMD
		sd_journal_send("MESSAGE=close failed",
				"MESSAGE_ID=%s", SD_ID128_CONST_STR(MESSAGE_ERROR),
				"PID_FILE=%s", pidfile, "STRERROR=%s", strerror(errno), "PRIORITY=3", NULL);
#else
		syslog(LOG_ERR, "close failed: %s: %s", pidfile, strerror(errno));
#endif
	free(pidfile);
	return 0;
}
Exemple #2
0
static void run_server(struct runtime_config *rtc)
{
	struct sockaddr_in client_addr;
	socklen_t addr_len;
	pthread_attr_t attr;
#ifdef USE_SYSTEMD
	int ret;

	ret = sd_listen_fds(0);
	if (1 < ret)
		faillog("no or too many file descriptors received");
	else if (ret == 1)
		rtc->server_s = SD_LISTEN_FDS_START + 0;
	else {
#endif
		if (!(rtc->server_s = socket(rtc->res->ai_family, rtc->res->ai_socktype, rtc->res->ai_protocol)))
			err(EXIT_FAILURE, "cannot create socket");
		if (bind(rtc->server_s, rtc->res->ai_addr, rtc->res->ai_addrlen))
			err(EXIT_FAILURE, "unable to bind");
		if (listen(rtc->server_s, SOMAXCONN))
			err(EXIT_FAILURE, "unable to listen");
#ifdef USE_SYSTEMD
	}
#endif
	if (pthread_attr_init(&attr))
		err(EXIT_FAILURE, "cannot init thread attribute");

	if (pthread_rwlock_init(&(rtc->lock), NULL))
		err(EXIT_FAILURE, "cannot init read-write lock");

	daemonize();
	if (rtc->msg_type == STATE_UNKNOWN)
		read_status_from_file(rtc);
	if (update_pid_file(rtc))
		faillog("cannot write pid file");
	openlog(PACKAGE_NAME, LOG_PID, LOG_DAEMON);
	signal(SIGHUP, stop_server);
	signal(SIGINT, stop_server);
	signal(SIGTERM, stop_server);
#ifdef USE_SYSTEMD
	sd_journal_send("MESSAGE=service started",
			"MESSAGE_ID=%s", SD_ID128_CONST_STR(MESSAGE_STOP_START),
			"STATE=%s", state_messages[rtc->msg_type], "PRIORITY=6", NULL);
#else
	syslog(LOG_INFO, "started in state %s", state_messages[rtc->msg_type]);
#endif

	while (1) {
		int accepted;
		pthread_t thread;
		int *newsock;

		addr_len = sizeof(client_addr);
		accepted = accept(rtc->server_s, (struct sockaddr *)&client_addr, &addr_len);
		newsock = malloc(sizeof(int));
		*newsock = accepted;
		pthread_create(&thread, NULL, handle_request, newsock);
		pthread_detach(thread);
	}
}
Exemple #3
0
    faillog(char *msg)
{
#ifdef USE_SYSTEMD
	sd_journal_send("MESSAGE=%s", msg,
			"STRERROR=%s", strerror(errno),
			"MESSAGE_ID=%s", SD_ID128_CONST_STR(MESSAGE_ERROR), "PRIORITY=3", NULL);
#else
	syslog(LOG_ERR, "%s: %s", msg, strerror(errno));
#endif
	exit(EXIT_FAILURE);
}
Exemple #4
0
static void stop_server(int sig __attribute__ ((__unused__)))
{
	pthread_rwlock_destroy(&(rtc.lock));
	freeaddrinfo(rtc.res);
	close(rtc.server_s);

#ifdef USE_SYSTEMD
	sd_journal_send("MESSAGE=service stopped", "MESSAGE_ID=%s", SD_ID128_CONST_STR(MESSAGE_STOP_START), "PRIORITY=6", NULL);
#else
	syslog(LOG_INFO, "stopped");
#endif
	closelog();
}
Exemple #5
0
static void catch_signals(int signal)
{
	if (pthread_rwlock_wrlock(&rtc.lock)) {
#ifdef USE_SYSTEMD
		sd_journal_send("MESSAGE=could not get lock",
				"MESSAGE_ID=%s", SD_ID128_CONST_STR(MESSAGE_ERROR),
				"STRERROR=%s", strerror(errno), "PRIORITY=3", NULL);
#else
		syslog(LOG_ERR, "could not get lock");
#endif
		return;
	}
	switch (signal) {
	case SIGUSR1:
		rtc.msg_type = STATE_DISABLE;
		break;
	case SIGUSR2:
		rtc.msg_type = STATE_MAINTENANCE;
		break;
	case SIGWINCH:
		rtc.msg_type = STATE_ENABLE;
		break;
	default:
		/* should be impossible to reach */
		abort();
	}
	rtc.msg_len = strlen(state_messages[rtc.msg_type]);
	update_pid_file(&rtc);
	pthread_rwlock_unlock(&(rtc.lock));
#ifdef USE_SYSTEMD
	sd_journal_send("MESSAGE=signal received",
			"MESSAGE_ID=%s", SD_ID128_CONST_STR(MESSAGE_STATE_CHANGE),
			"NEW_STATE=%s", state_messages[rtc.msg_type], "PRIORITY=6", NULL);
#else
	syslog(LOG_INFO, "signal received, state is %s", state_messages[rtc.msg_type]);
#endif
	closelog();
}
Exemple #6
0
int main(int argc, char **argv)
{
	int c, server = 0;
	char *listen = NULL, *port = PORT_NUM;
	struct addrinfo hints;
	int e;
	enum {
		STATEDIR_OPT = CHAR_MAX + 1
	};

	static const struct option longopts[] = {
		{"disable", no_argument, NULL, 'd'},
		{"maintenance", no_argument, NULL, 'm'},
		{"enable", no_argument, NULL, 'e'},
		{"server", no_argument, NULL, 's'},
		{"listen", required_argument, NULL, 'l'},
		{"port", required_argument, NULL, 'p'},
		{"state", required_argument, NULL, STATEDIR_OPT},
		{"version", no_argument, NULL, 'V'},
		{"help", no_argument, NULL, 'h'},
		{NULL, 0, NULL, 0}
	};

	set_program_name(argv[0]);
	atexit(close_stdout);

	memset(&rtc, 0, sizeof(struct runtime_config));
	rtc.argv = argv;
	rtc.statedir = F5GS_RUNDIR;

	while ((c = getopt_long(argc, argv, "dmesl:p:Vh", longopts, NULL)) != -1) {
		switch (c) {
		case 'd':
			rtc.send_signal = state_signals[STATE_DISABLE];
			break;
		case 'm':
			rtc.send_signal = state_signals[STATE_MAINTENANCE];
			break;
		case 'e':
			rtc.send_signal = state_signals[STATE_ENABLE];
			break;
		case 's':
			server = 1;
			break;
		case 'l':
			listen = optarg;
			break;
		case 'p':
			port = optarg;
			break;
		case STATEDIR_OPT:
			rtc.statedir = optarg;
			break;
		case 'V':
			printf("%s version %s", PACKAGE_NAME, PACKAGE_VERSION);
#ifdef USE_SYSTEMD
			puts(" with systemd support");
#else
			puts("");
#endif
			return EXIT_SUCCESS;
		case 'h':
			usage(stdout);
		default:
			usage(stderr);
		}
	}

	if (signal(state_signals[STATE_DISABLE], catch_signals) == SIG_ERR ||
	    signal(state_signals[STATE_MAINTENANCE], catch_signals) == SIG_ERR ||
	    signal(state_signals[STATE_ENABLE], catch_signals) == SIG_ERR)
		err(EXIT_FAILURE, "cannot set signal handler");

	memset(&hints, 0, sizeof(struct addrinfo));
	hints.ai_family = AF_UNSPEC;
	hints.ai_socktype = SOCK_STREAM;
	hints.ai_flags = AI_PASSIVE;
	e = getaddrinfo(listen, port, &hints, &(rtc.res));
	if (e) {
		warnx("getaddrinfo: %s port %s: %s", listen, port, gai_strerror(e));
		exit(EXIT_FAILURE);
	}

	if (rtc.send_signal && server)
		change_state(&rtc, getpid());
	else if (server) {
		rtc.msg_type = STATE_UNKNOWN;
		rtc.msg_len = strlen(state_messages[STATE_UNKNOWN]);
	} else if (rtc.send_signal) {
		char *eptr;
		pid_t pid;
		FILE *pidfd;
		char *pid_file = construct_pidfile(&rtc);
		if (!(pidfd = fopen(pid_file, "r")))
			err(EXIT_FAILURE, "cannot open pid file: %s", pid_file);
		fscanf(pidfd, "%d", &pid);
		if (close_stream(pidfd))
#ifdef USE_SYSTEMD
			sd_journal_send("MESSAGE=close failed",
					"PID_FILE=%s", pid_file,
					"MESSAGE_ID=%s", SD_ID128_CONST_STR(MESSAGE_ERROR),
					"STRERROR=%s", strerror(errno), "PRIORITY=3", NULL);
#else
			syslog(LOG_ERR, "close failed: %s: %s", pid_file, strerror(errno));
#endif
		free(pid_file);
		if (change_state(&rtc, pid)) {
			if (errno == 0) {
				errx(EXIT_FAILURE, "execution of %s failed", F5GS_PRE);
			} else {
				err(EXIT_FAILURE, "sending signal failed");
			}
		}
		openlog(PACKAGE_NAME, LOG_PID, LOG_DAEMON);
		eptr = getenv("USER");
		if (eptr != NULL)
#ifdef USE_SYSTEMD
			sd_journal_send("MESSAGE=signal was sent",
					"MESSAGE_ID=%s", SD_ID128_CONST_STR(MESSAGE_STATE_CHANGE),
					"USER=%s", eptr, "PRIORITY=6", NULL);
#else
			syslog(LOG_INFO, "signal was sent by USER: %s", eptr);
#endif
		eptr = getenv("SUDO_USER");
		if (eptr != NULL)
#ifdef USE_SYSTEMD
			sd_journal_send("MESSAGE=signal was sent",
					"MESSAGE_ID=%s", SD_ID128_CONST_STR(MESSAGE_STATE_CHANGE),
					"SUDO_USER=%s", eptr, "PRIORITY=6", NULL);
#else
			syslog(LOG_INFO, "signal was sent by SUDO_USER: %s", eptr);
#endif
	}

	if (server)
		run_server(&rtc);

	printf("current status is: %s\n", get_server_status(&rtc));

	freeaddrinfo(rtc.res);

	return EXIT_SUCCESS;
}
Exemple #7
0
int main(int argc, char* argv[])
{

#if 1
    // For debug with segment fault
    struct sigaction sa;
    sa.sa_handler = backtrace_info;
    sigaction(SIGSEGV, &sa, NULL);

    // ignore SIGPIPE
    signal(SIGPIPE, SIG_IGN);
    signal(SIGCHLD, SIG_IGN);
    signal(SIGABRT, SIG_IGN);

#endif

    int opt_g = 0;
    memset(&cltopt, 0, sizeof(CLT_OPT));

    cltopt.C_TYPE = C_USR;
    while( (opt_g = getopt(argc, argv, "Dh")) != -1 )
    {
        switch(opt_g)
        {
            case 'D':
                cltopt.C_TYPE = C_DAEMON;
                break;
            case 'h':
            default:
                usage();
                exit(EXIT_SUCCESS);
        }
    }

    if(load_settings_client(&cltopt) == RET_NO)
    {
        st_d_error("加载配置文件settings.json出错!");
        exit(EXIT_FAILURE);
    }

    OpenSSL_add_ssl_algorithms();
    SSL_load_error_strings();
    SSL_library_init();     //SSL_library_init() always returns "1"

    //int sd_id128_from_string(const char *s, sd_id128_t *ret);
    sd_id128_get_machine(&cltopt.mach_uuid);
    gethostname(cltopt.hostname, sizeof(cltopt.hostname)); 
    st_d_print("CURRENT MACH_ID:%s, HOSTNAME:%s", SD_ID128_CONST_STR(cltopt.mach_uuid), 
               cltopt.hostname);

    if (cltopt.C_TYPE == C_DAEMON) 
    {
        cltopt.session_uuid = cltopt.mach_uuid;
        st_d_print("PLEASE REMEMEBER SET MACH_ID FOR USER TYPE!");
    }

    dump_clt_opts(&cltopt);

    /*带配置产生event_base对象*/
    struct event_config *cfg;
    cfg = event_config_new();
    event_config_avoid_method(cfg, "select");   //避免使用select
    event_config_require_features(cfg, EV_FEATURE_ET);  //使用边沿触发类型
    base = event_base_new_with_config(cfg);
    event_config_free(cfg);
    st_d_print("当前复用Event模式: %s", event_base_get_method(base)); // epoll

    /*连接服务器*/
    int srv_fd = socket(AF_INET, SOCK_STREAM, 0);
    unsigned int optval = 1;
    setsockopt(srv_fd, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(optval));//禁用NAGLE算法

    if(sc_connect_srv(srv_fd) != RET_YES) 
    {
        SYS_ABORT("连接服务器失败!");
    }

    if(cltopt.C_TYPE == C_DAEMON) 
    {
        if (sc_daemon_init_srv(srv_fd) != RET_YES) 
            SYS_ABORT("(Daemon) 服务器返回错误!");
    }
    else
    {
        if (sc_usr_init_srv(srv_fd) != RET_YES) 
            SYS_ABORT("(Usr) 服务器返回错误!");
    }

    st_d_print("客户端连接服务器OK!");

    /**
     * USR 建立本地Listen侦听套接字
     */

    if (cltopt.C_TYPE == C_USR)
    {
        int i = 0;
        for (i=0; i<MAX_PORT_NUM; i++)
        {
            if (cltopt.maps[i].usrport) 
            {
                struct evconnlistener *listener;
                struct sockaddr_in sin;
                memset(&sin, 0, sizeof(sin));
                sin.sin_family = AF_INET;
                sin.sin_addr.s_addr = htonl(0);
                sin.sin_port = htons(cltopt.maps[i].usrport); /* Port Num */

                listener = evconnlistener_new_bind(base, accept_conn_cb, &cltopt.maps[i],
                        LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE, -1/*backlog 连接无限制*/,
                        (struct sockaddr*)&sin, sizeof(sin));

                if (!listener) 
                {
                    st_d_error("[USR]创建侦听套接字失败 %d:%d", 
                               cltopt.maps[i].usrport, cltopt.maps[i].daemonport); 
                    continue;
                }
                evconnlistener_set_error_cb(listener, accept_error_cb);

                st_d_print("[USR]创建侦听套接字 %d:%d OK", 
                               cltopt.maps[i].usrport, cltopt.maps[i].daemonport); 
            }
            else
                break;
        }
    }
    
    encrypt_init(SD_ID128_CONST_STR(cltopt.mach_uuid), cltopt.enc_key);

    if (cltopt.C_TYPE == C_DAEMON && cltopt.ss5_port ) 
    {
        /**
         * 目前只考虑将sockets5代理使用线程池来处理,其它的端口暴露 
         * 基本都是长连接,不单独处理 
         */
        cltopt.thread_num = 5;

        cltopt.main_thread_id = pthread_self(); 
        cltopt.thread_objs = (P_THREAD_OBJ)calloc(sizeof(THREAD_OBJ), cltopt.thread_num);
        if (!cltopt.thread_objs) 
        {
            SYS_ABORT("申请THREAD_OBJ出错");
        }


        sc_create_ss5_worker_threads(cltopt.thread_num, cltopt.thread_objs); 

        st_d_print("[DAEMON]创建sockets5代理端口:%d", cltopt.ss5_port); 

        struct evconnlistener *listener;
        struct sockaddr_in sin;
        memset(&sin, 0, sizeof(sin));
        sin.sin_family = AF_INET;
        sin.sin_addr.s_addr = htonl(0);
        sin.sin_port = htons(cltopt.ss5_port); /* Port Num */

        listener = evconnlistener_new_bind(base, ss5_accept_conn_cb, NULL,
                LEV_OPT_LEAVE_SOCKETS_BLOCKING/* 阻塞 */|LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE, 
                -1/*backlog 连接无限制*/,
                (struct sockaddr*)&sin, sizeof(sin));

        if (!listener) 
        {
            st_d_error("[DAEMON]sockets5代理创建侦听套接字失败 %d", cltopt.ss5_port); 
            exit(EXIT_FAILURE); 
        }
        evconnlistener_set_error_cb(listener, accept_error_cb);

        st_d_print("[DAEMON]sockets5代理创建侦听套接字OK %d", cltopt.ss5_port); 

    }



    if (cltopt.C_TYPE == C_DAEMON && cltopt.dns_port) 
    {
        st_d_print("[DAEMON]创建DNS代理端口:%d", cltopt.dns_port); 
        if (cltopt.dns_port != 53) 
        {
            st_d_print("[DAEMON]请注意标准DNS侦听#53端口!");
        }

        int dns_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
        if (dns_socket < 0 )
        {
            st_d_error("Create DNS socket error!");
            exit(EXIT_FAILURE);
        }

        unsigned int optval = 1;
        setsockopt(dns_socket, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(optval));//禁用NAGLE算法
        setsockopt(dns_socket, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval));
        evutil_make_socket_closeonexec(dns_socket);
        evutil_make_socket_nonblocking(dns_socket);

        struct sockaddr_in sin;
        memset(&sin, 0, sizeof(sin));
        sin.sin_family = AF_INET;
        sin.sin_addr.s_addr = htonl(0);
        sin.sin_port = htons(cltopt.dns_port); /* Port Num */

        if (bind(dns_socket, (struct sockaddr *)&sin, sizeof(sin)))
        {
            st_d_error("Bind DNS socket error!");
            exit(EXIT_FAILURE);
        }

        cltopt.dns_transid_port_map = (unsigned short*)malloc(sizeof(unsigned short) * 0xFFFF);
        if (!cltopt.dns_transid_port_map) 
        {
            st_d_error("Malloc for requestid-port failed!");
            exit(EXIT_FAILURE);
        }

        P_PORTTRANS p_trans = sc_create_trans(cltopt.dns_port); 
        if (!p_trans)
        {
            st_d_error("本地无空闲TRANS!");
            exit(EXIT_FAILURE);
        }
        p_trans->is_enc = 1;
        p_trans->l_port = cltopt.dns_port;
        encrypt_ctx_init(&p_trans->ctx_enc, p_trans->l_port, cltopt.enc_key, 1); 
        encrypt_ctx_init(&p_trans->ctx_dec, p_trans->l_port, cltopt.enc_key, 0);
        // 建立DNS UDP事件侦听
        p_trans->extra_ev = event_new(base, dns_socket, EV_READ | EV_PERSIST, 
                                      dns_client_to_proxy_cb, p_trans);


        int dns_srv_fd = socket(AF_INET, SOCK_STREAM, 0);
        if(sc_connect_srv(dns_srv_fd) != RET_YES) 
        {
            SYS_ABORT("连接服务器失败!");
        }

        sc_daemon_dns_init_srv(dns_srv_fd, p_trans->l_port, 12333);
        evutil_make_socket_nonblocking(dns_srv_fd);

        // later enabled
        //event_add(p_trans->extra_ev, NULL) != 0);

        p_trans->srv_bev = bufferevent_socket_new(base, dns_srv_fd, BEV_OPT_CLOSE_ON_FREE);
        bufferevent_setcb(p_trans->srv_bev, dns_bufferread_cb_enc, NULL, dns_bufferevent_cb, p_trans);

        st_d_print("[DAEMON]DNS代理创建侦听套接字OK %d", cltopt.dns_port); 
    }

    sc_set_eventcb_srv(srv_fd, base); 

    /**
     * Main Loop Here
     */

    event_base_loop(base, 0);
    event_base_free(base);
    st_d_print("程序退出!!!!");
    return 0;
}
/**
 * 客户端和远程服务器的交互
 */
void srv_bufferread_cb(struct bufferevent *bev, void *ptr)
{
    size_t n = 0;
    CTL_HEAD head;

    struct evbuffer *input = bufferevent_get_input(bev);
    struct evbuffer *output = bufferevent_get_output(bev);

    if ( evbuffer_remove(input, &head, CTL_HEAD_LEN) != CTL_HEAD_LEN)
    {
        st_d_print("读取数据包头%d错误!", CTL_HEAD_LEN);
        return;
    }

    if (!sd_id128_equal(head.mach_uuid, cltopt.session_uuid))
    {
        SYS_ABORT("服务端返回UUID校验失败:%s-%s",
                  SD_ID128_CONST_STR(head.mach_uuid), SD_ID128_CONST_STR(cltopt.session_uuid)); 
    }

    if (head.cmd == HD_CMD_ERROR) 
    {
        st_d_error("SERVER RETURNED ERROR!");
        exit(EXIT_SUCCESS);
    }

    if (head.cmd == HD_CMD_CONN_ACT)
    {
        P_PORTTRANS p_trans = sc_find_trans(head.extra_param); 
        if (!p_trans) 
        {
            SYS_ABORT("本地未找到连接信息:%d", head.extra_param);
        }

        bufferevent_enable(p_trans->local_bev, EV_READ|EV_WRITE);
        bufferevent_enable(p_trans->srv_bev, EV_READ|EV_WRITE); 

        st_d_print("开始传输数据:%d", head.extra_param); 
    }
    if (head.cmd == HD_CMD_END_TRANS) 
    {
        P_PORTTRANS p_trans = sc_find_trans(head.extra_param); 
        if (p_trans) 
        {
            st_d_print("EXTRA CLOSE TRANS: %d", head.extra_param);
            sc_free_trans(p_trans);
        }
    }
    if (head.cmd == HD_CMD_SS5_ACT) 
    {    
        // OK,返回给本地程序告知可以开始传输了
        // 这个绑定地址目前还没利用,主要是需要FTP这类需要带外传输另外连接端口的
        char ret_msg[10] = "\x05\x00\x00\x01\x00\x00\x00\x00\x10\x10";

        P_PORTTRANS p_trans = sc_find_trans(head.extra_param); 
        if (!p_trans) 
        {
            SYS_ABORT("本地SS5未找到连接信息:%d", head.extra_param);
        }

        bufferevent_enable(p_trans->local_bev, EV_READ|EV_WRITE);
        bufferevent_enable(p_trans->srv_bev, EV_READ|EV_WRITE); 

        bufferevent_write(p_trans->local_bev, ret_msg, sizeof(ret_msg));
        st_d_print("SS5准备传输数据:%d", head.extra_param); 

        return;
    }
    if (head.cmd == HD_CMD_CONN) 
    {
        assert(cltopt.C_TYPE == C_DAEMON);
        if (cltopt.C_TYPE == C_DAEMON) 
        {
            sc_find_daemon_portmap(head.daemonport, 1);
            P_PORTTRANS p_trans = sc_create_trans(head.extra_param); 
            p_trans->is_enc = 0;
            
            if (!p_trans)
            {
                st_d_error("本地无空闲TRANS!");
                return;
            }

            /*建立本地连接*/
            int local_fd = socket(AF_INET, SOCK_STREAM, 0);
            int reuseaddr_on = 1;
            if (setsockopt(local_fd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr_on, 
                sizeof(reuseaddr_on)) == -1)
            {
                st_d_error("Reuse socket opt faile!\n");
                return;
            }
            struct sockaddr_in  local_srv;
            local_srv.sin_family = AF_INET;
            local_srv.sin_addr.s_addr = inet_addr("127.0.0.1");
            local_srv.sin_port = htons(head.daemonport);

            if (connect(local_fd, (struct sockaddr *)&local_srv, sizeof(local_srv))) 
            {
                st_d_error("连接本地端口%d失败!", head.daemonport); 
                return;
            }
            else
            {
                st_d_print("连接本地端口%d OK!", head.daemonport); 
            }


            /*建立服务器连接*/
            int srv_fd = socket(AF_INET, SOCK_STREAM, 0);
            if(sc_connect_srv(srv_fd) != RET_YES) 
            {
                st_d_error("连接服务器失败!");
                return;
            }


            struct event_base *base = bufferevent_get_base(bev);

            evutil_make_socket_nonblocking(local_fd);
            struct bufferevent *local_bev = 
                bufferevent_socket_new(base, local_fd, BEV_OPT_CLOSE_ON_FREE);
            bufferevent_setcb(local_bev, bufferread_cb, NULL, bufferevent_cb, p_trans);
            //bufferevent_enable(local_bev, EV_READ|EV_WRITE);

            evutil_make_socket_nonblocking(srv_fd); 
            struct bufferevent *srv_bev = 
                bufferevent_socket_new(base, srv_fd, BEV_OPT_CLOSE_ON_FREE);
            bufferevent_setcb(srv_bev, bufferread_cb, NULL, bufferevent_cb, p_trans);
            //bufferevent_enable(srv_bev, EV_READ|EV_WRITE);


            p_trans->l_port = head.extra_param;
            p_trans->local_bev = local_bev;
            p_trans->srv_bev = srv_bev;

            /* 向服务器报告连接请求 */
            // 必须要发送CONN包,触发这个连接转移到线程池处理  
            CTL_HEAD ret_head;
            memset(&ret_head, 0, CTL_HEAD_LEN);
            ret_head.cmd = HD_CMD_CONN;
            ret_head.extra_param = p_trans->l_port; 
            ret_head.mach_uuid = cltopt.session_uuid;
            ret_head.direct = DAEMON_USR; 

            bufferevent_write(srv_bev, &ret_head, CTL_HEAD_LEN);

            st_d_print("DAEMON端准备OK!");
        }
    }

}