WATCHDOG *watchdog_create(unsigned timeout, WATCHDOG_FN action, char *context) { const char *myname = "watchdog_create"; struct sigaction sig_action; WATCHDOG *wp; wp = (WATCHDOG *) mymalloc(sizeof(*wp)); if ((wp->timeout = timeout / WATCHDOG_STEPS) == 0) msg_panic("%s: timeout %d is too small", myname, timeout); wp->action = action; wp->context = context; wp->saved_watchdog = watchdog_curr; wp->saved_time = alarm(0); sigemptyset(&sig_action.sa_mask); #ifdef SA_RESTART sig_action.sa_flags = SA_RESTART; #else sig_action.sa_flags = 0; #endif sig_action.sa_handler = watchdog_event; if (sigaction(SIGALRM, &sig_action, &wp->saved_action) < 0) msg_fatal("%s: sigaction(SIGALRM): %m", myname); if (msg_verbose > 1) msg_info("%s: %p %d", myname, (void *) wp, timeout); #ifdef USE_WATCHDOG_PIPE if (watchdog_curr == 0) { if (pipe(watchdog_pipe) < 0) msg_fatal("%s: pipe: %m", myname); non_blocking(watchdog_pipe[0], NON_BLOCKING); non_blocking(watchdog_pipe[1], NON_BLOCKING); event_enable_read(watchdog_pipe[0], watchdog_read, (void *) 0); } #endif return (watchdog_curr = wp); }
int inet_connect(const char *addr, int block_mode, int timeout) { char *buf; char *host; char *port; struct sockaddr_in sin; int sock; /* * Translate address information to internal form. No host defaults to * the local host. */ buf = inet_parse(addr, &host, &port); if (*host == 0) host = "localhost"; memset((char *) &sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = find_inet_addr(host); sin.sin_port = find_inet_port(port, "tcp"); myfree(buf); /* * Create a client socket. */ if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) msg_fatal("socket: %m"); /* * Timed connect. */ if (timeout > 0) { non_blocking(sock, NON_BLOCKING); if (timed_connect(sock, (struct sockaddr *) & sin, sizeof(sin), timeout) < 0) { close(sock); return (-1); } if (block_mode != NON_BLOCKING) non_blocking(sock, block_mode); return (sock); } /* * Maybe block until connected. */ else { non_blocking(sock, block_mode); if (sane_connect(sock, (struct sockaddr *) & sin, sizeof(sin)) < 0 && errno != EINPROGRESS) { close(sock); return (-1); } return (sock); } }
int unix_connect(const char *addr, int block_mode, int timeout) { #undef sun struct sockaddr_un sun; int len = strlen(addr); int sock; /* * Translate address information to internal form. */ if (len >= (int) sizeof(sun.sun_path)) msg_fatal("unix-domain name too long: %s", addr); memset((char *) &sun, 0, sizeof(sun)); sun.sun_family = AF_UNIX; #ifdef HAS_SUN_LEN sun.sun_len = len + 1; #endif memcpy(sun.sun_path, addr, len + 1); /* * Create a client socket. */ if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) return (-1); /* * Timed connect. */ if (timeout > 0) { non_blocking(sock, NON_BLOCKING); if (timed_connect(sock, (struct sockaddr *) & sun, sizeof(sun), timeout) < 0) { close(sock); return (-1); } if (block_mode != NON_BLOCKING) non_blocking(sock, block_mode); return (sock); } /* * Maybe block until connected. */ else { non_blocking(sock, block_mode); if (sane_connect(sock, (struct sockaddr *) & sun, sizeof(sun)) < 0 && errno != EINPROGRESS) { close(sock); return (-1); } return (sock); } }
static SMTP_SESSION *smtp_connect_sock(int sock, struct sockaddr *sa, int salen, SMTP_ITERATOR *iter, DSN_BUF *why, int sess_flags) { int conn_stat; int saved_errno; VSTREAM *stream; time_t start_time; const char *name = STR(iter->host); const char *addr = STR(iter->addr); unsigned port = iter->port; start_time = time((time_t *) 0); if (var_smtp_conn_tmout > 0) { non_blocking(sock, NON_BLOCKING); conn_stat = timed_connect(sock, sa, salen, var_smtp_conn_tmout); saved_errno = errno; non_blocking(sock, BLOCKING); errno = saved_errno; } else { conn_stat = sane_connect(sock, sa, salen); } if (conn_stat < 0) { if (port) dsb_simple(why, "4.4.1", "connect to %s[%s]:%d: %m", name, addr, ntohs(port)); else dsb_simple(why, "4.4.1", "connect to %s[%s]: %m", name, addr); close(sock); return (0); } stream = vstream_fdopen(sock, O_RDWR); /* * Avoid poor performance when TCP MSS > VSTREAM_BUFSIZE. */ if (sa->sa_family == AF_INET #ifdef AF_INET6 || sa->sa_family == AF_INET6 #endif ) vstream_tweak_tcp(stream); /* * Bundle up what we have into a nice SMTP_SESSION object. */ return (smtp_session_alloc(stream, iter, start_time, sess_flags)); }
size_t write_some( AsyncWriteStream& s, const ConstBufferSequence& buf ) { detail::non_blocking<AsyncWriteStream> non_blocking; if( non_blocking(s) || non_blocking(s,true) ) { boost::system::error_code ec; size_t r = s.write_some( buf, ec ); if( !ec ) return r; if( ec != boost::asio::error::would_block) { BOOST_THROW_EXCEPTION( boost::system::system_error(ec) ); } } promise<size_t>::ptr p(new promise<size_t>("fc::asio::write_some")); s.async_write_some( buf, boost::bind( detail::read_write_handler, p, _1, _2 ) ); return p->wait(); }
int stream_connect(const char *path, int block_mode, int unused_timeout) { #ifdef STREAM_CONNECTIONS const char *myname = "stream_connect"; int pair[2]; int fifo; /* * The requested file system object must exist, otherwise we can't reach * the server. */ if ((fifo = open(path, O_WRONLY | O_NONBLOCK, 0)) < 0) return (-1); /* * This is for {unix,inet}_connect() compatibility. */ if (block_mode == BLOCKING) non_blocking(fifo, BLOCKING); /* * Create a pipe, and send one pipe end to the server. */ if (pipe(pair) < 0) msg_fatal("%s: pipe: %m", myname); if (ioctl(fifo, I_SENDFD, pair[1]) < 0) msg_fatal("%s: send file descriptor: %m", myname); close(pair[1]); /* * This is for {unix,inet}_connect() compatibility. */ if (block_mode == NON_BLOCKING) non_blocking(pair[0], NON_BLOCKING); /* * Cleanup. */ close(fifo); /* * Keep the other end of the pipe. */ return (pair[0]); #else msg_fatal("stream connections are not implemented"); #endif }
static void psc_service(VSTREAM *smtp_client_stream, char *unused_service, char **unused_argv) { /* * For sanity, require that at least one of INET or INET6 is enabled. * Otherwise, we can't look up interface information, and we can't * convert names or addresses. */ if (inet_proto_info()->ai_family_list[0] == 0) msg_fatal("all network protocols are disabled (%s = %s)", VAR_INET_PROTOCOLS, var_inet_protocols); /* * This program handles all incoming connections, so it must not block. * We use event-driven code for all operations that introduce latency. * * Note: instead of using VSTREAM-level timeouts, we enforce limits on the * total amount of time to receive a complete SMTP command line. */ non_blocking(vstream_fileno(smtp_client_stream), NON_BLOCKING); /* * Look up the remote SMTP client address and port. */ psc_endpt_lookup(smtp_client_stream, psc_endpt_lookup_done); }
static void tlsp_service(VSTREAM *plaintext_stream, char *service, char **argv) { TLSP_STATE *state; int plaintext_fd = vstream_fileno(plaintext_stream); /* * Sanity check. This service takes no command-line arguments. */ if (argv[0]) msg_fatal("unexpected command-line argument: %s", argv[0]); /* * This program handles multiple connections, so it must not block. We * use event-driven code for all operations that introduce latency. * Except that attribute lists are sent/received synchronously, once the * socket is found to be ready for transmission. */ non_blocking(plaintext_fd, NON_BLOCKING); vstream_control(plaintext_stream, CA_VSTREAM_CTL_PATH("plaintext"), CA_VSTREAM_CTL_TIMEOUT(5), CA_VSTREAM_CTL_END); /* * Receive postscreen's remote SMTP client address/port and socket. */ state = tlsp_state_create(service, plaintext_stream); event_enable_read(plaintext_fd, tlsp_get_request_event, (void *) state); event_request_timer(tlsp_get_request_event, (void *) state, TLSP_INIT_TIMEOUT); }
static void start_connect(SESSION *session) { int fd; struct linger linger; /* * Some systems don't set the socket error when connect() fails early * (loopback) so we must deal with the error immediately, rather than * retrieving it later with getsockopt(). We can't use MSG_PEEK to * distinguish between server disconnect and connection refused. */ if ((fd = socket(sa->sa_family, SOCK_STREAM, 0)) < 0) msg_fatal("socket: %m"); (void) non_blocking(fd, NON_BLOCKING); linger.l_onoff = 1; linger.l_linger = 0; if (setsockopt(fd, SOL_SOCKET, SO_LINGER, (char *) &linger, sizeof(linger)) < 0) msg_warn("setsockopt SO_LINGER %d: %m", linger.l_linger); session->stream = vstream_fdopen(fd, O_RDWR); event_enable_write(fd, connect_done, (char *) session); smtp_timeout_setup(session->stream, var_timeout); if (inet_windowsize > 0) set_inet_windowsize(fd, inet_windowsize); if (sane_connect(fd, sa, sa_length) < 0 && errno != EINPROGRESS) fail_connect(session); }
static void connect_event(int unused_event, void *context) { int sock = CAST_ANY_PTR_TO_INT(context); struct sockaddr_storage ss; SOCKADDR_SIZE len = sizeof(ss); struct sockaddr *sa = (struct sockaddr *) &ss; SINK_STATE *state; int fd; if ((fd = accept(sock, sa, &len)) >= 0) { if (msg_verbose) msg_info("connect (%s)", #ifdef AF_LOCAL sa->sa_family == AF_LOCAL ? "AF_LOCAL" : #else sa->sa_family == AF_UNIX ? "AF_UNIX" : #endif sa->sa_family == AF_INET ? "AF_INET" : #ifdef AF_INET6 sa->sa_family == AF_INET6 ? "AF_INET6" : #endif "unknown protocol family"); non_blocking(fd, NON_BLOCKING); state = (SINK_STATE *) mymalloc(sizeof(*state)); state->stream = vstream_fdopen(fd, O_RDWR); vstream_tweak_sock(state->stream); netstring_setup(state->stream, var_tmout); event_enable_read(fd, read_length, (void *) state); } }
static void connect_done(int unused_event, char *context) { SESSION *session = (SESSION *) context; int fd = vstream_fileno(session->stream); /* * Try again after some delay when the connection failed, in case they * run a Mickey Mouse protocol stack. */ if (socket_error(fd) < 0) { fail_connect(session); } else { non_blocking(fd, BLOCKING); /* Disable write events. */ event_disable_readwrite(fd); event_enable_read(fd, read_banner, (char *) session); dequeue_connect(session); /* Avoid poor performance when TCP MSS > VSTREAM_BUFSIZE. */ if (sa->sa_family == AF_INET #ifdef AF_INET6 || sa->sa_family == AF_INET6 #endif ) vstream_tweak_tcp(session->stream); } }
int fifo_listen(const char *path, int permissions, int block_mode) { char buf[BUF_LEN]; static int open_mode = 0; char *myname = "fifo_listen"; struct stat st; int fd; int count; /* * Create a named pipe (fifo). Do whatever we can so we don't run into * trouble when this process is restarted after crash. Make sure that we * open a fifo and not something else, then change permissions to what we * wanted them to be, because mkfifo() is subject to umask settings. * Instead we could zero the umask temporarily before creating the FIFO, * but that would cost even more system calls. Figure out if the fifo * needs to be opened O_RDWR or O_RDONLY. Some systems need one, some * need the other. If we choose the wrong mode, the fifo will stay * readable, causing the program to go into a loop. */ if (unlink(path) && errno != ENOENT) msg_fatal("%s: remove %s: %m", myname, path); if (mkfifo(path, permissions) < 0) msg_fatal("%s: create fifo %s: %m", myname, path); switch (open_mode) { case 0: if ((fd = open(path, O_RDWR | O_NONBLOCK, 0)) < 0) msg_fatal("%s: open %s: %m", myname, path); if (readable(fd) == 0) { open_mode = O_RDWR | O_NONBLOCK; break; } else { open_mode = O_RDONLY | O_NONBLOCK; if (msg_verbose) msg_info("open O_RDWR makes fifo readable - trying O_RDONLY"); (void) close(fd); /* FALLTRHOUGH */ } default: if ((fd = open(path, open_mode, 0)) < 0) msg_fatal("%s: open %s: %m", myname, path); break; } /* * Make sure we opened a FIFO and skip any cruft that might have * accumulated before we opened it. */ if (fstat(fd, &st) < 0) msg_fatal("%s: fstat %s: %m", myname, path); if (S_ISFIFO(st.st_mode) == 0) msg_fatal("%s: not a fifo: %s", myname, path); if (fchmod(fd, permissions) < 0) msg_fatal("%s: fchmod %s: %m", myname, path); non_blocking(fd, block_mode); while ((count = peekfd(fd)) > 0 && read(fd, buf, BUF_LEN < count ? BUF_LEN : count) > 0) /* void */ ; return (fd); }
int inet_listen(const char *addr, int backlog, int block_mode) { struct sockaddr_in sin; int sock; int t = 1; char *buf; char *host; char *port; /* * Translate address information to internal form. */ buf = inet_parse(addr, &host, &port); memset((char *) &sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = find_inet_port(port, "tcp"); sin.sin_addr.s_addr = (*host ? find_inet_addr(host) : INADDR_ANY); myfree(buf); /* * Create a listener socket. */ if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) msg_fatal("socket: %m"); if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &t, sizeof(t)) < 0) msg_fatal("setsockopt: %m"); if (bind(sock, (struct sockaddr *) & sin, sizeof(sin)) < 0) msg_fatal("bind %s port %d: %m", sin.sin_addr.s_addr == INADDR_ANY ? "INADDR_ANY" : inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); non_blocking(sock, block_mode); if (listen(sock, backlog) < 0) msg_fatal("listen: %m"); return (sock); }
size_t read_some( AsyncReadStream& s, const MutableBufferSequence& buf ) { detail::non_blocking<AsyncReadStream> non_blocking; // TODO: determine if non_blocking query results in a system call that // will slow down every read... if( non_blocking(s) || non_blocking(s,true) ) { boost::system::error_code ec; size_t r = s.read_some( buf, ec ); if( !ec ) return r; if( ec != boost::asio::error::would_block ) BOOST_THROW_EXCEPTION( boost::system::system_error(ec) ); } promise<size_t>::ptr p(new promise<size_t>("fc::asio::read_some")); s.async_read_some( buf, boost::bind( detail::read_write_handler, p, _1, _2 ) ); return p->wait(); }
void master_sigsetup(void) { const char *myname = "master_sigsetup"; struct sigaction action; static int sigs[] = { SIGINT, SIGQUIT, SIGILL, SIGBUS, SIGSEGV, SIGTERM, }; unsigned i; sigemptyset(&action.sa_mask); action.sa_flags = 0; /* * Prepare to kill our children when we receive any of the above signals. */ action.sa_handler = master_sigdeath; for (i = 0; i < sizeof(sigs) / sizeof(sigs[0]); i++) if (sigaction(sigs[i], &action, (struct sigaction *) 0) < 0) msg_fatal("%s: sigaction(%d): %m", myname, sigs[i]); #ifdef USE_SIG_PIPE if (pipe(master_sig_pipe)) msg_fatal("pipe: %m"); non_blocking(SIG_PIPE_WRITE_FD, NON_BLOCKING); non_blocking(SIG_PIPE_READ_FD, NON_BLOCKING); close_on_exec(SIG_PIPE_WRITE_FD, CLOSE_ON_EXEC); close_on_exec(SIG_PIPE_READ_FD, CLOSE_ON_EXEC); event_enable_read(SIG_PIPE_READ_FD, master_sig_event, (void *) 0); #endif /* * Intercept SIGHUP (re-read config file) and SIGCHLD (child exit). */ #ifdef SA_RESTART action.sa_flags |= SA_RESTART; #endif action.sa_handler = master_sighup; if (sigaction(SIGHUP, &action, (struct sigaction *) 0) < 0) msg_fatal("%s: sigaction(%d): %m", myname, SIGHUP); action.sa_flags |= SA_NOCLDSTOP; action.sa_handler = master_sigchld; if (sigaction(SIGCHLD, &action, (struct sigaction *) 0) < 0) msg_fatal("%s: sigaction(%d): %m", myname, SIGCHLD); }
static void tlsp_get_fd_event(int event, void *context) { const char *myname = "tlsp_get_fd_event"; TLSP_STATE *state = (TLSP_STATE *) context; int plaintext_fd = vstream_fileno(state->plaintext_stream); /* * At this point we still manually manage plaintext read/write/timeout * events. Disable I/O and timer events. Don't assume that the first * plaintext request will be a read. */ event_disable_readwrite(plaintext_fd); if (event != EVENT_TIME) event_cancel_timer(tlsp_get_fd_event, (void *) state); else errno = ETIMEDOUT; /* * Initialize plaintext-related session state. Once we have this behind * us, the TLSP_STATE destructor will automagically clean up requests for * read/write/timeout events, which makes error recovery easier. * * Register the plaintext event handler for timer cleanup in the TLSP_STATE * destructor. Insert the NBBIO event-driven I/O layer between the * postscreen(8) server and the TLS engine. */ if (event != EVENT_READ || (state->ciphertext_fd = LOCAL_RECV_FD(plaintext_fd)) < 0) { msg_warn("%s: receive SMTP client file descriptor: %m", myname); tlsp_state_free(state); return; } non_blocking(state->ciphertext_fd, NON_BLOCKING); state->ciphertext_timer = tlsp_ciphertext_event; state->plaintext_buf = nbbio_create(plaintext_fd, VSTREAM_BUFSIZE, "postscreen", tlsp_plaintext_event, (void *) state); /* * Perform the TLS layer before-handshake initialization. We perform the * remainder after the TLS handshake completes. */ tlsp_start_tls(state); /* * Trigger the initial proxy server I/Os. */ tlsp_strategy(state); }
int main(int argc, char **argv) { GOOGLE_PROTOBUF_VERIFY_VERSION; // Queue of lambda expressions modifying the world object auto worldQueue = std::make_shared<std::queue<std::function<void()>>>(); // Mutex controlling pushes to the end of the world queue auto worldQueueMutex = std::make_shared<std::mutex>(); // Queue of buffers received by external hosts auto incomingBufferQueue = std::make_shared<std::queue<std::pair<boost::asio::ip::udp::endpoint, std::shared_ptr<boost::asio::streambuf>>>>(); // Queue of buffers waiting to be sent to clients auto outgoingBufferQueue = std::make_shared<std::queue<std::pair<boost::asio::ip::udp::endpoint, std::shared_ptr<boost::asio::streambuf>>>>(); // Controls send/receive cycle of server auto socketMutex = std::make_shared<std::mutex>(); // Set up socket for network communication boost::asio::io_service ioService; auto socket = std::make_shared<boost::asio::ip::udp::socket>(ioService, boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), PORT_NUMBER)); socket->non_blocking(true); unsigned int threadCount = std::thread::hardware_concurrency(); if (threadCount == 0) { std::cout << "Unable to detect the number of threads supported." << std::endl; threadCount = 4; } std::cout << "Thread count: " << threadCount << std::endl; // Starts listener thread //std::thread socketListener(listenOnSocket, incomingBufferQueue, socketMutex, socket); //std::thread socketSender(sendOnSocket, outgoingBufferQueue, socketMutex, socket); threadCount -= 2; // Starts worker threads // TODO: add mutexes and stuff for worker threads std::vector<std::thread> workers; //for (int i = 0; i < threadCount - 1; i++) //workers.emplace_back(processMessages); //socketListener.join(); //socketSender.join(); }
static void multi_server_wakeup(int fd, HTABLE *attr) { VSTREAM *stream; char *tmp; #if defined(F_DUPFD) && (EVENTS_STYLE != EVENTS_STYLE_SELECT) #ifndef THRESHOLD_FD_WORKAROUND #define THRESHOLD_FD_WORKAROUND 128 #endif int new_fd; /* * Leave some handles < FD_SETSIZE for DBMS libraries, in the unlikely * case of a multi-server with a thousand clients. */ if (fd < THRESHOLD_FD_WORKAROUND) { if ((new_fd = fcntl(fd, F_DUPFD, THRESHOLD_FD_WORKAROUND)) < 0) msg_fatal("fcntl F_DUPFD: %m"); (void) close(fd); fd = new_fd; } #endif if (msg_verbose) msg_info("connection established fd %d", fd); non_blocking(fd, BLOCKING); close_on_exec(fd, CLOSE_ON_EXEC); client_count++; stream = vstream_fdopen(fd, O_RDWR); tmp = concatenate(multi_server_name, " socket", (char *) 0); vstream_control(stream, VSTREAM_CTL_PATH, tmp, VSTREAM_CTL_CONTEXT, (char *) attr, VSTREAM_CTL_END); myfree(tmp); timed_ipc_setup(stream); multi_server_saved_flags = vstream_flags(stream); if (multi_server_in_flow_delay && mail_flow_get(1) < 0) event_request_timer(multi_server_enable_read, (char *) stream, var_in_flow_delay); else multi_server_enable_read(0, (char *) stream); }
static void single_server_wakeup(int fd, HTABLE *attr) { VSTREAM *stream; char *tmp; /* * If the accept() succeeds, be sure to disable non-blocking I/O, because * the application is supposed to be single-threaded. Notice the master * of our (un)availability to service connection requests. Commit suicide * when the master process disconnected from us. Don't drop the already * accepted client request after "postfix reload"; that would be rude. */ if (msg_verbose) msg_info("connection established"); non_blocking(fd, BLOCKING); close_on_exec(fd, CLOSE_ON_EXEC); stream = vstream_fdopen(fd, O_RDWR); tmp = concatenate(single_server_name, " socket", (char *) 0); vstream_control(stream, CA_VSTREAM_CTL_PATH(tmp), CA_VSTREAM_CTL_CONTEXT((void *) attr), CA_VSTREAM_CTL_END); myfree(tmp); timed_ipc_setup(stream); if (master_notify(var_pid, single_server_generation, MASTER_STAT_TAKEN) < 0) /* void */ ; if (single_server_in_flow_delay && mail_flow_get(1) < 0) doze(var_in_flow_delay * 1000000); single_server_service(stream, single_server_name, single_server_argv); (void) vstream_fclose(stream); if (master_notify(var_pid, single_server_generation, MASTER_STAT_AVAIL) < 0) single_server_abort(EVENT_NULL_TYPE, EVENT_NULL_CONTEXT); if (msg_verbose) msg_info("connection closed"); /* Avoid integer wrap-around in a persistent process. */ if (use_count < INT_MAX) use_count++; if (var_idle_limit > 0) event_request_timer(single_server_timeout, (void *) 0, var_idle_limit); if (attr) htable_free(attr, myfree); }
int unix_listen(const char *addr, int backlog, int block_mode) { #undef sun struct sockaddr_un sun; int len = strlen(addr); int sock; /* * Translate address information to internal form. */ if (len >= (int) sizeof(sun.sun_path)) msg_fatal("unix-domain name too long: %s", addr); memset((char *) &sun, 0, sizeof(sun)); sun.sun_family = AF_UNIX; #ifdef HAS_SUN_LEN sun.sun_len = len + 1; #endif memcpy(sun.sun_path, addr, len + 1); /* * Create a listener socket. Do whatever we can so we don't run into * trouble when this process is restarted after crash. */ if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) msg_fatal("socket: %m"); if (unlink(addr) < 0 && errno != ENOENT) msg_fatal("remove %s: %m", addr); if (bind(sock, (struct sockaddr *) & sun, sizeof(sun)) < 0) msg_fatal("bind: %s: %m", addr); #ifdef FCHMOD_UNIX_SOCKETS if (fchmod(sock, 0666) < 0) msg_fatal("fchmod socket %s: %m", addr); #else if (chmod(addr, 0666) < 0) msg_fatal("chmod socket %s: %m", addr); #endif non_blocking(sock, block_mode); if (listen(sock, backlog) < 0) msg_fatal("listen: %m"); return (sock); }
static void qmgr_transport_event(int unused_event, void *context) { QMGR_TRANSPORT_ALLOC *alloc = (QMGR_TRANSPORT_ALLOC *) context; /* * This routine notifies the application when the request given to * qmgr_transport_alloc() completes. */ if (msg_verbose) msg_info("transport_event: %s", alloc->transport->name); /* * Connection request completed. Stop the watchdog timer. */ event_cancel_timer(qmgr_transport_abort, context); /* * Disable further read events that end up calling this function, and * free up this pending connection pipeline slot. */ if (alloc->stream) { event_disable_readwrite(vstream_fileno(alloc->stream)); non_blocking(vstream_fileno(alloc->stream), BLOCKING); } alloc->transport->pending -= 1; /* * Notify the requestor. */ if (alloc->transport->xport_rate_delay > 0) { if ((alloc->transport->flags & QMGR_TRANSPORT_STAT_RATE_LOCK) == 0) msg_panic("transport_event: missing rate lock for transport %s", alloc->transport->name); event_request_timer(qmgr_transport_rate_event, (void *) alloc, alloc->transport->xport_rate_delay); } else { alloc->notify(alloc->transport, alloc->stream); myfree((void *) alloc); } }
int stream_pass_connect(const char *path, int block_mode, int unused_timeout) { #ifdef STREAM_CONNECTIONS const char *myname = "stream_pass_connect"; int fifo; /* * The requested file system object must exist, otherwise we can't reach * the server. */ if ((fifo = open(path, O_WRONLY | O_NONBLOCK, 0)) < 0) return (-1); /* * This is for {unix,inet}_connect() compatibility. */ non_blocking(fifo, block_mode); return (fifo); #else msg_fatal("stream connections are not implemented"); #endif }
void master_status_init(MASTER_SERV *serv) { const char *myname = "master_status_init"; /* * Sanity checks. */ if (serv->status_fd[0] >= 0 || serv->status_fd[1] >= 0) msg_panic("%s: status events already enabled", myname); if (msg_verbose) msg_info("%s: %s", myname, serv->name); /* * Make the read end of this service's status pipe non-blocking so that * we can detect partial writes on the child side. We use a duplex pipe * so that the child side becomes readable when the master goes away. */ if (duplex_pipe(serv->status_fd) < 0) msg_fatal("pipe: %m"); non_blocking(serv->status_fd[0], BLOCKING); close_on_exec(serv->status_fd[0], CLOSE_ON_EXEC); close_on_exec(serv->status_fd[1], CLOSE_ON_EXEC); event_enable_read(serv->status_fd[0], master_status_event, (char *) serv); }
/* * This is the actual startup routine for the connection. We expect that the * buffers are flushed and the "220 Ready to start TLS" was received by us, * so that we can immediately start the TLS handshake process. */ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props) { int sts; int protomask; const char *cipher_list; SSL_SESSION *session = 0; SSL_CIPHER_const SSL_CIPHER *cipher; X509 *peercert; TLS_SESS_STATE *TLScontext; TLS_APPL_STATE *app_ctx = props->ctx; char *myserverid; int log_mask = app_ctx->log_mask; /* * When certificate verification is required, log trust chain validation * errors even when disabled by default for opportunistic sessions. For * DANE this only applies when using trust-anchor associations. */ if (TLS_MUST_TRUST(props->tls_level) && (!TLS_DANE_BASED(props->tls_level) || TLS_DANE_HASTA(props->dane))) log_mask |= TLS_LOG_UNTRUSTED; if (log_mask & TLS_LOG_VERBOSE) msg_info("setting up TLS connection to %s", props->namaddr); /* * First make sure we have valid protocol and cipher parameters * * Per-session protocol restrictions must be applied to the SSL connection, * as restrictions in the global context cannot be cleared. */ protomask = tls_protocol_mask(props->protocols); if (protomask == TLS_PROTOCOL_INVALID) { /* tls_protocol_mask() logs no warning. */ msg_warn("%s: Invalid TLS protocol list \"%s\": aborting TLS session", props->namaddr, props->protocols); return (0); } /* DANE requires SSLv3 or later, not SSLv2. */ if (TLS_DANE_BASED(props->tls_level)) protomask |= TLS_PROTOCOL_SSLv2; /* * Per session cipher selection for sessions with mandatory encryption * * The cipherlist is applied to the global SSL context, since it is likely * to stay the same between connections, so we make use of a 1-element * cache to return the same result for identical inputs. */ cipher_list = tls_set_ciphers(app_ctx, "TLS", props->cipher_grade, props->cipher_exclusions); if (cipher_list == 0) { msg_warn("%s: %s: aborting TLS session", props->namaddr, vstring_str(app_ctx->why)); return (0); } if (log_mask & TLS_LOG_VERBOSE) msg_info("%s: TLS cipher list \"%s\"", props->namaddr, cipher_list); /* * OpenSSL will ignore cached sessions that use the wrong protocol. So we * do not need to filter out cached sessions with the "wrong" protocol, * rather OpenSSL will simply negotiate a new session. * * We salt the session lookup key with the protocol list, so that sessions * found in the cache are plausibly acceptable. * * By the time a TLS client is negotiating ciphers it has already offered to * re-use a session, it is too late to renege on the offer. So we must * not attempt to re-use sessions whose ciphers are too weak. We salt the * session lookup key with the cipher list, so that sessions found in the * cache are always acceptable. * * With DANE, (more generally any TLScontext where we specified explicit * trust-anchor or end-entity certificates) the verification status of * the SSL session depends on the specified list. Since we verify the * certificate only during the initial handshake, we must segregate * sessions with different TA lists. Note, that TA re-verification is * not possible with cached sessions, since these don't hold the complete * peer trust chain. Therefore, we compute a digest of the sorted TA * parameters and append it to the serverid. */ myserverid = tls_serverid_digest(props, protomask, cipher_list); /* * Allocate a new TLScontext for the new connection and get an SSL * structure. Add the location of TLScontext to the SSL to later retrieve * the information inside the tls_verify_certificate_callback(). * * If session caching was enabled when TLS was initialized, the cache type * is stored in the client SSL context. */ TLScontext = tls_alloc_sess_context(log_mask, props->namaddr); TLScontext->cache_type = app_ctx->cache_type; TLScontext->serverid = myserverid; TLScontext->stream = props->stream; TLScontext->mdalg = props->mdalg; /* Alias DANE digest info from props */ TLScontext->dane = props->dane; if ((TLScontext->con = SSL_new(app_ctx->ssl_ctx)) == NULL) { msg_warn("Could not allocate 'TLScontext->con' with SSL_new()"); tls_print_errors(); tls_free_context(TLScontext); return (0); } if (!SSL_set_ex_data(TLScontext->con, TLScontext_index, TLScontext)) { msg_warn("Could not set application data for 'TLScontext->con'"); tls_print_errors(); tls_free_context(TLScontext); return (0); } /* * Apply session protocol restrictions. */ if (protomask != 0) SSL_set_options(TLScontext->con, TLS_SSL_OP_PROTOMASK(protomask)); /* * XXX To avoid memory leaks we must always call SSL_SESSION_free() after * calling SSL_set_session(), regardless of whether or not the session * will be reused. */ if (TLScontext->cache_type) { session = load_clnt_session(TLScontext); if (session) { SSL_set_session(TLScontext->con, session); SSL_SESSION_free(session); /* 200411 */ } } #ifdef TLSEXT_MAXLEN_host_name if (TLS_DANE_BASED(props->tls_level) && strlen(props->host) <= TLSEXT_MAXLEN_host_name) { /* * With DANE sessions, send an SNI hint. We don't care whether the * server reports finding a matching certificate or not, so no * callback is required to process the server response. Our use of * SNI is limited to giving servers that are (mis)configured to use * SNI the best opportunity to find the certificate they promised via * the associated TLSA RRs. (Generally, server administrators should * avoid SNI, and there are no plans to support SNI in the Postfix * SMTP server). * * Since the hostname is DNSSEC-validated, it must be a DNS FQDN and * thererefore valid for use with SNI. Failure to set a valid SNI * hostname is a memory allocation error, and thus transient. Since * we must not cache the session if we failed to send the SNI name, * we have little choice but to abort. */ if (!SSL_set_tlsext_host_name(TLScontext->con, props->host)) { msg_warn("%s: error setting SNI hostname to: %s", props->namaddr, props->host); tls_free_context(TLScontext); return (0); } if (log_mask & TLS_LOG_DEBUG) msg_info("%s: SNI hostname: %s", props->namaddr, props->host); } #endif /* * Before really starting anything, try to seed the PRNG a little bit * more. */ tls_int_seed(); (void) tls_ext_seed(var_tls_daemon_rand_bytes); /* * Initialize the SSL connection to connect state. This should not be * necessary anymore since 0.9.3, but the call is still in the library * and maintaining compatibility never hurts. */ SSL_set_connect_state(TLScontext->con); /* * Connect the SSL connection with the network socket. */ if (SSL_set_fd(TLScontext->con, vstream_fileno(props->stream)) != 1) { msg_info("SSL_set_fd error to %s", props->namaddr); tls_print_errors(); uncache_session(app_ctx->ssl_ctx, TLScontext); tls_free_context(TLScontext); return (0); } /* * Turn on non-blocking I/O so that we can enforce timeouts on network * I/O. */ non_blocking(vstream_fileno(props->stream), NON_BLOCKING); /* * If the debug level selected is high enough, all of the data is dumped: * TLS_LOG_TLSPKTS will dump the SSL negotiation, TLS_LOG_ALLPKTS will * dump everything. * * We do have an SSL_set_fd() and now suddenly a BIO_ routine is called? * Well there is a BIO below the SSL routines that is automatically * created for us, so we can use it for debugging purposes. */ if (log_mask & TLS_LOG_TLSPKTS) BIO_set_callback(SSL_get_rbio(TLScontext->con), tls_bio_dump_cb); tls_dane_set_callback(app_ctx->ssl_ctx, TLScontext); /* * Start TLS negotiations. This process is a black box that invokes our * call-backs for certificate verification. * * Error handling: If the SSL handhake fails, we print out an error message * and remove all TLS state concerning this session. */ sts = tls_bio_connect(vstream_fileno(props->stream), props->timeout, TLScontext); if (sts <= 0) { if (ERR_peek_error() != 0) { msg_info("SSL_connect error to %s: %d", props->namaddr, sts); tls_print_errors(); } else if (errno != 0) { msg_info("SSL_connect error to %s: %m", props->namaddr); } else { msg_info("SSL_connect error to %s: lost connection", props->namaddr); } uncache_session(app_ctx->ssl_ctx, TLScontext); tls_free_context(TLScontext); return (0); } /* Turn off packet dump if only dumping the handshake */ if ((log_mask & TLS_LOG_ALLPKTS) == 0) BIO_set_callback(SSL_get_rbio(TLScontext->con), 0); /* * The caller may want to know if this session was reused or if a new * session was negotiated. */ TLScontext->session_reused = SSL_session_reused(TLScontext->con); if ((log_mask & TLS_LOG_CACHE) && TLScontext->session_reused) msg_info("%s: Reusing old session", TLScontext->namaddr); /* * Do peername verification if requested and extract useful information * from the certificate for later use. */ if ((peercert = SSL_get_peer_certificate(TLScontext->con)) != 0) { TLScontext->peer_status |= TLS_CERT_FLAG_PRESENT; /* * Peer name or fingerprint verification as requested. * Unconditionally set peer_CN, issuer_CN and peer_cert_fprint. Check * fingerprint first, and avoid logging verified as untrusted in the * call to verify_extract_name(). */ verify_extract_print(TLScontext, peercert, props); verify_extract_name(TLScontext, peercert, props); if (TLScontext->log_mask & (TLS_LOG_CERTMATCH | TLS_LOG_VERBOSE | TLS_LOG_PEERCERT)) msg_info("%s: subject_CN=%s, issuer_CN=%s, " "fingerprint=%s, pkey_fingerprint=%s", props->namaddr, TLScontext->peer_CN, TLScontext->issuer_CN, TLScontext->peer_cert_fprint, TLScontext->peer_pkey_fprint); X509_free(peercert); } else { TLScontext->issuer_CN = mystrdup(""); TLScontext->peer_CN = mystrdup(""); TLScontext->peer_cert_fprint = mystrdup(""); TLScontext->peer_pkey_fprint = mystrdup(""); } /* * Finally, collect information about protocol and cipher for logging */ TLScontext->protocol = SSL_get_version(TLScontext->con); cipher = SSL_get_current_cipher(TLScontext->con); TLScontext->cipher_name = SSL_CIPHER_get_name(cipher); TLScontext->cipher_usebits = SSL_CIPHER_get_bits(cipher, &(TLScontext->cipher_algbits)); /* * The TLS engine is active. Switch to the tls_timed_read/write() * functions and make the TLScontext available to those functions. */ tls_stream_start(props->stream, TLScontext); /* * All the key facts in a single log entry. */ if (log_mask & TLS_LOG_SUMMARY) msg_info("%s TLS connection established to %s: %s with cipher %s " "(%d/%d bits)", !TLS_CERT_IS_PRESENT(TLScontext) ? "Anonymous" : TLS_CERT_IS_MATCHED(TLScontext) ? "Verified" : TLS_CERT_IS_TRUSTED(TLScontext) ? "Trusted" : "Untrusted", props->namaddr, TLScontext->protocol, TLScontext->cipher_name, TLScontext->cipher_usebits, TLScontext->cipher_algbits); tls_int_seed(); return (TLScontext); }
int AttachGalley(OBJECT hd, OBJECT *inners, OBJECT *suspend_pt) { OBJECT hd_index; /* the index of hd in the enclosing galley */ OBJECT hd_inners; /* inner galleys of hd, if unsized */ OBJECT dest; /* the target @Galley hd empties into */ OBJECT dest_index; /* the index of dest */ OBJECT target; /* the target indefinite containing dest */ OBJECT target_index; /* the index of target */ OBJECT target_galley; /* the body of target, made into a galley */ OBJECT tg_inners; /* inner galleys of target_galley */ BOOLEAN need_precedes = FALSE;/* true if destination lies before galley */ OBJECT recs; /* list of recursive definite objects */ OBJECT link, y = nilobj; /* for scanning through the components of hd */ CONSTRAINT c; /* temporary variable holding a constraint */ OBJECT env, n1, tmp, zlink, z, sym; /* placeholders and temporaries */ BOOLEAN was_sized; /* true if sized(hd) initially */ int dim; /* the galley direction */ FULL_LENGTH perp_back, perp_fwd; OBJECT why, junk; debug2(DGA, D, "[ AttachGalley(Galley %s into %s)", SymName(actual(hd)), SymName(whereto(hd))); ifdebug(DGA, DD, DebugGalley(hd, nilobj, 4)); assert( Up(hd) != hd, "AttachGalley: no index!" ); Parent(hd_index, Up(hd)); assert( type(hd_index) == UNATTACHED, "AttachGalley: not UNATTACHED!" ); hd_inners = tg_inners = nilobj; was_sized = sized(hd); dim = gall_dir(hd); for(;;) { /*************************************************************************/ /* */ /* Search for a destination for hd. If hd is unsized, search for */ /* inner galleys preceding it first of all, then for receptive objects */ /* following it, possibly in inner galleys. If no luck, exit. */ /* If hd is sized, search only for receptive objects in the current */ /* galley below the current spot, and fail if cannot find any. */ /* */ /*************************************************************************/ sym = whereto(hd); if( sized(hd) ) { /* sized galley case: search on from current spot */ target_index = SearchGalley(Up(hd_index), sym, TRUE, FALSE, TRUE, TRUE); if( target_index == nilobj ) { /* search failed to find any new target, so kill the galley */ for( link = Down(hd); link != hd; link = NextDown(link) ) { Child(y, link); if( type(y) == SPLIT ) Child(y, DownDim(y, dim)); if( is_definite(type(y)) ) break; } if( link != hd ) Error(19, 1, "galley %s deleted from here (no target)", WARN, &fpos(y), SymName(actual(hd))); if( hd_inners != nilobj ) DisposeObject(hd_inners), hd_inners=nilobj; if( tg_inners != nilobj ) DisposeObject(tg_inners), tg_inners=nilobj; KillGalley(hd, FALSE); *inners = nilobj; debug0(DGA, D, "] AttachGalley returning ATTACH_KILLED"); return ATTACH_KILLED; } else if( actual(actual(target_index)) == InputSym ) { /* search found input object, so suspend on that */ DeleteNode(hd_index); Link(target_index, hd); *inners = nilobj; debug0(DGA, D, "] AttachGalley returning ATTACH_INPUT"); return ATTACH_INPUT; } } else /* unsized galley, either backwards or normal */ { if( foll_or_prec(hd) == GALL_PREC ) { target_index= SearchGalley(Up(hd_index), sym, FALSE, TRUE,TRUE,FALSE); need_precedes = FALSE; } else { target_index = SearchGalley(Up(hd_index), sym, FALSE,TRUE,FALSE,FALSE); need_precedes = (target_index != nilobj); if( target_index == nilobj ) target_index = SearchGalley(Up(hd_index), sym, TRUE,TRUE,TRUE,FALSE); } /* if no luck, exit without error */ if( target_index == nilobj ) { *inners = nilobj; debug0(DGA, D, "] AttachGalley returning ATTACH_NOTARGET"); return ATTACH_NOTARGET; } } assert( type(target_index) == RECEPTIVE, "AttachGalley: target_index!" ); target = actual(target_index); assert( type(target) == CLOSURE, "AttachGalley: target!" ); /* set target_galley to the expanded value of target */ debug1(DYY, D, "[ EnterErrorBlock(FALSE) (expanding target %s)", SymName(actual(target))); EnterErrorBlock(FALSE); New(target_galley, HEAD); force_gall(target_galley) = FALSE; enclose_obj(target_galley) = limiter(target_galley) = nilobj; ClearHeaders(target_galley); opt_components(target_galley) = opt_constraints(target_galley) = nilobj; gall_dir(target_galley) = external_hor(target) ? COLM : ROWM; FposCopy(fpos(target_galley), fpos(target)); actual(target_galley) = actual(target); whereto(target_galley) = ready_galls(target_galley) = nilobj; foll_or_prec(target_galley) = GALL_FOLL; must_expand(target_galley) = FALSE; sized(target_galley) = FALSE; /* get perpendicular constraint (none if horizontal galley) */ if( dim == ROWM ) { Constrained(target, &c, 1-dim, &junk); if( !constrained(c) ) Error(19, 2, "receptive symbol %s has unconstrained width", FATAL, &fpos(target), SymName(actual(target))); debug2(DSC, DD, "Constrained( %s, 1-dim ) = %s", EchoObject(target), EchoConstraint(&c)); if( !FitsConstraint(0, 0, c) ) { debug0(DGA, D, " reject: target_galley horizontal constraint is -1"); y = nilobj; goto REJECT; } } else /* actually unused */ SetConstraint(c, MAX_FULL_LENGTH, MAX_FULL_LENGTH, MAX_FULL_LENGTH); debug1(DGA, DDD, " expanding %s", EchoObject(target)); tmp = CopyObject(target, no_fpos); Link(target_galley, tmp); env = DetachEnv(tmp); debug4(DGM, D, " external_ver(%s) = %s, external_hor(%s) = %s", SymName(actual(target)), bool(external_ver(target)), SymName(actual(target)), bool(external_hor(target))); SizeGalley(target_galley, env, external_ver(target) || external_hor(target), threaded(target), non_blocking(target_index), trigger_externs(target_index), &save_style(target), &c, whereto(hd), &dest_index, &recs, &tg_inners, enclose_obj(hd) != nilobj ? CopyObject(enclose_obj(hd), no_fpos):nilobj); debug1(DGA, DD, " SizeGalley tg_inners: %s", DebugInnersNames(tg_inners)); if( recs != nilobj ) ExpandRecursives(recs); dest = actual(dest_index); if( underline(dest) == UNDER_UNDEF ) underline(dest) = UNDER_OFF; /* verify that hd satisfies any horizontal constraint on dest */ if( dim == ROWM ) { debug1(DGA, DDD, " checking hor fit of hd in %s",SymName(actual(dest))); Constrained(dest, &c, 1-dim, &junk); debug3(DSC, DD, "Constrained( %s, %s ) = %s", EchoObject(dest), dimen(1-dim), EchoConstraint(&c)); assert( constrained(c), "AttachGalley: dest unconstrained!" ); if( !FitsConstraint(0, 0, c) ) { debug0(DGA, D, " reject: hd horizontal constraint is -1"); y = nilobj; goto REJECT; } } /* manifest and size the galley if not done yet */ if( !sized(hd) ) { debug2(DYY, D, "[ EnterErrorBlock(TRUE) (sizing galley %s into %s)", SymName(actual(hd)), SymName(whereto(hd))); EnterErrorBlock(TRUE); n1 = nilobj; Child(y, Down(hd)); env = DetachEnv(y); /*** threaded() only defined in ROWM case SizeGalley(hd, env, TRUE, threaded(dest), non_blocking(target_index), TRUE, &save_style(dest), &c, nilobj, &n1, &recs, &hd_inners); *** */ SizeGalley(hd, env, TRUE, dim == ROWM ? threaded(dest) : FALSE, non_blocking(target_index), TRUE, &save_style(dest), &c, nilobj, &n1, &recs, &hd_inners, nilobj); debug1(DGA,DD," SizeGalley hd_inners: %s", DebugInnersNames(hd_inners)); if( recs != nilobj ) ExpandRecursives(recs); if( need_precedes ) /* need an ordering constraint */ { OBJECT index1, index2; New(index1, PRECEDES); New(index2, FOLLOWS); blocked(index2) = FALSE; tmp = MakeWord(WORD, STR_EMPTY, no_fpos); Link(index1, tmp); Link(index2, tmp); Link(Up(hd_index), index1); Link(Down(hd), index2); debug0(DGA, D, " inserting PRECEDES and FOLLOWS"); } LeaveErrorBlock(TRUE); debug0(DYY, D, "] LeaveErrorBlock(TRUE) (finished sizing galley)"); } if( dim == ROWM ) { if( !FitsConstraint(back(hd, 1-dim), fwd(hd, 1-dim), c) ) { debug3(DGA, D, " reject: hd %s,%s does not fit target_galley %s", EchoLength(back(hd, 1-dim)), EchoLength(fwd(hd, 1-dim)), EchoConstraint(&c)); Error(19, 3, "too little horizontal space for galley %s at %s", WARN, &fpos(hd), SymName(actual(hd)), SymName(actual(dest))); goto REJECT; } } /* check status of first component of hd */ debug0(DGA, DDD, " now ready to attach; hd ="); ifdebug(DGA, DDD, DebugObject(hd)); for( link = Down(hd); link != hd; link = NextDown(link) ) { Child(y, link); debug1(DGA, DDD, " examining %s", EchoIndex(y)); if( type(y) == SPLIT ) Child(y, DownDim(y, dim)); switch( type(y) ) { case EXPAND_IND: case SCALE_IND: case COVER_IND: case GALL_PREC: case GALL_FOLL: case GALL_FOLL_OR_PREC: case GALL_TARG: case CROSS_PREC: case CROSS_FOLL: case CROSS_FOLL_OR_PREC: case CROSS_TARG: case PAGE_LABEL_IND: break; case PRECEDES: case UNATTACHED: if( was_sized ) { /* SizeGalley was not called, so hd_inners was not set by it */ if( hd_inners == nilobj ) New(hd_inners, ACAT); Link(hd_inners, y); } break; case RECEPTIVE: goto SUSPEND; case RECEIVING: goto SUSPEND; case FOLLOWS: Child(tmp, Down(y)); if( Up(tmp) == LastUp(tmp) ) { link = pred(link, CHILD); debug0(DGA, DD, " disposing FOLLOWS"); DisposeChild(NextDown(link)); break; } Parent(tmp, Up(tmp)); assert(type(tmp) == PRECEDES, "Attach: PRECEDES!"); switch( CheckComponentOrder(tmp, target_index) ) { case CLEAR: DeleteNode(tmp); link = pred(link, CHILD); DisposeChild(NextDown(link)); break; case PROMOTE: break; case BLOCK: debug0(DGA, DD, "CheckContraint: BLOCK"); goto SUSPEND; case CLOSE: debug0(DGA, D, " reject: CheckContraint"); goto REJECT; } break; case GAP_OBJ: underline(y) = underline(dest); if( !join(gap(y)) ) seen_nojoin(hd) = TRUE; break; case BEGIN_HEADER: case END_HEADER: case SET_HEADER: case CLEAR_HEADER: /* do nothing until actually promoted out of here */ underline(y) = underline(dest); break; case CLOSURE: case CROSS: case FORCE_CROSS: case NULL_CLOS: case PAGE_LABEL: underline(y) = underline(dest); break; case WORD: case QWORD: case ONE_COL: case ONE_ROW: case WIDE: case HIGH: case HSHIFT: case VSHIFT: case HMIRROR: case VMIRROR: case HSCALE: case VSCALE: case HCOVER: case VCOVER: case HCONTRACT: case VCONTRACT: case HLIMITED: case VLIMITED: case HEXPAND: case VEXPAND: case START_HVSPAN: case START_HSPAN: case START_VSPAN: case HSPAN: case VSPAN: case ROTATE: case BACKGROUND: case SCALE: case KERN_SHRINK: case INCGRAPHIC: case SINCGRAPHIC: case PLAIN_GRAPHIC: case GRAPHIC: case LINK_SOURCE: case LINK_DEST: case LINK_DEST_NULL: case LINK_URL: case ACAT: case HCAT: case VCAT: case ROW_THR: case COL_THR: underline(y) = underline(dest); if( dim == ROWM ) { /* make sure y is not joined to a target below (vertical only) */ for( zlink = NextDown(link); zlink != hd; zlink = NextDown(zlink) ) { Child(z, zlink); switch( type(z) ) { case RECEPTIVE: if( non_blocking(z) ) { zlink = PrevDown(zlink); DeleteNode(z); } else { y = z; goto SUSPEND; } break; case RECEIVING: if( non_blocking(z) ) { zlink = PrevDown(zlink); while( Down(z) != z ) { Child(tmp, Down(y)); if( opt_components(tmp) != nilobj ) { DisposeObject(opt_components(tmp)); opt_components(tmp) = nilobj; debug3(DOG, D, "AttachGalley(%s) de-optimizing %s %s", SymName(actual(hd)), SymName(actual(tmp)), "(join)"); } DetachGalley(tmp); KillGalley(tmp, FALSE); } DeleteNode(z); } else { y = z; goto SUSPEND; } break; case GAP_OBJ: if( !join(gap(z)) ) zlink = PrevDown(hd); break; default: break; } } /* if HCAT, try vertical hyphenation (vertical galleys only) */ if( type(y) == HCAT ) VerticalHyphenate(y); } /* check availability of parallel space for the first component */ why = nilobj; Constrained(dest, &c, dim, &why); debug3(DGF, DD, " dest parallel Constrained(%s, %s) = %s", EchoObject(dest), dimen(dim), EchoConstraint(&c)); if( !FitsConstraint(back(y, dim), fwd(y, dim), c) ) { BOOLEAN scaled; /* if forcing galley doesn't fit, try scaling first component */ scaled = FALSE; if( force_gall(hd) && size(y, dim) > 0 ) { int scale_factor; scale_factor = ScaleToConstraint(back(y,dim), fwd(y,dim), &c); if( scale_factor > 0.5 * SF ) { char num1[20], num2[20]; sprintf(num1, "%.1fc", (float) size(y, dim) / CM); sprintf(num2, "%.1fc", (float) bfc(c) / CM); if( dim == ROWM ) Error(19, 4, "%s object too high for %s space; %s inserted", WARN, &fpos(y), num1, num2, KW_SCALE); else Error(19, 5, "%s object too wide for %s space; %s inserted", WARN, &fpos(y), num1, num2, KW_SCALE); y = InterposeScale(y, scale_factor, dim); scaled = TRUE; } } /* otherwise we must reject, and warn the user */ if( !scaled ) { char num1[20], num2[20]; debug3(DGA, D, " reject: vsize %s,%s in %s; y=", EchoLength(back(y, dim)), EchoLength(fwd(y, dim)), EchoConstraint(&c)); ifdebug(DGA, D, DebugObject(y)); if( size(y, dim) > 0 ) { sprintf(num1, "%.1fc", (float) size(y, dim) / CM); sprintf(num2, "%.1fc", (float) bfc(c) / CM); if( dim == ROWM ) Error(19, 12, "%s object too high for %s space; will try elsewhere", WARN, &fpos(y), num1, num2); else Error(19, 13, "%s object too wide for %s space; will try elsewhere", WARN, &fpos(y), num1, num2); } goto REJECT; } } /* check availability of perpendicular space for first component */ if( dim == ROWM ) { perp_back = back(hd, 1-dim); perp_fwd = fwd(hd, 1-dim); } else { perp_back = back(y, 1-dim); perp_fwd = fwd(y, 1-dim); } Constrained(dest, &c, 1-dim, &junk); debug3(DGF, DD, " dest perpendicular Constrained(%s, %s) = %s", EchoObject(dest), dimen(1-dim), EchoConstraint(&c)); if( !FitsConstraint(perp_back, perp_fwd, c) ) { BOOLEAN scaled; /* if forcing galley doesn't fit, try scaling first component */ scaled = FALSE; if( force_gall(hd) && perp_back + perp_fwd > 0 ) { int scale_factor; scale_factor = ScaleToConstraint(perp_back, perp_fwd, &c); if( scale_factor > 0.5 * SF ) { char num1[20], num2[20]; sprintf(num1, "%.1fc", (float) (perp_back + perp_fwd) / CM); sprintf(num2, "%.1fc", (float) bfc(c) / CM); if( 1-dim == ROWM ) Error(19, 6, "%s object too high for %s space; %s inserted", WARN, &fpos(y), num1, num2, KW_SCALE); else Error(19, 7, "%s object too wide for %s space; %s inserted", WARN, &fpos(y), num1, num2, KW_SCALE); y = InterposeScale(y, scale_factor, 1-dim); scaled = TRUE; } } /* otherwise we must reject, and warn the user */ if( !scaled ) { debug3(DGA, D, " reject: vsize %s,%s in %s; y=", EchoLength(perp_back), EchoLength(perp_fwd), EchoConstraint(&c)); ifdebug(DGA, D, DebugObject(y)); goto REJECT; } } /* dest seems OK, so perform its size adjustments */ debug0(DSA, D, "calling AdjustSize from AttachGalley (a)"); AdjustSize(dest, back(y, dim), fwd(y, dim), dim); debug0(DSA, D, "calling AdjustSize from AttachGalley (b)"); AdjustSize(dest, perp_back, perp_fwd, 1-dim); /* now check parallel space for target_galley in target */ Constrained(target, &c, dim, &why); debug3(DGF, DD, " target parallel Constrained(%s, %s) = %s", EchoObject(target), dimen(dim), EchoConstraint(&c)); Child(z, LastDown(target_galley)); /* works in all cases? */ assert( !is_index(type(z)), "AttachGalley: is_index(z)!" ); assert( back(z, dim)>=0 && fwd(z, dim)>=0, "AttachGalley: z size!" ); if( !FitsConstraint(back(z, dim), fwd(z, dim), c) ) { BOOLEAN scaled; debug2(DGA, D, " why = %d %s", (int) why, EchoObject(why)); debug2(DGA, D, " limiter = %d %s", (int) limiter(hd), EchoObject(limiter(hd))); /* if forcing galley doesn't fit, try scaling z */ scaled = FALSE; if( force_gall(hd) && size(z, dim) > 0 && limiter(hd) != why ) { int scale_factor; scale_factor = ScaleToConstraint(back(z,dim), fwd(z,dim), &c); if( scale_factor > 0.5 * SF ) { char num1[20], num2[20]; sprintf(num1, "%.1fc", (float) size(z, dim) / CM); sprintf(num2, "%.1fc", (float) bfc(c) / CM); if( dim == ROWM ) Error(19, 8, "%s object too high for %s space; %s inserted", WARN, &fpos(y), num1, num2, KW_SCALE); else Error(19, 9, "%s object too wide for %s space; %s inserted", WARN, &fpos(y), num1, num2, KW_SCALE); z = InterposeWideOrHigh(z, dim); z = InterposeScale(z, scale_factor, dim); scaled = TRUE; } } if( !scaled ) { char num1[20], num2[20]; limiter(hd) = why; debug3(DGA, D, " set limiter(%s) = %d %s", SymName(actual(hd)), (int) limiter(hd), EchoObject(limiter(hd))); debug3(DGA, D, " reject: size was %s,%s in %s; y =", EchoLength(back(z, dim)), EchoLength(fwd(z, dim)), EchoConstraint(&c)); ifdebug(DGA, D, DebugObject(y)); if( size(z, dim) > 0 ) { sprintf(num1, "%.1fc", (float) size(z, dim) / CM); sprintf(num2, "%.1fc", (float) bfc(c) / CM); if( dim == ROWM ) Error(19, 14, "%s object too high for %s space; will try elsewhere", WARN, &fpos(y), num1, num2); else Error(19, 15, "%s object too wide for %s space; will try elsewhere", WARN, &fpos(y), num1, num2); } goto REJECT; } } limiter(hd) = why; debug3(DGA, D, " set limiter(%s) = %d %s", SymName(actual(hd)), (int) limiter(hd), EchoObject(limiter(hd))); /* now check perpendicular space for target_galley in target */ Constrained(target, &c, 1-dim, &junk); debug3(DGF, DD, " target perpendicular Constrained(%s, %s) = %s", EchoObject(target), dimen(1-dim), EchoConstraint(&c)); Child(z, LastDown(target_galley)); /* works in all cases? */ assert( !is_index(type(z)), "AttachGalley: is_index(z)!" ); assert( back(z, 1-dim)>=0 && fwd(z, 1-dim)>=0, "AttachGalley: z size (perpendicular)!" ); if( !FitsConstraint(back(z, 1-dim), fwd(z, 1-dim), c) ) { BOOLEAN scaled; /* if forcing galley doesn't fit, try scaling z */ scaled = FALSE; if( force_gall(hd) && size(z, 1-dim) > 0 ) { int scale_factor; scale_factor = ScaleToConstraint(back(z,1-dim), fwd(z,1-dim), &c); if( scale_factor > 0.5 * SF ) { char num1[20], num2[20]; sprintf(num1, "%.1fc", (float) size(z, 1-dim) / CM); sprintf(num2, "%.1fc", (float) bfc(c) / CM); if( 1-dim == ROWM ) Error(19, 10, "%s object too high for %s space; %s inserted", WARN, &fpos(y), num1, num2, KW_SCALE); else Error(19, 11, "%s object too wide for %s space; %s inserted", WARN, &fpos(y), num1, num2, KW_SCALE); z = InterposeWideOrHigh(z, 1-dim); z = InterposeScale(z, scale_factor, 1-dim); scaled = TRUE; } } if( !scaled ) { debug3(DGA, D, " reject: size was %s,%s in %s; y =", EchoLength(back(z, 1-dim)), EchoLength(fwd(z, 1-dim)), EchoConstraint(&c)); ifdebug(DGA, D, DebugObject(y)); goto REJECT; } } /* target seems OK, so adjust sizes and accept */ if( external_hor(target) ) { /* don't adjust any sizes, none to adjust */ debug0(DSA, D, "not calling AdjustSize from AttachGalley (c)"); } else if( external_ver(target) ) { /* adjust perp size only, to galley size */ debug0(DSA, D, "calling AdjustSize from AttachGalley (d)"); AdjustSize(target, back(target_galley, 1-dim), fwd(target_galley, 1-dim), 1-dim); } else { /* adjust both directions, using z (last component) */ Child(z, LastDown(target_galley)); debug0(DSA, D, "AttachGalley AdjustSize using z ="); ifdebug(DSA, D, DebugObject(z)); debug0(DSA, D, "calling AdjustSize from AttachGalley (e)"); AdjustSize(target, back(z, dim), fwd(z, dim), dim); debug0(DSA, D, "calling AdjustSize from AttachGalley (f)"); AdjustSize(target, back(z, 1-dim), fwd(z, 1-dim), 1-dim); } goto ACCEPT; default: assert1(FALSE, "AttachGalley:", Image(type(y))); break; } /* end switch */ } /* end for */ /* null galley: promote whole galley without expanding the target */ debug0(DGA, D, " null galley"); if( tg_inners != nilobj ) DisposeObject(tg_inners), tg_inners = nilobj; DisposeObject(target_galley); LeaveErrorBlock(FALSE); debug0(DYY, D, "] LeaveErrorBlock(FALSE) (null galley)"); /* kill off any null objects within the galley, then transfer it */ /* don't use Promote() since it does extra unwanted things here */ for( link = Down(hd); link != hd; link = NextDown(link) ) { Child(y, link); switch( type(y) ) { case GAP_OBJ: case CLOSURE: case CROSS: case FORCE_CROSS: case NULL_CLOS: case PAGE_LABEL: link = PrevDown(link); debug1(DGA, D, " null galley, disposing %s", Image(type(y))); DisposeChild(NextDown(link)); break; default: break; } } TransferLinks(NextDown(hd), hd, Up(target_index)); /* attach hd temporarily to target_index */ MoveLink(Up(hd), target_index, PARENT); assert( type(hd_index) == UNATTACHED, "AttachGalley: type(hd_index)!" ); DeleteNode(hd_index); /* return; only hd_inners needs to be flushed now */ *inners = hd_inners; debug0(DGA, D, "] AttachGalley returning ATTACH_NULL"); return ATTACH_NULL; REJECT: /* reject first component */ /* debug1(DGA, D, " reject %s", EchoObject(y)); */ debug0(DGA, D, " reject first component"); LeaveErrorBlock(TRUE); debug0(DYY, D, "] LeaveErrorBlock(TRUE) (REJECT)"); if( tg_inners != nilobj ) DisposeObject(tg_inners), tg_inners = nilobj; DisposeObject(target_galley); if( foll_or_prec(hd) == GALL_PREC && !sized(hd) ) { /* move to just before the failed target */ MoveLink(Up(hd_index), Up(target_index), PARENT); } else { /* move to just after the failed target */ MoveLink(Up(hd_index), NextDown(Up(target_index)), PARENT); } continue; SUSPEND: /* suspend at first component */ debug1(DGA, D, " suspend %s", EchoIndex(y)); blocked(y) = TRUE; LeaveErrorBlock(FALSE); debug0(DYY, D, "] LeaveErrorBlock(FALSE) (SUSPEND)"); if( tg_inners != nilobj ) DisposeObject(tg_inners), tg_inners = nilobj; DisposeObject(target_galley); MoveLink(Up(hd_index), Up(target_index), PARENT); if( was_sized ) { /* nothing new to flush if suspending and already sized */ if( hd_inners != nilobj ) DisposeObject(hd_inners), hd_inners=nilobj; *inners = nilobj; } else { /* flush newly discovered inners if not sized before */ *inners = hd_inners; } debug0(DGA, D, "] AttachGalley returning ATTACH_SUSPEND"); *suspend_pt = y; return ATTACH_SUSPEND; ACCEPT: /* accept first component; now committed to the attach */ debug3(DGA, D, " accept %s %s %s", Image(type(y)), EchoObject(y), EchoFilePos(&fpos(y))); LeaveErrorBlock(TRUE); debug0(DYY, D, "] LeaveErrorBlock(TRUE) (ACCEPT)"); /* attach hd to dest */ MoveLink(Up(hd), dest_index, PARENT); assert( type(hd_index) == UNATTACHED, "AttachGalley: type(hd_index)!" ); DeleteNode(hd_index); /* move first component of hd into dest */ /* nb Interpose must be done after all AdjustSize calls */ if( dim == ROWM && !external_ver(dest) ) Interpose(dest, VCAT, hd, y); else if( dim == COLM && !external_hor(dest) ) { Interpose(dest, ACAT, y, y); Parent(junk, Up(dest)); assert( type(junk) == ACAT, "AttachGalley: type(junk) != ACAT!" ); StyleCopy(save_style(junk), save_style(dest)); adjust_cat(junk) = padjust(save_style(junk)); } debug1(DGS, D, "calling Promote(hd, %s) from AttachGalley/ACCEPT", link == hd ? "hd" : "NextDown(link)"); Promote(hd, link == hd ? hd : NextDown(link), dest_index, TRUE); /* move target_galley into target */ /* nb Interpose must be done after all AdjustSize calls */ if( !(external_ver(target) || external_hor(target)) ) { Child(z, LastDown(target_galley)); Interpose(target, VCAT, z, z); } debug0(DGS, D, "calling Promote(target_galley) from AttachGalley/ACCEPT"); Promote(target_galley, target_galley, target_index, TRUE); DeleteNode(target_galley); assert(Down(target_index)==target_index, "AttachGalley: target_ind"); if( blocked(target_index) ) blocked(dest_index) = TRUE; DeleteNode(target_index); /* return; both tg_inners and hd_inners need to be flushed now; */ /* if was_sized, hd_inners contains the inners of the first component; */ /* otherwise it contains the inners of all components, from SizeGalley */ if( tg_inners == nilobj ) *inners = hd_inners; else if( hd_inners == nilobj ) *inners = tg_inners; else { TransferLinks(Down(hd_inners), hd_inners, tg_inners); DeleteNode(hd_inners); *inners = tg_inners; } debug0(DGA, D, "] AttachGalley returning ATTACH_ACCEPT"); ifdebug(DGA, D, if( dim == COLM && !external_hor(dest) ) { OBJECT z; Parent(z, Up(dest)); debug2(DGA, D, " COLM dest_encl on exit = %s %s", Image(type(z)), EchoObject(z)); } ) return ATTACH_ACCEPT; } /* end for */
static int select_mode(struct conf *cfp, int sock, int *pipefd, int parentfd) { char sockbuf[BUFSIZ], pipebuf[PIPE_BUF]; struct timeval timeout, *tp = NULL; ssize_t pcc = 0, scc = 0, n; char *pbp = NULL, *sbp = NULL; int rsel, maxfd; int bye = 0; (void)non_blocking(sock); (void)non_blocking(parentfd); maxfd = sock > parentfd ? sock : parentfd; if (pipefd[0] != -1) { (void)non_blocking(pipefd[0]); if (pipefd[0] > maxfd) maxfd = pipefd[0]; } if (pipefd[1] != -1) { (void)non_blocking(pipefd[1]); if (pipefd[1] > maxfd) maxfd = pipefd[1]; } maxfd += 1; if (cfp->mode == READ) (void)shutdown(sock, SHUT_RD); if (cfp->mode == BLIND) (void)shutdown(sock, SHUT_WR); for (;;) { fd_set rset, wset, *omask; FD_ZERO(&rset); FD_ZERO(&wset); omask = (fd_set *) NULL; switch (cfp->mode) { case READWRITE: if (!bye) { if (scc) { FD_SET(pipefd[1], &wset); omask = &wset; } else FD_SET(sock, &rset); } if (pcc >= 0) { if (pcc) { FD_SET(sock, &wset); omask = &wset; } else FD_SET(pipefd[0], &rset); } break; case BLIND: if (bye) return 0; if (scc) { FD_SET(pipefd[1], &wset); omask = &wset; } else FD_SET(sock, &rset); break; case READ: if (pcc >= 0) { if (pcc) { FD_SET(sock, &wset); omask = &wset; } else FD_SET(pipefd[0], &rset); } break; default: return -1; } FD_SET(parentfd, &rset); /* * Posix.1g defines timeout parameter to const * Linux uses a value-result timeout */ if (cfp->timeout) { timeout.tv_sec = cfp->timeout; timeout.tv_usec = 0; tp = &timeout; } if ((rsel = select(maxfd, &rset, omask, NULL, tp)) == -1) { if (errno == EINTR) continue; else { syslog(LOG_ERR, "selectfd %m"); return -1; } } if (rsel == 0) { if (tp) { syslog(LOG_INFO, "[%s] session time expired for %s", cfp->session, get_pwentry(cfp->setuser)->pw_name); return 0; } else continue; } if (FD_ISSET(parentfd, &rset)) { char c; (void)read(parentfd, &c, 1); return 0; } if (cfp->mode != BLIND && FD_ISSET(pipefd[0], &rset)) { pcc = read(pipefd[0], pipebuf, sizeof(pipebuf)); if (pcc < 0 && errno == EWOULDBLOCK) pcc = 0; else { if (pcc <= 0) return (pcc) ? -1 : 0; pbp = pipebuf; FD_SET(sock, &wset); } } if (FD_ISSET(sock, &wset) && pcc > 0) { n = write(sock, pbp, (size_t)pcc); if (n < 0 && errno == EWOULDBLOCK) continue; else if (n < 0) return -1; if (n > 0) pcc -= n, pbp += n; } if (cfp->mode != READ && FD_ISSET(sock, &rset)) { scc = read(sock, sockbuf, sizeof(sockbuf)); if (scc < 0 && errno == EWOULDBLOCK) scc = 0; else { if (scc < 0) return -1; /* * client write-shutdown or disconnection */ if (scc == 0) { bye = 1; (void)close(pipefd[1]); continue; } sbp = sockbuf; FD_SET(pipefd[1], &wset); } } if (cfp->mode != READ && FD_ISSET(pipefd[1], &wset)) { n = write(pipefd[1], sbp, (size_t)scc); if (n < 0 && errno == EWOULDBLOCK) continue; else if (n < 0) return -1; if (n > 0) scc -= n, sbp += n; } } }
socket1.set_option(settable_socket_option2); socket1.set_option(settable_socket_option2, ec); socket1.set_option(settable_socket_option3); socket1.set_option(settable_socket_option3, ec); socket1.get_option(gettable_socket_option1); socket1.get_option(gettable_socket_option1, ec); socket1.get_option(gettable_socket_option2); socket1.get_option(gettable_socket_option2, ec); socket1.get_option(gettable_socket_option3); socket1.get_option(gettable_socket_option3, ec); socket1.io_control(io_control_command); socket1.io_control(io_control_command, ec); bool non_blocking1 = socket1.non_blocking(); (void)non_blocking1; socket1.non_blocking(true); socket1.non_blocking(false, ec); bool non_blocking2 = socket1.native_non_blocking(); (void)non_blocking2; socket1.native_non_blocking(true); socket1.native_non_blocking(false, ec); ip::udp::endpoint endpoint1 = socket1.local_endpoint(); ip::udp::endpoint endpoint2 = socket1.local_endpoint(ec); ip::udp::endpoint endpoint3 = socket1.remote_endpoint(); ip::udp::endpoint endpoint4 = socket1.remote_endpoint(ec);
static int select_fd(struct conf *cfp, int sock, int master, int ctrls, int parentfd) { char sockbuf[1024], ptybuf[1024]; struct timeval timeout, *tp = NULL; char *pbp = NULL, *sbp = NULL; ssize_t pcc = 0, scc = 0, n; int rsel, maxfd; (void)non_blocking(sock); (void)non_blocking(master); (void)non_blocking(ctrls); (void)non_blocking(parentfd); (void)signal(SIGTTOU, SIG_IGN); maxfd = sock > master ? sock : master; if (maxfd < ctrls) maxfd = ctrls; if (maxfd < pipechld[0]) maxfd = pipechld[0]; if (maxfd < parentfd) maxfd = parentfd; maxfd += 1; for (;;) { fd_set rset, wset, *omask; FD_ZERO(&rset); FD_ZERO(&wset); omask = (fd_set *) NULL; if (scc) { FD_SET(master, &wset); omask = &wset; } else FD_SET(sock, &rset); if (pcc >= 0) { if (pcc) { FD_SET(sock, &wset); omask = &wset; } else FD_SET(master, &rset); } FD_SET(ctrls, &rset); FD_SET(pipechld[0], &rset); FD_SET(parentfd, &rset); /* * Posix.1g defines timeout parameter to const * Linux uses a value-result timeout */ if (cfp->timeout) { timeout.tv_sec = cfp->timeout; timeout.tv_usec = 0; tp = &timeout; } if ((rsel = select(maxfd, &rset, omask, NULL, tp)) == -1) { if (errno == EINTR) continue; else { syslog(LOG_ERR, "selectfd %m"); return -1; } } if (rsel == 0) { if (tp) { syslog(LOG_INFO, "[%s] session time expired for %s", cfp->session, get_pwentry(cfp->setuser)->pw_name); /* * giving a chance to know timeout * XXX: writen()? */ (void)write(sock, "\r\nsession timeout\r\n", (size_t)19); return 0; } else continue; } if (FD_ISSET(parentfd, &rset)) { char c; (void)read(parentfd, &c, 1); return 0; } if (FD_ISSET(pipechld[0], &rset)) { pid_t pid; int st; char c; if (pipechld[0] != -1) (void)read(pipechld[0], &c, 1); while ((pid = waitpid(-1, &st, WNOHANG|WUNTRACED))) { if (pid < 0) { if (errno == EINTR) continue; else break; } if (WIFSTOPPED(st)) { (void)kill(pid, SIGCONT); continue; } } } if (FD_ISSET(ctrls, &rset)) { struct schck sck; n = read(ctrls, &sck, sizeof(sck)); if (n == sizeof(sck)) (void)control_check(ctrls, &sck, master); } if (FD_ISSET(sock, &rset)) { scc = read(sock, sockbuf, sizeof(sockbuf)); if (scc < 0 && errno == EWOULDBLOCK) scc = 0; else { if (scc <= 0) return (scc) ? -1 : 0; sbp = sockbuf; FD_SET(master, &wset); } } if (FD_ISSET(master, &wset) && scc > 0) { n = write(master, sbp, (size_t)scc); if (n < 0 && errno == EWOULDBLOCK) continue; else if (n < 0) return -1; if (n > 0) scc -= n, sbp += n; } if (FD_ISSET(master, &rset)) { pcc = read(master, ptybuf, sizeof(ptybuf)); if (pcc < 0 && errno == EWOULDBLOCK) pcc = 0; else { if (pcc <= 0) return (pcc) ? -1 : 0; pbp = ptybuf; FD_SET(sock, &wset); } } if (FD_ISSET(sock, &wset) && pcc > 0) { n = write(sock, pbp, (size_t)pcc); if (n < 0 && errno == EWOULDBLOCK) continue; else if (n < 0) return -1; if (n > 0) pcc -= n, pbp += n; } } /* * never reached */ return -1; }
void exec_shell(struct conf *cfp, int fd, int parentfd) { int master, slave; pid_t pid; char line[MAXPATHLEN]; char *tty; #ifdef HAVE_UTMP_H struct utmp ut; #elif HAVE_UTMPX_H struct utmpx ut; struct timeval tv; #endif #if defined(HAVE_UTMP_H) || defined(HAVE_UTMPX_H) memset(&ut, 0, sizeof(ut)); #endif if (pipe(pipechld) < 0) { syslog(LOG_ERR, "exec_shell() pipe %m"); _exit(1); } (void)non_blocking(pipechld[0]); (void)non_blocking(pipechld[1]); if (openpty(&master, &slave, line, NULL, NULL) == -1) { syslog(LOG_ERR, "openpty %m"); _exit(1) ; } /* * pts/x compatible */ if ((tty = strstr(line, "/dev/"))) tty += 5; else tty = line; #if defined(HAVE_UTMP_H) || defined(HAVE_UTMPX_H) if (cfp->utmp) { if (cfp->utname) { #ifdef HAVE_UTMP_H (void)strncpy(ut.ut_name, cfp->utname, sizeof(ut.ut_name)-1); ut.ut_name[sizeof(ut.ut_name)-1] = '\0'; #elif HAVE_UTMPX_H (void)strncpy(ut.ut_user, cfp->utname, sizeof(ut.ut_user)-1); ut.ut_user[sizeof(ut.ut_user)-1] = '\0'; #endif } else { struct passwd *pw; pw = get_pwentry(cfp->havesetuser ? cfp->setuser : \ 0); #ifdef HAVE_UTMP_H (void)strncpy(ut.ut_name, pw->pw_name, sizeof(ut.ut_name)-1); ut.ut_name[sizeof(ut.ut_name)-1] = '\0'; #elif HAVE_UTMPX_H (void)strncpy(ut.ut_user, pw->pw_name, sizeof(ut.ut_user)-1); ut.ut_user[sizeof(ut.ut_user)-1] = '\0'; #endif } (void)strncpy(ut.ut_line, tty, sizeof(ut.ut_line)-1); ut.ut_line[sizeof(ut.ut_line)-1] = '\0'; if (cfp->uthost) { (void)strncpy(ut.ut_host, cfp->uthost, sizeof(ut.ut_host)-1); ut.ut_host[sizeof(ut.ut_host)-1] = '\0'; } #ifdef HAVE_UTMP_H (void)time(&ut.ut_time); #elif HAVE_UTMPX_H (void)gettimeofday(&tv, NULL); ut.ut_tv.tv_sec = tv.tv_sec; ut.ut_tv.tv_usec = tv.tv_usec; (void)strncpy(ut.ut_id, ut.ut_line, sizeof(ut.ut_id)-1); ut.ut_line[sizeof(ut.ut_line)-1] = '\0'; ut.ut_pid = getpid(); ut.ut_type = USER_PROCESS; #endif } #endif /* * overwriting signal disposition */ (void)signal(SIGCHLD, sig_chld); switch (pid = fork()) { case -1: syslog(LOG_ERR, "forkpty: %m"); _exit(1); case 0: (void)close(parentfd); (void)close(pipechld[0]); (void)close(pipechld[1]); (void)close(master); (void)close(fd); (void)login_tty(slave); #ifdef HAVE_UTMP_H login(&ut); #elif HAVE_UTMPX_H setutxent(); (void)pututxline(&ut); #endif set_privileges(cfp); /* * SUIP PROGRAM HERE */ #ifdef __NetBSD__ (void)execl(_PATH_BSHELL,"sh", "-c",cfp->suipfile,(char *)NULL); #else (void)execl(_PATH_BSHELL, "sh", "-p", "-c", cfp->suipfile, (char *)NULL); #endif _exit(127); default: { int ctrls; int exit_status = 0; (void)close(slave); /* * trying to open a control channel * control_create() returns the number of bytes which were * written * select_fd() returns -1 if errors exist you can check errno */ if (control_create(&ctrls, fd) == 1) { if (select_fd(cfp, fd, master, ctrls, parentfd) < 0) exit_status = 1; } else { syslog(LOG_ERR, "can't open ctrl chan"); exit_status = 1; } #if defined(HAVE_UTMP_H) || defined(HAVE_UTMPX_H) if (cfp->utmp) { #ifdef HAVE_UTMP_H if (!logout(tty)) { syslog(LOG_ERR, "unable to logout on %s", tty); exit_status = 1; } else logwtmp(tty, "", ""); #elif HAVE_UTMPX_H ut.ut_type = DEAD_PROCESS; (void)gettimeofday(&tv, NULL); ut.ut_tv.tv_sec = tv.tv_sec; ut.ut_tv.tv_usec = tv.tv_usec; (void)memset(&ut.ut_user, 0, sizeof(ut.ut_user)); setutxent(); if (pututxline(&ut) == NULL) { syslog(LOG_ERR, "unable to logout on %s (utmpx)", tty); exit_status = 1; } endutxent(); #endif } #endif cleanup(line); _exit(exit_status); }} /* * never reached */ _exit(1); }
static void psc_service(VSTREAM *smtp_client_stream, char *unused_service, char **unused_argv) { const char *myname = "psc_service"; PSC_STATE *state; struct sockaddr_storage addr_storage; SOCKADDR_SIZE addr_storage_len = sizeof(addr_storage); MAI_HOSTADDR_STR smtp_client_addr; MAI_SERVPORT_STR smtp_client_port; MAI_HOSTADDR_STR smtp_server_addr; MAI_SERVPORT_STR smtp_server_port; int aierr; const char *stamp_str; int saved_flags; /* * For sanity, require that at least one of INET or INET6 is enabled. * Otherwise, we can't look up interface information, and we can't * convert names or addresses. */ if (inet_proto_info()->ai_family_list[0] == 0) msg_fatal("all network protocols are disabled (%s = %s)", VAR_INET_PROTOCOLS, var_inet_protocols); /* * This program handles all incoming connections, so it must not block. * We use event-driven code for all operations that introduce latency. * * Note: instead of using VSTREAM-level timeouts, we enforce limits on the * total amount of time to receive a complete SMTP command line. */ non_blocking(vstream_fileno(smtp_client_stream), NON_BLOCKING); /* * We use the event_server framework. This means we get already-accepted * connections so we have to invoke getpeername() to find out the remote * address and port. */ /* Best effort - if this non-blocking write(2) fails, so be it. */ #define PSC_SERVICE_DISCONNECT_AND_RETURN(stream) do { \ (void) write(vstream_fileno(stream), \ "421 4.3.2 No system resources\r\n", \ sizeof("421 4.3.2 No system resources\r\n") - 1); \ event_server_disconnect(stream); \ return; \ } while (0); /* * Look up the remote SMTP client address and port. */ if (getpeername(vstream_fileno(smtp_client_stream), (struct sockaddr *) & addr_storage, &addr_storage_len) < 0) { msg_warn("getpeername: %m -- dropping this connection"); PSC_SERVICE_DISCONNECT_AND_RETURN(smtp_client_stream); } /* * Convert the remote SMTP client address and port to printable form for * logging and access control. */ if ((aierr = sockaddr_to_hostaddr((struct sockaddr *) & addr_storage, addr_storage_len, &smtp_client_addr, &smtp_client_port, 0)) != 0) { msg_warn("cannot convert client address/port to string: %s" " -- dropping this connection", MAI_STRERROR(aierr)); PSC_SERVICE_DISCONNECT_AND_RETURN(smtp_client_stream); } if (strncasecmp("::ffff:", smtp_client_addr.buf, 7) == 0) memmove(smtp_client_addr.buf, smtp_client_addr.buf + 7, sizeof(smtp_client_addr.buf) - 7); if (msg_verbose > 1) msg_info("%s: sq=%d cq=%d connect from [%s]:%s", myname, psc_post_queue_length, psc_check_queue_length, smtp_client_addr.buf, smtp_client_port.buf); /* * Look up the local SMTP server address and port. */ if (getsockname(vstream_fileno(smtp_client_stream), (struct sockaddr *) & addr_storage, &addr_storage_len) < 0) { msg_warn("getsockname: %m -- dropping this connection"); PSC_SERVICE_DISCONNECT_AND_RETURN(smtp_client_stream); } /* * Convert the local SMTP server address and port to printable form for * logging and access control. */ if ((aierr = sockaddr_to_hostaddr((struct sockaddr *) & addr_storage, addr_storage_len, &smtp_server_addr, &smtp_server_port, 0)) != 0) { msg_warn("cannot convert server address/port to string: %s" " -- dropping this connection", MAI_STRERROR(aierr)); PSC_SERVICE_DISCONNECT_AND_RETURN(smtp_client_stream); } if (strncasecmp("::ffff:", smtp_server_addr.buf, 7) == 0) memmove(smtp_server_addr.buf, smtp_server_addr.buf + 7, sizeof(smtp_server_addr.buf) - 7); msg_info("CONNECT from [%s]:%s to [%s]:%s", smtp_client_addr.buf, smtp_client_port.buf, smtp_server_addr.buf, smtp_server_port.buf); /* * Bundle up all the loose session pieces. This zeroes all flags and time * stamps. */ state = psc_new_session_state(smtp_client_stream, smtp_client_addr.buf, smtp_client_port.buf); /* * Reply with 421 when the client has too many open connections. */ if (var_psc_cconn_limit > 0 && state->client_concurrency > var_psc_cconn_limit) { msg_info("NOQUEUE: reject: CONNECT from [%s]:%s: too many connections", state->smtp_client_addr, state->smtp_client_port); PSC_DROP_SESSION_STATE(state, "421 4.7.0 Error: too many connections\r\n"); return; } /* * Reply with 421 when we can't forward more connections. */ if (var_psc_post_queue_limit > 0 && psc_post_queue_length >= var_psc_post_queue_limit) { msg_info("NOQUEUE: reject: CONNECT from [%s]:%s: all server ports busy", state->smtp_client_addr, state->smtp_client_port); PSC_DROP_SESSION_STATE(state, "421 4.3.2 All server ports are busy\r\n"); return; } /* * The permanent white/blacklist has highest precedence. */ if (psc_acl != 0) { switch (psc_acl_eval(state, psc_acl, VAR_PSC_ACL)) { /* * Permanently blacklisted. */ case PSC_ACL_ACT_BLACKLIST: msg_info("BLACKLISTED [%s]:%s", PSC_CLIENT_ADDR_PORT(state)); PSC_FAIL_SESSION_STATE(state, PSC_STATE_FLAG_BLIST_FAIL); switch (psc_blist_action) { case PSC_ACT_DROP: PSC_DROP_SESSION_STATE(state, "521 5.3.2 Service currently unavailable\r\n"); return; case PSC_ACT_ENFORCE: PSC_ENFORCE_SESSION_STATE(state, "550 5.3.2 Service currently unavailable\r\n"); break; case PSC_ACT_IGNORE: PSC_UNFAIL_SESSION_STATE(state, PSC_STATE_FLAG_BLIST_FAIL); /* * Not: PSC_PASS_SESSION_STATE. Repeat this test the next * time. */ break; default: msg_panic("%s: unknown blacklist action value %d", myname, psc_blist_action); } break; /* * Permanently whitelisted. */ case PSC_ACL_ACT_WHITELIST: msg_info("WHITELISTED [%s]:%s", PSC_CLIENT_ADDR_PORT(state)); psc_conclude(state); return; /* * Other: dunno (don't know) or error. */ default: break; } } /* * The temporary whitelist (i.e. the postscreen cache) has the lowest * precedence. This cache contains information about the results of prior * tests. Whitelist the client when all enabled test results are still * valid. */ if ((state->flags & PSC_STATE_MASK_ANY_FAIL) == 0 && psc_cache_map != 0 && (stamp_str = psc_cache_lookup(psc_cache_map, state->smtp_client_addr)) != 0) { saved_flags = state->flags; psc_parse_tests(state, stamp_str, event_time()); state->flags |= saved_flags; if (msg_verbose) msg_info("%s: cached + recent flags: %s", myname, psc_print_state_flags(state->flags, myname)); if ((state->flags & PSC_STATE_MASK_ANY_TODO_FAIL) == 0) { msg_info("PASS OLD [%s]:%s", PSC_CLIENT_ADDR_PORT(state)); psc_conclude(state); return; } } else { saved_flags = state->flags; psc_new_tests(state); state->flags |= saved_flags; if (msg_verbose) msg_info("%s: new + recent flags: %s", myname, psc_print_state_flags(state->flags, myname)); } /* * Don't whitelist clients that connect to backup MX addresses. Fail * "closed" on error. */ if (addr_match_list_match(psc_wlist_if, smtp_server_addr.buf) == 0) { state->flags |= (PSC_STATE_FLAG_WLIST_FAIL | PSC_STATE_FLAG_NOFORWARD); msg_info("WHITELIST VETO [%s]:%s", PSC_CLIENT_ADDR_PORT(state)); } /* * Reply with 421 when we can't analyze more connections. That also means * no deep protocol tests when the noforward flag is raised. */ if (var_psc_pre_queue_limit > 0 && psc_check_queue_length - psc_post_queue_length >= var_psc_pre_queue_limit) { msg_info("reject: connect from [%s]:%s: all screening ports busy", state->smtp_client_addr, state->smtp_client_port); PSC_DROP_SESSION_STATE(state, "421 4.3.2 All screening ports are busy\r\n"); return; } /* * If the client has no up-to-date results for some tests, do those tests * first. Otherwise, skip the tests and hand off the connection. */ if (state->flags & PSC_STATE_MASK_EARLY_TODO) psc_early_tests(state); else if (state->flags & (PSC_STATE_MASK_SMTPD_TODO | PSC_STATE_FLAG_NOFORWARD)) psc_smtpd_tests(state); else psc_conclude(state); }