📄 ipcsocket.c
字号:
temp_ch->bytes_remaining = 0; temp_ch->should_send_blocking = FALSE; temp_ch->send_queue = socket_queue_new(); temp_ch->recv_queue = socket_queue_new(); temp_ch->pool = NULL; temp_ch->high_flow_mark = temp_ch->send_queue->max_qlen; temp_ch->low_flow_mark = -1; return temp_ch; }struct IPC_CHANNEL * socket_server_channel_new(int sockfd){ struct IPC_CHANNEL * temp_ch; struct SOCKET_CH_PRIVATE* conn_info; int flags; temp_ch = g_new(struct IPC_CHANNEL, 1); if (temp_ch == NULL){ cl_log(LOG_ERR, "socket_server_channel_new:" " allocating memory for channel failed"); return NULL; } memset(temp_ch, 0, sizeof(struct IPC_CHANNEL)); conn_info = g_new(struct SOCKET_CH_PRIVATE, 1); flags = fcntl(sockfd, F_GETFL, O_NONBLOCK); if (flags == -1) { cl_perror("socket_server_channel_new: cannot read file descriptor flags"); g_free(conn_info); conn_info = NULL; g_free(temp_ch); return NULL; } flags |= O_NONBLOCK; if (fcntl(sockfd, F_SETFL, flags) < 0) { cl_perror("socket_server_channel_new: cannot set O_NONBLOCK"); g_free(conn_info); conn_info = NULL; g_free(temp_ch); return NULL; } conn_info->s = sockfd; conn_info->remaining_data = 0; conn_info->buf_msg = NULL; conn_info->peer_addr = NULL; strcpy(conn_info->path_name, "?");#ifdef DEBUG cl_log(LOG_INFO, "Initializing server socket %d to DISCONNECT", sockfd);#endif temp_ch->ch_status = IPC_DISCONNECT; temp_ch->ch_private = (void*) conn_info; temp_ch->ops = (struct IPC_OPS *)&socket_ops; temp_ch->msgpad = sizeof(struct SOCKET_MSG_HEAD); temp_ch->bytes_remaining = 0; temp_ch->should_send_blocking = FALSE; temp_ch->send_queue = socket_queue_new(); temp_ch->recv_queue = socket_queue_new(); temp_ch->pool = NULL; temp_ch->high_flow_mark = temp_ch->send_queue->max_qlen; temp_ch->low_flow_mark = -1; return temp_ch; }/* * Create a new pair of pre-connected IPC channels similar to * the result of pipe(2), or socketpair(2). */intipc_channel_pair(IPC_Channel* channels[2]){ int sockets[2]; int rc; int j; if ((rc = socketpair(AF_LOCAL, SOCK_STREAM, 0, sockets)) < 0) { return IPC_FAIL; } if ((channels[0] = socket_server_channel_new(sockets[0])) == NULL) { close(sockets[0]); close(sockets[1]); return IPC_FAIL; } if ((channels[1] = socket_server_channel_new(sockets[1])) == NULL) { close(sockets[0]); close(sockets[1]); channels[0]->ops->destroy(channels[0]); return IPC_FAIL; } for (j=0; j < 2; ++j) { struct SOCKET_CH_PRIVATE* p = channels[j]->ch_private; channels[j]->ch_status = IPC_CONNECT; /* Valid, but not terribly meaningful */ channels[j]->farside_pid = getpid(); strncpy(p->path_name, "[socketpair]", sizeof(p->path_name)); } return IPC_OK; }/* * create a new ipc message whose msg_body's length is msg_len. * * parameters : * msg_len (IN) the length of this message body in this message. * * return : * the pointer to the new message or NULL if the message can't be created. */static struct IPC_MESSAGE*socket_message_new(struct IPC_CHANNEL *ch, int msg_len){ struct IPC_MESSAGE * temp_msg; temp_msg = g_new(struct IPC_MESSAGE, 1); memset(temp_msg, 0, sizeof(struct IPC_MESSAGE)); if (msg_len != 0){ temp_msg->msg_buf = g_malloc(msg_len + ch->msgpad); temp_msg->msg_body = (char*)temp_msg->msg_buf + ch->msgpad; }else{ temp_msg->msg_buf = temp_msg->msg_body = NULL; } temp_msg->msg_len = msg_len; temp_msg->msg_private = NULL; temp_msg->msg_ch = ch; temp_msg->msg_done = socket_free_message; return temp_msg;}/* brief free the memory space allocated to msg and destroy msg. */voidsocket_free_message(struct IPC_MESSAGE * msg) {#if 0 memset(msg->msg_body, 0xff, msg->msg_len);#endif if (msg->msg_buf){ g_free(msg->msg_buf); }else { g_free(msg->msg_body); }#if 0 memset(msg, 0xff, sizeof(*msg));#endif g_free((void *)msg);}/*********************************************************************** * * IPC authentication schemes... More machine dependent than * we'd like, but don't know any better way... * ***********************************************************************//*********************************************************************** * SO_PEERCRED VERSION... (Linux) ***********************************************************************/#ifdef USE_SO_PEERCRED/* verify the authentication information. */static int socket_verify_auth(struct IPC_CHANNEL* ch, struct IPC_AUTH * auth_info){ struct SOCKET_CH_PRIVATE * conn_info; int ret = IPC_FAIL; struct ucred cred; socklen_t n = sizeof(cred); if (ch == NULL || ch->ch_private == NULL) { return IPC_FAIL; } if (auth_info == NULL || (auth_info->uid == NULL && auth_info->gid == NULL)) { return IPC_OK; /* no restriction for authentication */ } /* Get the credential information for our peer */ conn_info = (struct SOCKET_CH_PRIVATE *) ch->ch_private; if (getsockopt(conn_info->s, SOL_SOCKET, SO_PEERCRED, &cred, &n) != 0 || (size_t)n != sizeof(cred)) { return IPC_FAIL; }#if 1 cl_log(LOG_DEBUG, "SO_PEERCRED returned [%d, (%ld:%ld)]" , cred.pid, (long)cred.uid, (long)cred.uid); cl_log(LOG_DEBUG, "Verifying authentication: cred.uid=%d cred.gid=%d" , cred.uid, cred.gid); cl_log(LOG_DEBUG, "Verifying authentication: uidptr=0x%lx gidptr=0x%lx" , (unsigned long)auth_info->uid , (unsigned long)auth_info->gid);#endif /* verify the credential information. */ if ( auth_info->uid && (g_hash_table_lookup(auth_info->uid , GUINT_TO_POINTER((guint)cred.uid)) != NULL)) { ret = IPC_OK; }else if (auth_info->gid && (g_hash_table_lookup(auth_info->gid , GUINT_TO_POINTER((guint)cred.gid)) != NULL)) { ret = IPC_OK; } return ret;}/* get farside pid for our peer process */pid_tsocket_get_farside_pid(int sockfd){ socklen_t n; struct ucred *cred; pid_t f_pid; /* Get the credential information from peer */ n = sizeof(struct ucred); cred = g_new(struct ucred, 1); if (getsockopt(sockfd, SOL_SOCKET, SO_PEERCRED, cred, &n) != 0) { g_free(cred); return -1; } f_pid = cred->pid; g_free(cred); return f_pid;}#endif /* SO_PEERCRED version */#ifdef USE_GETPEEREID/* * This is implemented in OpenBSD and FreeBSD. * * It's not a half-bad interface... * * This should probably be our standard way of doing it, and put it * as a replacement library. That would simplify things... */static int socket_verify_auth(struct IPC_CHANNEL* ch, struct IPC_AUTH * auth_info){ struct SOCKET_CH_PRIVATE *conn_info; uid_t euid; gid_t egid; int ret = IPC_FAIL; if (auth_info == NULL || (auth_info->uid == NULL && auth_info->gid == NULL)) { return IPC_OK; /* no restriction for authentication */ } conn_info = (struct SOCKET_CH_PRIVATE *) ch->ch_private; if (getpeereid(conn_info->s, &euid, &egid) < 0) { cl_perror("getpeereid() failure"); return IPC_FAIL; } /* Check credentials against authorization information */ if ( auth_info->uid && (g_hash_table_lookup(auth_info->uid , GUINT_TO_POINTER((guint)euid)) != NULL)) { ret = IPC_OK; }else if (auth_info->gid && (g_hash_table_lookup(auth_info->gid , GUINT_TO_POINTER((guint)egid)) != NULL)) { ret = IPC_OK; } return ret;}pid_tsocket_get_farside_pid(int sock){ return -1;}#endif /* USE_GETPEEREID *//*********************************************************************** * SCM_CREDS VERSION... (*BSD systems) ***********************************************************************/#ifdef USE_SCM_CREDS/* FIXME! Need to implement SCM_CREDS mechanism for BSD-based systems * This isn't an emergency, but should be done in the future... * Hint: * Postgresql does both types of authentication... * see src/backend/libpq/auth.c * Not clear its SO_PEERCRED implementation works though ;-) *//* Done.... Haven't tested yet. */static int socket_verify_auth(struct IPC_CHANNEL* ch, struct IPC_AUTH * auth_info){ struct msghdr msg; /* Credentials structure */#define EXTRASPACE 0#ifdef HAVE_STRUCT_CMSGCRED /* FreeBSD */ typedef struct cmsgcred Cred;# define crRuid cmcred_uid# define crEuid cmcred_euid# define crRgid cmcred_gid# define crEgid cmcred_groups[0] /* Best guess */# define crpid cmcred_pid# define crngrp cmcred_ngroups# define crgrps cmcred_groups#elif HAVE_STRUCT_FCRED /* Stevens' book */ typedef struct fcred Cred;# define crRuid fc_uid# define crRgid fc_rgid# define crEgid fc_gid# define crngrp fc_ngroups# define crgrps fc_groups#elif HAVE_STRUCT_SOCKCRED /* NetBSD */ typedef struct sockcred Cred;# define crRuid sc_uid# define crEuid sc_euid# define crRgid sc_gid# define crEgid sc_egid# define crngrp sc_ngroups# define crgrps sc_groups# define EXTRASPACE SOCKCREDSIZE(ngroups)#elif HAVE_STRUCT_CRED typedef struct cred Cred;#define cruid c_uid#elif HAVE_STRUCT_UCRED typedef struct ucred Cred; /* reuse this define for the moment */# if HAVE_STRUCT_UCRED_DARWIN# define crEuid cr_uid# define crEgid cr_groups[0] /* Best guess */# define crgrps cr_groups# define crngrp cr_ngroups# else# define crEuid c_uid# define crEgid c_gid# endif#else# error "No credential type found!"#endif struct SOCKET_CH_PRIVATE *conn_info; int ret = IPC_OK; char buf; /* Compute size without padding */ #define CMSGSIZE (sizeof(struct cmsghdr)+(sizeof(Cred))+EXTRASPACE) union { char mem[CMSGSIZE]; struct cmsghdr hdr; Cred credu; }cmsgmem; Cred cred; /* Point to start of first structure */ struct cmsghdr *cmsg = &cmsgmem.hdr; if (auth_info == NULL || (auth_info->uid == NULL && auth_info->gid == NULL)) { return IPC_OK; /* no restriction for authentication */ } conn_info = (struct SOCKET_CH_PRIVATE *) ch->ch_private; memset(&msg, 0, sizeof(msg)); msg.msg_iov = g_new(struct iovec, 1); msg.msg_iovlen = 1; msg.msg_control = (char *) cmsg; msg.msg_controllen = CMSGSIZE; memset(cmsg, 0, sizeof(cmsgmem)); /* * The one character which is received here is not meaningful; its * purpose is only to make sure that recvmsg() blocks long enough for * the other side to send its credentials. */ msg.msg_iov->iov_base = &buf; msg.msg_iov->iov_len = 1; if (recvmsg(conn_info->s, &msg, 0) < 0 || cmsg->cmsg_len < CMSGSIZE || cmsg->cmsg_type != SCM_CREDS) { cl_perror("can't get credential information from peer"); return IPC_FAIL; } /* Avoid alignment issues - just copy it! */ memcpy(&cred, CMSG_DATA(cmsg), sizeof(cred)); if ( auth_info->uid && g_hash_table_lookup(auth_info->uid, &(cred.crEuid)) == NULL) { ret = IPC_FAIL; } if ( auth_info->gid && g_hash_table_lookup(auth_info->gid, &(cred.crEgid)) == NULL) { ret = IPC_FAIL; } return ret;}/* * FIXME! Need to implement SCM_CREDS mechanism for BSD-based systems * this is similar to the SCM_CREDS mechanism for verify_auth() function. * here we just want to get the pid of the other side from the credential * information. */pid_tsocket_get_farside_pid(int sock){ /* FIXME! */ return -1;}#endif /* SCM_CREDS version *//*********************************************************************** * Bind/Stat VERSION... (Supported on OSX/Darwin and 4.3+BSD at least...) * * This is for use on systems such as OSX-Darwin and maybe Solaris where * none of the other options are available. * * This implementation has been adapted from "Advanced Programming * in the Unix Environment", Section 15.5.2, by W. Richard Stevens. * */#ifdef USE_BINDSTAT_CREDSstatic int socket_verify_auth(struct IPC_CHANNEL* ch, struct IPC_AUTH * auth_info){ int len = 0; int ret = IPC_OK; struct stat stat_buf; struct sockaddr_un *peer_addr = NULL; struct SOCKET_CH_PRIVATE *ch_private = NULL; if(ch != NULL) { ch_private = (struct SOCKET_CH_PRIVATE *)(ch->ch_private); if(ch_private != NULL) { peer_addr = ch_private->peer_addr; } } if(ch == NULL) { cl_log(LOG_ERR, "No channel to authenticate"); return IPC_FAIL; } else if (auth_info == NULL || (auth_info->uid == NULL && auth_info->gid == NULL)) { return IPC_OK; /* no restriction for authentication */ } else if(ch_private == NULL) { cl_log(LOG_ERR, "No channel private data available"); return IPC_FAIL; } else if(peer_addr == NULL) { cl_log(LOG_ERR, "No peer information available"); return IPC_FAIL; } len = SUN_LEN(peer_addr); if(len < 1) { cl_log(LOG_ERR, "No peer information available"); return IPC_FAIL; } peer_addr->sun_path[len] = 0; stat(peer_addr->sun_path, &stat_buf); if ((auth_info->uid == NULL || g_hash_table_size(auth_info->uid) == 0) && auth_info->gid != NULL && g_hash_table_size(auth_info->gid) != 0) { cl_log(LOG_WARNING, "GID-Only IPC security is not supported" " on this platform."); return IPC_BROKEN; } if (auth_info->uid != NULL && g_hash_table_size(auth_info->uid) > 0 && g_hash_table_lookup( auth_info->uid, GUINT_TO_POINTER(stat_buf.st_uid))==NULL) { ret = IPC_FAIL; } if (auth_info->gid != NULL && g_hash_table_size(auth_info->gid) > 0 && g_hash_table_lookup( auth_info->gid, GUINT_TO_POINTER(stat_buf.st_gid))==NULL) { ret = IPC_FAIL; } return ret;}pid_tsocket_get_farside_pid(int sock){ return -1;}#endif /* Bind/stat version *//*********************************************************************** * DUMMY VERSION... (other systems...) * * I'm afraid Solaris falls into this category :-( * Other options that seem to be out there include * SCM_CREDENTIALS and LOCAL_CREDS * Or maybe something called doors for Solaris * Unfortunately, it looks like Doors is tied to threads :-( * Can the streams credentials code be used with local domain sockets? * There are some kludgy things you can do with SCM_RIGHTS * to pass an fd which could only be opened by the user id to * validate the user id, but I don't know of a similar kludge which * would work for group ids. And, even the uid one will fail * if normal users are allowed to give away (chown) files. * * Unfortunately, this set of authentication routines have become * very important to this API and its users (like heartbeat). * ***********************************************************************/#ifdef USE_DUMMY_CREDSstatic int socket_verify_auth(struct IPC_CHANNEL* ch, struct IPC_AUTH * auth_info){ return IPC_FAIL;}pid_tsocket_get_farside_pid(int sock){ return -1;}#endif /* Dummy version */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -