int zmq::router_t::xsend (msg_t *msg_) { // If this is the first part of the message it's the ID of the // peer to send the message to. if (!more_out) { zmq_assert (!current_out); // If we have malformed message (prefix with no subsequent message) // then just silently ignore it. // TODO: The connections should be killed instead. if (msg_->flags () & msg_t::more) { more_out = true; // Find the pipe associated with the identity stored in the prefix. // If there's no such pipe just silently ignore the message, unless // router_mandatory is set. blob_t identity ((unsigned char*) msg_->data (), msg_->size ()); outpipes_t::iterator it = outpipes.find (identity); if (it != outpipes.end ()) { current_out = it->second.pipe; if (!current_out->check_write ()) { it->second.active = false; current_out = NULL; if (mandatory) { more_out = false; errno = EAGAIN; return -1; } } } else if (mandatory) { more_out = false; errno = EHOSTUNREACH; return -1; } } int rc = msg_->close (); errno_assert (rc == 0); rc = msg_->init (); errno_assert (rc == 0); return 0; } // Ignore the MORE flag for raw-sock or assert? if (options.raw_sock) msg_->reset_flags (msg_t::more); // Check whether this is the last part of the message. more_out = msg_->flags () & msg_t::more ? true : false; // Push the message into the pipe. If there's no out pipe, just drop it. if (current_out) { // Close the remote connection if user has asked to do so // by sending zero length message. // Pending messages in the pipe will be dropped (on receiving term- ack) if (raw_sock && msg_->size() == 0) { current_out->terminate (false); int rc = msg_->close (); errno_assert (rc == 0); current_out = NULL; return 0; } bool ok = current_out->write (msg_); if (unlikely (!ok)) current_out = NULL; else if (!more_out) { current_out->flush (); current_out = NULL; } } else { int rc = msg_->close (); errno_assert (rc == 0); } // Detach the message from the data buffer. int rc = msg_->init (); errno_assert (rc == 0); return 0; }
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_rdma); 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); /* 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.socks[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'; } nn_fsm_start(&self.fsm); }
zmq::dish_t::~dish_t () { int rc = _message.close (); errno_assert (rc == 0); }
int zmq::curve_server_t::receive_and_process_zap_reply () { int rc = 0; msg_t msg [7]; // ZAP reply consists of 7 frames // Initialize all reply frames for (int i = 0; i < 7; i++) { rc = msg [i].init (); errno_assert (rc == 0); } for (int i = 0; i < 7; i++) { rc = session->read_zap_msg (&msg [i]); if (rc == -1) break; if ((msg [i].flags () & msg_t::more) == (i < 6? 0: msg_t::more)) { // Temporary support for security debugging puts ("CURVE I: ZAP handler sent incomplete reply message"); errno = EPROTO; rc = -1; break; } } if (rc != 0) goto error; // Address delimiter frame if (msg [0].size () > 0) { // Temporary support for security debugging puts ("CURVE I: ZAP handler sent malformed reply message"); errno = EPROTO; rc = -1; goto error; } // Version frame if (msg [1].size () != 3 || memcmp (msg [1].data (), "1.0", 3)) { // Temporary support for security debugging puts ("CURVE I: ZAP handler sent bad version number"); errno = EPROTO; rc = -1; goto error; } // Request id frame if (msg [2].size () != 1 || memcmp (msg [2].data (), "1", 1)) { // Temporary support for security debugging puts ("CURVE I: ZAP handler sent bad request ID"); errno = EPROTO; rc = -1; goto error; } // Status code frame if (msg [3].size () != 3) { // Temporary support for security debugging puts ("CURVE I: ZAP handler rejected client authentication"); errno = EACCES; rc = -1; goto error; } // Save status code status_code.assign (static_cast <char *> (msg [3].data ()), 3); // Save user id set_user_id (msg [5].data (), msg [5].size ()); // Process metadata frame rc = parse_metadata (static_cast <const unsigned char*> (msg [6].data ()), msg [6].size (), true); error: for (int i = 0; i < 7; i++) { const int rc2 = msg [i].close (); errno_assert (rc2 == 0); } return rc; }
int zmq::signaler_t::make_fdpair (fd_t *r_, fd_t *w_) { #if defined ZMQ_HAVE_EVENTFD // Create eventfd object. fd_t fd = eventfd (0, 0); errno_assert (fd != -1); *w_ = fd; *r_ = fd; return 0; #elif defined ZMQ_HAVE_WINDOWS SECURITY_DESCRIPTOR sd = {0}; SECURITY_ATTRIBUTES sa = {0}; InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION); SetSecurityDescriptorDacl(&sd, TRUE, 0, FALSE); sa.nLength = sizeof(SECURITY_ATTRIBUTES); sa.lpSecurityDescriptor = &sd; // This function has to be in a system-wide critical section so that // two instances of the library don't accidentally create signaler // crossing the process boundary. // We'll use named event object to implement the critical section. // Note that if the event object already exists, the CreateEvent requests // EVENT_ALL_ACCESS access right. If this fails, we try to open // the event object asking for SYNCHRONIZE access only. HANDLE sync = CreateEvent (&sa, FALSE, TRUE, TEXT ("Global\\zmq-signaler-port-sync")); if (sync == NULL && GetLastError () == ERROR_ACCESS_DENIED) sync = OpenEvent (SYNCHRONIZE | EVENT_MODIFY_STATE, FALSE, TEXT ("Global\\zmq-signaler-port-sync")); win_assert (sync != NULL); // Enter the critical section. DWORD dwrc = WaitForSingleObject (sync, INFINITE); zmq_assert (dwrc == WAIT_OBJECT_0); // Windows has no 'socketpair' function. CreatePipe is no good as pipe // handles cannot be polled on. Here we create the socketpair by hand. *w_ = INVALID_SOCKET; *r_ = INVALID_SOCKET; // Create listening socket. SOCKET listener; listener = open_socket (AF_INET, SOCK_STREAM, 0); wsa_assert (listener != INVALID_SOCKET); // Set SO_REUSEADDR and TCP_NODELAY on listening socket. BOOL so_reuseaddr = 1; int rc = setsockopt (listener, SOL_SOCKET, SO_REUSEADDR, (char *)&so_reuseaddr, sizeof (so_reuseaddr)); wsa_assert (rc != SOCKET_ERROR); BOOL tcp_nodelay = 1; rc = setsockopt (listener, IPPROTO_TCP, TCP_NODELAY, (char *)&tcp_nodelay, sizeof (tcp_nodelay)); wsa_assert (rc != SOCKET_ERROR); // Bind listening socket to any free local port. struct sockaddr_in addr; memset (&addr, 0, sizeof (addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = htonl (INADDR_LOOPBACK); addr.sin_port = htons (signaler_port); rc = bind (listener, (const struct sockaddr*) &addr, sizeof (addr)); wsa_assert (rc != SOCKET_ERROR); // Listen for incomming connections. rc = listen (listener, 1); wsa_assert (rc != SOCKET_ERROR); // Create the writer socket. *w_ = WSASocket (AF_INET, SOCK_STREAM, 0, NULL, 0, 0); wsa_assert (*w_ != INVALID_SOCKET); // On Windows, preventing sockets to be inherited by child processes. BOOL brc = SetHandleInformation ((HANDLE) *w_, HANDLE_FLAG_INHERIT, 0); win_assert (brc); // Set TCP_NODELAY on writer socket. rc = setsockopt (*w_, IPPROTO_TCP, TCP_NODELAY, (char *)&tcp_nodelay, sizeof (tcp_nodelay)); wsa_assert (rc != SOCKET_ERROR); // Connect writer to the listener. rc = connect (*w_, (struct sockaddr*) &addr, sizeof (addr)); wsa_assert (rc != SOCKET_ERROR); // Accept connection from writer. *r_ = accept (listener, NULL, NULL); wsa_assert (*r_ != INVALID_SOCKET); // On Windows, preventing sockets to be inherited by child processes. brc = SetHandleInformation ((HANDLE) *r_, HANDLE_FLAG_INHERIT, 0); win_assert (brc); // We don't need the listening socket anymore. Close it. rc = closesocket (listener); wsa_assert (rc != SOCKET_ERROR); // Exit the critical section. brc = SetEvent (sync); win_assert (brc != 0); // Release the kernel object brc = CloseHandle (sync); win_assert (brc != 0); return 0; #elif defined ZMQ_HAVE_OPENVMS // Whilst OpenVMS supports socketpair - it maps to AF_INET only. Further, // it does not set the socket options TCP_NODELAY and TCP_NODELACK which // can lead to performance problems. // // The bug will be fixed in V5.6 ECO4 and beyond. In the meantime, we'll // create the socket pair manually. struct sockaddr_in lcladdr; memset (&lcladdr, 0, sizeof (lcladdr)); lcladdr.sin_family = AF_INET; lcladdr.sin_addr.s_addr = htonl (INADDR_LOOPBACK); lcladdr.sin_port = 0; int listener = open_socket (AF_INET, SOCK_STREAM, 0); errno_assert (listener != -1); int on = 1; int rc = setsockopt (listener, IPPROTO_TCP, TCP_NODELAY, &on, sizeof (on)); errno_assert (rc != -1); rc = setsockopt (listener, IPPROTO_TCP, TCP_NODELACK, &on, sizeof (on)); errno_assert (rc != -1); rc = bind(listener, (struct sockaddr*) &lcladdr, sizeof (lcladdr)); errno_assert (rc != -1); socklen_t lcladdr_len = sizeof (lcladdr); rc = getsockname (listener, (struct sockaddr*) &lcladdr, &lcladdr_len); errno_assert (rc != -1); rc = listen (listener, 1); errno_assert (rc != -1); *w_ = open_socket (AF_INET, SOCK_STREAM, 0); errno_assert (*w_ != -1); rc = setsockopt (*w_, IPPROTO_TCP, TCP_NODELAY, &on, sizeof (on)); errno_assert (rc != -1); rc = setsockopt (*w_, IPPROTO_TCP, TCP_NODELACK, &on, sizeof (on)); errno_assert (rc != -1); rc = connect (*w_, (struct sockaddr*) &lcladdr, sizeof (lcladdr)); errno_assert (rc != -1); *r_ = accept (listener, NULL, NULL); errno_assert (*r_ != -1); close (listener); return 0; #else // All other implementations support socketpair() int sv [2]; int rc = socketpair (AF_UNIX, SOCK_STREAM, 0, sv); errno_assert (rc == 0); *w_ = sv [0]; *r_ = sv [1]; return 0; #endif }
int zmq::plain_server_t::send_zap_request (const std::string &username, const std::string &password) { int rc; msg_t msg; // Address delimiter frame rc = msg.init (); errno_assert (rc == 0); msg.set_flags (msg_t::more); rc = session->write_zap_msg (&msg); if (rc != 0) return close_and_return (&msg, -1); // Version frame rc = msg.init_size (3); errno_assert (rc == 0); memcpy (msg.data (), "1.0", 3); msg.set_flags (msg_t::more); rc = session->write_zap_msg (&msg); if (rc != 0) return close_and_return (&msg, -1); // Request id frame rc = msg.init_size (1); errno_assert (rc == 0); memcpy (msg.data (), "1", 1); msg.set_flags (msg_t::more); rc = session->write_zap_msg (&msg); if (rc != 0) return close_and_return (&msg, -1); // Domain frame rc = msg.init_size (options.zap_domain.length ()); errno_assert (rc == 0); memcpy (msg.data (), options.zap_domain.c_str (), options.zap_domain.length ()); msg.set_flags (msg_t::more); rc = session->write_zap_msg (&msg); if (rc != 0) return close_and_return (&msg, -1); // Address frame rc = msg.init_size (peer_address.length ()); errno_assert (rc == 0); memcpy (msg.data (), peer_address.c_str (), peer_address.length ()); msg.set_flags (msg_t::more); rc = session->write_zap_msg (&msg); if (rc != 0) return close_and_return (&msg, -1); // Identity frame rc = msg.init_size (options.identity_size); errno_assert (rc == 0); memcpy (msg.data (), options.identity, options.identity_size); msg.set_flags (msg_t::more); rc = session->write_zap_msg (&msg); if (rc != 0) return close_and_return (&msg, -1); // Mechanism frame rc = msg.init_size (5); errno_assert (rc == 0); memcpy (msg.data (), "PLAIN", 5); msg.set_flags (msg_t::more); rc = session->write_zap_msg (&msg); if (rc != 0) return close_and_return (&msg, -1); // Username frame rc = msg.init_size (username.length ()); errno_assert (rc == 0); memcpy (msg.data (), username.c_str (), username.length ()); msg.set_flags (msg_t::more); rc = session->write_zap_msg (&msg); if (rc != 0) return close_and_return (&msg, -1); // Password frame rc = msg.init_size (password.length ()); errno_assert (rc == 0); memcpy (msg.data (), password.c_str (), password.length ()); rc = session->write_zap_msg (&msg); if (rc != 0) return close_and_return (&msg, -1); return 0; }
void zmq::session_base_t::start_connecting (bool wait_) { zmq_assert (connect); // Choose I/O thread to run connecter in. Given that we are already // running in an I/O thread, there must be at least one available. io_thread_t *io_thread = choose_io_thread (options.affinity); zmq_assert (io_thread); // Create the connecter object. if (addr->protocol == "tcp") { tcp_connecter_t *connecter = new (std::nothrow) tcp_connecter_t ( io_thread, this, options, addr, wait_); alloc_assert (connecter); launch_child (connecter); return; } #if !defined ZMQ_HAVE_WINDOWS && !defined ZMQ_HAVE_OPENVMS if (addr->protocol == "ipc") { ipc_connecter_t *connecter = new (std::nothrow) ipc_connecter_t ( io_thread, this, options, addr, wait_); alloc_assert (connecter); launch_child (connecter); return; } #endif #if defined ZMQ_HAVE_OPENPGM // Both PGM and EPGM transports are using the same infrastructure. if (addr->protocol == "pgm" || addr->protocol == "epgm") { // For EPGM transport with UDP encapsulation of PGM is used. bool udp_encapsulation = (addr->protocol == "epgm"); // At this point we'll create message pipes to the session straight // away. There's no point in delaying it as no concept of 'connect' // exists with PGM anyway. if (options.type == ZMQ_PUB || options.type == ZMQ_XPUB) { // PGM sender. pgm_sender_t *pgm_sender = new (std::nothrow) pgm_sender_t ( io_thread, options); alloc_assert (pgm_sender); int rc = pgm_sender->init (udp_encapsulation, addr->address.c_str ()); errno_assert (rc == 0); send_attach (this, pgm_sender); } else if (options.type == ZMQ_SUB || options.type == ZMQ_XSUB) { // PGM receiver. pgm_receiver_t *pgm_receiver = new (std::nothrow) pgm_receiver_t ( io_thread, options); alloc_assert (pgm_receiver); int rc = pgm_receiver->init (udp_encapsulation, addr->address.c_str ()); errno_assert (rc == 0); send_attach (this, pgm_receiver); } else zmq_assert (false); return; } #endif zmq_assert (false); }
int zmq::socket_base_t::connect (const char *addr_) { if (unlikely (ctx_terminated)) { errno = ETERM; return -1; } // Process pending commands, if any. int rc = process_commands (0, false); if (unlikely (rc != 0)) return -1; // Parse addr_ string. std::string protocol; std::string address; rc = parse_uri (addr_, protocol, address); if (rc != 0) return -1; rc = check_protocol (protocol); if (rc != 0) return -1; if (protocol == "inproc") { // TODO: inproc connect is specific with respect to creating pipes // as there's no 'reconnect' functionality implemented. Once that // is in place we should follow generic pipe creation algorithm. // Find the peer endpoint. endpoint_t peer = find_endpoint (addr_); if (!peer.socket) return -1; // The total HWM for an inproc connection should be the sum of // the binder's HWM and the connector's HWM. int sndhwm = 0; if (options.sndhwm != 0 && peer.options.rcvhwm != 0) sndhwm = options.sndhwm + peer.options.rcvhwm; int rcvhwm = 0; if (options.rcvhwm != 0 && peer.options.sndhwm != 0) rcvhwm = options.rcvhwm + peer.options.sndhwm; // Create a bi-directional pipe to connect the peers. object_t *parents [2] = {this, peer.socket}; pipe_t *new_pipes [2] = {NULL, NULL}; bool conflate = options.conflate && (options.type == ZMQ_DEALER || options.type == ZMQ_PULL || options.type == ZMQ_PUSH || options.type == ZMQ_PUB || options.type == ZMQ_SUB); int hwms [2] = {conflate? -1 : sndhwm, conflate? -1 : rcvhwm}; bool delays [2] = {options.delay_on_disconnect, options.delay_on_close}; bool conflates [2] = {conflate, conflate}; int rc = pipepair (parents, new_pipes, hwms, delays, conflates); errno_assert (rc == 0); // Attach local end of the pipe to this socket object. attach_pipe (new_pipes [0]); // If required, send the identity of the local socket to the peer. if (peer.options.recv_identity) { msg_t id; rc = id.init_size (options.identity_size); errno_assert (rc == 0); memcpy (id.data (), options.identity, options.identity_size); id.set_flags (msg_t::identity); bool written = new_pipes [0]->write (&id); zmq_assert (written); new_pipes [0]->flush (); } // If required, send the identity of the peer to the local socket. if (options.recv_identity) { msg_t id; rc = id.init_size (peer.options.identity_size); errno_assert (rc == 0); memcpy (id.data (), peer.options.identity, peer.options.identity_size); id.set_flags (msg_t::identity); bool written = new_pipes [1]->write (&id); zmq_assert (written); new_pipes [1]->flush (); } // Attach remote end of the pipe to the peer socket. Note that peer's // seqnum was incremented in find_endpoint function. We don't need it // increased here. send_bind (peer.socket, new_pipes [1], false); // Save last endpoint URI last_endpoint.assign (addr_); // remember inproc connections for disconnect inprocs.insert (inprocs_t::value_type (std::string (addr_), new_pipes[0])); return 0; } // Choose the I/O thread to run the session in. io_thread_t *io_thread = choose_io_thread (options.affinity); if (!io_thread) { errno = EMTHREAD; return -1; } address_t *paddr = new (std::nothrow) address_t (protocol, address); alloc_assert (paddr); // Resolve address (if needed by the protocol) if (protocol == "tcp") { paddr->resolved.tcp_addr = new (std::nothrow) tcp_address_t (); alloc_assert (paddr->resolved.tcp_addr); int rc = paddr->resolved.tcp_addr->resolve ( address.c_str (), false, options.ipv6); if (rc != 0) { delete paddr; return -1; } } #if !defined ZMQ_HAVE_WINDOWS && !defined ZMQ_HAVE_OPENVMS else if (protocol == "ipc") { paddr->resolved.ipc_addr = new (std::nothrow) ipc_address_t (); alloc_assert (paddr->resolved.ipc_addr); int rc = paddr->resolved.ipc_addr->resolve (address.c_str ()); if (rc != 0) { delete paddr; return -1; } } #endif #ifdef ZMQ_HAVE_OPENPGM if (protocol == "pgm" || protocol == "epgm") { struct pgm_addrinfo_t *res = NULL; uint16_t port_number = 0; int rc = pgm_socket_t::init_address(address.c_str(), &res, &port_number); if (res != NULL) pgm_freeaddrinfo (res); if (rc != 0 || port_number == 0) return -1; } #endif // Create session. session_base_t *session = session_base_t::create (io_thread, true, this, options, paddr); errno_assert (session); // PGM does not support subscription forwarding; ask for all data to be // sent to this pipe. bool icanhasall = protocol == "pgm" || protocol == "epgm"; pipe_t *newpipe = NULL; if (options.immediate != 1 || icanhasall) { // Create a bi-directional pipe. object_t *parents [2] = {this, session}; pipe_t *new_pipes [2] = {NULL, NULL}; bool conflate = options.conflate && (options.type == ZMQ_DEALER || options.type == ZMQ_PULL || options.type == ZMQ_PUSH || options.type == ZMQ_PUB || options.type == ZMQ_SUB); int hwms [2] = {conflate? -1 : options.sndhwm, conflate? -1 : options.rcvhwm}; bool delays [2] = {options.delay_on_disconnect, options.delay_on_close}; bool conflates [2] = {conflate, conflate}; rc = pipepair (parents, new_pipes, hwms, delays, conflates); errno_assert (rc == 0); // Attach local end of the pipe to the socket object. attach_pipe (new_pipes [0], icanhasall); newpipe = new_pipes [0]; // Attach remote end of the pipe to the session object later on. session->attach_pipe (new_pipes [1]); } // Save last endpoint URI paddr->to_string (last_endpoint); add_endpoint (addr_, (own_t *) session, newpipe); return 0; }
static int make_fdpair (xs::fd_t *r_, xs::fd_t *w_) { #if defined XS_HAVE_EVENTFD // Create eventfd object. #if defined EFD_CLOEXEC xs::fd_t fd = eventfd (0, EFD_CLOEXEC); if (fd == -1) return -1; #else xs::fd_t fd = eventfd (0, 0); if (fd == -1) return -1; #if defined FD_CLOEXEC int rc = fcntl (fd, F_SETFD, FD_CLOEXEC); errno_assert (rc != -1); #endif #endif *w_ = fd; *r_ = fd; return 0; #elif defined XS_HAVE_WINDOWS // On Windows we are using TCP sockets for in-process communication. // That is a security hole -- other processes on the same box may connect // to the bound TCP port and hook into internal signal processing of // the library. To solve this problem we should use a proper in-process // signaling mechanism such as private semaphore. However, on Windows, // these cannot be polled on using select(). Other functions that allow // polling on these objects (e.g. WaitForMulitpleObjects) don't allow // to poll on sockets. Thus, the only way to fix the problem is to // implement IOCP polling mechanism that allows to poll on both sockets // and in-process synchronisation objects. // Make the following critical section accessible to everyone. SECURITY_ATTRIBUTES sa = {0}; sa.nLength = sizeof (sa); sa.bInheritHandle = FALSE; SECURITY_DESCRIPTOR sd; BOOL ok = InitializeSecurityDescriptor (&sd, SECURITY_DESCRIPTOR_REVISION); win_assert (ok); ok = SetSecurityDescriptorDacl(&sd, TRUE, (PACL) NULL, FALSE); win_assert (ok); sa.lpSecurityDescriptor = &sd; // This function has to be in a system-wide critical section so that // two instances of the library don't accidentally create signaler // crossing the process boundary. // We'll use named event object to implement the critical section. HANDLE sync = CreateEvent (&sa, FALSE, TRUE, "xs-signaler-port-sync"); win_assert (sync != NULL); // Enter the critical section. DWORD dwrc = WaitForSingleObject (sync, INFINITE); xs_assert (dwrc == WAIT_OBJECT_0); // Windows has no 'socketpair' function. CreatePipe is no good as pipe // handles cannot be polled on. Here we create the socketpair by hand. *w_ = INVALID_SOCKET; *r_ = INVALID_SOCKET; // Create listening socket. SOCKET listener; listener = xs::open_socket (AF_INET, SOCK_STREAM, 0); if (listener == xs::retired_fd) return -1; // Set SO_REUSEADDR and TCP_NODELAY on listening socket. BOOL so_reuseaddr = 1; int rc = setsockopt (listener, SOL_SOCKET, SO_REUSEADDR, (char *)&so_reuseaddr, sizeof (so_reuseaddr)); wsa_assert (rc != SOCKET_ERROR); BOOL tcp_nodelay = 1; rc = setsockopt (listener, IPPROTO_TCP, TCP_NODELAY, (char *)&tcp_nodelay, sizeof (tcp_nodelay)); wsa_assert (rc != SOCKET_ERROR); // Bind listening socket to the local port. struct sockaddr_in addr; memset (&addr, 0, sizeof (addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = htonl (INADDR_LOOPBACK); addr.sin_port = htons (xs::signaler_port); rc = bind (listener, (const struct sockaddr*) &addr, sizeof (addr)); wsa_assert (rc != SOCKET_ERROR); // Listen for incomming connections. rc = listen (listener, 1); wsa_assert (rc != SOCKET_ERROR); // Create the writer socket. *w_ = WSASocket (AF_INET, SOCK_STREAM, 0, NULL, 0, 0); if (*w_ == xs::retired_fd) { rc = closesocket (listener); wsa_assert (rc != SOCKET_ERROR); return -1; } // Set TCP_NODELAY on writer socket. rc = setsockopt (*w_, IPPROTO_TCP, TCP_NODELAY, (char *)&tcp_nodelay, sizeof (tcp_nodelay)); wsa_assert (rc != SOCKET_ERROR); // Connect writer to the listener. rc = connect (*w_, (sockaddr *) &addr, sizeof (addr)); wsa_assert (rc != SOCKET_ERROR); // Accept connection from writer. *r_ = accept (listener, NULL, NULL); if (*r_ == xs::retired_fd) { rc = closesocket (listener); wsa_assert (rc != SOCKET_ERROR); rc = closesocket (*w_); wsa_assert (rc != SOCKET_ERROR); return -1; } // We don't need the listening socket anymore. Close it. rc = closesocket (listener); wsa_assert (rc != SOCKET_ERROR); // Exit the critical section. BOOL brc = SetEvent (sync); win_assert (brc != 0); return 0; #elif defined XS_HAVE_OPENVMS // Whilst OpenVMS supports socketpair - it maps to AF_INET only. Further, // it does not set the socket options TCP_NODELAY and TCP_NODELACK which // can lead to performance problems. // // The bug will be fixed in V5.6 ECO4 and beyond. In the meantime, we'll // create the socket pair manually. sockaddr_in lcladdr; memset (&lcladdr, 0, sizeof (lcladdr)); lcladdr.sin_family = AF_INET; lcladdr.sin_addr.s_addr = htonl (INADDR_LOOPBACK); lcladdr.sin_port = 0; int listener = open_socket (AF_INET, SOCK_STREAM, 0); errno_assert (listener != -1); int on = 1; int rc = setsockopt (listener, IPPROTO_TCP, TCP_NODELAY, &on, sizeof (on)); errno_assert (rc != -1); rc = setsockopt (listener, IPPROTO_TCP, TCP_NODELACK, &on, sizeof (on)); errno_assert (rc != -1); rc = bind(listener, (struct sockaddr*) &lcladdr, sizeof (lcladdr)); errno_assert (rc != -1); socklen_t lcladdr_len = sizeof (lcladdr); rc = getsockname (listener, (struct sockaddr*) &lcladdr, &lcladdr_len); errno_assert (rc != -1); rc = listen (listener, 1); errno_assert (rc != -1); *w_ = open_socket (AF_INET, SOCK_STREAM, 0); errno_assert (*w_ != -1); rc = setsockopt (*w_, IPPROTO_TCP, TCP_NODELAY, &on, sizeof (on)); errno_assert (rc != -1); rc = setsockopt (*w_, IPPROTO_TCP, TCP_NODELACK, &on, sizeof (on)); errno_assert (rc != -1); rc = connect (*w_, (struct sockaddr*) &lcladdr, sizeof (lcladdr)); errno_assert (rc != -1); *r_ = accept (listener, NULL, NULL); errno_assert (*r_ != -1); close (listener); return 0; #else // All other implementations support socketpair() int sv [2]; #if defined XS_HAVE_SOCK_CLOEXEC int rc = socketpair (AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sv); if (rc == -1) return -1; #else int rc = socketpair (AF_UNIX, SOCK_STREAM, 0, sv); if (rc == -1) return -1; errno_assert (rc == 0); #if defined FD_CLOEXEC rc = fcntl (sv [0], F_SETFD, FD_CLOEXEC); errno_assert (rc != -1); rc = fcntl (sv [1], F_SETFD, FD_CLOEXEC); errno_assert (rc != -1); #endif #endif *w_ = sv [0]; *r_ = sv [1]; return 0; #endif }
int zmq::xrep_t::xsend (msg_t *msg_, int flags_) { // If this is the first part of the message it's the ID of the // peer to send the message to. if (!more_out) { zmq_assert (!current_out); // If we have malformed message (prefix with no subsequent message) // then just silently ignore it. // TODO: The connections should be killed instead. if (msg_->flags () & msg_t::label) { more_out = true; // Find the pipe associated with the peer ID stored in the prefix. // If there's no such pipe just silently ignore the message. if (msg_->size () == 4) { uint32_t peer_id = get_uint32 ((unsigned char*) msg_->data ()); outpipes_t::iterator it = outpipes.find (peer_id); if (it != outpipes.end ()) { current_out = it->second.pipe; msg_t empty; int rc = empty.init (); errno_assert (rc == 0); if (!current_out->check_write (&empty)) { it->second.active = false; more_out = false; current_out = NULL; } rc = empty.close (); errno_assert (rc == 0); } } } int rc = msg_->close (); errno_assert (rc == 0); rc = msg_->init (); errno_assert (rc == 0); return 0; } // Check whether this is the last part of the message. more_out = msg_->flags () & (msg_t::more | msg_t::label) ? true : false; // Push the message into the pipe. If there's no out pipe, just drop it. if (current_out) { bool ok = current_out->write (msg_); if (unlikely (!ok)) current_out = NULL; else if (!more_out) { current_out->flush (); current_out = NULL; } } else { int rc = msg_->close (); errno_assert (rc == 0); } // Detach the message from the data buffer. int rc = msg_->init (); errno_assert (rc == 0); return 0; }
int zmq::req_t::xsend (msg_t *msg_) { // If we've sent a request and we still haven't got the reply, // we can't send another request unless the strict option is disabled. if (receiving_reply) { if (strict) { errno = EFSM; return -1; } receiving_reply = false; message_begins = true; } // First part of the request is the request identity. if (message_begins) { reply_pipe = NULL; if (request_id_frames_enabled) { request_id++; // Copy request id before sending (see issue #1695 for details). uint32_t *request_id_copy = (uint32_t *) malloc (sizeof (uint32_t)); *request_id_copy = request_id; msg_t id; int rc = id.init_data (request_id_copy, sizeof (uint32_t), free_id, NULL); errno_assert (rc == 0); id.set_flags (msg_t::more); rc = dealer_t::sendpipe (&id, &reply_pipe); if (rc != 0) return -1; } msg_t bottom; int rc = bottom.init (); errno_assert (rc == 0); bottom.set_flags (msg_t::more); rc = dealer_t::sendpipe (&bottom, &reply_pipe); if (rc != 0) return -1; zmq_assert (reply_pipe); message_begins = false; // Eat all currently available messages before the request is fully // sent. This is done to avoid: // REQ sends request to A, A replies, B replies too. // A's reply was first and matches, that is used. // An hour later REQ sends a request to B. B's old reply is used. msg_t drop; while (true) { rc = drop.init (); errno_assert (rc == 0); rc = dealer_t::xrecv (&drop); if (rc != 0) break; drop.close (); } } bool more = msg_->flags () & msg_t::more ? true : false; int rc = dealer_t::xsend (msg_); if (rc != 0) return rc; // If the request was fully sent, flip the FSM into reply-receiving state. if (!more) { receiving_reply = true; message_begins = true; } return 0; }
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 zmq::tcp_listener_t::set_address (const char *protocol_, const char *addr_) { if (strcmp (protocol_, "tcp") == 0 ) { // Resolve the sockaddr to bind to. int rc = resolve_ip_interface (&addr, &addr_len, addr_); if (rc != 0) return -1; // Create a listening socket. s = socket (addr.ss_family, SOCK_STREAM, IPPROTO_TCP); if (s == -1) return -1; // Allow reusing of the address. int flag = 1; rc = setsockopt (s, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof (int)); errno_assert (rc == 0); // Set the non-blocking flag. flag = fcntl (s, F_GETFL, 0); if (flag == -1) flag = 0; rc = fcntl (s, F_SETFL, flag | O_NONBLOCK); errno_assert (rc != -1); // Bind the socket to the network interface and port. rc = bind (s, (struct sockaddr*) &addr, addr_len); if (rc != 0) { close (); return -1; } // Listen for incomming connections. rc = listen (s, tcp_connection_backlog); if (rc != 0) { close (); return -1; } return 0; } else if (strcmp (protocol_, "ipc") == 0) { // Get rid of the file associated with the UNIX domain socket that // may have been left behind by the previous run of the application. ::unlink (addr_); // Convert the address into sockaddr_un structure. int rc = resolve_local_path (&addr, &addr_len, addr_); if (rc != 0) return -1; // Create a listening socket. s = socket (AF_UNIX, SOCK_STREAM, 0); if (s == -1) return -1; // Set the non-blocking flag. int flag = fcntl (s, F_GETFL, 0); if (flag == -1) flag = 0; rc = fcntl (s, F_SETFL, flag | O_NONBLOCK); errno_assert (rc != -1); // Bind the socket to the file path. rc = bind (s, (struct sockaddr*) &addr, sizeof (sockaddr_un)); if (rc != 0) { close (); return -1; } // Listen for incomming connections. rc = listen (s, tcp_connection_backlog); if (rc != 0) { close (); return -1; } return 0; } else { errno = EPROTONOSUPPORT; return -1; } }
zmq::fd_t zmq::tcp_listener_t::accept () { zmq_assert (s != retired_fd); // Accept one incoming connection. fd_t sock = ::accept (s, NULL, NULL); #if (defined ZMQ_HAVE_LINUX || defined ZMQ_HAVE_FREEBSD || \ defined ZMQ_HAVE_OPENBSD || defined ZMQ_HAVE_OSX || \ defined ZMQ_HAVE_OPENVMS || defined ZMQ_HAVE_NETBSD) if (sock == -1 && (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR || errno == ECONNABORTED)) return retired_fd; #elif (defined ZMQ_HAVE_SOLARIS || defined ZMQ_HAVE_AIX) if (sock == -1 && (errno == EWOULDBLOCK || errno == EINTR || errno == ECONNABORTED || errno == EPROTO)) return retired_fd; #elif defined ZMQ_HAVE_HPUX if (sock == -1 && (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR || errno == ECONNABORTED || errno == ENOBUFS)) return retired_fd; #elif defined ZMQ_HAVE_QNXNTO if (sock == -1 && (errno == EWOULDBLOCK || errno == EINTR || errno == ECONNABORTED)) return retired_fd; #endif errno_assert (sock != -1); // Set to non-blocking mode. #ifdef ZMQ_HAVE_OPENVMS int flags = 1; int rc = ioctl (sock, FIONBIO, &flags); errno_assert (rc != -1); #else int flags = fcntl (s, F_GETFL, 0); if (flags == -1) flags = 0; int rc = fcntl (sock, F_SETFL, flags | O_NONBLOCK); errno_assert (rc != -1); #endif struct sockaddr *sa = (struct sockaddr*) &addr; if (AF_UNIX != sa->sa_family) { // Disable Nagle's algorithm. int flag = 1; rc = setsockopt (sock, IPPROTO_TCP, TCP_NODELAY, (char*) &flag, sizeof (int)); errno_assert (rc == 0); #ifdef ZMQ_HAVE_OPENVMS // Disable delayed acknowledgements. flag = 1; rc = setsockopt (sock, IPPROTO_TCP, TCP_NODELACK, (char*) &flag, sizeof (int)); errno_assert (rc != SOCKET_ERROR); #endif } return sock; }
int zmq::tcp_listener_t::set_address (const char *addr_) { // Convert the textual address into address structure. int rc = address.resolve (addr_, true, options.ipv6); if (rc != 0) return -1; address.to_string (endpoint); if (options.use_fd != -1) { s = options.use_fd; socket->event_listening (endpoint, (int) s); return 0; } // Create a listening socket. s = open_socket (address.family (), SOCK_STREAM, IPPROTO_TCP); // IPv6 address family not supported, try automatic downgrade to IPv4. if (s == -1 && address.family () == AF_INET6 && errno == EAFNOSUPPORT && options.ipv6) { rc = address.resolve (addr_, true, false); if (rc != 0) return rc; s = open_socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); } #ifdef ZMQ_HAVE_WINDOWS if (s == INVALID_SOCKET) { errno = wsa_error_to_errno (WSAGetLastError ()); return -1; } #if !defined _WIN32_WCE // On Windows, preventing sockets to be inherited by child processes. BOOL brc = SetHandleInformation ((HANDLE) s, HANDLE_FLAG_INHERIT, 0); win_assert (brc); #endif #else if (s == -1) return -1; #endif // On some systems, IPv4 mapping in IPv6 sockets is disabled by default. // Switch it on in such cases. if (address.family () == AF_INET6) enable_ipv4_mapping (s); // Set the IP Type-Of-Service for the underlying socket if (options.tos != 0) set_ip_type_of_service (s, options.tos); // Set the socket buffer limits for the underlying socket. if (options.sndbuf >= 0) set_tcp_send_buffer (s, options.sndbuf); if (options.rcvbuf >= 0) set_tcp_receive_buffer (s, options.rcvbuf); // Allow reusing of the address. int flag = 1; #ifdef ZMQ_HAVE_WINDOWS rc = setsockopt (s, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (const char*) &flag, sizeof (int)); wsa_assert (rc != SOCKET_ERROR); #else rc = setsockopt (s, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof (int)); errno_assert (rc == 0); #endif // Bind the socket to the network interface and port. rc = bind (s, address.addr (), address.addrlen ()); #ifdef ZMQ_HAVE_WINDOWS if (rc == SOCKET_ERROR) { errno = wsa_error_to_errno (WSAGetLastError ()); goto error; } #else if (rc != 0) goto error; #endif // Listen for incoming connections. rc = listen (s, options.backlog); #ifdef ZMQ_HAVE_WINDOWS if (rc == SOCKET_ERROR) { errno = wsa_error_to_errno (WSAGetLastError ()); goto error; } #else if (rc != 0) goto error; #endif socket->event_listening (endpoint, (int) s); return 0; error: int err = errno; close (); errno = err; return -1; }
void zmq::udp_engine_t::out_event () { msg_t group_msg; int rc = session->pull_msg (&group_msg); errno_assert (rc == 0 || (rc == -1 && errno == EAGAIN)); if (rc == 0) { msg_t body_msg; rc = session->pull_msg (&body_msg); size_t group_size = group_msg.size (); size_t body_size = body_msg.size (); size_t size; if (options.raw_socket) { rc = resolve_raw_address ((char *) group_msg.data (), group_size); // We discard the message if address is not valid if (rc != 0) { rc = group_msg.close (); errno_assert (rc == 0); body_msg.close (); errno_assert (rc == 0); return; } size = body_size; memcpy (out_buffer, body_msg.data (), body_size); } else { size = group_size + body_size + 1; // TODO: check if larger than maximum size out_buffer[0] = (unsigned char) group_size; memcpy (out_buffer + 1, group_msg.data (), group_size); memcpy (out_buffer + 1 + group_size, body_msg.data (), body_size); } rc = group_msg.close (); errno_assert (rc == 0); body_msg.close (); errno_assert (rc == 0); #ifdef ZMQ_HAVE_WINDOWS rc = sendto (fd, (const char *) out_buffer, (int) size, 0, out_address, (int) out_addrlen); wsa_assert (rc != SOCKET_ERROR); #elif defined ZMQ_HAVE_VXWORKS rc = sendto (fd, (caddr_t) out_buffer, size, 0, (sockaddr *) out_address, (int) out_addrlen); errno_assert (rc != -1); #else rc = sendto (fd, out_buffer, size, 0, out_address, out_addrlen); errno_assert (rc != -1); #endif } else reset_pollout (handle); }
zmq::fd_t zmq::tcp_listener_t::accept () { // The situation where connection cannot be accepted due to insufficient // resources is considered valid and treated by ignoring the connection. // Accept one connection and deal with different failure modes. zmq_assert (s != retired_fd); struct sockaddr_storage ss; memset (&ss, 0, sizeof (ss)); #ifdef ZMQ_HAVE_HPUX int ss_len = sizeof (ss); #else socklen_t ss_len = sizeof (ss); #endif fd_t sock = ::accept (s, (struct sockaddr *) &ss, &ss_len); #ifdef ZMQ_HAVE_WINDOWS if (sock == INVALID_SOCKET) { const int last_error = WSAGetLastError(); wsa_assert (last_error == WSAEWOULDBLOCK || last_error == WSAECONNRESET || last_error == WSAEMFILE || last_error == WSAENOBUFS); return retired_fd; } #if !defined _WIN32_WCE // On Windows, preventing sockets to be inherited by child processes. BOOL brc = SetHandleInformation ((HANDLE) sock, HANDLE_FLAG_INHERIT, 0); win_assert (brc); #endif #else if (sock == -1) { errno_assert (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR || errno == ECONNABORTED || errno == EPROTO || errno == ENOBUFS || errno == ENOMEM || errno == EMFILE || errno == ENFILE); return retired_fd; } #endif // Race condition can cause socket not to be closed (if fork happens // between accept and this point). #ifdef FD_CLOEXEC int rc = fcntl (sock, F_SETFD, FD_CLOEXEC); errno_assert (rc != -1); #endif if (!options.tcp_accept_filters.empty ()) { bool matched = false; for (options_t::tcp_accept_filters_t::size_type i = 0; i != options.tcp_accept_filters.size (); ++i) { if (options.tcp_accept_filters[i].match_address ((struct sockaddr *) &ss, ss_len)) { matched = true; break; } } if (!matched) { #ifdef ZMQ_HAVE_WINDOWS int rc = closesocket (sock); wsa_assert (rc != SOCKET_ERROR); #else int rc = ::close (sock); errno_assert (rc == 0); #endif return retired_fd; } } // Set the IP Type-Of-Service priority for this client socket if (options.tos != 0) set_ip_type_of_service (sock, options.tos); return sock; }
void zmq::udp_engine_t::in_event () { struct sockaddr_in in_address; socklen_t in_addrlen = sizeof (sockaddr_in); #ifdef ZMQ_HAVE_WINDOWS int nbytes = recvfrom (fd, (char *) in_buffer, MAX_UDP_MSG, 0, (sockaddr *) &in_address, &in_addrlen); const int last_error = WSAGetLastError (); if (nbytes == SOCKET_ERROR) { wsa_assert (last_error == WSAENETDOWN || last_error == WSAENETRESET || last_error == WSAEWOULDBLOCK); return; } #elif defined ZMQ_HAVE_VXWORKS int nbytes = recvfrom (fd, (char *) in_buffer, MAX_UDP_MSG, 0, (sockaddr *) &in_address, (int *) &in_addrlen); if (nbytes == -1) { errno_assert (errno != EBADF && errno != EFAULT && errno != ENOMEM && errno != ENOTSOCK); return; } #else int nbytes = recvfrom (fd, in_buffer, MAX_UDP_MSG, 0, (sockaddr *) &in_address, &in_addrlen); if (nbytes == -1) { errno_assert (errno != EBADF && errno != EFAULT && errno != ENOMEM && errno != ENOTSOCK); return; } #endif int rc; int body_size; int body_offset; msg_t msg; if (options.raw_socket) { sockaddr_to_msg (&msg, &in_address); body_size = nbytes; body_offset = 0; } else { char *group_buffer = (char *) in_buffer + 1; int group_size = in_buffer[0]; rc = msg.init_size (group_size); errno_assert (rc == 0); msg.set_flags (msg_t::more); memcpy (msg.data (), group_buffer, group_size); // This doesn't fit, just ingore if (nbytes - 1 < group_size) return; body_size = nbytes - 1 - group_size; body_offset = 1 + group_size; } // Push group description to session rc = session->push_msg (&msg); errno_assert (rc == 0 || (rc == -1 && errno == EAGAIN)); // Group description message doesn't fit in the pipe, drop if (rc != 0) { rc = msg.close (); errno_assert (rc == 0); reset_pollin (handle); return; } rc = msg.close (); errno_assert (rc == 0); rc = msg.init_size (body_size); errno_assert (rc == 0); memcpy (msg.data (), in_buffer + body_offset, body_size); // Push message body to session rc = session->push_msg (&msg); // Message body doesn't fit in the pipe, drop and reset session state if (rc != 0) { rc = msg.close (); errno_assert (rc == 0); session->reset (); reset_pollin (handle); return; } rc = msg.close (); errno_assert (rc == 0); session->flush (); }
int zmq::ctx_t::terminate () { // Connect up any pending inproc connections, otherwise we will hang pending_connections_t copy = pending_connections; for (pending_connections_t::iterator p = copy.begin (); p != copy.end (); ++p) { zmq::socket_base_t *s = create_socket (ZMQ_PAIR); s->bind (p->first.c_str ()); s->close (); } slot_sync.lock (); if (!starting) { #ifdef HAVE_FORK if (pid != getpid()) { // we are a forked child process. Close all file descriptors // inherited from the parent. for (sockets_t::size_type i = 0; i != sockets.size (); i++) { sockets[i]->get_mailbox()->forked(); } term_mailbox.forked(); } #endif // Check whether termination was already underway, but interrupted and now // restarted. bool restarted = terminating; terminating = true; // First attempt to terminate the context. if (!restarted) { // First send stop command to sockets so that any blocking calls // can be interrupted. If there are no sockets we can ask reaper // thread to stop. for (sockets_t::size_type i = 0; i != sockets.size (); i++) sockets [i]->stop (); if (sockets.empty ()) reaper->stop (); } slot_sync.unlock(); // Wait till reaper thread closes all the sockets. command_t cmd; int rc = term_mailbox.recv (&cmd, -1); if (rc == -1 && errno == EINTR) return -1; errno_assert (rc == 0); zmq_assert (cmd.type == command_t::done); slot_sync.lock (); zmq_assert (sockets.empty ()); } slot_sync.unlock (); // Deallocate the resources. delete this; return 0; }
void zmq::udp_engine_t::plug (io_thread_t *io_thread_, session_base_t *session_) { zmq_assert (!plugged); plugged = true; zmq_assert (!session); zmq_assert (session_); session = session_; // Connect to I/O threads poller object. io_object_t::plug (io_thread_); handle = add_fd (fd); // Bind the socket to a device if applicable if (!options.bound_device.empty ()) bind_to_device (fd, options.bound_device); if (send_enabled) { if (!options.raw_socket) { out_address = address->resolved.udp_addr->dest_addr (); out_addrlen = address->resolved.udp_addr->dest_addrlen (); } else { out_address = (sockaddr *) &raw_address; out_addrlen = sizeof (sockaddr_in); } set_pollout (handle); } if (recv_enabled) { int on = 1; int rc = setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof (on)); #ifdef ZMQ_HAVE_WINDOWS wsa_assert (rc != SOCKET_ERROR); #else errno_assert (rc == 0); #endif #ifdef ZMQ_HAVE_VXWORKS rc = bind (fd, (sockaddr *) address->resolved.udp_addr->bind_addr (), address->resolved.udp_addr->bind_addrlen ()); #else rc = bind (fd, address->resolved.udp_addr->bind_addr (), address->resolved.udp_addr->bind_addrlen ()); #endif #ifdef ZMQ_HAVE_WINDOWS wsa_assert (rc != SOCKET_ERROR); #else errno_assert (rc == 0); #endif if (address->resolved.udp_addr->is_mcast ()) { struct ip_mreq mreq; mreq.imr_multiaddr = address->resolved.udp_addr->multicast_ip (); mreq.imr_interface = address->resolved.udp_addr->interface_ip (); rc = setsockopt (fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &mreq, sizeof (mreq)); #ifdef ZMQ_HAVE_WINDOWS wsa_assert (rc != SOCKET_ERROR); #else errno_assert (rc == 0); #endif } set_pollin (handle); // Call restart output to drop all join/leave commands restart_output (); } }
void zmq::curve_server_t::send_zap_request (const uint8_t *key) { int rc; msg_t msg; // Address delimiter frame rc = msg.init (); errno_assert (rc == 0); msg.set_flags (msg_t::more); rc = session->write_zap_msg (&msg); errno_assert (rc == 0); // Version frame rc = msg.init_size (3); errno_assert (rc == 0); memcpy (msg.data (), "1.0", 3); msg.set_flags (msg_t::more); rc = session->write_zap_msg (&msg); errno_assert (rc == 0); // Request ID frame rc = msg.init_size (1); errno_assert (rc == 0); memcpy (msg.data (), "1", 1); msg.set_flags (msg_t::more); rc = session->write_zap_msg (&msg); errno_assert (rc == 0); // Domain frame rc = msg.init_size (options.zap_domain.length ()); errno_assert (rc == 0); memcpy (msg.data (), options.zap_domain.c_str (), options.zap_domain.length ()); msg.set_flags (msg_t::more); rc = session->write_zap_msg (&msg); errno_assert (rc == 0); // Address frame rc = msg.init_size (peer_address.length ()); errno_assert (rc == 0); memcpy (msg.data (), peer_address.c_str (), peer_address.length ()); msg.set_flags (msg_t::more); rc = session->write_zap_msg (&msg); errno_assert (rc == 0); // Identity frame rc = msg.init_size (options.identity_size); errno_assert (rc == 0); memcpy (msg.data (), options.identity, options.identity_size); msg.set_flags (msg_t::more); rc = session->write_zap_msg (&msg); errno_assert (rc == 0); // Mechanism frame rc = msg.init_size (5); errno_assert (rc == 0); memcpy (msg.data (), "CURVE", 5); msg.set_flags (msg_t::more); rc = session->write_zap_msg (&msg); errno_assert (rc == 0); // Credentials frame rc = msg.init_size (crypto_box_PUBLICKEYBYTES); errno_assert (rc == 0); memcpy (msg.data (), key, crypto_box_PUBLICKEYBYTES); rc = session->write_zap_msg (&msg); errno_assert (rc == 0); }
coroutine static void delay(int n, int ch) { int rc = msleep(now() + n); errno_assert(rc == 0); rc = chsend(ch, &n, sizeof(n), -1); errno_assert(rc == 0); }
int zmq::tcp_connecter_t::open () { zmq_assert (s == retired_fd); struct sockaddr *sa = (struct sockaddr*) &addr; if (AF_UNIX != sa->sa_family) { // Create the socket. s = socket (sa->sa_family, SOCK_STREAM, IPPROTO_TCP); if (s == -1) return -1; // Set to non-blocking mode. #ifdef ZMQ_HAVE_OPENVMS int flags = 1; int rc = ioctl (s, FIONBIO, &flags); errno_assert (rc != -1); #else int flags = fcntl (s, F_GETFL, 0); if (flags == -1) flags = 0; int rc = fcntl (s, F_SETFL, flags | O_NONBLOCK); errno_assert (rc != -1); #endif // Disable Nagle's algorithm. int flag = 1; rc = setsockopt (s, IPPROTO_TCP, TCP_NODELAY, (char*) &flag, sizeof (int)); errno_assert (rc == 0); #ifdef ZMQ_HAVE_OPENVMS // Disable delayed acknowledgements. flag = 1; rc = setsockopt (s, IPPROTO_TCP, TCP_NODELACK, (char*) &flag, sizeof (int)); errno_assert (rc != SOCKET_ERROR); #endif // Connect to the remote peer. rc = ::connect (s, (struct sockaddr*) &addr, addr_len); // Connect was successfull immediately. if (rc == 0) return 0; // Asynchronous connect was launched. if (rc == -1 && errno == EINPROGRESS) { errno = EAGAIN; return -1; } // Error occured. int err = errno; close (); errno = err; return -1; } #ifndef ZMQ_HAVE_OPENVMS else { // Create the socket. zmq_assert (AF_UNIX == sa->sa_family); s = socket (AF_UNIX, SOCK_STREAM, 0); if (s == -1) return -1; // Set the non-blocking flag. int flag = fcntl (s, F_GETFL, 0); if (flag == -1) flag = 0; int rc = fcntl (s, F_SETFL, flag | O_NONBLOCK); errno_assert (rc != -1); // Connect to the remote peer. rc = ::connect (s, (struct sockaddr*) &addr, sizeof (sockaddr_un)); // Connect was successfull immediately. if (rc == 0) return 0; // Error occured. int err = errno; close (); errno = err; return -1; } #endif zmq_assert (false); return -1; }
int main() { /* Test 'msleep'. */ int64_t deadline = now() + 100; int rc = msleep(deadline); errno_assert(rc == 0); int64_t diff = now () - deadline; time_assert(diff, 0); /* msleep-sort */ int ch = chmake(sizeof(int)); errno_assert(ch >= 0); int hndls[4]; hndls[0] = go(delay(30, ch)); errno_assert(hndls[0] >= 0); hndls[1] = go(delay(40, ch)); errno_assert(hndls[1] >= 0); hndls[2] = go(delay(10, ch)); errno_assert(hndls[2] >= 0); hndls[3] = go(delay(20, ch)); errno_assert(hndls[3] >= 0); int val; rc = chrecv(ch, &val, sizeof(val), -1); errno_assert(rc == 0); assert(val == 10); rc = chrecv(ch, &val, sizeof(val), -1); errno_assert(rc == 0); assert(val == 20); rc = chrecv(ch, &val, sizeof(val), -1); errno_assert(rc == 0); assert(val == 30); rc = chrecv(ch, &val, sizeof(val), -1); errno_assert(rc == 0); assert(val == 40); rc = hclose(hndls[0]); errno_assert(rc == 0); rc = hclose(hndls[1]); errno_assert(rc == 0); rc = hclose(hndls[2]); errno_assert(rc == 0); rc = hclose(hndls[3]); errno_assert(rc == 0); rc = hclose(ch); errno_assert(rc == 0); return 0; }
zmq::v1_decoder_t::~v1_decoder_t () { int rc = in_progress.close (); errno_assert (rc == 0); }
int zmq::curve_client_t::produce_initiate (msg_t *msg_) { uint8_t vouch_nonce [crypto_box_NONCEBYTES]; uint8_t vouch_plaintext [crypto_box_ZEROBYTES + 64]; uint8_t vouch_box [crypto_box_BOXZEROBYTES + 80]; // Create vouch = Box [C',S](C->S') memset (vouch_plaintext, 0, crypto_box_ZEROBYTES); memcpy (vouch_plaintext + crypto_box_ZEROBYTES, cn_public, 32); memcpy (vouch_plaintext + crypto_box_ZEROBYTES + 32, server_key, 32); memcpy (vouch_nonce, "VOUCH---", 8); randombytes (vouch_nonce + 8, 16); int rc = crypto_box (vouch_box, vouch_plaintext, sizeof vouch_plaintext, vouch_nonce, cn_server, secret_key); if (rc == -1) return -1; // Assume here that metadata is limited to 256 bytes uint8_t initiate_nonce [crypto_box_NONCEBYTES]; uint8_t initiate_plaintext [crypto_box_ZEROBYTES + 128 + 256]; uint8_t initiate_box [crypto_box_BOXZEROBYTES + 144 + 256]; // Create Box [C + vouch + metadata](C'->S') memset (initiate_plaintext, 0, crypto_box_ZEROBYTES); memcpy (initiate_plaintext + crypto_box_ZEROBYTES, public_key, 32); memcpy (initiate_plaintext + crypto_box_ZEROBYTES + 32, vouch_nonce + 8, 16); memcpy (initiate_plaintext + crypto_box_ZEROBYTES + 48, vouch_box + crypto_box_BOXZEROBYTES, 80); // Metadata starts after vouch uint8_t *ptr = initiate_plaintext + crypto_box_ZEROBYTES + 128; // Add socket type property const char *socket_type = socket_type_string (options.type); ptr += add_property (ptr, "Socket-Type", socket_type, strlen (socket_type)); // Add identity property if (options.type == ZMQ_REQ || options.type == ZMQ_DEALER || options.type == ZMQ_ROUTER) ptr += add_property (ptr, "Identity", options.identity, options.identity_size); const size_t mlen = ptr - initiate_plaintext; memcpy (initiate_nonce, "CurveZMQINITIATE", 16); put_uint64 (initiate_nonce + 16, cn_nonce); rc = crypto_box (initiate_box, initiate_plaintext, mlen, initiate_nonce, cn_server, cn_secret); if (rc == -1) return -1; rc = msg_->init_size (113 + mlen - crypto_box_BOXZEROBYTES); errno_assert (rc == 0); uint8_t *initiate = static_cast <uint8_t *> (msg_->data ()); memcpy (initiate, "\x08INITIATE", 9); // Cookie provided by the server in the WELCOME command memcpy (initiate + 9, cn_cookie, 96); // Short nonce, prefixed by "CurveZMQINITIATE" memcpy (initiate + 105, initiate_nonce + 16, 8); // Box [C + vouch + metadata](C'->S') memcpy (initiate + 113, initiate_box + crypto_box_BOXZEROBYTES, mlen - crypto_box_BOXZEROBYTES); cn_nonce++; return 0; }
int zmq_poll (zmq_pollitem_t *items_, int nitems_, long timeout_) { // TODO: the function implementation can just call zmq_pollfd_poll with // pollfd as NULL, however pollfd is not yet stable. #if defined ZMQ_HAVE_POLLER // if poller is present, use that. return zmq_poller_poll(items_, nitems_, timeout_); #else #if defined ZMQ_POLL_BASED_ON_POLL if (unlikely (nitems_ < 0)) { errno = EINVAL; return -1; } if (unlikely (nitems_ == 0)) { if (timeout_ == 0) return 0; #if defined ZMQ_HAVE_WINDOWS Sleep (timeout_ > 0 ? timeout_ : INFINITE); return 0; #elif defined ZMQ_HAVE_ANDROID usleep (timeout_ * 1000); return 0; #else return usleep (timeout_ * 1000); #endif } if (!items_) { errno = EFAULT; return -1; } zmq::clock_t clock; uint64_t now = 0; uint64_t end = 0; pollfd spollfds[ZMQ_POLLITEMS_DFLT]; pollfd *pollfds = spollfds; if (nitems_ > ZMQ_POLLITEMS_DFLT) { pollfds = (pollfd*) malloc (nitems_ * sizeof (pollfd)); alloc_assert (pollfds); } // Build pollset for poll () system call. for (int i = 0; i != nitems_; i++) { // If the poll item is a 0MQ socket, we poll on the file descriptor // retrieved by the ZMQ_FD socket option. if (items_ [i].socket) { size_t zmq_fd_size = sizeof (zmq::fd_t); if (zmq_getsockopt (items_ [i].socket, ZMQ_FD, &pollfds [i].fd, &zmq_fd_size) == -1) { if (pollfds != spollfds) free (pollfds); return -1; } pollfds [i].events = items_ [i].events ? POLLIN : 0; } // Else, the poll item is a raw file descriptor. Just convert the // events to normal POLLIN/POLLOUT for poll (). else { pollfds [i].fd = items_ [i].fd; pollfds [i].events = (items_ [i].events & ZMQ_POLLIN ? POLLIN : 0) | (items_ [i].events & ZMQ_POLLOUT ? POLLOUT : 0) | (items_ [i].events & ZMQ_POLLPRI ? POLLPRI : 0); } } bool first_pass = true; int nevents = 0; while (true) { // Compute the timeout for the subsequent poll. int timeout; if (first_pass) timeout = 0; else if (timeout_ < 0) timeout = -1; else timeout = end - now; // Wait for events. { int rc = poll (pollfds, nitems_, timeout); if (rc == -1 && errno == EINTR) { if (pollfds != spollfds) free (pollfds); return -1; } errno_assert (rc >= 0); } // Check for the events. for (int i = 0; i != nitems_; i++) { items_ [i].revents = 0; // The poll item is a 0MQ socket. Retrieve pending events // using the ZMQ_EVENTS socket option. if (items_ [i].socket) { size_t zmq_events_size = sizeof (uint32_t); uint32_t zmq_events; if (zmq_getsockopt (items_ [i].socket, ZMQ_EVENTS, &zmq_events, &zmq_events_size) == -1) { if (pollfds != spollfds) free (pollfds); return -1; } if ((items_ [i].events & ZMQ_POLLOUT) && (zmq_events & ZMQ_POLLOUT)) items_ [i].revents |= ZMQ_POLLOUT; if ((items_ [i].events & ZMQ_POLLIN) && (zmq_events & ZMQ_POLLIN)) items_ [i].revents |= ZMQ_POLLIN; } // Else, the poll item is a raw file descriptor, simply convert // the events to zmq_pollitem_t-style format. else { if (pollfds [i].revents & POLLIN) items_ [i].revents |= ZMQ_POLLIN; if (pollfds [i].revents & POLLOUT) items_ [i].revents |= ZMQ_POLLOUT; if (pollfds [i].revents & POLLPRI) items_ [i].revents |= ZMQ_POLLPRI; if (pollfds [i].revents & ~(POLLIN | POLLOUT | POLLPRI)) items_ [i].revents |= ZMQ_POLLERR; } if (items_ [i].revents) nevents++; } // If timeout is zero, exit immediately whether there are events or not. if (timeout_ == 0) break; // If there are events to return, we can exit immediately. if (nevents) break; // At this point we are meant to wait for events but there are none. // If timeout is infinite we can just loop until we get some events. if (timeout_ < 0) { if (first_pass) first_pass = false; continue; } // The timeout is finite and there are no events. In the first pass // we get a timestamp of when the polling have begun. (We assume that // first pass have taken negligible time). We also compute the time // when the polling should time out. if (first_pass) { now = clock.now_ms (); end = now + timeout_; if (now == end) break; first_pass = false; continue; } // Find out whether timeout have expired. now = clock.now_ms (); if (now >= end) break; } if (pollfds != spollfds) free (pollfds); return nevents; #elif defined ZMQ_POLL_BASED_ON_SELECT if (unlikely (nitems_ < 0)) { errno = EINVAL; return -1; } if (unlikely (nitems_ == 0)) { if (timeout_ == 0) return 0; #if defined ZMQ_HAVE_WINDOWS Sleep (timeout_ > 0 ? timeout_ : INFINITE); return 0; #else return usleep (timeout_ * 1000); #endif } zmq::clock_t clock; uint64_t now = 0; uint64_t end = 0; // Ensure we do not attempt to select () on more than FD_SETSIZE // file descriptors. zmq_assert (nitems_ <= FD_SETSIZE); fd_set pollset_in = { 0 }; fd_set pollset_out = { 0 }; fd_set pollset_err = { 0 }; zmq::fd_t maxfd = 0; // Build the fd_sets for passing to select (). for (int i = 0; i != nitems_; i++) { // If the poll item is a 0MQ socket we are interested in input on the // notification file descriptor retrieved by the ZMQ_FD socket option. if (items_ [i].socket) { size_t zmq_fd_size = sizeof (zmq::fd_t); zmq::fd_t notify_fd; if (zmq_getsockopt (items_ [i].socket, ZMQ_FD, ¬ify_fd, &zmq_fd_size) == -1) return -1; if (items_ [i].events) { FD_SET (notify_fd, &pollset_in); if (maxfd < notify_fd) maxfd = notify_fd; } } // Else, the poll item is a raw file descriptor. Convert the poll item // events to the appropriate fd_sets. else { if (items_ [i].events & ZMQ_POLLIN) FD_SET (items_ [i].fd, &pollset_in); if (items_ [i].events & ZMQ_POLLOUT) FD_SET (items_ [i].fd, &pollset_out); if (items_ [i].events & ZMQ_POLLERR) FD_SET (items_ [i].fd, &pollset_err); if (maxfd < items_ [i].fd) maxfd = items_ [i].fd; } } bool first_pass = true; int nevents = 0; fd_set inset, outset, errset; while (true) { // Compute the timeout for the subsequent poll. timeval timeout; timeval *ptimeout; if (first_pass) { timeout.tv_sec = 0; timeout.tv_usec = 0; ptimeout = &timeout; } else if (timeout_ < 0) ptimeout = NULL; else { timeout.tv_sec = (long) ((end - now) / 1000); timeout.tv_usec = (long) ((end - now) % 1000 * 1000); ptimeout = &timeout; } // Wait for events. Ignore interrupts if there's infinite timeout. while (true) { memcpy (&inset, &pollset_in, sizeof (fd_set)); memcpy (&outset, &pollset_out, sizeof (fd_set)); memcpy (&errset, &pollset_err, sizeof (fd_set)); #if defined ZMQ_HAVE_WINDOWS int rc = select (0, &inset, &outset, &errset, ptimeout); if (unlikely (rc == SOCKET_ERROR)) { errno = zmq::wsa_error_to_errno (WSAGetLastError ()); wsa_assert (errno == ENOTSOCK); return -1; } #else int rc = select (maxfd + 1, &inset, &outset, &errset, ptimeout); if (unlikely (rc == -1)) { errno_assert (errno == EINTR || errno == EBADF); return -1; } #endif break; } // Check for the events. for (int i = 0; i != nitems_; i++) { items_ [i].revents = 0; // The poll item is a 0MQ socket. Retrieve pending events // using the ZMQ_EVENTS socket option. if (items_ [i].socket) { size_t zmq_events_size = sizeof (uint32_t); uint32_t zmq_events; if (zmq_getsockopt (items_ [i].socket, ZMQ_EVENTS, &zmq_events, &zmq_events_size) == -1) return -1; if ((items_ [i].events & ZMQ_POLLOUT) && (zmq_events & ZMQ_POLLOUT)) items_ [i].revents |= ZMQ_POLLOUT; if ((items_ [i].events & ZMQ_POLLIN) && (zmq_events & ZMQ_POLLIN)) items_ [i].revents |= ZMQ_POLLIN; } // Else, the poll item is a raw file descriptor, simply convert // the events to zmq_pollitem_t-style format. else { if (FD_ISSET (items_ [i].fd, &inset)) items_ [i].revents |= ZMQ_POLLIN; if (FD_ISSET (items_ [i].fd, &outset)) items_ [i].revents |= ZMQ_POLLOUT; if (FD_ISSET (items_ [i].fd, &errset)) items_ [i].revents |= ZMQ_POLLERR; } if (items_ [i].revents) nevents++; } // If timeout is zero, exit immediately whether there are events or not. if (timeout_ == 0) break; // If there are events to return, we can exit immediately. if (nevents) break; // At this point we are meant to wait for events but there are none. // If timeout is infinite we can just loop until we get some events. if (timeout_ < 0) { if (first_pass) first_pass = false; continue; } // The timeout is finite and there are no events. In the first pass // we get a timestamp of when the polling have begun. (We assume that // first pass have taken negligible time). We also compute the time // when the polling should time out. if (first_pass) { now = clock.now_ms (); end = now + timeout_; if (now == end) break; first_pass = false; continue; } // Find out whether timeout have expired. now = clock.now_ms (); if (now >= end) break; } return nevents; #else // Exotic platforms that support neither poll() nor select(). errno = ENOTSUP; return -1; #endif #endif // ZMQ_HAVE_POLLER }
/* 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 zmq::router_t::xsend (msg_t *msg_, int flags_) { // If this is the first part of the message it's the ID of the // peer to send the message to. if (!more_out) { zmq_assert (!current_out); int retval = 0; // If we have malformed message (prefix with no subsequent message) // then just silently ignore it. // TODO: The connections should be killed instead. if (msg_->flags () & msg_t::more) { more_out = true; // Find the pipe associated with the identity stored in the prefix. // If there's no such pipe just silently ignore the message, unless // fail_unreachable is set. blob_t identity ((unsigned char*) msg_->data (), msg_->size ()); outpipes_t::iterator it = outpipes.find (identity); if (it != outpipes.end ()) { current_out = it->second.pipe; msg_t empty; int rc = empty.init (); errno_assert (rc == 0); if (!current_out->check_write (&empty)) { it->second.active = false; more_out = false; current_out = NULL; } rc = empty.close (); errno_assert (rc == 0); } else if(fail_unroutable) { more_out = false; retval = EHOSTUNREACH; } } int rc = msg_->close (); errno_assert (rc == 0); rc = msg_->init (); errno_assert (rc == 0); return retval; } // Check whether this is the last part of the message. more_out = msg_->flags () & msg_t::more ? true : false; // Push the message into the pipe. If there's no out pipe, just drop it. if (current_out) { bool ok = current_out->write (msg_); if (unlikely (!ok)) current_out = NULL; else if (!more_out) { current_out->flush (); current_out = NULL; } } else { int rc = msg_->close (); errno_assert (rc == 0); } // Detach the message from the data buffer. int rc = msg_->init (); errno_assert (rc == 0); return 0; }
int zmq::router_t::xrecv (msg_t *msg_) { if (prefetched) { if (!identity_sent) { int rc = msg_->move (prefetched_id); errno_assert (rc == 0); identity_sent = true; } else { int rc = msg_->move (prefetched_msg); errno_assert (rc == 0); prefetched = false; } more_in = msg_->flags () & msg_t::more ? true : false; if (!more_in) { if (terminate_current_in) { current_in->terminate (true); terminate_current_in = false; } current_in = NULL; } return 0; } pipe_t *pipe = NULL; int rc = fq.recvpipe (msg_, &pipe); // It's possible that we receive peer's identity. That happens // after reconnection. The current implementation assumes that // the peer always uses the same identity. while (rc == 0 && msg_->is_identity ()) rc = fq.recvpipe (msg_, &pipe); if (rc != 0) return -1; zmq_assert (pipe != NULL); // If we are in the middle of reading a message, just return the next part. if (more_in) { more_in = msg_->flags () & msg_t::more ? true : false; if (!more_in) { if (terminate_current_in) { current_in->terminate (true); terminate_current_in = false; } current_in = NULL; } } else { // We are at the beginning of a message. // Keep the message part we have in the prefetch buffer // and return the ID of the peer instead. rc = prefetched_msg.move (*msg_); errno_assert (rc == 0); prefetched = true; current_in = pipe; blob_t identity = pipe->get_identity (); rc = msg_->init_size (identity.size ()); errno_assert (rc == 0); memcpy (msg_->data (), identity.data (), identity.size ()); msg_->set_flags (msg_t::more); if (prefetched_msg.metadata()) msg_->set_metadata(prefetched_msg.metadata()); identity_sent = true; } return 0; }