static void nn_btcp_start_listening (struct nn_btcp *self) { int rc; struct sockaddr_storage ss; size_t sslen; int ipv4only; size_t ipv4onlylen; const char *addr; const char *end; const char *pos; uint16_t port; /* First, resolve the IP address. */ addr = nn_epbase_getaddr (&self->epbase); memset (&ss, 0, sizeof (ss)); /* Parse the port. */ end = addr + strlen (addr); pos = strrchr (addr, ':'); nn_assert (pos); ++pos; rc = nn_port_resolve (pos, end - pos); nn_assert (rc >= 0); port = rc; /* Parse the address. */ ipv4onlylen = sizeof (ipv4only); nn_epbase_getopt (&self->epbase, NN_SOL_SOCKET, NN_IPV4ONLY, &ipv4only, &ipv4onlylen); nn_assert (ipv4onlylen == sizeof (ipv4only)); rc = nn_iface_resolve (addr, pos - addr - 1, ipv4only, &ss, &sslen); errnum_assert (rc == 0, -rc); /* Combine the port and the address. */ if (ss.ss_family == AF_INET) { ((struct sockaddr_in*) &ss)->sin_port = htons (port); sslen = sizeof (struct sockaddr_in); } else if (ss.ss_family == AF_INET6) { ((struct sockaddr_in6*) &ss)->sin6_port = htons (port); sslen = sizeof (struct sockaddr_in6); } else nn_assert (0); /* Start listening for incoming connections. */ rc = nn_usock_start (&self->usock, ss.ss_family, SOCK_STREAM, 0); if (nn_slow (rc < 0)) { nn_backoff_start (&self->retry); self->state = NN_BTCP_STATE_WAITING; return; } rc = nn_usock_bind (&self->usock, (struct sockaddr*) &ss, (size_t) sslen); if (nn_slow (rc < 0)) { nn_usock_stop (&self->usock); self->state = NN_BTCP_STATE_CLOSING; return; } rc = nn_usock_listen (&self->usock, NN_BTCP_BACKLOG); if (nn_slow (rc < 0)) { nn_usock_stop (&self->usock); self->state = NN_BTCP_STATE_CLOSING; return; } nn_btcp_start_accepting(self); self->state = NN_BTCP_STATE_ACTIVE; }
int zmq_setsockopt (void *s, int option, const void *optval, size_t optvallen) { int fd; int val; int level; fd = (int) (((uint8_t*) s) - ((uint8_t*) 0) - 1); /* First, try to map ZeroMQ options to nanomsg options. */ switch (option) { case ZMQ_SUBSCRIBE: return nn_setsockopt (fd, NN_SUB, NN_SUBSCRIBE, optval, optvallen); case ZMQ_UNSUBSCRIBE: return nn_setsockopt (fd, NN_SUB, NN_UNSUBSCRIBE, optval, optvallen); case ZMQ_SNDBUF: if (optvallen != sizeof (uint64_t)) { errno = EINVAL; return -1; } val = (int) (*(uint64_t*) optval); return nn_setsockopt (fd, NN_SOL_SOCKET, NN_SNDBUF, &val, sizeof (val)); case ZMQ_RCVBUF: if (optvallen != sizeof (uint64_t)) { errno = EINVAL; return -1; } val = (int) (*(uint64_t*) optval); return nn_setsockopt (fd, NN_SOL_SOCKET, NN_RCVBUF, &val, sizeof (val)); case ZMQ_LINGER: return nn_setsockopt (fd, NN_SOL_SOCKET, NN_LINGER, optval, optvallen); case ZMQ_RECONNECT_IVL: return nn_setsockopt (fd, NN_SOL_SOCKET, NN_RECONNECT_IVL, optval, optvallen); case ZMQ_RECONNECT_IVL_MAX: return nn_setsockopt (fd, NN_SOL_SOCKET, NN_RECONNECT_IVL_MAX, optval, optvallen); case ZMQ_RCVTIMEO: return nn_setsockopt (fd, NN_SOL_SOCKET, NN_RCVTIMEO, optval, optvallen); case ZMQ_SNDTIMEO: return nn_setsockopt (fd, NN_SOL_SOCKET, NN_SNDTIMEO, optval, optvallen); } /* If the native ZeroMQ option is not supported by nanomsg, report it. */ if (option >= 0 && option < 100) { errno = ENOTSUP; return -1; } /* Provide a mechanism to expose native nanomsg options via ZeroMQ language bindings. */ if (option < 0) { level = -(-option / 100); option = -option % 100; } else { level = option / 100; option %= 100; nn_assert (level > 0); --level; } return nn_setsockopt (fd, level, option, optval, optvallen); }
void nn_list_item_term (struct nn_list_item *self) { nn_assert (!nn_list_item_isinlist (self)); }
static void nn_global_init (void) { int i; char *envvar; int rc; char *addr; #if defined NN_HAVE_WINDOWS WSADATA data; #endif /* Check whether the library was already initialised. If so, do nothing. */ if (self.socks) return; /* On Windows, initialise the socket library. */ #if defined NN_HAVE_WINDOWS rc = WSAStartup (MAKEWORD (2, 2), &data); nn_assert (rc == 0); nn_assert (LOBYTE (data.wVersion) == 2 && HIBYTE (data.wVersion) == 2); #endif /* Initialise the memory allocation subsystem. */ nn_alloc_init (); /* Seed the pseudo-random number generator. */ nn_random_seed (); /* Allocate the global table of SP sockets. */ self.socks = nn_alloc ((sizeof (struct nn_sock*) * NN_MAX_SOCKETS) + (sizeof (uint16_t) * NN_MAX_SOCKETS), "socket table"); alloc_assert (self.socks); for (i = 0; i != NN_MAX_SOCKETS; ++i) self.socks [i] = NULL; self.nsocks = 0; self.flags = 0; /* Print connection and accepting errors to the stderr */ envvar = getenv("NN_PRINT_ERRORS"); /* any non-empty string is true */ self.print_errors = envvar && *envvar; /* Print socket statistics to stderr */ envvar = getenv("NN_PRINT_STATISTICS"); self.print_statistics = envvar && *envvar; /* Allocate the stack of unused file descriptors. */ self.unused = (uint16_t*) (self.socks + NN_MAX_SOCKETS); alloc_assert (self.unused); for (i = 0; i != NN_MAX_SOCKETS; ++i) self.unused [i] = NN_MAX_SOCKETS - i - 1; /* Initialise other parts of the global state. */ nn_list_init (&self.transports); nn_list_init (&self.socktypes); /* Plug in individual transports. */ nn_global_add_transport (nn_inproc); nn_global_add_transport (nn_ipc); nn_global_add_transport (nn_tcp); nn_global_add_transport (nn_ws); nn_global_add_transport (nn_tcpmux); /* Plug in individual socktypes. */ nn_global_add_socktype (nn_pair_socktype); nn_global_add_socktype (nn_xpair_socktype); nn_global_add_socktype (nn_pub_socktype); nn_global_add_socktype (nn_sub_socktype); nn_global_add_socktype (nn_xpub_socktype); nn_global_add_socktype (nn_xsub_socktype); nn_global_add_socktype (nn_rep_socktype); nn_global_add_socktype (nn_req_socktype); nn_global_add_socktype (nn_xrep_socktype); nn_global_add_socktype (nn_xreq_socktype); nn_global_add_socktype (nn_push_socktype); nn_global_add_socktype (nn_xpush_socktype); nn_global_add_socktype (nn_pull_socktype); nn_global_add_socktype (nn_xpull_socktype); nn_global_add_socktype (nn_respondent_socktype); nn_global_add_socktype (nn_surveyor_socktype); nn_global_add_socktype (nn_xrespondent_socktype); nn_global_add_socktype (nn_xsurveyor_socktype); nn_global_add_socktype (nn_bus_socktype); nn_global_add_socktype (nn_xbus_socktype); /* Start the worker threads. */ nn_pool_init (&self.pool); /* Start FSM */ nn_fsm_init_root (&self.fsm, nn_global_handler, nn_global_shutdown, &self.ctx); self.state = NN_GLOBAL_STATE_IDLE; nn_ctx_init (&self.ctx, nn_global_getpool (), NULL); nn_timer_init (&self.stat_timer, NN_GLOBAL_SRC_STAT_TIMER, &self.fsm); nn_fsm_start (&self.fsm); /* Initializing special sockets. */ addr = getenv ("NN_STATISTICS_SOCKET"); if (addr) { self.statistics_socket = nn_global_create_socket (AF_SP, NN_PUB); errno_assert (self.statistics_socket >= 0); rc = nn_global_create_ep (self.statistics_socket, addr, 0); errno_assert (rc >= 0); } else { self.statistics_socket = -1; } addr = getenv ("NN_APPLICATION_NAME"); if (addr) { strncpy (self.appname, addr, 63); self.appname[63] = '\0'; } else { /* No cross-platform way to find out application binary. Also, MSVC suggests using _getpid() instead of getpid(), however, it's not clear whether the former is supported by older versions of Windows/MSVC. */ #if defined _MSC_VER #pragma warning (push) #pragma warning (disable:4996) #endif sprintf (self.appname, "nanomsg.%d", getpid()); #if defined _MSC_VER #pragma warning (pop) #endif } addr = getenv ("NN_HOSTNAME"); if (addr) { strncpy (self.hostname, addr, 63); self.hostname[63] = '\0'; } else { rc = gethostname (self.hostname, 63); errno_assert (rc == 0); self.hostname[63] = '\0'; } }
static void nn_atcp_handler (struct nn_fsm *self, int src, int type, NN_UNUSED void *srcptr) { struct nn_atcp *atcp; int val; size_t sz; atcp = nn_cont (self, struct nn_atcp, fsm); switch (atcp->state) { /******************************************************************************/ /* IDLE state. */ /* The state machine wasn't yet started. */ /******************************************************************************/ case NN_ATCP_STATE_IDLE: switch (src) { case NN_FSM_ACTION: switch (type) { case NN_FSM_START: nn_usock_accept(&atcp->usock, atcp->listener); printf("accepted\n"); atcp->state = NN_ATCP_STATE_ACCEPTING; return; default: nn_fsm_bad_action (atcp->state, src, type); } default: nn_fsm_bad_source (atcp->state, src, type); } /******************************************************************************/ /* ACCEPTING state. */ /* Waiting for incoming connection. */ /******************************************************************************/ case NN_ATCP_STATE_ACCEPTING: switch ( src ) { case NN_ATCP_SRC_USOCK: switch ( type ) { case NN_USOCK_ACCEPTED: nn_epbase_clear_error (atcp->epbase); // Set the relevant socket options sz = sizeof(val); nn_epbase_getopt (atcp->epbase,NN_SOL_SOCKET,NN_SNDBUF,&val,&sz); nn_assert (sz == sizeof(val)); nn_usock_setsockopt (&atcp->usock,SOL_SOCKET,SO_SNDBUF,&val,sizeof(val)); sz = sizeof(val); nn_epbase_getopt(atcp->epbase,NN_SOL_SOCKET,NN_RCVBUF,&val,&sz); nn_assert (sz == sizeof(val)); nn_usock_setsockopt(&atcp->usock,SOL_SOCKET,SO_RCVBUF,&val,sizeof(val)); // Return ownership of the listening socket to the parent nn_usock_swap_owner(atcp->listener,&atcp->listener_owner); atcp->listener = NULL; atcp->listener_owner.src = -1; atcp->listener_owner.fsm = NULL; nn_fsm_raise(&atcp->fsm, &atcp->accepted,NN_ATCP_ACCEPTED); // Start the stcp state machine nn_usock_activate(&atcp->usock); nn_stcp_start(&atcp->stcp, &atcp->usock); printf("start accepting socket\n"); atcp->state = NN_ATCP_STATE_ACTIVE; nn_epbase_stat_increment(atcp->epbase,NN_STAT_ACCEPTED_CONNECTIONS,1); return; default: nn_fsm_bad_action(atcp->state,src,type); } case NN_ATCP_SRC_LISTENER: switch ( type ) { case NN_USOCK_ACCEPT_ERROR: nn_epbase_set_error(atcp->epbase,nn_usock_geterrno(atcp->listener),__FILE__,__LINE__); nn_epbase_stat_increment(atcp->epbase,NN_STAT_ACCEPT_ERRORS,1); nn_usock_accept(&atcp->usock, atcp->listener); return; default: nn_fsm_bad_action (atcp->state, src, type); } default: nn_fsm_bad_source (atcp->state, src, type); } /******************************************************************************/ /* ACTIVE state. */ /******************************************************************************/ case NN_ATCP_STATE_ACTIVE: switch ( src ) { case NN_ATCP_SRC_STCP: switch ( type ) { case NN_STCP_ERROR: nn_stcp_stop(&atcp->stcp); atcp->state = NN_ATCP_STATE_STOPPING_STCP; nn_epbase_stat_increment(atcp->epbase,NN_STAT_BROKEN_CONNECTIONS,1); return; default: nn_fsm_bad_action (atcp->state, src, type); } default: nn_fsm_bad_source (atcp->state, src, type); } /******************************************************************************/ /* STOPPING_STCP state. */ /******************************************************************************/ case NN_ATCP_STATE_STOPPING_STCP: switch ( src ) { case NN_ATCP_SRC_STCP: switch ( type ) { case NN_USOCK_SHUTDOWN: return; case NN_STCP_STOPPED: nn_usock_stop(&atcp->usock); atcp->state = NN_ATCP_STATE_STOPPING_USOCK; return; default: nn_fsm_bad_action(atcp->state,src,type); } default: nn_fsm_bad_source (atcp->state, src, type); } /******************************************************************************/ /* STOPPING_USOCK state. */ /******************************************************************************/ case NN_ATCP_STATE_STOPPING_USOCK: switch ( src ) { case NN_ATCP_SRC_USOCK: switch ( type ) { case NN_USOCK_SHUTDOWN: return; case NN_USOCK_STOPPED: nn_fsm_raise(&atcp->fsm,&atcp->done,NN_ATCP_ERROR); atcp->state = NN_ATCP_STATE_DONE; return; default: nn_fsm_bad_action (atcp->state, src, type); } default: nn_fsm_bad_source (atcp->state, src, type); } /******************************************************************************/ /* Invalid state. */ /******************************************************************************/ default: nn_fsm_bad_state (atcp->state, src, type); } }
int main (int argc, const char *argv[]) { int rc; int sb; int sc; unsigned char *buf1, *buf2; int i; struct nn_iovec iov; struct nn_msghdr hdr; char socket_address_tcp[128]; test_addr_from(socket_address_tcp, "tcp", "127.0.0.1", get_test_port(argc, argv)); sb = test_socket (AF_SP, NN_PAIR); test_bind (sb, SOCKET_ADDRESS); sc = test_socket (AF_SP, NN_PAIR); test_connect (sc, SOCKET_ADDRESS); buf1 = nn_allocmsg (256, 0); alloc_assert (buf1); for (i = 0; i != 256; ++i) buf1 [i] = (unsigned char) i; rc = nn_send (sc, &buf1, NN_MSG, 0); errno_assert (rc >= 0); nn_assert (rc == 256); buf2 = NULL; rc = nn_recv (sb, &buf2, NN_MSG, 0); errno_assert (rc >= 0); nn_assert (rc == 256); nn_assert (buf2); for (i = 0; i != 256; ++i) nn_assert (buf2 [i] == (unsigned char) i); rc = nn_freemsg (buf2); errno_assert (rc == 0); buf1 = nn_allocmsg (256, 0); alloc_assert (buf1); for (i = 0; i != 256; ++i) buf1 [i] = (unsigned char) i; iov.iov_base = &buf1; iov.iov_len = NN_MSG; memset (&hdr, 0, sizeof (hdr)); hdr.msg_iov = &iov; hdr.msg_iovlen = 1; rc = nn_sendmsg (sc, &hdr, 0); errno_assert (rc >= 0); nn_assert (rc == 256); buf2 = NULL; iov.iov_base = &buf2; iov.iov_len = NN_MSG; memset (&hdr, 0, sizeof (hdr)); hdr.msg_iov = &iov; hdr.msg_iovlen = 1; rc = nn_recvmsg (sb, &hdr, 0); errno_assert (rc >= 0); nn_assert (rc == 256); nn_assert (buf2); for (i = 0; i != 256; ++i) nn_assert (buf2 [i] == (unsigned char) i); rc = nn_freemsg (buf2); errno_assert (rc == 0); test_close (sc); test_close (sb); /* Test receiving of large message */ sb = test_socket (AF_SP, NN_PAIR); test_bind (sb, socket_address_tcp); sc = test_socket (AF_SP, NN_PAIR); test_connect (sc, socket_address_tcp); for (i = 0; i < (int) sizeof (longdata); ++i) longdata[i] = '0' + (i % 10); longdata [sizeof (longdata) - 1] = 0; test_send (sb, longdata); rc = nn_recv (sc, &buf2, NN_MSG, 0); errno_assert (rc >= 0); nn_assert (rc == sizeof (longdata) - 1); nn_assert (buf2); for (i = 0; i < (int) sizeof (longdata) - 1; ++i) nn_assert (buf2 [i] == longdata [i]); rc = nn_freemsg (buf2); errno_assert (rc == 0); test_close (sc); test_close (sb); /* Test reallocmsg */ buf1 = nn_allocmsg (8, 0); alloc_assert (buf1); buf2 = nn_reallocmsg (buf1, 1); nn_assert (buf2 == buf1); buf1 = nn_reallocmsg (buf2, 100); nn_assert (buf1 != buf2); nn_assert (buf1 != 0); nn_freemsg (buf1); return 0; }
static void nn_ctcp_start_connecting(struct nn_ctcp *self,struct sockaddr_storage *ss, size_t sslen) { int rc; struct sockaddr_storage remote; size_t remotelen; struct sockaddr_storage local; size_t locallen; const char *addr; const char *end; const char *colon; const char *semicolon; uint16_t port; int ipv4only; size_t ipv4onlylen; int val; size_t sz; /* Create IP address from the address string. */ addr = nn_epbase_getaddr (&self->epbase); memset (&remote, 0, sizeof (remote)); /* Parse the port. */ end = addr + strlen (addr); colon = strrchr (addr, ':'); rc = nn_port_resolve (colon + 1, end - colon - 1); errnum_assert (rc > 0, -rc); port = rc; /* Check whether IPv6 is to be used. */ ipv4onlylen = sizeof (ipv4only); nn_epbase_getopt (&self->epbase, NN_SOL_SOCKET, NN_IPV4ONLY, &ipv4only, &ipv4onlylen); nn_assert (ipv4onlylen == sizeof (ipv4only)); /* Parse the local address, if any. */ semicolon = strchr (addr, ';'); memset (&local, 0, sizeof (local)); if (semicolon) rc = nn_iface_resolve (addr, semicolon - addr, ipv4only, &local, &locallen); else rc = nn_iface_resolve ("*", 1, ipv4only, &local, &locallen); if (nn_slow (rc < 0)) { nn_backoff_start (&self->retry); self->state = NN_CTCP_STATE_WAITING; return; } /* Combine the remote address and the port. */ remote = *ss; remotelen = sslen; if (remote.ss_family == AF_INET) ((struct sockaddr_in*) &remote)->sin_port = htons (port); else if (remote.ss_family == AF_INET6) ((struct sockaddr_in6*) &remote)->sin6_port = htons (port); else nn_assert (0); /* Try to start the underlying socket. */ //printf("CTCP start connecting\n"); rc = nn_usock_start (&self->usock, remote.ss_family, SOCK_STREAM, 0); if ( nn_slow(rc < 0) ) { printf("BACKOFF due to rc.%d\n",rc); nn_backoff_start(&self->retry); self->state = NN_CTCP_STATE_WAITING; return; } //printf("got rc.%d\n",rc); /* Set the relevant socket options. */ sz = sizeof (val); nn_epbase_getopt (&self->epbase, NN_SOL_SOCKET, NN_SNDBUF, &val, &sz); nn_assert (sz == sizeof (val)); nn_usock_setsockopt (&self->usock, SOL_SOCKET, SO_SNDBUF,&val, sizeof (val)); sz = sizeof (val); nn_epbase_getopt (&self->epbase, NN_SOL_SOCKET, NN_RCVBUF, &val, &sz); nn_assert (sz == sizeof (val)); nn_usock_setsockopt (&self->usock, SOL_SOCKET, SO_RCVBUF,&val, sizeof (val)); /* Bind the socket to the local network interface. */ rc = nn_usock_bind (&self->usock, (struct sockaddr*) &local, locallen); if ( nn_slow(rc != 0) ) { printf("bind error.%d, backoff\n",rc); nn_backoff_start (&self->retry); self->state = NN_CTCP_STATE_WAITING; return; } //printf("start connecting\n"); /* Start connecting. */ nn_usock_connect (&self->usock, (struct sockaddr*) &remote, remotelen); self->state = NN_CTCP_STATE_CONNECTING; nn_epbase_stat_increment (&self->epbase,NN_STAT_INPROGRESS_CONNECTIONS, 1); }
static int nn_sock_setopt_inner (struct nn_sock *self, int level, int option, const void *optval, size_t optvallen) { struct nn_optset *optset; int val; int *dst; /* Protocol-specific socket options. */ if (level > NN_SOL_SOCKET) return self->sockbase->vfptr->setopt (self->sockbase, level, option, optval, optvallen); /* Transport-specific options. */ if (level < NN_SOL_SOCKET) { optset = nn_sock_optset (self, level); if (!optset) return -ENOPROTOOPT; return optset->vfptr->setopt (optset, option, optval, optvallen); } /* Special-casing socket name for now as it's the only string option */ if (level == NN_SOL_SOCKET && option == NN_SOCKET_NAME) { if (optvallen > 63) return -EINVAL; memcpy (self->socket_name, optval, optvallen); self->socket_name [optvallen] = 0; return 0; } /* At this point we assume that all options are of type int. */ if (optvallen != sizeof (int)) return -EINVAL; val = *(int*) optval; /* Generic socket-level options. */ if (level == NN_SOL_SOCKET) { switch (option) { case NN_LINGER: dst = &self->linger; break; case NN_SNDBUF: if (nn_slow (val <= 0)) return -EINVAL; dst = &self->sndbuf; break; case NN_RCVBUF: if (nn_slow (val <= 0)) return -EINVAL; dst = &self->rcvbuf; break; case NN_SNDTIMEO: dst = &self->sndtimeo; break; case NN_RCVTIMEO: dst = &self->rcvtimeo; break; case NN_RECONNECT_IVL: if (nn_slow (val < 0)) return -EINVAL; dst = &self->reconnect_ivl; break; case NN_RECONNECT_IVL_MAX: if (nn_slow (val < 0)) return -EINVAL; dst = &self->reconnect_ivl_max; break; case NN_SNDPRIO: if (nn_slow (val < 1 || val > 16)) return -EINVAL; dst = &self->ep_template.sndprio; break; case NN_IPV4ONLY: if (nn_slow (val != 0 && val != 1)) return -EINVAL; dst = &self->ep_template.ipv4only; break; default: return -ENOPROTOOPT; } *dst = val; return 0; } nn_assert (0); }
int nn_sock_getopt_inner (struct nn_sock *self, int level, int option, void *optval, size_t *optvallen) { int rc; struct nn_optset *optset; int intval; nn_fd fd; /* Generic socket-level options. */ if (level == NN_SOL_SOCKET) { switch (option) { case NN_DOMAIN: intval = self->socktype->domain; break; case NN_PROTOCOL: intval = self->socktype->protocol; break; case NN_LINGER: intval = self->linger; break; case NN_SNDBUF: intval = self->sndbuf; break; case NN_RCVBUF: intval = self->rcvbuf; break; case NN_SNDTIMEO: intval = self->sndtimeo; break; case NN_RCVTIMEO: intval = self->rcvtimeo; break; case NN_RECONNECT_IVL: intval = self->reconnect_ivl; break; case NN_RECONNECT_IVL_MAX: intval = self->reconnect_ivl_max; break; case NN_SNDPRIO: intval = self->ep_template.sndprio; break; case NN_IPV4ONLY: intval = self->ep_template.ipv4only; break; case NN_SNDFD: if (self->socktype->flags & NN_SOCKTYPE_FLAG_NOSEND) return -ENOPROTOOPT; fd = nn_efd_getfd (&self->sndfd); memcpy (optval, &fd, *optvallen < sizeof (nn_fd) ? *optvallen : sizeof (nn_fd)); *optvallen = sizeof (nn_fd); return 0; case NN_RCVFD: if (self->socktype->flags & NN_SOCKTYPE_FLAG_NORECV) return -ENOPROTOOPT; fd = nn_efd_getfd (&self->rcvfd); memcpy (optval, &fd, *optvallen < sizeof (nn_fd) ? *optvallen : sizeof (nn_fd)); *optvallen = sizeof (nn_fd); return 0; case NN_SOCKET_NAME: strncpy (optval, self->socket_name, *optvallen); *optvallen = strlen(self->socket_name); return 0; default: return -ENOPROTOOPT; } memcpy (optval, &intval, *optvallen < sizeof (int) ? *optvallen : sizeof (int)); *optvallen = sizeof (int); return 0; } /* Protocol-specific socket options. */ if (level > NN_SOL_SOCKET) return rc = self->sockbase->vfptr->getopt (self->sockbase, level, option, optval, optvallen); /* Transport-specific options. */ if (level < NN_SOL_SOCKET) { optset = nn_sock_optset (self, level); if (!optset) return -ENOPROTOOPT; return optset->vfptr->getopt (optset, option, optval, optvallen); } nn_assert (0); }
int nn_cws_create (struct nn_ep *ep) { int rc; const char *addr; size_t addrlen; const char *semicolon; const char *hostname; size_t hostlen; const char *colon; const char *slash; const char *resource; size_t resourcelen; struct sockaddr_storage ss; size_t sslen; int ipv4only; size_t ipv4onlylen; struct nn_cws *self; int reconnect_ivl; int reconnect_ivl_max; int msg_type; size_t sz; /* Allocate the new endpoint object. */ self = nn_alloc (sizeof (struct nn_cws), "cws"); alloc_assert (self); self->ep = ep; /* Initalise the endpoint. */ nn_ep_tran_setup (ep, &nn_cws_ep_ops, self); /* Check whether IPv6 is to be used. */ ipv4onlylen = sizeof (ipv4only); nn_ep_getopt (ep, NN_SOL_SOCKET, NN_IPV4ONLY, &ipv4only, &ipv4onlylen); nn_assert (ipv4onlylen == sizeof (ipv4only)); /* Start parsing the address. */ addr = nn_ep_getaddr (ep); addrlen = strlen (addr); semicolon = strchr (addr, ';'); hostname = semicolon ? semicolon + 1 : addr; colon = strrchr (addr, ':'); slash = colon ? strchr (colon, '/') : strchr (addr, '/'); resource = slash ? slash : addr + addrlen; self->remote_hostname_len = colon ? colon - hostname : resource - hostname; /* Host contains both hostname and port. */ hostlen = resource - hostname; /* Parse the port; assume port 80 if not explicitly declared. */ if (colon != NULL) { rc = nn_port_resolve (colon + 1, resource - colon - 1); if (rc < 0) { return -EINVAL; } self->remote_port = rc; } else { self->remote_port = 80; } /* Check whether the host portion of the address is either a literal or a valid hostname. */ if (nn_dns_check_hostname (hostname, self->remote_hostname_len) < 0 && nn_literal_resolve (hostname, self->remote_hostname_len, ipv4only, &ss, &sslen) < 0) { return -EINVAL; } /* If local address is specified, check whether it is valid. */ if (semicolon) { rc = nn_iface_resolve (addr, semicolon - addr, ipv4only, &ss, &sslen); if (rc < 0) { return -ENODEV; } } /* At this point, the address is valid, so begin allocating resources. */ nn_chunkref_init (&self->remote_host, hostlen + 1); memcpy (nn_chunkref_data (&self->remote_host), hostname, hostlen); ((uint8_t *) nn_chunkref_data (&self->remote_host)) [hostlen] = '\0'; if (semicolon) { nn_chunkref_init (&self->nic, semicolon - addr); memcpy (nn_chunkref_data (&self->nic), addr, semicolon - addr); } else { nn_chunkref_init (&self->nic, 1); memcpy (nn_chunkref_data (&self->nic), "*", 1); } /* The requested resource is used in opening handshake. */ resourcelen = strlen (resource); if (resourcelen) { nn_chunkref_init (&self->resource, resourcelen + 1); strncpy (nn_chunkref_data (&self->resource), resource, resourcelen + 1); } else { /* No resource specified, so allocate base path. */ nn_chunkref_init (&self->resource, 2); strncpy (nn_chunkref_data (&self->resource), "/", 2); } /* Initialise the structure. */ nn_fsm_init_root (&self->fsm, nn_cws_handler, nn_cws_shutdown, nn_ep_getctx (ep)); self->state = NN_CWS_STATE_IDLE; nn_usock_init (&self->usock, NN_CWS_SRC_USOCK, &self->fsm); sz = sizeof (msg_type); nn_ep_getopt (ep, NN_WS, NN_WS_MSG_TYPE, &msg_type, &sz); nn_assert (sz == sizeof (msg_type)); self->msg_type = (uint8_t) msg_type; sz = sizeof (reconnect_ivl); nn_ep_getopt (ep, NN_SOL_SOCKET, NN_RECONNECT_IVL, &reconnect_ivl, &sz); nn_assert (sz == sizeof (reconnect_ivl)); sz = sizeof (reconnect_ivl_max); nn_ep_getopt (ep, NN_SOL_SOCKET, NN_RECONNECT_IVL_MAX, &reconnect_ivl_max, &sz); nn_assert (sz == sizeof (reconnect_ivl_max)); if (reconnect_ivl_max == 0) reconnect_ivl_max = reconnect_ivl; nn_backoff_init (&self->retry, NN_CWS_SRC_RECONNECT_TIMER, reconnect_ivl, reconnect_ivl_max, &self->fsm); nn_sws_init (&self->sws, NN_CWS_SRC_SWS, ep, &self->fsm); nn_dns_init (&self->dns, NN_CWS_SRC_DNS, &self->fsm); /* Start the state machine. */ nn_fsm_start (&self->fsm); return 0; }
static void nn_cws_start_connecting (struct nn_cws *self, struct sockaddr_storage *ss, size_t sslen) { int rc; struct sockaddr_storage remote; size_t remotelen; struct sockaddr_storage local; size_t locallen; int ipv4only; int val; size_t sz; memset (&remote, 0, sizeof (remote)); memset (&local, 0, sizeof (local)); /* Check whether IPv6 is to be used. */ sz = sizeof (ipv4only); nn_ep_getopt (self->ep, NN_SOL_SOCKET, NN_IPV4ONLY, &ipv4only, &sz); nn_assert (sz == sizeof (ipv4only)); rc = nn_iface_resolve (nn_chunkref_data (&self->nic), nn_chunkref_size (&self->nic), ipv4only, &local, &locallen); if (nn_slow (rc < 0)) { nn_backoff_start (&self->retry); self->state = NN_CWS_STATE_WAITING; return; } /* Combine the remote address and the port. */ remote = *ss; remotelen = sslen; if (remote.ss_family == AF_INET) ((struct sockaddr_in*) &remote)->sin_port = htons (self->remote_port); else if (remote.ss_family == AF_INET6) ((struct sockaddr_in6*) &remote)->sin6_port = htons (self->remote_port); else nn_assert (0); /* Try to start the underlying socket. */ rc = nn_usock_start (&self->usock, remote.ss_family, SOCK_STREAM, 0); if (nn_slow (rc < 0)) { nn_backoff_start (&self->retry); self->state = NN_CWS_STATE_WAITING; return; } /* Set the relevant socket options. */ sz = sizeof (val); nn_ep_getopt (self->ep, NN_SOL_SOCKET, NN_SNDBUF, &val, &sz); nn_assert (sz == sizeof (val)); nn_usock_setsockopt (&self->usock, SOL_SOCKET, SO_SNDBUF, &val, sizeof (val)); sz = sizeof (val); nn_ep_getopt (self->ep, NN_SOL_SOCKET, NN_RCVBUF, &val, &sz); nn_assert (sz == sizeof (val)); nn_usock_setsockopt (&self->usock, SOL_SOCKET, SO_RCVBUF, &val, sizeof (val)); /* Bind the socket to the local network interface. */ rc = nn_usock_bind (&self->usock, (struct sockaddr*) &local, locallen); if (nn_slow (rc != 0)) { nn_backoff_start (&self->retry); self->state = NN_CWS_STATE_WAITING; return; } /* Start connecting. */ nn_usock_connect (&self->usock, (struct sockaddr*) &remote, remotelen); self->state = NN_CWS_STATE_CONNECTING; nn_ep_stat_increment (self->ep, NN_STAT_INPROGRESS_CONNECTIONS, 1); }
int nn_iface_resolve (const char *addr, size_t addrlen, int ipv4only, struct sockaddr_storage *result, size_t *resultlen) { int rc; struct ifaddrs *ifaces; struct ifaddrs *it; struct ifaddrs *ipv4; struct ifaddrs *ipv6; size_t ifalen; /* Asterisk is a special name meaning "all interfaces". */ if (addrlen == 1 && addr [0] == '*') { nn_iface_any (ipv4only, result, resultlen); return 0; } /* Try to resolve the supplied string as a literal address. */ rc = nn_literal_resolve (addr, addrlen, ipv4only, result, resultlen); if (rc == 0) return 0; errnum_assert (rc == -EINVAL, -rc); /* Get the list of local network interfaces from the system. */ ifaces = NULL; rc = getifaddrs (&ifaces); errno_assert (rc == 0); nn_assert (ifaces); /* Find the NIC with the specified name. */ ipv4 = NULL; ipv6 = NULL; for (it = ifaces; it != NULL; it = it->ifa_next) { if (!it->ifa_addr) continue; ifalen = strlen (it->ifa_name); if (ifalen != addrlen || memcmp (it->ifa_name, addr, addrlen) != 0) continue; switch (it->ifa_addr->sa_family) { case AF_INET: nn_assert (!ipv4); ipv4 = it; break; case AF_INET6: nn_assert (!ipv6); ipv6 = it; break; } } /* IPv6 address is preferable. */ if (ipv6 && !ipv4only) { if (result) { result->ss_family = AF_INET6; memcpy (result, ipv6->ifa_addr, sizeof (struct sockaddr_in6)); } if (resultlen) *resultlen = sizeof (struct sockaddr_in6); freeifaddrs (ifaces); return 0; } /* Use IPv4 address. */ if (ipv4) { if (result) { result->ss_family = AF_INET; memcpy (result, ipv4->ifa_addr, sizeof (struct sockaddr_in)); } if (resultlen) *resultlen = sizeof (struct sockaddr_in); freeifaddrs (ifaces); return 0; } /* There's no such interface. */ freeifaddrs (ifaces); return -ENODEV; }
static void nn_timer_handler (struct nn_fsm *self, int src, int type, void *srcptr) { struct nn_timer *timer; timer = nn_cont (self, struct nn_timer, fsm); switch (timer->state) { /******************************************************************************/ /* IDLE state. */ /******************************************************************************/ case NN_TIMER_STATE_IDLE: switch (src) { case NN_FSM_ACTION: switch (type) { case NN_FSM_START: /* Send start event to the worker thread. */ timer->state = NN_TIMER_STATE_ACTIVE; nn_worker_execute (timer->worker, &timer->start_task); return; default: nn_fsm_bad_action (timer->state, src, type); } default: nn_fsm_bad_source (timer->state, src, type); } /******************************************************************************/ /* ACTIVE state. */ /******************************************************************************/ case NN_TIMER_STATE_ACTIVE: if (src == NN_TIMER_SRC_START_TASK) { nn_assert (type == NN_WORKER_TASK_EXECUTE); nn_assert (timer->timeout >= 0); nn_worker_add_timer (timer->worker, timer->timeout, &timer->wtimer); timer->timeout = -1; return; } if (srcptr == &timer->wtimer) { switch (type) { case NN_WORKER_TIMER_TIMEOUT: /* Notify the user about the timeout. */ nn_assert (timer->timeout == -1); nn_fsm_raise (&timer->fsm, &timer->done, NN_TIMER_TIMEOUT); return; default: nn_fsm_bad_action (timer->state, src, type); } } nn_fsm_bad_source (timer->state, src, type); /******************************************************************************/ /* Invalid state. */ /******************************************************************************/ default: nn_fsm_bad_state (timer->state, src, type); } }
static void nn_stcp_handler (struct nn_fsm *self, int src, int type, void *srcptr) { int rc; struct nn_stcp *stcp; uint64_t size; stcp = nn_cont (self, struct nn_stcp, fsm); /******************************************************************************/ /* STOP procedure. */ /******************************************************************************/ if (nn_slow (src == NN_FSM_ACTION && type == NN_FSM_STOP)) { nn_pipebase_stop (&stcp->pipebase); nn_streamhdr_stop (&stcp->streamhdr); stcp->state = NN_STCP_STATE_STOPPING; } if (nn_slow (stcp->state == NN_STCP_STATE_STOPPING)) { if (nn_streamhdr_isidle (&stcp->streamhdr)) { nn_usock_swap_owner (stcp->usock, &stcp->usock_owner); stcp->usock = NULL; stcp->usock_owner.src = -1; stcp->usock_owner.fsm = NULL; stcp->state = NN_STCP_STATE_IDLE; nn_fsm_stopped (&stcp->fsm, NN_STCP_STOPPED); return; } return; } switch (stcp->state) { /******************************************************************************/ /* IDLE state. */ /******************************************************************************/ case NN_STCP_STATE_IDLE: switch (src) { case NN_FSM_ACTION: switch (type) { case NN_FSM_START: nn_streamhdr_start (&stcp->streamhdr, stcp->usock, &stcp->pipebase); stcp->state = NN_STCP_STATE_PROTOHDR; return; default: nn_assert (0); } default: nn_assert (0); } /******************************************************************************/ /* PROTOHDR state. */ /******************************************************************************/ case NN_STCP_STATE_PROTOHDR: switch (src) { case NN_STCP_SRC_STREAMHDR: switch (type) { case NN_STREAMHDR_OK: /* Before moving to the active state stop the streamhdr state machine. */ nn_streamhdr_stop (&stcp->streamhdr); stcp->state = NN_STCP_STATE_STOPPING_STREAMHDR; return; case NN_STREAMHDR_ERROR: /* Raise the error and move directly to the DONE state. streamhdr object will be stopped later on. */ stcp->state = NN_STCP_STATE_DONE; nn_fsm_raise (&stcp->fsm, &stcp->done, NN_STCP_ERROR); return; default: nn_assert (0); } default: nn_assert (0); } /******************************************************************************/ /* STOPPING_STREAMHDR state. */ /******************************************************************************/ case NN_STCP_STATE_STOPPING_STREAMHDR: switch (src) { case NN_STCP_SRC_STREAMHDR: switch (type) { case NN_STREAMHDR_STOPPED: /* Start the pipe. */ rc = nn_pipebase_start (&stcp->pipebase); errnum_assert (rc == 0, -rc); /* Start receiving a message in asynchronous manner. */ stcp->instate = NN_STCP_INSTATE_HDR; nn_usock_recv (stcp->usock, &stcp->inhdr, sizeof (stcp->inhdr)); /* Mark the pipe as available for sending. */ stcp->outstate = NN_STCP_OUTSTATE_IDLE; stcp->state = NN_STCP_STATE_ACTIVE; return; default: nn_assert (0); } default: nn_assert (0); } /******************************************************************************/ /* ACTIVE state. */ /******************************************************************************/ case NN_STCP_STATE_ACTIVE: switch (src) { case NN_STCP_SRC_USOCK: switch (type) { case NN_USOCK_SENT: /* The message is now fully sent. */ nn_assert (stcp->outstate == NN_STCP_OUTSTATE_SENDING); stcp->outstate = NN_STCP_OUTSTATE_IDLE; nn_msg_term (&stcp->outmsg); nn_msg_init (&stcp->outmsg, 0); nn_pipebase_sent (&stcp->pipebase); return; case NN_USOCK_RECEIVED: switch (stcp->instate) { case NN_STCP_INSTATE_HDR: /* Message header was received. Allocate memory for the message. */ size = nn_getll (stcp->inhdr); nn_msg_term (&stcp->inmsg); nn_msg_init (&stcp->inmsg, (size_t) size); /* Special case when size of the message body is 0. */ if (!size) { stcp->instate = NN_STCP_INSTATE_HASMSG; nn_pipebase_received (&stcp->pipebase); return; } /* Start receiving the message body. */ stcp->instate = NN_STCP_INSTATE_BODY; nn_usock_recv (stcp->usock, nn_chunkref_data (&stcp->inmsg.body), (size_t) size); return; case NN_STCP_INSTATE_BODY: /* Message body was received. Notify the owner that it can receive it. */ stcp->instate = NN_STCP_INSTATE_HASMSG; nn_pipebase_received (&stcp->pipebase); return; default: nn_assert (0); } case NN_USOCK_ERROR: nn_pipebase_stop (&stcp->pipebase); stcp->state = NN_STCP_STATE_DONE; nn_fsm_raise (&stcp->fsm, &stcp->done, NN_STCP_ERROR); return; default: nn_assert (0); } default: nn_assert (0); } /******************************************************************************/ /* DONE state. */ /* The underlying connection is closed. There's nothing that can be done in */ /* this state except stopping the object. */ /******************************************************************************/ case NN_STCP_STATE_DONE: nn_assert (0); /******************************************************************************/ /* Invalid state. */ /******************************************************************************/ default: nn_assert (0); } }
static void nn_btcp_handler (struct nn_fsm *self, int src, int type, void *srcptr) { struct nn_btcp *btcp; struct nn_list_item *it; struct nn_atcp *atcp; btcp = nn_cont (self, struct nn_btcp, fsm); /******************************************************************************/ /* STOP procedure. */ /******************************************************************************/ if (nn_slow (src == NN_FSM_ACTION && type == NN_FSM_STOP)) { nn_atcp_stop (btcp->atcp); btcp->state = NN_BTCP_STATE_STOPPING_ATCP; } if (nn_slow (btcp->state == NN_BTCP_STATE_STOPPING_ATCP)) { if (!nn_atcp_isidle (btcp->atcp)) return; nn_atcp_term (btcp->atcp); nn_free (btcp->atcp); btcp->atcp = NULL; nn_usock_stop (&btcp->usock); btcp->state = NN_BTCP_STATE_STOPPING_USOCK; } if (nn_slow (btcp->state == NN_BTCP_STATE_STOPPING_USOCK)) { if (!nn_usock_isidle (&btcp->usock)) return; for (it = nn_list_begin (&btcp->atcps); it != nn_list_end (&btcp->atcps); it = nn_list_next (&btcp->atcps, it)) { atcp = nn_cont (it, struct nn_atcp, item); nn_atcp_stop (atcp); } btcp->state = NN_BTCP_STATE_STOPPING_ATCPS; goto atcps_stopping; } if (nn_slow (btcp->state == NN_BTCP_STATE_STOPPING_ATCPS)) { nn_assert (src == NN_BTCP_SRC_ATCP && type == NN_ATCP_STOPPED); atcp = (struct nn_atcp *) srcptr; nn_list_erase (&btcp->atcps, &atcp->item); nn_atcp_term (atcp); nn_free (atcp); /* If there are no more atcp state machines, we can stop the whole btcp object. */ atcps_stopping: if (nn_list_empty (&btcp->atcps)) { btcp->state = NN_BTCP_STATE_IDLE; nn_fsm_stopped_noevent (&btcp->fsm); nn_epbase_stopped (&btcp->epbase); return; } return; } switch (btcp->state) { /******************************************************************************/ /* IDLE state. */ /******************************************************************************/ case NN_BTCP_STATE_IDLE: switch (src) { case NN_FSM_ACTION: switch (type) { case NN_FSM_START: nn_btcp_start_listening (btcp); nn_btcp_start_accepting (btcp); btcp->state = NN_BTCP_STATE_ACTIVE; return; default: nn_fsm_bad_action (btcp->state, src, type); } default: nn_fsm_bad_source (btcp->state, src, type); } /******************************************************************************/ /* ACTIVE state. */ /* The execution is yielded to the atcp state machine in this state. */ /******************************************************************************/ case NN_BTCP_STATE_ACTIVE: if (srcptr == btcp->atcp) { switch (type) { case NN_ATCP_ACCEPTED: /* Move the newly created connection to the list of existing connections. */ nn_list_insert (&btcp->atcps, &btcp->atcp->item, nn_list_end (&btcp->atcps)); btcp->atcp = NULL; /* Start waiting for a new incoming connection. */ nn_btcp_start_accepting (btcp); return; default: nn_fsm_bad_action (btcp->state, src, type); } } /* For all remaining events we'll assume they are coming from one of remaining child atcp objects. */ nn_assert (src == NN_BTCP_SRC_ATCP); atcp = (struct nn_atcp*) srcptr; switch (type) { case NN_ATCP_ERROR: nn_atcp_stop (atcp); return; case NN_ATCP_STOPPED: nn_list_erase (&btcp->atcps, &atcp->item); nn_atcp_term (atcp); nn_free (atcp); return; default: nn_fsm_bad_action (btcp->state, src, type); } /******************************************************************************/ /* Invalid state. */ /******************************************************************************/ default: nn_fsm_bad_state (btcp->state, src, type); } }
int nn_sock_recv (struct nn_sock *self, struct nn_msg *msg, int flags) { int rc; uint64_t deadline; uint64_t now; int timeout; /* Some sockets types cannot be used for receiving messages. */ if (nn_slow (self->socktype->flags & NN_SOCKTYPE_FLAG_NORECV)) return -ENOTSUP; nn_ctx_enter (&self->ctx); /* Compute the deadline for RCVTIMEO timer. */ if (self->rcvtimeo < 0) { deadline = -1; timeout = -1; } else { deadline = nn_clock_now (&self->clock) + self->rcvtimeo; timeout = self->rcvtimeo; } while (1) { /* If nn_term() was already called, return ETERM. */ if (nn_slow (self->state == NN_SOCK_STATE_ZOMBIE)) { nn_ctx_leave (&self->ctx); return -ETERM; } /* Try to receive the message in a non-blocking way. */ rc = self->sockbase->vfptr->recv (self->sockbase, msg); if (nn_fast (rc == 0)) { nn_ctx_leave (&self->ctx); return 0; } nn_assert (rc < 0); /* Any unexpected error is forwarded to the caller. */ if (nn_slow (rc != -EAGAIN)) { nn_ctx_leave (&self->ctx); return rc; } /* If the message cannot be received at the moment and the recv call is non-blocking, return immediately. */ if (nn_fast (flags & NN_DONTWAIT)) { nn_ctx_leave (&self->ctx); return -EAGAIN; } /* With blocking recv, wait while there are new pipes available for receiving. */ nn_ctx_leave (&self->ctx); rc = nn_efd_wait (&self->rcvfd, timeout); if (nn_slow (rc == -ETIMEDOUT)) return -EAGAIN; if (nn_slow (rc == -EINTR)) return -EINTR; errnum_assert (rc == 0, rc); nn_ctx_enter (&self->ctx); /* If needed, re-compute the timeout to reflect the time that have already elapsed. */ if (self->rcvtimeo >= 0) { now = nn_clock_now (&self->clock); timeout = (int) (now > deadline ? 0 : deadline - now); } } }
int nn_btcp_create (void *hint, struct nn_epbase **epbase) { int rc; struct nn_btcp *self; const char *addr; size_t addrlen; const char *end; const char *pos; int port; struct sockaddr_storage ss; size_t sslen; int ipv4only; size_t ipv4onlylen; /* Allocate the new endpoint object. */ self = nn_alloc (sizeof (struct nn_btcp), "btcp"); alloc_assert (self); /* Initalise the epbase. */ nn_epbase_init (&self->epbase, &nn_btcp_epbase_vfptr, hint); addr = nn_epbase_getaddr (&self->epbase); addrlen = strlen (addr); /* Parse the port. */ end = addr + strlen (addr); pos = strrchr (addr, ':'); if (nn_slow (!pos)) { nn_epbase_term (&self->epbase); return -EINVAL; } ++pos; rc = nn_port_resolve (pos, end - pos); if (nn_slow (rc < 0)) { nn_epbase_term (&self->epbase); return -EINVAL; } port = rc; /* Check whether IPv6 is to be used. */ ipv4onlylen = sizeof (ipv4only); nn_epbase_getopt (&self->epbase, NN_SOL_SOCKET, NN_IPV4ONLY, &ipv4only, &ipv4onlylen); nn_assert (ipv4onlylen == sizeof (ipv4only)); /* Parse the address. */ rc = nn_iface_resolve (addr, pos - addr - 1, ipv4only, &ss, &sslen); if (nn_slow (rc < 0)) { nn_epbase_term (&self->epbase); return -ENODEV; } /* Initialise the structure. */ nn_fsm_init_root (&self->fsm, nn_btcp_handler, nn_epbase_getctx (&self->epbase)); self->state = NN_BTCP_STATE_IDLE; nn_usock_init (&self->usock, NN_BTCP_SRC_USOCK, &self->fsm); self->atcp = NULL; nn_list_init (&self->atcps); /* Start the state machine. */ nn_fsm_start (&self->fsm); /* Return the base class as an out parameter. */ *epbase = &self->epbase; return 0; }
int nn_sock_init (struct nn_sock *self, struct nn_socktype *socktype, int fd) { int rc; int i; /* Make sure that at least one message direction is supported. */ nn_assert (!(socktype->flags & NN_SOCKTYPE_FLAG_NOSEND) || !(socktype->flags & NN_SOCKTYPE_FLAG_NORECV)); /* Create the AIO context for the SP socket. */ nn_ctx_init (&self->ctx, nn_global_getpool (), nn_sock_onleave); /* Initialise the state machine. */ nn_fsm_init_root (&self->fsm, nn_sock_handler, nn_sock_shutdown, &self->ctx); self->state = NN_SOCK_STATE_INIT; /* Open the NN_SNDFD and NN_RCVFD efds. Do so, only if the socket type supports send/recv, as appropriate. */ if (socktype->flags & NN_SOCKTYPE_FLAG_NOSEND) memset (&self->sndfd, 0xcd, sizeof (self->sndfd)); else { rc = nn_efd_init (&self->sndfd); if (nn_slow (rc < 0)) return rc; } if (socktype->flags & NN_SOCKTYPE_FLAG_NORECV) memset (&self->rcvfd, 0xcd, sizeof (self->rcvfd)); else { rc = nn_efd_init (&self->rcvfd); if (nn_slow (rc < 0)) { if (!(socktype->flags & NN_SOCKTYPE_FLAG_NOSEND)) nn_efd_term (&self->sndfd); return rc; } } nn_sem_init (&self->termsem); if (nn_slow (rc < 0)) { if (!(socktype->flags & NN_SOCKTYPE_FLAG_NORECV)) nn_efd_term (&self->rcvfd); if (!(socktype->flags & NN_SOCKTYPE_FLAG_NOSEND)) nn_efd_term (&self->sndfd); return rc; } self->flags = 0; nn_clock_init (&self->clock); nn_list_init (&self->eps); nn_list_init (&self->sdeps); self->eid = 1; /* Default values for NN_SOL_SOCKET options. */ self->linger = 1000; self->sndbuf = 128 * 1024; self->rcvbuf = 128 * 1024; self->sndtimeo = -1; self->rcvtimeo = -1; self->reconnect_ivl = 100; self->reconnect_ivl_max = 0; self->ep_template.sndprio = 8; self->ep_template.ipv4only = 1; /* Initialize statistic entries */ self->statistics.established_connections = 0; self->statistics.accepted_connections = 0; self->statistics.dropped_connections = 0; self->statistics.broken_connections = 0; self->statistics.connect_errors = 0; self->statistics.bind_errors = 0; self->statistics.accept_errors = 0; self->statistics.messages_sent = 0; self->statistics.messages_received = 0; self->statistics.bytes_sent = 0; self->statistics.bytes_received = 0; self->statistics.current_connections = 0; self->statistics.inprogress_connections = 0; self->statistics.current_snd_priority = 0; self->statistics.current_ep_errors = 0; /* Should be pretty much enough space for just the number */ sprintf(self->socket_name, "%d", fd); /* The transport-specific options are not initialised immediately, rather, they are allocated later on when needed. */ for (i = 0; i != NN_MAX_TRANSPORT; ++i) self->optsets [i] = NULL; /* Create the specific socket type itself. */ rc = socktype->create ((void*) self, &self->sockbase); errnum_assert (rc == 0, -rc); self->socktype = socktype; /* Launch the state machine. */ nn_ctx_enter (&self->ctx); nn_fsm_start (&self->fsm); nn_ctx_leave (&self->ctx); return 0; }
int nn_ctcp_create (void *hint, struct nn_epbase **epbase) { int rc; const char *addr; size_t addrlen; const char *semicolon; const char *hostname; const char *colon; const char *end; struct sockaddr_storage ss; size_t sslen; int ipv4only; size_t ipv4onlylen; struct nn_ctcp *self; int reconnect_ivl; int reconnect_ivl_max; size_t sz; /* Allocate the new endpoint object. */ self = nn_alloc (sizeof (struct nn_ctcp), "ctcp"); alloc_assert (self); /* Initalise the endpoint. */ nn_epbase_init (&self->epbase, &nn_ctcp_epbase_vfptr, hint); /* Check whether IPv6 is to be used. */ ipv4onlylen = sizeof (ipv4only); nn_epbase_getopt (&self->epbase, NN_SOL_SOCKET, NN_IPV4ONLY, &ipv4only, &ipv4onlylen); nn_assert (ipv4onlylen == sizeof (ipv4only)); /* Start parsing the address. */ addr = nn_epbase_getaddr (&self->epbase); addrlen = strlen (addr); semicolon = strchr (addr, ';'); hostname = semicolon ? semicolon + 1 : addr; colon = strrchr (addr, ':'); end = addr + addrlen; /* Parse the port. */ if (nn_slow (!colon)) { nn_epbase_term (&self->epbase); return -EINVAL; } rc = nn_port_resolve (colon + 1, end - colon - 1); if (nn_slow (rc < 0)) { nn_epbase_term (&self->epbase); return -EINVAL; } /* Check whether the host portion of the address is either a literal or a valid hostname. */ if (nn_dns_check_hostname (hostname, colon - hostname) < 0 && nn_literal_resolve (hostname, colon - hostname, ipv4only, &ss, &sslen) < 0) { nn_epbase_term (&self->epbase); return -EINVAL; } /* If local address is specified, check whether it is valid. */ if (semicolon) { rc = nn_iface_resolve (addr, semicolon - addr, ipv4only, &ss, &sslen); if (rc < 0) { nn_epbase_term (&self->epbase); return -ENODEV; } } /* Initialise the structure. */ nn_fsm_init_root (&self->fsm, nn_ctcp_handler, nn_ctcp_shutdown, nn_epbase_getctx (&self->epbase)); self->state = NN_CTCP_STATE_IDLE; nn_usock_init (&self->usock, NN_CTCP_SRC_USOCK, &self->fsm); sz = sizeof (reconnect_ivl); nn_epbase_getopt (&self->epbase, NN_SOL_SOCKET, NN_RECONNECT_IVL, &reconnect_ivl, &sz); nn_assert (sz == sizeof (reconnect_ivl)); sz = sizeof (reconnect_ivl_max); nn_epbase_getopt (&self->epbase, NN_SOL_SOCKET, NN_RECONNECT_IVL_MAX, &reconnect_ivl_max, &sz); nn_assert (sz == sizeof (reconnect_ivl_max)); if (reconnect_ivl_max == 0) reconnect_ivl_max = reconnect_ivl; nn_backoff_init (&self->retry, NN_CTCP_SRC_RECONNECT_TIMER, reconnect_ivl, reconnect_ivl_max, &self->fsm); nn_stcp_init (&self->stcp, NN_CTCP_SRC_STCP, &self->epbase, &self->fsm); nn_dns_init (&self->dns, NN_CTCP_SRC_DNS, &self->fsm); /* Start the state machine. */ nn_fsm_start (&self->fsm); /* Return the base class as an out parameter. */ *epbase = &self->epbase; return 0; }
/* Main body of the daemon. */ static void nn_tcpmuxd_routine (void *arg) { int rc; struct nn_tcpmuxd_ctx *ctx; int conn; int pos; char service [256]; struct nn_tcpmuxd_conn *tc = 0; size_t sz; ssize_t ssz; int i; struct nn_list_item *it; unsigned char buf [2]; struct timeval tv; ctx = (struct nn_tcpmuxd_ctx*) arg; while (1) { /* Wait for events. */ rc = (int32_t)poll (ctx->pfd, (int32_t)ctx->pfd_size, -1); errno_assert (rc >= 0); nn_assert (rc != 0); /* There's an incoming TCP connection. */ if (ctx->pfd [0].revents & POLLIN) { /* Accept the connection. */ conn = accept (ctx->tcp_listener, NULL, NULL); if (conn < 0 && errno == ECONNABORTED) continue; errno_assert (conn >= 0); /* Set timeouts to prevent malevolent client blocking the service. Note that these options are not supported on Solaris. */ tv.tv_sec = 0; tv.tv_usec = 100000; rc = setsockopt (conn, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof (tv)); errno_assert (rc == 0 || (rc < 0 && errno == ENOPROTOOPT)); rc = setsockopt (conn, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof (tv)); errno_assert (rc == 0 || (rc < 0 && errno == ENOPROTOOPT)); /* Read TCPMUX header. */ pos = 0; while (1) { nn_assert (pos < sizeof (service)); ssz = recv (conn, &service [pos], 1, 0); if (ssz < 0 && errno == EAGAIN) { close (conn); continue; } errno_assert (ssz >= 0); nn_assert (ssz == 1); service [pos] = tolower ((uint32_t)service [pos]); if (pos > 0 && service [pos - 1] == 0x0d && service [pos] == 0x0a) break; ++pos; } service [pos - 1] = 0; /* Check whether specified service is listening. */ for (it = nn_list_begin (&ctx->conns); it != nn_list_end (&ctx->conns); it = nn_list_next (&ctx->conns, it)) { tc = nn_cont (it, struct nn_tcpmuxd_conn, item); if (strcmp (service, tc->service) == 0) break; } /* If no one is listening, tear down the connection. */ if (it == nn_list_end (&ctx->conns)) { ssz = send (conn, "-\x0d\x0a", 3, 0); if (ssz < 0 && errno == EAGAIN) { close (conn); continue; } errno_assert (ssz >= 0); nn_assert (ssz == 3); close (conn); continue; } /* Send TCPMUX reply. */ ssz = send (conn, "+\x0d\x0a", 3, 0); if (ssz < 0 && errno == EAGAIN) { close (conn); continue; } errno_assert (ssz >= 0); nn_assert (ssz == 3); nn_assert (tc != 0); /* Pass the file descriptor to the listening process. */ rc = nn_tcpmuxd_send_fd (tc->fd, conn); errno_assert (rc == 0); } /* There's an incoming IPC connection. */ if (ctx->pfd [1].revents & POLLIN) { /* Accept the connection. */ conn = accept (ctx->ipc_listener, NULL, NULL); if (conn < 0 && errno == ECONNABORTED) continue; errno_assert (conn >= 0); /* Create new connection entry. */ tc = nn_alloc (sizeof (struct nn_tcpmuxd_conn), "tcpmuxd_conn"); nn_assert (tc); tc->fd = conn; nn_list_item_init (&tc->item); /* Adjust the pollset. We will poll for errors only. */ ctx->pfd_size++; if (ctx->pfd_size > ctx->pfd_capacity) { ctx->pfd_capacity *= 2; ctx->pfd = nn_realloc (ctx->pfd, sizeof (struct pollfd) * ctx->pfd_capacity); alloc_assert (ctx->pfd); } ctx->pfd [ctx->pfd_size - 1].fd = conn; ctx->pfd [ctx->pfd_size - 1].events = 0; ctx->pfd [ctx->pfd_size - 1].revents = 0; /* Read the connection header. */ ssz = recv (conn, buf, 2, 0); errno_assert (ssz >= 0); nn_assert (ssz == 2); sz = nn_gets (buf); tc->service = nn_alloc (sz + 1, "tcpmuxd_conn.service"); nn_assert (tc->service); ssz = recv (conn, tc->service, sz, 0); errno_assert (ssz >= 0); nn_assert (ssz == sz); for (i = 0; i != sz; ++i) tc->service [i] = tolower ((uint32_t)tc->service [i]); tc->service [sz] = 0; /* Add the entry to the IPC connections list. */ nn_list_insert (&ctx->conns, &tc->item, nn_list_end (&ctx->conns)); } for (i = 2; i < ctx->pfd_size; ++i) { if (ctx->pfd [i].revents & POLLERR || ctx->pfd [i].revents & POLLHUP) { nn_tcpmuxd_disconnect (ctx, i); i--; } } }
int main () { int rc; int push1; int push2; int pull1; int pull2; int sndprio; int rcvprio; /* Test send priorities. */ pull1 = test_socket (AF_SP, NN_PULL); test_bind (pull1, SOCKET_ADDRESS_A); pull2 = test_socket (AF_SP, NN_PULL); test_bind (pull2, SOCKET_ADDRESS_B); push1 = test_socket (AF_SP, NN_PUSH); sndprio = 1; rc = nn_setsockopt (push1, NN_SOL_SOCKET, NN_SNDPRIO, &sndprio, sizeof (sndprio)); errno_assert (rc == 0); test_connect (push1, SOCKET_ADDRESS_A); sndprio = 2; rc = nn_setsockopt (push1, NN_SOL_SOCKET, NN_SNDPRIO, &sndprio, sizeof (sndprio)); errno_assert (rc == 0); test_connect (push1, SOCKET_ADDRESS_B); test_send (push1, "ABC"); test_send (push1, "DEF"); test_recv (pull1, "ABC"); test_recv (pull1, "DEF"); test_close (pull1); test_close (push1); test_close (pull2); /* Test receive priorities. */ push1 = test_socket (AF_SP, NN_PUSH); test_bind (push1, SOCKET_ADDRESS_A); push2 = test_socket (AF_SP, NN_PUSH); test_bind (push2, SOCKET_ADDRESS_B); pull1 = test_socket (AF_SP, NN_PULL); rcvprio = 2; rc = nn_setsockopt (pull1, NN_SOL_SOCKET, NN_RCVPRIO, &rcvprio, sizeof (rcvprio)); errno_assert (rc == 0); test_connect (pull1, SOCKET_ADDRESS_A); rcvprio = 1; rc = nn_setsockopt (pull1, NN_SOL_SOCKET, NN_RCVPRIO, &rcvprio, sizeof (rcvprio)); errno_assert (rc == 0); test_connect (pull1, SOCKET_ADDRESS_B); test_send (push1, "ABC"); test_send (push2, "DEF"); nn_sleep (100); test_recv (pull1, "DEF"); test_recv (pull1, "ABC"); test_close (pull1); test_close (push2); test_close (push1); /* Test removing a pipe from the list. */ push1 = test_socket (AF_SP, NN_PUSH); test_bind (push1, SOCKET_ADDRESS_A); pull1 = test_socket (AF_SP, NN_PULL); test_connect (pull1, SOCKET_ADDRESS_A); test_send (push1, "ABC"); test_recv (pull1, "ABC"); test_close (pull1); rc = nn_send (push1, "ABC", 3, NN_DONTWAIT); nn_assert (rc == -1 && nn_errno() == EAGAIN); pull1 = test_socket (AF_SP, NN_PULL); test_connect (pull1, SOCKET_ADDRESS_A); test_send (push1, "ABC"); test_recv (pull1, "ABC"); test_close (pull1); test_close (push1); return 0; }
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; int nnmsg; 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) { 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)) { /* 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; }
/* Main body of the daemon. */ static void nn_tcpmuxd_routine (void *arg) { int rc; struct nn_tcpmuxd_ctx *ctx; struct pollfd pfd [2]; int conn; int pos; char service [256]; struct nn_tcpmuxd_conn *tc; size_t sz; ssize_t ssz; int i; struct nn_list_item *it; unsigned char buf [2]; struct timeval tv; ctx = (struct nn_tcpmuxd_ctx*) arg; pfd [0].fd = ctx->tcp_listener; pfd [0].events = POLLIN; pfd [1].fd = ctx->ipc_listener; pfd [1].events = POLLIN; while (1) { /* Wait for events. */ rc = poll (pfd, 2, -1); errno_assert (rc >= 0); nn_assert (rc != 0); /* There's an incoming TCP connection. */ if (pfd [0].revents & POLLIN) { /* Accept the connection. */ conn = accept (ctx->tcp_listener, NULL, NULL); if (conn < 0 && errno == ECONNABORTED) continue; errno_assert (conn >= 0); tv.tv_sec = 0; tv.tv_usec = 100000; rc = setsockopt (conn, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof (tv)); errno_assert (rc == 0); rc = setsockopt (conn, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof (tv)); errno_assert (rc == 0); /* Read TCPMUX header. */ pos = 0; while (1) { nn_assert (pos < sizeof (service)); ssz = recv (conn, &service [pos], 1, 0); if (ssz < 0 && errno == EAGAIN) { close (conn); continue; } errno_assert (ssz >= 0); nn_assert (ssz == 1); service [pos] = tolower (service [pos]); if (pos > 0 && service [pos - 1] == 0x0d && service [pos] == 0x0a) break; ++pos; } service [pos - 1] = 0; /* Check whether specified service is listening. */ for (it = nn_list_begin (&ctx->conns); it != nn_list_end (&ctx->conns); it = nn_list_next (&ctx->conns, it)) { tc = nn_cont (it, struct nn_tcpmuxd_conn, item); if (strcmp (service, tc->service) == 0) break; } /* If no one is listening, tear down the connection. */ if (it == nn_list_end (&ctx->conns)) { ssz = send (conn, "-\x0d\x0a", 3, 0); if (ssz < 0 && errno == EAGAIN) { close (conn); continue; } errno_assert (ssz >= 0); nn_assert (ssz == 3); close (conn); continue; } /* Send TCPMUX reply. */ ssz = send (conn, "+\x0d\x0a", 3, 0); if (ssz < 0 && errno == EAGAIN) { close (conn); continue; } errno_assert (ssz >= 0); nn_assert (ssz == 3); /* Pass the file descriptor to the listening process. */ rc = send_fd (tc->fd, conn); errno_assert (rc == 0); } /* There's an incoming IPC connection. */ if (pfd [1].revents & POLLIN) { /* Accept the connection. */ conn = accept (ctx->ipc_listener, NULL, NULL); if (conn < 0 && errno == ECONNABORTED) continue; errno_assert (conn >= 0); /* Create new connection entry. */ tc = nn_alloc (sizeof (struct nn_tcpmuxd_conn), "tcpmuxd_conn"); nn_assert (tc); tc->fd = conn; nn_list_item_init (&tc->item); /* Read the connection header. */ ssz = recv (conn, buf, 2, 0); errno_assert (ssz >= 0); nn_assert (ssz == 2); sz = nn_gets (buf); tc->service = nn_alloc (sz + 1, "tcpmuxd_conn.service"); nn_assert (tc->service); ssz = recv (conn, tc->service, sz, 0); errno_assert (ssz >= 0); nn_assert (ssz == sz); for (i = 0; i != sz; ++i) tc->service [sz] = tolower (tc->service [sz]); tc->service [sz] = 0; /* Add the entry to the IPC connections list. */ nn_list_insert (&ctx->conns, &tc->item, nn_list_end (&ctx->conns)); } }
int nn_recvmsg (int s, struct nn_msghdr *msghdr, int flags) { int rc; struct nn_msg msg; uint8_t *data; size_t sz; int i; struct nn_iovec *iov; void *chunk; NN_BASIC_CHECKS; if (nn_slow (!msghdr)) { errno = EINVAL; return -1; } if (nn_slow (msghdr->msg_iovlen < 0)) { errno = EMSGSIZE; return -1; } /* Get a message. */ rc = nn_sock_recv (self.socks [s], &msg, flags); if (nn_slow (rc < 0)) { errno = -rc; return -1; } if (msghdr->msg_iovlen == 1 && msghdr->msg_iov [0].iov_len == NN_MSG) { chunk = nn_chunkref_getchunk (&msg.body); *(void**) (msghdr->msg_iov [0].iov_base) = chunk; sz = nn_chunk_size (chunk); } else { /* Copy the message content into the supplied gather array. */ data = nn_chunkref_data (&msg.body); sz = nn_chunkref_size (&msg.body); for (i = 0; i != msghdr->msg_iovlen; ++i) { iov = &msghdr->msg_iov [i]; if (nn_slow (iov->iov_len == NN_MSG)) { nn_msg_term (&msg); errno = EINVAL; return -1; } if (iov->iov_len > sz) { memcpy (iov->iov_base, data, sz); break; } memcpy (iov->iov_base, data, iov->iov_len); data += iov->iov_len; sz -= iov->iov_len; } sz = nn_chunkref_size (&msg.body); } /* Retrieve the ancillary data from the message. */ if (msghdr->msg_control) { if (msghdr->msg_controllen == NN_MSG) { chunk = nn_chunkref_getchunk (&msg.hdr); *((void**) msghdr->msg_control) = chunk; } else { /* TODO: Copy the data to the supplied buffer, prefix them with size. */ nn_assert (0); } } nn_msg_term (&msg); return (int) sz; }
void nn_queue_item_term (struct nn_queue_item *self) { nn_assert (self->next == NN_QUEUE_NOTINQUEUE); }
int main (int argc, const char *argv[]) { int rc; int sb; int sc; int sb2; int opt; size_t sz; int i; char any_address[128]; test_addr_from (socket_address, "ws", "127.0.0.1", get_test_port (argc, argv)); test_addr_from (any_address, "ws", "*", get_test_port (argc, argv)); /* Try closing bound but unconnected socket. */ sb = test_socket (AF_SP, NN_PAIR); test_bind (sb, any_address); test_close (sb); /* Try closing a TCP socket while it not connected. At the same time test specifying the local address for the connection. */ sc = test_socket (AF_SP, NN_PAIR); test_connect (sc, socket_address); test_close (sc); /* Open the socket anew. */ sc = test_socket (AF_SP, NN_PAIR); /* Check socket options. */ sz = sizeof (opt); rc = nn_getsockopt (sc, NN_WS, NN_WS_MSG_TYPE, &opt, &sz); errno_assert (rc == 0); nn_assert (sz == sizeof (opt)); nn_assert (opt == NN_WS_MSG_TYPE_BINARY); /* Default port 80 should be assumed if not explicitly declared. */ rc = nn_connect (sc, "ws://127.0.0.1"); errno_assert (rc >= 0); /* Try using invalid address strings. */ rc = nn_connect (sc, "ws://*:"); nn_assert (rc < 0); errno_assert (nn_errno () == EINVAL); rc = nn_connect (sc, "ws://*:1000000"); nn_assert (rc < 0); errno_assert (nn_errno () == EINVAL); rc = nn_connect (sc, "ws://*:some_port"); nn_assert (rc < 0); rc = nn_connect (sc, "ws://eth10000;127.0.0.1:5555"); nn_assert (rc < 0); errno_assert (nn_errno () == ENODEV); rc = nn_bind (sc, "ws://127.0.0.1:"); nn_assert (rc < 0); errno_assert (nn_errno () == EINVAL); rc = nn_bind (sc, "ws://127.0.0.1:1000000"); nn_assert (rc < 0); errno_assert (nn_errno () == EINVAL); rc = nn_bind (sc, "ws://eth10000:5555"); nn_assert (rc < 0); errno_assert (nn_errno () == ENODEV); rc = nn_connect (sc, "ws://:5555"); nn_assert (rc < 0); errno_assert (nn_errno () == EINVAL); rc = nn_connect (sc, "ws://-hostname:5555"); nn_assert (rc < 0); errno_assert (nn_errno () == EINVAL); rc = nn_connect (sc, "ws://abc.123.---.#:5555"); nn_assert (rc < 0); errno_assert (nn_errno () == EINVAL); rc = nn_connect (sc, "ws://[::1]:5555"); nn_assert (rc < 0); errno_assert (nn_errno () == EINVAL); rc = nn_connect (sc, "ws://abc.123.:5555"); nn_assert (rc < 0); errno_assert (nn_errno () == EINVAL); rc = nn_connect (sc, "ws://abc...123:5555"); nn_assert (rc < 0); errno_assert (nn_errno () == EINVAL); rc = nn_connect (sc, "ws://.123:5555"); nn_assert (rc < 0); errno_assert (nn_errno () == EINVAL); test_close (sc); sb = test_socket (AF_SP, NN_PAIR); test_bind (sb, socket_address); sc = test_socket (AF_SP, NN_PAIR); test_connect (sc, socket_address); /* Ping-pong test. */ for (i = 0; i != 100; ++i) { test_send (sc, "ABC"); test_recv (sb, "ABC"); test_send (sb, "DEF"); test_recv (sc, "DEF"); } /* Batch transfer test. */ for (i = 0; i != 100; ++i) { test_send (sc, "0123456789012345678901234567890123456789"); } for (i = 0; i != 100; ++i) { test_recv (sb, "0123456789012345678901234567890123456789"); } test_close (sc); test_close (sb); /* Test that NN_RCVMAXSIZE can be -1, but not lower */ sb = test_socket (AF_SP, NN_PAIR); opt = -1; rc = nn_setsockopt (sb, NN_SOL_SOCKET, NN_RCVMAXSIZE, &opt, sizeof (opt)); nn_assert (rc >= 0); opt = -2; rc = nn_setsockopt (sb, NN_SOL_SOCKET, NN_RCVMAXSIZE, &opt, sizeof (opt)); nn_assert (rc < 0); errno_assert (nn_errno () == EINVAL); test_close (sb); /* Test NN_RCVMAXSIZE limit */ sb = test_socket (AF_SP, NN_PAIR); test_bind (sb, socket_address); sc = test_socket (AF_SP, NN_PAIR); test_connect (sc, socket_address); opt = 1000; test_setsockopt (sc, NN_SOL_SOCKET, NN_SNDTIMEO, &opt, sizeof (opt)); nn_assert (opt == 1000); opt = 1000; test_setsockopt (sb, NN_SOL_SOCKET, NN_RCVTIMEO, &opt, sizeof (opt)); nn_assert (opt == 1000); opt = 4; test_setsockopt (sb, NN_SOL_SOCKET, NN_RCVMAXSIZE, &opt, sizeof (opt)); test_send (sc, "ABC"); test_recv (sb, "ABC"); test_send (sc, "ABCD"); test_recv (sb, "ABCD"); test_send (sc, "ABCDE"); test_drop (sb, ETIMEDOUT); /* Increase the size limit, reconnect, then try sending again. The reason a reconnect is necessary is because after a protocol violation, the connecting socket will not continue automatic reconnection attempts. */ opt = 5; test_setsockopt (sb, NN_SOL_SOCKET, NN_RCVMAXSIZE, &opt, sizeof (opt)); test_connect (sc, socket_address); test_send (sc, "ABCDE"); test_recv (sb, "ABCDE"); test_close (sb); test_close (sc); test_text (); /* Test closing a socket that is waiting to bind. */ sb = test_socket (AF_SP, NN_PAIR); test_bind (sb, socket_address); sb2 = test_socket (AF_SP, NN_PAIR); test_bind (sb2, socket_address); sc = test_socket (AF_SP, NN_PAIR); test_connect (sc, socket_address); test_send (sb, "ABC"); test_recv (sc, "ABC"); test_close (sb2); test_send (sb, "ABC"); test_recv (sc, "ABC"); test_close (sb); test_close (sc); return 0; }
static void nn_sipc_handler (struct nn_fsm *self, int src, int type, NN_UNUSED void *srcptr) { int rc; struct nn_sipc *sipc; uint64_t size; sipc = nn_cont (self, struct nn_sipc, fsm); switch (sipc->state) { /******************************************************************************/ /* IDLE state. */ /******************************************************************************/ case NN_SIPC_STATE_IDLE: switch (src) { case NN_FSM_ACTION: switch (type) { case NN_FSM_START: nn_streamhdr_start (&sipc->streamhdr, sipc->usock, &sipc->pipebase); sipc->state = NN_SIPC_STATE_PROTOHDR; return; default: nn_fsm_bad_action (sipc->state, src, type); } default: nn_fsm_bad_source (sipc->state, src, type); } /******************************************************************************/ /* PROTOHDR state. */ /******************************************************************************/ case NN_SIPC_STATE_PROTOHDR: switch (src) { case NN_SIPC_SRC_STREAMHDR: switch (type) { case NN_STREAMHDR_OK: /* Before moving to the active state stop the streamhdr state machine. */ nn_streamhdr_stop (&sipc->streamhdr); sipc->state = NN_SIPC_STATE_STOPPING_STREAMHDR; return; case NN_STREAMHDR_ERROR: /* Raise the error and move directly to the DONE state. streamhdr object will be stopped later on. */ sipc->state = NN_SIPC_STATE_DONE; nn_fsm_raise (&sipc->fsm, &sipc->done, NN_SIPC_ERROR); return; default: nn_fsm_bad_action (sipc->state, src, type); } default: nn_fsm_bad_source (sipc->state, src, type); } /******************************************************************************/ /* STOPPING_STREAMHDR state. */ /******************************************************************************/ case NN_SIPC_STATE_STOPPING_STREAMHDR: switch (src) { case NN_SIPC_SRC_STREAMHDR: switch (type) { case NN_STREAMHDR_STOPPED: /* Start the pipe. */ rc = nn_pipebase_start (&sipc->pipebase); if (nn_slow (rc < 0)) { sipc->state = NN_SIPC_STATE_DONE; nn_fsm_raise (&sipc->fsm, &sipc->done, NN_SIPC_ERROR); return; } /* Start receiving a message in asynchronous manner. */ sipc->instate = NN_SIPC_INSTATE_HDR; nn_usock_recv (sipc->usock, &sipc->inhdr, sizeof (sipc->inhdr)); /* Mark the pipe as available for sending. */ sipc->outstate = NN_SIPC_OUTSTATE_IDLE; sipc->state = NN_SIPC_STATE_ACTIVE; return; default: nn_fsm_bad_action (sipc->state, src, type); } default: nn_fsm_bad_source (sipc->state, src, type); } /******************************************************************************/ /* ACTIVE state. */ /******************************************************************************/ case NN_SIPC_STATE_ACTIVE: switch (src) { case NN_SIPC_SRC_USOCK: switch (type) { case NN_USOCK_SENT: /* The message is now fully sent. */ nn_assert (sipc->outstate == NN_SIPC_OUTSTATE_SENDING); sipc->outstate = NN_SIPC_OUTSTATE_IDLE; nn_msg_term (&sipc->outmsg); nn_msg_init (&sipc->outmsg, 0); nn_pipebase_sent (&sipc->pipebase); return; case NN_USOCK_RECEIVED: switch (sipc->instate) { case NN_SIPC_INSTATE_HDR: /* Message header was received. Allocate memory for the message. */ nn_assert (sipc->inhdr [0] == NN_SIPC_MSG_NORMAL); size = nn_getll (sipc->inhdr + 1); nn_msg_term (&sipc->inmsg); nn_msg_init (&sipc->inmsg, (size_t) size); /* Special case when size of the message body is 0. */ if (!size) { sipc->instate = NN_SIPC_INSTATE_HASMSG; nn_pipebase_received (&sipc->pipebase); return; } /* Start receiving the message body. */ sipc->instate = NN_SIPC_INSTATE_BODY; nn_usock_recv (sipc->usock, nn_chunkref_data (&sipc->inmsg.body), (size_t) size); return; case NN_SIPC_INSTATE_BODY: /* Message body was received. Notify the owner that it can receive it. */ sipc->instate = NN_SIPC_INSTATE_HASMSG; nn_pipebase_received (&sipc->pipebase); return; default: nn_assert (0); } case NN_USOCK_SHUTDOWN: nn_pipebase_stop (&sipc->pipebase); sipc->state = NN_SIPC_STATE_SHUTTING_DOWN; return; case NN_USOCK_ERROR: nn_pipebase_stop (&sipc->pipebase); sipc->state = NN_SIPC_STATE_DONE; nn_fsm_raise (&sipc->fsm, &sipc->done, NN_SIPC_ERROR); return; default: nn_fsm_bad_action (sipc->state, src, type); } default: nn_fsm_bad_source (sipc->state, src, type); } /******************************************************************************/ /* SHUTTING_DOWN state. */ /* The underlying connection is closed. We are just waiting that underlying */ /* usock being closed */ /******************************************************************************/ case NN_SIPC_STATE_SHUTTING_DOWN: switch (src) { case NN_SIPC_SRC_USOCK: switch (type) { case NN_USOCK_ERROR: sipc->state = NN_SIPC_STATE_DONE; nn_fsm_raise (&sipc->fsm, &sipc->done, NN_SIPC_ERROR); return; default: nn_fsm_bad_action (sipc->state, src, type); } default: nn_fsm_bad_source (sipc->state, src, type); } /******************************************************************************/ /* DONE state. */ /* The underlying connection is closed. There's nothing that can be done in */ /* this state except stopping the object. */ /******************************************************************************/ case NN_SIPC_STATE_DONE: nn_fsm_bad_source (sipc->state, src, type); /******************************************************************************/ /* Invalid state. */ /******************************************************************************/ default: nn_fsm_bad_state (sipc->state, src, type); } }
int nn_poll (struct nn_pollfd *fds, int nfds, int timeout) { int rc; int i; int pos; int fd; int res; size_t sz; struct pollfd *pfd; /* Construct a pollset to be used with OS-level 'poll' function. */ pfd = nn_alloc (sizeof (struct pollfd) * nfds * 2, "pollset"); alloc_assert (pfd); pos = 0; for (i = 0; i != nfds; ++i) { if (fds [i].events & NN_POLLIN) { sz = sizeof (fd); rc = nn_getsockopt (fds [i].fd, NN_SOL_SOCKET, NN_RCVFD, &fd, &sz); if (nn_slow (rc < 0)) { nn_free (pfd); errno = -rc; return -1; } nn_assert (sz == sizeof (fd)); pfd [pos].fd = fd; pfd [pos].events = POLLIN; ++pos; } if (fds [i].events & NN_POLLOUT) { sz = sizeof (fd); rc = nn_getsockopt (fds [i].fd, NN_SOL_SOCKET, NN_SNDFD, &fd, &sz); if (nn_slow (rc < 0)) { nn_free (pfd); errno = -rc; return -1; } nn_assert (sz == sizeof (fd)); pfd [pos].fd = fd; pfd [pos].events = POLLIN; ++pos; } } /* Do the polling itself. */ rc = poll (pfd, pos, timeout); if (nn_slow (rc <= 0)) { res = errno; nn_free (pfd); errno = res; return rc; } /* Move the results from OS-level poll to nn_poll's pollset. */ res = 0; pos = 0; for (i = 0; i != nfds; ++i) { fds [i].revents = 0; if (fds [i].events & NN_POLLIN) { if (pfd [pos].revents & POLLIN) fds [i].revents |= NN_POLLIN; ++pos; } if (fds [i].events & NN_POLLOUT) { if (pfd [pos].revents & POLLIN) fds [i].revents |= NN_POLLOUT; ++pos; } if (fds [i].revents) ++res; } nn_free (pfd); return res; }
void nn_list_term (struct nn_list *self) { nn_assert (self->first == NULL); nn_assert (self->last == NULL); }
static void nn_btcp_handler (struct nn_fsm *self, int src, int type, void *srcptr) { struct nn_btcp *btcp; struct nn_atcp *atcp; btcp = nn_cont (self, struct nn_btcp, fsm); switch (btcp->state) { /******************************************************************************/ /* IDLE state. */ /******************************************************************************/ case NN_BTCP_STATE_IDLE: switch (src) { case NN_FSM_ACTION: switch (type) { case NN_FSM_START: nn_btcp_start_listening (btcp); return; default: nn_fsm_bad_action (btcp->state, src, type); } default: nn_fsm_bad_source (btcp->state, src, type); } /******************************************************************************/ /* ACTIVE state. */ /* The execution is yielded to the atcp state machine in this state. */ /******************************************************************************/ case NN_BTCP_STATE_ACTIVE: if (srcptr == btcp->atcp) { switch (type) { case NN_ATCP_ACCEPTED: /* Move the newly created connection to the list of existing connections. */ nn_list_insert (&btcp->atcps, &btcp->atcp->item, nn_list_end (&btcp->atcps)); btcp->atcp = NULL; /* Start waiting for a new incoming connection. */ nn_btcp_start_accepting (btcp); return; default: nn_fsm_bad_action (btcp->state, src, type); } } /* For all remaining events we'll assume they are coming from one of remaining child atcp objects. */ nn_assert (src == NN_BTCP_SRC_ATCP); atcp = (struct nn_atcp*) srcptr; switch (type) { case NN_ATCP_ERROR: nn_ardma_stop (atcp); return; case NN_ATCP_STOPPED: nn_list_erase (&btcp->atcps, &atcp->item); nn_ardma_term (atcp); nn_free (atcp); return; default: nn_fsm_bad_action (btcp->state, src, type); } /******************************************************************************/ /* CLOSING_USOCK state. */ /* usock object was asked to stop but it haven't stopped yet. */ /******************************************************************************/ case NN_BTCP_STATE_CLOSING: switch (src) { case NN_BTCP_SRC_USOCK: switch (type) { case NN_USOCK_SHUTDOWN: return; case NN_USOCK_STOPPED: nn_backoff_start (&btcp->retry); btcp->state = NN_BTCP_STATE_WAITING; return; default: nn_fsm_bad_action (btcp->state, src, type); } default: nn_fsm_bad_source (btcp->state, src, type); } /******************************************************************************/ /* WAITING state. */ /* Waiting before re-bind is attempted. This way we won't overload */ /* the system by continuous re-bind attemps. */ /******************************************************************************/ case NN_BTCP_STATE_WAITING: switch (src) { case NN_BTCP_SRC_RECONNECT_TIMER: switch (type) { case NN_BACKOFF_TIMEOUT: nn_backoff_stop (&btcp->retry); btcp->state = NN_BTCP_STATE_STOPPING_BACKOFF; return; default: nn_fsm_bad_action (btcp->state, src, type); } default: nn_fsm_bad_source (btcp->state, src, type); } /******************************************************************************/ /* STOPPING_BACKOFF state. */ /* backoff object was asked to stop, but it haven't stopped yet. */ /******************************************************************************/ case NN_BTCP_STATE_STOPPING_BACKOFF: switch (src) { case NN_BTCP_SRC_RECONNECT_TIMER: switch (type) { case NN_BACKOFF_STOPPED: nn_btcp_start_listening (btcp); return; default: nn_fsm_bad_action (btcp->state, src, type); } default: nn_fsm_bad_source (btcp->state, src, type); } /******************************************************************************/ /* Invalid state. */ /******************************************************************************/ default: nn_fsm_bad_state (btcp->state, src, type); } }