static void nn_cws_destroy (void *self) { struct nn_cws *cws = self; nn_chunkref_term (&cws->resource); nn_chunkref_term (&cws->remote_host); nn_chunkref_term (&cws->nic); nn_dns_term (&cws->dns); nn_sws_term (&cws->sws); nn_backoff_term (&cws->retry); nn_usock_term (&cws->usock); nn_fsm_term (&cws->fsm); nn_free (cws); }
int nn_xreq_recv (struct nn_sockbase *self, struct nn_msg *msg) { int rc; rc = nn_fq_recv (&nn_cont (self, struct nn_xreq, sockbase)->fq, msg, NULL); if (rc == -EAGAIN) return -EAGAIN; errnum_assert (rc >= 0, -rc); if (!(rc & NN_PIPE_PARSED)) { /* Ignore malformed replies. */ if (nn_slow (nn_chunkref_size (&msg->body) < sizeof (uint32_t))) { nn_msg_term (msg); return -EAGAIN; } /* Split the message into the header and the body. */ nn_assert (nn_chunkref_size (&msg->sphdr) == 0); nn_chunkref_term (&msg->sphdr); nn_chunkref_init (&msg->sphdr, sizeof (uint32_t)); memcpy (nn_chunkref_data (&msg->sphdr), nn_chunkref_data (&msg->body), sizeof (uint32_t)); nn_chunkref_trim (&msg->body, sizeof (uint32_t)); } return 0; }
static int nn_surveyor_send (struct nn_sockbase *self, struct nn_msg *msg) { int rc; struct nn_surveyor *surveyor; surveyor = nn_cont (self, struct nn_surveyor, xsurveyor.sockbase); /* Cancel any ongoing survey. */ if (nn_slow (surveyor->flags & NN_SURVEYOR_INPROGRESS)) { surveyor->flags &= ~NN_SURVEYOR_INPROGRESS; nn_timer_stop (&surveyor->deadline_timer); } /* Generate new survey ID. */ ++surveyor->surveyid; /* Tag the survey body with survey ID. */ nn_assert (nn_chunkref_size (&msg->hdr) == 0); nn_chunkref_term (&msg->hdr); nn_chunkref_init (&msg->hdr, 4); nn_putl (nn_chunkref_data (&msg->hdr), surveyor->surveyid); /* Send the survey. */ rc = nn_xsurveyor_send (&surveyor->xsurveyor.sockbase, msg); errnum_assert (rc == 0, -rc); surveyor->flags |= NN_SURVEYOR_INPROGRESS; /* Set up the re-send timer. */ nn_timer_start (&surveyor->deadline_timer, surveyor->deadline); return 0; }
int nn_rep_recv (struct nn_sockbase *self, struct nn_msg *msg) { int rc; struct nn_rep *rep; rep = nn_cont (self, struct nn_rep, xrep.sockbase); /* If a request is already being processed, cancel it. */ if (nn_slow (rep->flags & NN_REP_INPROGRESS)) { nn_chunkref_term (&rep->backtrace); rep->flags &= ~NN_REP_INPROGRESS; } /* Receive the request. */ rc = nn_xrep_recv (&rep->xrep.sockbase, msg); if (nn_slow (rc == -EAGAIN)) return -EAGAIN; errnum_assert (rc == 0, -rc); /* Store the backtrace. */ nn_chunkref_mv (&rep->backtrace, &msg->sphdr); nn_chunkref_init (&msg->sphdr, 0); rep->flags |= NN_REP_INPROGRESS; return 0; }
int nn_xrespondent_recv (struct nn_sockbase *self, struct nn_msg *msg) { int rc; struct nn_xrespondent *xrespondent; xrespondent = nn_cont (self, struct nn_xrespondent, sockbase); /* Get the survey. */ rc = nn_excl_recv (&xrespondent->excl, msg); if (rc == -EAGAIN) return -EAGAIN; errnum_assert (rc >= 0, -rc); /* Split the survey ID from the body, if needed. */ if (!(rc & NN_PIPE_PARSED)) { if (nn_slow (nn_chunkref_size (&msg->body) < sizeof (uint32_t))) { nn_msg_term (msg); return -EAGAIN; } nn_chunkref_term (&msg->hdr); nn_chunkref_init (&msg->hdr, sizeof (uint32_t)); memcpy (nn_chunkref_data (&msg->hdr), nn_chunkref_data (&msg->body), sizeof (uint32_t)); nn_chunkref_trim (&msg->body, sizeof (uint32_t)); } return 0; }
int nn_xbus_recv (struct nn_sockbase *self, struct nn_msg *msg) { int rc; struct nn_xbus *xbus; struct nn_pipe *pipe; xbus = nn_cont (self, struct nn_xbus, sockbase); while (1) { /* Get next message in fair-queued manner. */ rc = nn_fq_recv (&xbus->inpipes, msg, &pipe); if (nn_slow (rc < 0)) return rc; /* The message should have no header. Drop malformed messages. */ if (nn_chunkref_size (&msg->hdr) == 0) break; nn_msg_term (msg); } /* Add pipe ID to the message header. */ nn_chunkref_term (&msg->hdr); nn_chunkref_init (&msg->hdr, sizeof (uint64_t)); memset (nn_chunkref_data (&msg->hdr), 0, sizeof (uint64_t)); memcpy (nn_chunkref_data (&msg->hdr), &pipe, sizeof (pipe)); return 0; }
static int nn_respondent_send (struct nn_sockbase *self, struct nn_msg *msg) { int rc; struct nn_respondent *respondent; respondent = nn_cont (self, struct nn_respondent, xrespondent.sockbase); /* If there's no survey going on, report EFSM error. */ if (nn_slow (!(respondent->flags & NN_RESPONDENT_INPROGRESS))) return -EFSM; /* Tag the message with survey ID. */ nn_assert (nn_chunkref_size (&msg->sphdr) == 0); nn_chunkref_term (&msg->sphdr); nn_chunkref_init (&msg->sphdr, 4); nn_putl (nn_chunkref_data (&msg->sphdr), respondent->surveyid); /* Try to send the message. If it cannot be sent due to pushback, drop it silently. */ rc = nn_xrespondent_send (&respondent->xrespondent.sockbase, msg); if (nn_slow (rc == -EAGAIN)) { nn_msg_term (msg); return -EAGAIN; } errnum_assert (rc == 0, -rc); /* Remember that no survey is being processed. */ respondent->flags &= ~NN_RESPONDENT_INPROGRESS; return 0; }
int nn_xsurveyor_recv (struct nn_sockbase *self, struct nn_msg *msg) { int rc; struct nn_xsurveyor *xsurveyor; xsurveyor = nn_cont (self, struct nn_xsurveyor, sockbase); rc = nn_fq_recv (&xsurveyor->inpipes, msg, NULL); if (nn_slow (rc < 0)) return rc; /* Split the header from the body, if needed. */ if (!(rc & NN_PIPE_PARSED)) { if (nn_slow (nn_chunkref_size (&msg->body) < sizeof (uint32_t))) { nn_msg_term (msg); return -EAGAIN; } nn_assert (nn_chunkref_size (&msg->hdr) == 0); nn_chunkref_term (&msg->hdr); nn_chunkref_init (&msg->hdr, sizeof (uint32_t)); memcpy (nn_chunkref_data (&msg->hdr), nn_chunkref_data (&msg->body), sizeof (uint32_t)); nn_chunkref_trim (&msg->body, sizeof (uint32_t)); } return 0; }
static int nn_respondent_recv (struct nn_sockbase *self, struct nn_msg *msg) { int rc; struct nn_respondent *respondent; respondent = nn_cont (self, struct nn_respondent, xrespondent.sockbase); /* Cancel current survey, if it exists. */ respondent->flags &= ~NN_RESPONDENT_INPROGRESS; /* Get next survey. */ rc = nn_xrespondent_recv (&respondent->xrespondent.sockbase, msg); if (nn_slow (rc == -EAGAIN)) return -EAGAIN; errnum_assert (rc == 0, -rc); /* Remember the survey ID. */ nn_assert (nn_chunkref_size (&msg->sphdr) == sizeof (uint32_t)); respondent->surveyid = nn_getl (nn_chunkref_data (&msg->sphdr)); nn_chunkref_term (&msg->sphdr); nn_chunkref_init (&msg->sphdr, 0); /* Remember that survey is being processed. */ respondent->flags |= NN_RESPONDENT_INPROGRESS; return 0; }
/* Deallocate a message chunk and remove it from array. */ static void nn_msg_chunk_term (struct msg_chunk *it, struct nn_list *msg_array) { nn_chunkref_term (&it->chunk); nn_list_erase (msg_array, &it->item); nn_list_item_term (&it->item); nn_free (it); }
static void nn_cws_destroy (struct nn_epbase *self) { struct nn_cws *cws; cws = nn_cont (self, struct nn_cws, epbase); nn_chunkref_term (&cws->resource); nn_chunkref_term (&cws->remote_host); nn_chunkref_term (&cws->nic); nn_dns_term (&cws->dns); nn_sws_term (&cws->sws); nn_backoff_term (&cws->retry); nn_usock_term (&cws->usock); nn_fsm_term (&cws->fsm); nn_epbase_term (&cws->epbase); nn_free (cws); }
void nn_req_in (struct nn_sockbase *self, struct nn_pipe *pipe) { int rc; struct nn_req *req; uint32_t reqid; req = nn_cont (self, struct nn_req, xreq.sockbase); /* Pass the pipe to the raw REQ socket. */ nn_xreq_in (&req->xreq.sockbase, pipe); while (1) { /* Get new reply. */ rc = nn_xreq_recv (&req->xreq.sockbase, &req->task.reply); if (nn_slow (rc == -EAGAIN)) return; errnum_assert (rc == 0, -rc); /* No request was sent. Getting a reply doesn't make sense. */ if (nn_slow (!nn_req_inprogress (req))) { nn_msg_term (&req->task.reply); continue; } /* Ignore malformed replies. */ if (nn_slow (nn_chunkref_size (&req->task.reply.sphdr) != sizeof (uint32_t))) { nn_msg_term (&req->task.reply); continue; } /* Ignore replies with incorrect request IDs. */ reqid = nn_getl (nn_chunkref_data (&req->task.reply.sphdr)); if (nn_slow (!(reqid & 0x80000000))) { nn_msg_term (&req->task.reply); continue; } if (nn_slow (reqid != (req->task.id | 0x80000000))) { nn_msg_term (&req->task.reply); continue; } /* Trim the request ID. */ nn_chunkref_term (&req->task.reply.sphdr); nn_chunkref_init (&req->task.reply.sphdr, 0); /* TODO: Deallocate the request here? */ /* Notify the state machine. */ if (req->state == NN_REQ_STATE_ACTIVE) nn_fsm_action (&req->fsm, NN_REQ_ACTION_IN); return; } }
static void nn_req_in (struct nn_sockbase *self, struct nn_pipe *pipe) { int rc; struct nn_req *req; uint32_t reqid; req = nn_cont (self, struct nn_req, xreq.sockbase); /* Pass the pipe to the raw REQ socket. */ nn_xreq_in (&req->xreq.sockbase, pipe); while (1) { /* Get new reply. */ rc = nn_xreq_recv (&req->xreq.sockbase, &req->reply); if (nn_slow (rc == -EAGAIN)) return; errnum_assert (rc == 0, -rc); /* No request was sent. Getting a reply doesn't make sense. */ if (nn_slow (req->state != NN_REQ_STATE_SENT)) { nn_msg_term (&req->reply); continue; } /* Ignore malformed replies. */ if (nn_slow (nn_chunkref_size (&req->reply.hdr) != sizeof (uint32_t))) { nn_msg_term (&req->reply); continue; } /* Ignore replies with incorrect request IDs. */ reqid = nn_getl (nn_chunkref_data (&req->reply.hdr)); if (nn_slow (!(reqid & 0x80000000))) { nn_msg_term (&req->reply); continue; } if (nn_slow (reqid != (req->reqid | 0x80000000))) { nn_msg_term (&req->reply); continue; } /* Trim the request ID. */ nn_chunkref_term (&req->reply.hdr); nn_chunkref_init (&req->reply.hdr, 0); /* Swtich to RECEIVED state. */ nn_timer_stop (&req->resend_timer); nn_msg_term (&req->request); req->state = NN_REQ_STATE_RECEIVED; return; } }
int nn_send (int s, const void *buf, size_t len, int flags) { int rc; struct nn_msg msg; void *chunk; int nnmsg; NN_BASIC_CHECKS; if (nn_slow (!buf && len)) { errno = EFAULT; return -1; } /* Create a message object. */ if (len == NN_MSG) { chunk = *(void**) buf; if (nn_slow (chunk == NULL)) { errno = EFAULT; return -1; } len = nn_chunk_size (chunk); nn_msg_init_chunk (&msg, chunk); nnmsg = 1; } else { nn_msg_init (&msg, len); memcpy (nn_chunkref_data (&msg.body), buf, len); nnmsg = 0; } /* Send it further down the stack. */ rc = nn_sock_send (self.socks [s], &msg, flags); if (nn_slow (rc < 0)) { nn_chunkref_term (&msg.hdr); /* If we are dealing with user-supplied buffer, detach it from the message object. */ if (nnmsg) nn_chunkref_init (&msg.body, 0); nn_msg_term (&msg); errno = -rc; return -1; } return (int) len; }
static int nn_req_send (struct nn_sockbase *self, struct nn_msg *msg) { int rc; struct nn_req *req; req = nn_cont (self, struct nn_req, xreq.sockbase); /* If there's a request in progress, cancel it. */ if (nn_slow (req->state != NN_REQ_STATE_IDLE)) { if (req->state == NN_REQ_STATE_UNSENT || req->state == NN_REQ_STATE_SENT) nn_msg_term (&req->request); if (req->state == NN_REQ_STATE_RECEIVED) nn_msg_term (&req->reply); nn_timer_term (&req->resend_timer); req->state = NN_REQ_STATE_IDLE; } /* Generate new request ID for the new request and put it into message header. The most important bit is set to 1 to indicate that this is the bottom of the backtrace stack. */ ++req->reqid; nn_assert (nn_chunkref_size (&msg->hdr) == 0); nn_chunkref_term (&msg->hdr); nn_chunkref_init (&msg->hdr, 4); nn_putl (nn_chunkref_data (&msg->hdr), req->reqid | 0x80000000); /* Store the message so that it can be re-sent if there's no reply. Then make a copy of it and send it. */ nn_msg_cp (&req->request, msg); rc = nn_xreq_send (&req->xreq.sockbase, msg); errnum_assert (rc == 0 || rc == -EAGAIN, -rc); /* If the request cannot be sent at the moment switch to UNSENT state. It will be sent as soon as a new outbound pipe arrives. */ if (nn_slow (rc == -EAGAIN)) { nn_msg_term (msg); req->state = NN_REQ_STATE_UNSENT; return 0; } /* If the request was successgfully sent set up the re-send timer in case it get lost somewhere further out in the topology. */ nn_timer_start (&req->resend_timer, req->resend_ivl); req->state = NN_REQ_STATE_SENT; return 0; }
int nn_xbus_send (struct nn_sockbase *self, struct nn_msg *msg) { size_t hdrsz; struct nn_pipe *exclude; hdrsz = nn_chunkref_size (&msg->hdr); if (hdrsz == 0) exclude = NULL; else if (hdrsz == sizeof (uint64_t)) { memcpy (&exclude, nn_chunkref_data (&msg->hdr), sizeof (exclude)); nn_chunkref_term (&msg->hdr); nn_chunkref_init (&msg->hdr, 0); } else return -EINVAL; return nn_dist_send (&nn_cont (self, struct nn_xbus, sockbase)->outpipes, msg, exclude); }
static int nn_surveyor_recv (struct nn_sockbase *self, struct nn_msg *msg) { int rc; struct nn_surveyor *surveyor; uint32_t surveyid; surveyor = nn_cont (self, struct nn_surveyor, xsurveyor.sockbase); /* If no survey is going on return EFSM error. */ if (nn_slow (!nn_surveyor_inprogress (surveyor))) { if (surveyor->timedout == NN_SURVEYOR_TIMEDOUT) { surveyor->timedout = 0; return -ETIMEDOUT; } else return -EFSM; } while (1) { /* Get next response. */ rc = nn_xsurveyor_recv (&surveyor->xsurveyor.sockbase, msg); if (nn_slow (rc == -EAGAIN)) return -EAGAIN; errnum_assert (rc == 0, -rc); /* Get the survey ID. Ignore any stale responses. */ /* TODO: This should be done asynchronously! */ if (nn_slow (nn_chunkref_size (&msg->sphdr) != sizeof (uint32_t))) continue; surveyid = nn_getl (nn_chunkref_data (&msg->sphdr)); if (nn_slow (surveyid != surveyor->surveyid)) continue; /* Discard the header and return the message to the user. */ nn_chunkref_term (&msg->sphdr); nn_chunkref_init (&msg->sphdr, 0); break; } return 0; }
static int nn_surveyor_send (struct nn_sockbase *self, struct nn_msg *msg) { struct nn_surveyor *surveyor; surveyor = nn_cont (self, struct nn_surveyor, xsurveyor.sockbase); /* Generate new survey ID. */ ++surveyor->surveyid; surveyor->surveyid |= 0x80000000; /* Tag the survey body with survey ID. */ nn_assert (nn_chunkref_size (&msg->sphdr) == 0); nn_chunkref_term (&msg->sphdr); nn_chunkref_init (&msg->sphdr, 4); nn_putl (nn_chunkref_data (&msg->sphdr), surveyor->surveyid); /* Store the survey, so that it can be sent later on. */ nn_msg_term (&surveyor->tosend); nn_msg_mv (&surveyor->tosend, msg); nn_msg_init (msg, 0); /* Cancel any ongoing survey, if any. */ if (nn_slow (nn_surveyor_inprogress (surveyor))) { /* First check whether the survey can be sent at all. */ if (!(nn_xsurveyor_events (&surveyor->xsurveyor.sockbase) & NN_SOCKBASE_EVENT_OUT)) return -EAGAIN; /* Cancel the current survey. */ nn_fsm_action (&surveyor->fsm, NN_SURVEYOR_ACTION_CANCEL); return 0; } /* Notify the state machine that the survey was started. */ nn_fsm_action (&surveyor->fsm, NN_SURVEYOR_ACTION_START); return 0; }
static int nn_surveyor_recv (struct nn_sockbase *self, struct nn_msg *msg) { int rc; struct nn_surveyor *surveyor; uint32_t surveyid; surveyor = nn_cont (self, struct nn_surveyor, xsurveyor.sockbase); /* If no survey is going on return EFSM error. */ if (nn_slow (!(surveyor->flags & NN_SURVEYOR_INPROGRESS))) return -EFSM; while (1) { /* Get next response. */ rc = nn_xsurveyor_recv (&surveyor->xsurveyor.sockbase, msg); if (nn_slow (rc == -EAGAIN)) return -EAGAIN; errnum_assert (rc == 0, -rc); /* Get the survey ID. Ignore any stale responses. */ if (nn_slow (nn_chunkref_size (&msg->hdr) != sizeof (uint32_t))) { nn_msg_term (msg); continue; } surveyid = nn_getl (nn_chunkref_data (&msg->hdr)); if (nn_slow (surveyid != surveyor->surveyid)) { nn_msg_term (msg); continue; } /* Discard the header and return the message to the user. */ nn_chunkref_term (&msg->hdr); nn_chunkref_init (&msg->hdr, 0); break; } return 0; }
int nn_rep_send (struct nn_sockbase *self, struct nn_msg *msg) { int rc; struct nn_rep *rep; rep = nn_cont (self, struct nn_rep, xrep.sockbase); /* If no request was received, there's nowhere to send the reply to. */ if (nn_slow (!(rep->flags & NN_REP_INPROGRESS))) return -EFSM; /* Move the stored backtrace into the message header. */ nn_assert (nn_chunkref_size (&msg->sphdr) == 0); nn_chunkref_term (&msg->sphdr); nn_chunkref_mv (&msg->sphdr, &rep->backtrace); rep->flags &= ~NN_REP_INPROGRESS; /* Send the reply. If it cannot be sent because of pushback, drop it silently. */ rc = nn_xrep_send (&rep->xrep.sockbase, msg); errnum_assert (rc == 0 || rc == -EAGAIN, -rc); return 0; }
static int nn_req_send (struct nn_sockbase *self, struct nn_msg *msg) { struct nn_req *req; req = nn_cont (self, struct nn_req, xreq.sockbase); /* Generate new request ID for the new request and put it into message header. The most important bit is set to 1 to indicate that this is the bottom of the backtrace stack. */ ++req->reqid; nn_assert (nn_chunkref_size (&msg->hdr) == 0); nn_chunkref_term (&msg->hdr); nn_chunkref_init (&msg->hdr, 4); nn_putl (nn_chunkref_data (&msg->hdr), req->reqid | 0x80000000); /* Store the message so that it can be re-sent if there's no reply. */ nn_msg_term (&req->request); nn_msg_mv (&req->request, msg); /* Notify the state machine. */ nn_fsm_action (&req->fsm, NN_REQ_ACTION_SENT); return 0; }
void nn_msg_term (struct nn_msg *self) { nn_chunkref_term (&self->hdr); nn_chunkref_term (&self->body); }
void nn_rep_term (struct nn_rep *self) { if (self->flags & NN_REP_INPROGRESS) nn_chunkref_term (&self->backtrace); nn_xrep_term (&self->xrep); }
int nn_sendmsg (int s, const struct nn_msghdr *msghdr, int flags) { int rc; size_t sz; size_t spsz; int i; struct nn_iovec *iov; struct nn_msg msg; void *chunk; int nnmsg; struct nn_cmsghdr *cmsg; NN_BASIC_CHECKS; if (nn_slow (!msghdr)) { errno = EINVAL; return -1; } if (nn_slow (msghdr->msg_iovlen < 0)) { errno = EMSGSIZE; return -1; } if (msghdr->msg_iovlen == 1 && msghdr->msg_iov [0].iov_len == NN_MSG) { chunk = *(void**) msghdr->msg_iov [0].iov_base; if (nn_slow (chunk == NULL)) { errno = EFAULT; return -1; } sz = nn_chunk_size (chunk); nn_msg_init_chunk (&msg, chunk); nnmsg = 1; } else { /* Compute the total size of the message. */ sz = 0; for (i = 0; i != msghdr->msg_iovlen; ++i) { iov = &msghdr->msg_iov [i]; if (nn_slow (iov->iov_len == NN_MSG)) { errno = EINVAL; return -1; } if (nn_slow (!iov->iov_base && iov->iov_len)) { errno = EFAULT; return -1; } if (nn_slow (sz + iov->iov_len < sz)) { errno = EINVAL; return -1; } sz += iov->iov_len; } /* Create a message object from the supplied scatter array. */ nn_msg_init (&msg, sz); sz = 0; for (i = 0; i != msghdr->msg_iovlen; ++i) { iov = &msghdr->msg_iov [i]; memcpy (((uint8_t*) nn_chunkref_data (&msg.body)) + sz, iov->iov_base, iov->iov_len); sz += iov->iov_len; } nnmsg = 0; } /* Add ancillary data to the message. */ if (msghdr->msg_control) { /* Copy all headers. */ /* TODO: SP_HDR should not be copied here! */ if (msghdr->msg_controllen == NN_MSG) { chunk = *((void**) msghdr->msg_control); nn_chunkref_term (&msg.hdrs); nn_chunkref_init_chunk (&msg.hdrs, chunk); } else { nn_chunkref_term (&msg.hdrs); nn_chunkref_init (&msg.hdrs, msghdr->msg_controllen); memcpy (nn_chunkref_data (&msg.hdrs), msghdr->msg_control, msghdr->msg_controllen); } /* Search for SP_HDR property. */ cmsg = NN_CMSG_FIRSTHDR (msghdr); while (cmsg) { if (cmsg->cmsg_level == PROTO_SP && cmsg->cmsg_type == SP_HDR) { /* Copy body of SP_HDR property into 'sphdr'. */ nn_chunkref_term (&msg.sphdr); spsz = cmsg->cmsg_len - NN_CMSG_SPACE (0); nn_chunkref_init (&msg.sphdr, spsz); memcpy (nn_chunkref_data (&msg.sphdr), NN_CMSG_DATA (cmsg), spsz); break; } cmsg = NN_CMSG_NXTHDR (msghdr, cmsg); } } /* Send it further down the stack. */ rc = nn_sock_send (self.socks [s], &msg, flags); if (nn_slow (rc < 0)) { /* If we are dealing with user-supplied buffer, detach it from the message object. */ if (nnmsg) nn_chunkref_init (&msg.body, 0); nn_msg_term (&msg); errno = -rc; return -1; } /* Adjust the statistics. */ nn_sock_stat_increment (self.socks [s], NN_STAT_MESSAGES_SENT, 1); nn_sock_stat_increment (self.socks [s], NN_STAT_BYTES_SENT, sz); return (int) sz; }
int nn_sendmsg (int s, const struct nn_msghdr *msghdr, int flags) { int rc; size_t sz; int i; struct nn_iovec *iov; struct nn_msg msg; void *chunk; NN_BASIC_CHECKS; if (nn_slow (!msghdr)) { errno = EINVAL; return -1; } if (nn_slow (msghdr->msg_iovlen < 0)) { errno = EMSGSIZE; return -1; } if (msghdr->msg_iovlen == 1 && msghdr->msg_iov [0].iov_len == NN_MSG) { chunk = *(void**) msghdr->msg_iov [0].iov_base; if (nn_slow (chunk == NULL)) { errno = EFAULT; return -1; } sz = nn_chunk_size (chunk); nn_msg_init_chunk (&msg, chunk); } else { /* Compute the total size of the message. */ sz = 0; for (i = 0; i != msghdr->msg_iovlen; ++i) { iov = &msghdr->msg_iov [i]; if (nn_slow (iov->iov_len == NN_MSG)) { errno = EINVAL; return -1; } if (nn_slow (!iov->iov_base && iov->iov_len)) { errno = EFAULT; return -1; } if (nn_slow (sz + iov->iov_len < sz)) { errno = EINVAL; return -1; } sz += iov->iov_len; } /* Create a message object from the supplied scatter array. */ nn_msg_init (&msg, sz); sz = 0; for (i = 0; i != msghdr->msg_iovlen; ++i) { iov = &msghdr->msg_iov [i]; memcpy (((uint8_t*) nn_chunkref_data (&msg.body)) + sz, iov->iov_base, iov->iov_len); sz += iov->iov_len; } } /* Add ancillary data to the message. */ if (msghdr->msg_control) { if (msghdr->msg_controllen == NN_MSG) { chunk = *((void**) msghdr->msg_control); nn_chunkref_term (&msg.hdr); nn_chunkref_init_chunk (&msg.hdr, chunk); } else { /* TODO: Copy the control data to the message. */ nn_assert (0); } } /* Send it further down the stack. */ rc = nn_sock_send (self.socks [s], &msg, flags); if (nn_slow (rc < 0)) { nn_msg_term (&msg); errno = -rc; return -1; } return (int) sz; }