mirror of
https://gitlab.com/openconnect/ocserv.git
synced 2026-10-12 01:26:48 +08:00
main: use two sockets to communicate with sec-mod
That allows to have a reliable synchronous socket, and a socket where messages are sent and received asynchronously.
This commit is contained in:
+16
-3
@@ -595,11 +595,14 @@ int handle_commands(main_server_st * s, struct proc_st *proc)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Returns a file descriptor to be used for communication with sec-mod
|
||||
/* Returns two file descriptors to be used for communication with sec-mod.
|
||||
* The sync_fd is used by main to send synchronous commands- commands which
|
||||
* expect a reply immediately.
|
||||
*/
|
||||
int run_sec_mod(main_server_st * s)
|
||||
int run_sec_mod(main_server_st * s, int *sync_fd)
|
||||
{
|
||||
int e, fd[2], ret;
|
||||
int sfd[2];
|
||||
pid_t pid;
|
||||
const char *p;
|
||||
|
||||
@@ -621,6 +624,12 @@ int run_sec_mod(main_server_st * s)
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ret = socketpair(AF_UNIX, SOCK_STREAM, 0, sfd);
|
||||
if (ret < 0) {
|
||||
mslog(s, NULL, LOG_ERR, "error creating sec-mod sync command socket");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
pid = fork();
|
||||
if (pid == 0) { /* child */
|
||||
clear_lists(s);
|
||||
@@ -633,13 +642,17 @@ int run_sec_mod(main_server_st * s)
|
||||
#endif
|
||||
setproctitle(PACKAGE_NAME "-secmod");
|
||||
close(fd[1]);
|
||||
close(sfd[1]);
|
||||
set_cloexec_flag (fd[0], 1);
|
||||
sec_mod_server(s->main_pool, s->perm_config, p, s->cookie_key, fd[0]);
|
||||
set_cloexec_flag (sfd[0], 1);
|
||||
sec_mod_server(s->main_pool, s->perm_config, p, s->cookie_key, fd[0], sfd[0]);
|
||||
exit(0);
|
||||
} else if (pid > 0) { /* parent */
|
||||
close(fd[0]);
|
||||
s->sec_mod_pid = pid;
|
||||
set_cloexec_flag (fd[1], 1);
|
||||
set_cloexec_flag (sfd[1], 1);
|
||||
*sync_fd = sfd[1];
|
||||
return fd[1];
|
||||
} else {
|
||||
e = errno;
|
||||
|
||||
@@ -209,7 +209,7 @@ int session_open(main_server_st * s, struct proc_st *proc, const uint8_t *cookie
|
||||
|
||||
mslog(s, proc, LOG_DEBUG, "sending msg %s to sec-mod", cmd_request_to_str(SM_CMD_AUTH_SESSION_OPEN));
|
||||
|
||||
ret = send_msg(proc, s->sec_mod_fd, SM_CMD_AUTH_SESSION_OPEN,
|
||||
ret = send_msg(proc, s->sec_mod_fd_sync, SM_CMD_AUTH_SESSION_OPEN,
|
||||
&ireq, (pack_size_func)sec_auth_session_msg__get_packed_size,
|
||||
(pack_func)sec_auth_session_msg__pack);
|
||||
if (ret < 0) {
|
||||
@@ -218,7 +218,7 @@ int session_open(main_server_st * s, struct proc_st *proc, const uint8_t *cookie
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = recv_msg(proc, s->sec_mod_fd, SM_CMD_AUTH_SESSION_REPLY,
|
||||
ret = recv_msg(proc, s->sec_mod_fd_sync, SM_CMD_AUTH_SESSION_REPLY,
|
||||
(void *)&msg, (unpack_func) sec_auth_session_reply_msg__unpack, MAIN_SEC_MOD_TIMEOUT);
|
||||
if (ret < 0) {
|
||||
e = errno;
|
||||
@@ -340,7 +340,7 @@ int session_close(main_server_st * s, struct proc_st *proc)
|
||||
|
||||
mslog(s, proc, LOG_DEBUG, "sending msg %s to sec-mod", cmd_request_to_str(SM_CMD_AUTH_SESSION_CLOSE));
|
||||
|
||||
ret = send_msg(proc, s->sec_mod_fd, SM_CMD_AUTH_SESSION_CLOSE,
|
||||
ret = send_msg(proc, s->sec_mod_fd_sync, SM_CMD_AUTH_SESSION_CLOSE,
|
||||
&ireq, (pack_size_func)sec_auth_session_msg__get_packed_size,
|
||||
(pack_func)sec_auth_session_msg__pack);
|
||||
if (ret < 0) {
|
||||
@@ -349,7 +349,7 @@ int session_close(main_server_st * s, struct proc_st *proc)
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = recv_msg(proc, s->sec_mod_fd, SM_CMD_AUTH_CLI_STATS,
|
||||
ret = recv_msg(proc, s->sec_mod_fd_sync, SM_CMD_AUTH_CLI_STATS,
|
||||
(void *)&msg, (unpack_func) cli_stats_msg__unpack, MAIN_SEC_MOD_TIMEOUT);
|
||||
if (ret < 0) {
|
||||
e = errno;
|
||||
|
||||
+1
-1
@@ -1030,7 +1030,7 @@ int main(int argc, char** argv)
|
||||
|
||||
write_pid_file();
|
||||
|
||||
s->sec_mod_fd = run_sec_mod(s);
|
||||
s->sec_mod_fd = run_sec_mod(s, &s->sec_mod_fd_sync);
|
||||
|
||||
ret = ctl_handler_init(s);
|
||||
if (ret < 0) {
|
||||
|
||||
+3
-2
@@ -204,7 +204,8 @@ typedef struct main_server_st {
|
||||
#else
|
||||
int ctl_fd;
|
||||
#endif
|
||||
int sec_mod_fd;
|
||||
int sec_mod_fd; /* messages are sent and received async */
|
||||
int sec_mod_fd_sync; /* messages are received in a sync order (ping-pong) */
|
||||
void *main_pool; /* talloc main pool */
|
||||
} main_server_st;
|
||||
|
||||
@@ -263,7 +264,7 @@ int handle_auth_cookie_req(main_server_st* s, struct proc_st* proc,
|
||||
int check_multiple_users(main_server_st *s, struct proc_st* proc);
|
||||
int handle_script_exit(main_server_st *s, struct proc_st* proc, int code);
|
||||
|
||||
int run_sec_mod(main_server_st * s);
|
||||
int run_sec_mod(main_server_st * s, int *sync_fd);
|
||||
|
||||
struct proc_st *new_proc(main_server_st * s, pid_t pid, int cmd_fd,
|
||||
struct sockaddr_storage *remote_addr, socklen_t remote_addr_len,
|
||||
|
||||
+18
-18
@@ -367,7 +367,7 @@ static void stats_add_to(stats_st *dst, stats_st *src1, stats_st *src2)
|
||||
}
|
||||
|
||||
static
|
||||
int send_failed_session_open_reply(sec_mod_st *sec)
|
||||
int send_failed_session_open_reply(sec_mod_st *sec, int fd)
|
||||
{
|
||||
SecAuthSessionReplyMsg rep = SEC_AUTH_SESSION_REPLY_MSG__INIT;
|
||||
void *lpool;
|
||||
@@ -380,7 +380,7 @@ int send_failed_session_open_reply(sec_mod_st *sec)
|
||||
return ERR_BAD_COMMAND;
|
||||
}
|
||||
|
||||
ret = send_msg(lpool, sec->cmd_fd, SM_CMD_AUTH_SESSION_REPLY, &rep,
|
||||
ret = send_msg(lpool, fd, SM_CMD_AUTH_SESSION_REPLY, &rep,
|
||||
(pack_size_func) sec_auth_session_reply_msg__get_packed_size,
|
||||
(pack_func) sec_auth_session_reply_msg__pack);
|
||||
if (ret < 0) {
|
||||
@@ -393,7 +393,7 @@ int send_failed_session_open_reply(sec_mod_st *sec)
|
||||
}
|
||||
|
||||
static
|
||||
int handle_sec_auth_session_open(sec_mod_st *sec, const SecAuthSessionMsg *req)
|
||||
int handle_sec_auth_session_open(sec_mod_st *sec, int fd, const SecAuthSessionMsg *req)
|
||||
{
|
||||
client_entry_st *e;
|
||||
void *lpool;
|
||||
@@ -403,7 +403,7 @@ int handle_sec_auth_session_open(sec_mod_st *sec, const SecAuthSessionMsg *req)
|
||||
if (req->sid.len != SID_SIZE) {
|
||||
seclog(sec, LOG_ERR, "auth session open but with illegal sid size (%d)!",
|
||||
(int)req->sid.len);
|
||||
return send_failed_session_open_reply(sec);
|
||||
return send_failed_session_open_reply(sec, fd);
|
||||
}
|
||||
|
||||
e = find_client_entry(sec, req->sid.data);
|
||||
@@ -411,25 +411,25 @@ int handle_sec_auth_session_open(sec_mod_st *sec, const SecAuthSessionMsg *req)
|
||||
char tmp[BASE64_LENGTH(SID_SIZE) + 1];
|
||||
base64_encode((char *)req->sid.data, req->sid.len, (char *)tmp, sizeof(tmp));
|
||||
seclog(sec, LOG_INFO, "session open but with non-existing SID: %s!", tmp);
|
||||
return send_failed_session_open_reply(sec);
|
||||
return send_failed_session_open_reply(sec, fd);
|
||||
}
|
||||
|
||||
if (e->status != PS_AUTH_COMPLETED) {
|
||||
seclog(sec, LOG_ERR, "session open received in unauthenticated client %s "SESSION_STR"!", e->auth_info.username, e->auth_info.psid);
|
||||
return send_failed_session_open_reply(sec);
|
||||
return send_failed_session_open_reply(sec, fd);
|
||||
}
|
||||
|
||||
if (e->time != -1 && time(0) > e->time + sec->config->cookie_timeout) {
|
||||
seclog(sec, LOG_ERR, "session expired; denied session for user '%s' "SESSION_STR, e->auth_info.username, e->auth_info.psid);
|
||||
e->status = PS_AUTH_FAILED;
|
||||
return send_failed_session_open_reply(sec);
|
||||
return send_failed_session_open_reply(sec, fd);
|
||||
}
|
||||
|
||||
if (req->has_cookie == 0 || (req->cookie.len != e->cookie_size) ||
|
||||
memcmp(req->cookie.data, e->cookie, e->cookie_size) != 0) {
|
||||
seclog(sec, LOG_ERR, "cookie error; denied session for user '%s' "SESSION_STR, e->auth_info.username, e->auth_info.psid);
|
||||
e->status = PS_AUTH_FAILED;
|
||||
return send_failed_session_open_reply(sec);
|
||||
return send_failed_session_open_reply(sec, fd);
|
||||
}
|
||||
|
||||
if (req->ipv4)
|
||||
@@ -442,7 +442,7 @@ int handle_sec_auth_session_open(sec_mod_st *sec, const SecAuthSessionMsg *req)
|
||||
if (ret < 0) {
|
||||
e->status = PS_AUTH_FAILED;
|
||||
seclog(sec, LOG_INFO, "denied session for user '%s' "SESSION_STR, e->auth_info.username, e->auth_info.psid);
|
||||
return send_failed_session_open_reply(sec);
|
||||
return send_failed_session_open_reply(sec, fd);
|
||||
} else {
|
||||
e->session_is_open = 1;
|
||||
}
|
||||
@@ -460,7 +460,7 @@ int handle_sec_auth_session_open(sec_mod_st *sec, const SecAuthSessionMsg *req)
|
||||
if (ret < 0) {
|
||||
seclog(sec, LOG_ERR, "error reading additional configuration for '%s' "SESSION_STR, e->auth_info.username, e->auth_info.psid);
|
||||
talloc_free(lpool);
|
||||
return send_failed_session_open_reply(sec);
|
||||
return send_failed_session_open_reply(sec, fd);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -470,7 +470,7 @@ int handle_sec_auth_session_open(sec_mod_st *sec, const SecAuthSessionMsg *req)
|
||||
rep.has_interim_update_secs = 1;
|
||||
}
|
||||
|
||||
ret = send_msg(lpool, sec->cmd_fd, SM_CMD_AUTH_SESSION_REPLY, &rep,
|
||||
ret = send_msg(lpool, fd, SM_CMD_AUTH_SESSION_REPLY, &rep,
|
||||
(pack_size_func) sec_auth_session_reply_msg__get_packed_size,
|
||||
(pack_func) sec_auth_session_reply_msg__pack);
|
||||
if (ret < 0) {
|
||||
@@ -487,7 +487,7 @@ int handle_sec_auth_session_open(sec_mod_st *sec, const SecAuthSessionMsg *req)
|
||||
}
|
||||
|
||||
static
|
||||
int handle_sec_auth_session_close(sec_mod_st *sec, const SecAuthSessionMsg *req)
|
||||
int handle_sec_auth_session_close(sec_mod_st *sec, int fd, const SecAuthSessionMsg *req)
|
||||
{
|
||||
client_entry_st *e;
|
||||
int ret;
|
||||
@@ -504,14 +504,14 @@ int handle_sec_auth_session_close(sec_mod_st *sec, const SecAuthSessionMsg *req)
|
||||
char tmp[BASE64_LENGTH(SID_SIZE) + 1];
|
||||
base64_encode((char *)req->sid.data, req->sid.len, (char *)tmp, sizeof(tmp));
|
||||
seclog(sec, LOG_INFO, "session close but with non-existing SID: %s", tmp);
|
||||
return send_msg(e, sec->cmd_fd, SM_CMD_AUTH_CLI_STATS, &rep,
|
||||
return send_msg(e, fd, SM_CMD_AUTH_CLI_STATS, &rep,
|
||||
(pack_size_func) cli_stats_msg__get_packed_size,
|
||||
(pack_func) cli_stats_msg__pack);
|
||||
}
|
||||
|
||||
if (e->status < PS_AUTH_COMPLETED) {
|
||||
seclog(sec, LOG_DEBUG, "session close received in unauthenticated client %s "SESSION_STR"!", e->auth_info.username, e->auth_info.psid);
|
||||
return send_msg(e, sec->cmd_fd, SM_CMD_AUTH_CLI_STATS, &rep,
|
||||
return send_msg(e, fd, SM_CMD_AUTH_CLI_STATS, &rep,
|
||||
(pack_size_func) cli_stats_msg__get_packed_size,
|
||||
(pack_func) cli_stats_msg__pack);
|
||||
}
|
||||
@@ -533,7 +533,7 @@ int handle_sec_auth_session_close(sec_mod_st *sec, const SecAuthSessionMsg *req)
|
||||
rep.has_secmod_client_entries = 1;
|
||||
rep.secmod_client_entries = sec_mod_client_db_elems(sec);
|
||||
|
||||
ret = send_msg(e, sec->cmd_fd, SM_CMD_AUTH_CLI_STATS, &rep,
|
||||
ret = send_msg(e, fd, SM_CMD_AUTH_CLI_STATS, &rep,
|
||||
(pack_size_func) cli_stats_msg__get_packed_size,
|
||||
(pack_func) cli_stats_msg__pack);
|
||||
if (ret < 0) {
|
||||
@@ -550,13 +550,13 @@ int handle_sec_auth_session_close(sec_mod_st *sec, const SecAuthSessionMsg *req)
|
||||
}
|
||||
|
||||
|
||||
int handle_sec_auth_session_cmd(sec_mod_st *sec, const SecAuthSessionMsg *req,
|
||||
int handle_sec_auth_session_cmd(sec_mod_st *sec, int fd, const SecAuthSessionMsg *req,
|
||||
unsigned cmd)
|
||||
{
|
||||
if (cmd == SM_CMD_AUTH_SESSION_OPEN)
|
||||
return handle_sec_auth_session_open(sec, req);
|
||||
return handle_sec_auth_session_open(sec, fd, req);
|
||||
else
|
||||
return handle_sec_auth_session_close(sec, req);
|
||||
return handle_sec_auth_session_close(sec, fd, req);
|
||||
}
|
||||
|
||||
void handle_sec_auth_ban_ip_reply(sec_mod_st *sec, const BanIpReplyMsg *msg)
|
||||
|
||||
+20
-10
@@ -304,7 +304,7 @@ int process_packet(void *pool, int cfd, pid_t pid, sec_mod_st * sec, cmd_request
|
||||
}
|
||||
|
||||
static
|
||||
int process_packet_from_main(void *pool, sec_mod_st * sec, cmd_request_t cmd,
|
||||
int process_packet_from_main(void *pool, int fd, sec_mod_st * sec, cmd_request_t cmd,
|
||||
uint8_t * buffer, size_t buffer_size)
|
||||
{
|
||||
gnutls_datum_t data;
|
||||
@@ -345,7 +345,7 @@ int process_packet_from_main(void *pool, sec_mod_st * sec, cmd_request_t cmd,
|
||||
return ERR_BAD_COMMAND;
|
||||
}
|
||||
|
||||
ret = handle_sec_auth_session_cmd(sec, msg, cmd);
|
||||
ret = handle_sec_auth_session_cmd(sec, fd, msg, cmd);
|
||||
sec_auth_session_msg__free_unpacked(msg, &pa);
|
||||
|
||||
return ret;
|
||||
@@ -404,7 +404,7 @@ static void check_other_work(sec_mod_st *sec)
|
||||
}
|
||||
|
||||
static
|
||||
int serve_request_main(sec_mod_st *sec, uint8_t *buffer, unsigned buffer_size)
|
||||
int serve_request_main(sec_mod_st *sec, int fd, uint8_t *buffer, unsigned buffer_size)
|
||||
{
|
||||
int ret, e;
|
||||
unsigned cmd, length;
|
||||
@@ -412,7 +412,7 @@ int serve_request_main(sec_mod_st *sec, uint8_t *buffer, unsigned buffer_size)
|
||||
void *pool = buffer;
|
||||
|
||||
/* read request */
|
||||
ret = force_read_timeout(sec->cmd_fd, buffer, 3, MAIN_SEC_MOD_TIMEOUT);
|
||||
ret = force_read_timeout(fd, buffer, 3, MAIN_SEC_MOD_TIMEOUT);
|
||||
if (ret == 0)
|
||||
goto leave;
|
||||
else if (ret < 3) {
|
||||
@@ -441,7 +441,7 @@ int serve_request_main(sec_mod_st *sec, uint8_t *buffer, unsigned buffer_size)
|
||||
}
|
||||
|
||||
/* read the body */
|
||||
ret = force_read_timeout(sec->cmd_fd, buffer, length, MAIN_SEC_MOD_TIMEOUT);
|
||||
ret = force_read_timeout(fd, buffer, length, MAIN_SEC_MOD_TIMEOUT);
|
||||
if (ret < 0) {
|
||||
e = errno;
|
||||
seclog(sec, LOG_ERR, "error receiving msg body of cmd %u with length %u: %s",
|
||||
@@ -450,7 +450,7 @@ int serve_request_main(sec_mod_st *sec, uint8_t *buffer, unsigned buffer_size)
|
||||
goto leave;
|
||||
}
|
||||
|
||||
ret = process_packet_from_main(pool, sec, cmd, buffer, ret);
|
||||
ret = process_packet_from_main(pool, fd, sec, cmd, buffer, ret);
|
||||
if (ret < 0) {
|
||||
seclog(sec, LOG_ERR, "error processing data for '%s' command (%d)", cmd_request_to_str(cmd), ret);
|
||||
}
|
||||
@@ -512,6 +512,7 @@ int serve_request(sec_mod_st *sec, int cfd, pid_t pid, uint8_t *buffer, unsigned
|
||||
* @config: server configuration
|
||||
* @socket_file: the name of the socket
|
||||
* @cmd_fd: socket to exchange commands with main
|
||||
* @cmd_fd_sync: socket to received sync commands from main
|
||||
*
|
||||
* This is the main part of the security module.
|
||||
* It creates the unix domain socket identified by @socket_file
|
||||
@@ -537,11 +538,11 @@ int serve_request(sec_mod_st *sec, int cfd, pid_t pid, uint8_t *buffer, unsigned
|
||||
* key operations.
|
||||
*/
|
||||
void sec_mod_server(void *main_pool, struct perm_cfg_st *perm_config, const char *socket_file,
|
||||
uint8_t cookie_key[COOKIE_KEY_SIZE], int cmd_fd)
|
||||
uint8_t cookie_key[COOKIE_KEY_SIZE], int cmd_fd, int cmd_fd_sync)
|
||||
{
|
||||
struct sockaddr_un sa;
|
||||
socklen_t sa_len;
|
||||
int cfd, ret, e, n;
|
||||
int cfd, ret, e, n, tfd;
|
||||
unsigned i, buffer_size;
|
||||
uid_t uid;
|
||||
uint8_t *buffer;
|
||||
@@ -600,6 +601,7 @@ void sec_mod_server(void *main_pool, struct perm_cfg_st *perm_config, const char
|
||||
|
||||
sec_auth_init(sec, perm_config);
|
||||
sec->cmd_fd = cmd_fd;
|
||||
sec->cmd_fd_sync = cmd_fd_sync;
|
||||
|
||||
#ifdef HAVE_PKCS11
|
||||
ret = gnutls_pkcs11_reinit();
|
||||
@@ -706,6 +708,9 @@ void sec_mod_server(void *main_pool, struct perm_cfg_st *perm_config, const char
|
||||
FD_SET(cmd_fd, &rd_set);
|
||||
n = MAX(n, cmd_fd);
|
||||
|
||||
FD_SET(cmd_fd_sync, &rd_set);
|
||||
n = MAX(n, cmd_fd_sync);
|
||||
|
||||
FD_SET(sd, &rd_set);
|
||||
n = MAX(n, sd);
|
||||
|
||||
@@ -726,13 +731,18 @@ void sec_mod_server(void *main_pool, struct perm_cfg_st *perm_config, const char
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (FD_ISSET(cmd_fd, &rd_set)) {
|
||||
if (FD_ISSET(cmd_fd, &rd_set) || FD_ISSET(cmd_fd_sync, &rd_set)) {
|
||||
if (FD_ISSET(cmd_fd_sync, &rd_set))
|
||||
tfd = cmd_fd_sync;
|
||||
else
|
||||
tfd = cmd_fd;
|
||||
|
||||
buffer_size = MAX_MSG_SIZE;
|
||||
buffer = talloc_size(sec, buffer_size);
|
||||
if (buffer == NULL) {
|
||||
seclog(sec, LOG_ERR, "error in memory allocation");
|
||||
} else {
|
||||
ret = serve_request_main(sec, buffer, buffer_size);
|
||||
ret = serve_request_main(sec, tfd, buffer, buffer_size);
|
||||
if (ret < 0 && ret == ERR_BAD_COMMAND) {
|
||||
seclog(sec, LOG_ERR, "error processing command from main");
|
||||
exit(1);
|
||||
|
||||
+3
-2
@@ -38,6 +38,7 @@ typedef struct sec_mod_st {
|
||||
unsigned key_size;
|
||||
struct htable *client_db;
|
||||
int cmd_fd;
|
||||
int cmd_fd_sync;
|
||||
|
||||
struct config_mod_st *config_module;
|
||||
} sec_mod_st;
|
||||
@@ -127,11 +128,11 @@ void sec_auth_init(sec_mod_st *sec, struct perm_cfg_st *config);
|
||||
void handle_sec_auth_ban_ip_reply(sec_mod_st *sec, const BanIpReplyMsg *msg);
|
||||
int handle_sec_auth_init(int cfd, sec_mod_st *sec, const SecAuthInitMsg * req, pid_t pid);
|
||||
int handle_sec_auth_cont(int cfd, sec_mod_st *sec, const SecAuthContMsg * req);
|
||||
int handle_sec_auth_session_cmd(sec_mod_st *sec, const SecAuthSessionMsg *req, unsigned cmd);
|
||||
int handle_sec_auth_session_cmd(sec_mod_st *sec, int fd, const SecAuthSessionMsg *req, unsigned cmd);
|
||||
int handle_sec_auth_stats_cmd(sec_mod_st * sec, const CliStatsMsg * req);
|
||||
void sec_auth_user_deinit(sec_mod_st * sec, client_entry_st * e);
|
||||
|
||||
void sec_mod_server(void *main_pool, struct perm_cfg_st *config, const char *socket_file,
|
||||
uint8_t cookie_key[COOKIE_KEY_SIZE], int cmd_fd);
|
||||
uint8_t cookie_key[COOKIE_KEY_SIZE], int cmd_fd, int cmd_fd_sync);
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user