static void replica_connect(const char *channel) { int wait; struct protoent *proto; sasl_callback_t *cb; int timeout; const char *port, *auth_status = NULL; cb = mysasl_callbacks(NULL, get_config(channel, "sync_authname"), get_config(channel, "sync_realm"), get_config(channel, "sync_password")); /* get the right port */ port = get_config(channel, "sync_port"); if (port) { imap_csync_protocol.service = port; csync_protocol.service = port; } for (wait = 15;; wait *= 2) { sync_backend = backend_connect(sync_backend, servername, &imap_csync_protocol, "", cb, &auth_status, (verbose > 1 ? fileno(stderr) : -1)); if (sync_backend) { if (sync_backend->capability & CAPA_REPLICATION) { /* attach our IMAP tag buffer to our protstreams as userdata */ sync_backend->in->userdata = sync_backend->out->userdata = &tagbuf; break; } else { backend_disconnect(sync_backend); sync_backend = NULL; } } sync_backend = backend_connect(sync_backend, servername, &csync_protocol, "", cb, NULL, (verbose > 1 ? fileno(stderr) : -1)); if (sync_backend || auth_status || connect_once || wait > 1000) break; fprintf(stderr, "Can not connect to server '%s', retrying in %d seconds\n", servername, wait); sleep(wait); } free_callbacks(cb); cb = NULL; if (!sync_backend) { fprintf(stderr, "Can not connect to server '%s'\n", servername); syslog(LOG_ERR, "Can not connect to server '%s'", servername); _exit(1); } /* Disable Nagle's Algorithm => increase throughput * * http://en.wikipedia.org/wiki/Nagle's_algorithm */ if (servername[0] != '/') { if (sync_backend->sock >= 0 && (proto = getprotobyname("tcp")) != NULL) { int on = 1; if (setsockopt(sync_backend->sock, proto->p_proto, TCP_NODELAY, (void *) &on, sizeof(on)) != 0) { syslog(LOG_ERR, "unable to setsocketopt(TCP_NODELAY): %m"); } /* turn on TCP keepalive if set */ if (config_getswitch(IMAPOPT_TCP_KEEPALIVE)) { int r; int optval = 1; socklen_t optlen = sizeof(optval); struct protoent *proto = getprotobyname("TCP"); r = setsockopt(sync_backend->sock, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen); if (r < 0) { syslog(LOG_ERR, "unable to setsocketopt(SO_KEEPALIVE): %m"); } #ifdef TCP_KEEPCNT optval = config_getint(IMAPOPT_TCP_KEEPALIVE_CNT); if (optval) { r = setsockopt(sync_backend->sock, proto->p_proto, TCP_KEEPCNT, &optval, optlen); if (r < 0) { syslog(LOG_ERR, "unable to setsocketopt(TCP_KEEPCNT): %m"); } } #endif #ifdef TCP_KEEPIDLE optval = config_getint(IMAPOPT_TCP_KEEPALIVE_IDLE); if (optval) { r = setsockopt(sync_backend->sock, proto->p_proto, TCP_KEEPIDLE, &optval, optlen); if (r < 0) { syslog(LOG_ERR, "unable to setsocketopt(TCP_KEEPIDLE): %m"); } } #endif #ifdef TCP_KEEPINTVL optval = config_getint(IMAPOPT_TCP_KEEPALIVE_INTVL); if (optval) { r = setsockopt(sync_backend->sock, proto->p_proto, TCP_KEEPINTVL, &optval, optlen); if (r < 0) { syslog(LOG_ERR, "unable to setsocketopt(TCP_KEEPINTVL): %m"); } } #endif } } else { syslog(LOG_ERR, "unable to getprotobyname(\"tcp\"): %m"); } } #ifdef HAVE_ZLIB /* Does the backend support compression? */ if (CAPA(sync_backend, CAPA_COMPRESS)) { prot_printf(sync_backend->out, "%s\r\n", sync_backend->prot->u.std.compress_cmd.cmd); prot_flush(sync_backend->out); if (sync_parse_response("COMPRESS", sync_backend->in, NULL)) { if (do_compress) fatal("Failed to enable compression, aborting", EC_SOFTWARE); syslog(LOG_NOTICE, "Failed to enable compression, continuing uncompressed"); } else { prot_setcompress(sync_backend->in); prot_setcompress(sync_backend->out); } } else if (do_compress) fatal("Backend does not support compression, aborting", EC_SOFTWARE); #endif /* links to sockets */ sync_in = sync_backend->in; sync_out = sync_backend->out; if (verbose > 1) { prot_setlog(sync_in, fileno(stderr)); prot_setlog(sync_out, fileno(stderr)); } /* Set inactivity timer */ timeout = config_getint(IMAPOPT_SYNC_TIMEOUT); if (timeout < 3) timeout = 3; prot_settimeout(sync_in, timeout); /* Force use of LITERAL+ so we don't need two way communications */ prot_setisclient(sync_in, 1); prot_setisclient(sync_out, 1); }
static void replica_connect(const char *channel) { int wait; sasl_callback_t *cb; int timeout; const char *port, *auth_status = NULL; cb = mysasl_callbacks(NULL, get_config(channel, "sync_authname"), get_config(channel, "sync_realm"), get_config(channel, "sync_password")); /* get the right port */ port = get_config(channel, "sync_port"); if (port) { imap_csync_protocol.service = port; csync_protocol.service = port; } for (wait = 15;; wait *= 2) { sync_backend = backend_connect(sync_backend, servername, &imap_csync_protocol, "", cb, &auth_status, (verbose > 1 ? fileno(stderr) : -1)); if (sync_backend) { if (sync_backend->capability & CAPA_REPLICATION) { /* attach our IMAP tag buffer to our protstreams as userdata */ sync_backend->in->userdata = sync_backend->out->userdata = &tagbuf; break; } else { backend_disconnect(sync_backend); sync_backend = NULL; } } sync_backend = backend_connect(sync_backend, servername, &csync_protocol, "", cb, NULL, (verbose > 1 ? fileno(stderr) : -1)); if (sync_backend || auth_status || connect_once || wait > 1000) break; fprintf(stderr, "Can not connect to server '%s', retrying in %d seconds\n", servername, wait); sleep(wait); } free_callbacks(cb); cb = NULL; if (!sync_backend) { fprintf(stderr, "Can not connect to server '%s'\n", servername); syslog(LOG_ERR, "Can not connect to server '%s'", servername); _exit(1); } if (servername[0] != '/' && sync_backend->sock >= 0) { tcp_disable_nagle(sync_backend->sock); tcp_enable_keepalive(sync_backend->sock); } #ifdef HAVE_ZLIB /* Does the backend support compression? */ if (CAPA(sync_backend, CAPA_COMPRESS)) { prot_printf(sync_backend->out, "%s\r\n", sync_backend->prot->u.std.compress_cmd.cmd); prot_flush(sync_backend->out); if (sync_parse_response("COMPRESS", sync_backend->in, NULL)) { if (do_compress) fatal("Failed to enable compression, aborting", EC_SOFTWARE); syslog(LOG_NOTICE, "Failed to enable compression, continuing uncompressed"); } else { prot_setcompress(sync_backend->in); prot_setcompress(sync_backend->out); } } else if (do_compress) fatal("Backend does not support compression, aborting", EC_SOFTWARE); #endif /* links to sockets */ sync_in = sync_backend->in; sync_out = sync_backend->out; if (verbose > 1) { prot_setlog(sync_in, fileno(stderr)); prot_setlog(sync_out, fileno(stderr)); } /* Set inactivity timer */ timeout = config_getint(IMAPOPT_SYNC_TIMEOUT); if (timeout < 3) timeout = 3; prot_settimeout(sync_in, timeout); /* Force use of LITERAL+ so we don't need two way communications */ prot_setisclient(sync_in, 1); prot_setisclient(sync_out, 1); }
static struct backend *restore_connect(const char *servername, struct buf *tagbuf, const struct restore_options *options) { struct backend *backend = NULL; sasl_callback_t *cb; int timeout; const char *auth_status = NULL; cb = mysasl_callbacks(NULL, config_getstring(IMAPOPT_RESTORE_AUTHNAME), config_getstring(IMAPOPT_RESTORE_REALM), config_getstring(IMAPOPT_RESTORE_PASSWORD)); /* try to connect over IMAP */ backend = backend_connect(backend, servername, &imap_csync_protocol, "", cb, &auth_status, (options->verbose > 1 ? fileno(stderr) : -1)); if (backend) { if (backend->capability & CAPA_REPLICATION) { /* attach our IMAP tag buffer to our protstreams as userdata */ backend->in->userdata = backend->out->userdata = tagbuf; } else { backend_disconnect(backend); backend = NULL; } } /* if that didn't work, fall back to csync */ if (!backend) { backend = backend_connect(backend, servername, &csync_protocol, "", cb, NULL, (options->verbose > 1 ? fileno(stderr) : -1)); } free_callbacks(cb); cb = NULL; if (!backend) { fprintf(stderr, "Can not connect to server '%s'\n", servername); syslog(LOG_ERR, "Can not connect to server '%s'", servername); return NULL; } if (servername[0] != '/' && backend->sock >= 0) { tcp_disable_nagle(backend->sock); tcp_enable_keepalive(backend->sock); } #ifdef HAVE_ZLIB /* Does the backend support compression? */ if (CAPA(backend, CAPA_COMPRESS)) { prot_printf(backend->out, "%s\r\n", backend->prot->u.std.compress_cmd.cmd); prot_flush(backend->out); if (sync_parse_response("COMPRESS", backend->in, NULL)) { if (options->require_compression) fatal("Failed to enable compression, aborting", EX_SOFTWARE); syslog(LOG_NOTICE, "Failed to enable compression, continuing uncompressed"); } else { prot_setcompress(backend->in); prot_setcompress(backend->out); } } else if (options->require_compression) { fatal("Backend does not support compression, aborting", EX_SOFTWARE); } #endif if (options->verbose > 1) { /* XXX did we do this during backend_connect already? */ prot_setlog(backend->in, fileno(stderr)); prot_setlog(backend->out, fileno(stderr)); } /* Set inactivity timer */ timeout = config_getint(IMAPOPT_SYNC_TIMEOUT); if (timeout < 3) timeout = 3; prot_settimeout(backend->in, timeout); /* Force use of LITERAL+ so we don't need two way communications */ prot_setisclient(backend->in, 1); prot_setisclient(backend->out, 1); return backend; }
EXPORTED struct backend *backend_connect(struct backend *ret_backend, const char *server, struct protocol_t *prot, const char *userid, sasl_callback_t *cb, const char **auth_status, int logfd) { /* need to (re)establish connection to server or create one */ int sock = -1; int r; int err = -1; int do_tls = 0; int noauth = 0; struct addrinfo hints, *res0 = NULL, *res; struct sockaddr_un sunsock; struct backend *ret; if (!ret_backend) { ret = xzmalloc(sizeof(struct backend)); strlcpy(ret->hostname, server, sizeof(ret->hostname)); ret->timeout = NULL; } else ret = ret_backend; if (server[0] == '/') { /* unix socket */ res0 = &hints; memset(res0, 0, sizeof(struct addrinfo)); res0->ai_family = PF_UNIX; res0->ai_socktype = SOCK_STREAM; res0->ai_addr = (struct sockaddr *) &sunsock; res0->ai_addrlen = sizeof(sunsock.sun_family) + strlen(server) + 1; #ifdef SIN6_LEN res0->ai_addrlen += sizeof(sunsock.sun_len); sunsock.sun_len = res0->ai_addrlen; #endif sunsock.sun_family = AF_UNIX; strlcpy(sunsock.sun_path, server, sizeof(sunsock.sun_path)); if (!strcmp(prot->sasl_service, "lmtp") || !strcmp(prot->sasl_service, "csync")) { noauth = 1; } } else { /* inet socket */ char host[1024], *p; const char *service = prot->service; /* Parse server string for possible port and options */ strlcpy(host, server, sizeof(host)); if ((p = strchr(host, ':'))) { *p++ = '\0'; service = p; if ((p = strchr(service, '/'))) { tok_t tok; char *opt; *p++ = '\0'; tok_initm(&tok, p, "/", 0); while ((opt = tok_next(&tok))) { if (!strcmp(opt, "tls")) do_tls = 1; else if (!strcmp(opt, "noauth")) noauth = 1; } tok_fini(&tok); } } memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; err = getaddrinfo(host, service, &hints, &res0); if (err) { syslog(LOG_ERR, "getaddrinfo(%s) failed: %s", server, gai_strerror(err)); goto error; } } for (res = res0; res; res = res->ai_next) { sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (sock < 0) continue; /* Do a non-blocking connect() */ nonblock(sock, 1); if (!connect(sock, res->ai_addr, res->ai_addrlen)) { /* connect() succeeded immediately */ break; } else if (errno == EINPROGRESS) { /* connect() in progress */ int n; fd_set wfds, rfds; time_t now = time(NULL); time_t timeout = now + config_getint(IMAPOPT_CLIENT_TIMEOUT); struct timeval waitfor; /* select() socket for writing until we succeed, fail, or timeout */ do { FD_ZERO(&wfds); FD_SET(sock, &wfds); rfds = wfds; waitfor.tv_sec = timeout - now; waitfor.tv_usec = 0; n = select(sock + 1, &rfds, &wfds, NULL, &waitfor); now = time(NULL); /* Retry select() if interrupted */ } while (n < 0 && errno == EINTR && now < timeout); if (!n) { /* select() timed out */ errno = ETIMEDOUT; } else if (FD_ISSET(sock, &rfds) || FD_ISSET(sock, &wfds)) { /* Socket is ready for I/O - get SO_ERROR to determine status */ socklen_t errlen = sizeof(err); if (!getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &errlen) && !(errno = err)) { /* connect() succeeded */ break; } } } close(sock); sock = -1; } if (sock < 0) { if (res0 != &hints) freeaddrinfo(res0); syslog(LOG_ERR, "connect(%s) failed: %m", server); goto error; } /* Reset socket to blocking */ nonblock(sock, 0); memcpy(&ret->addr, res->ai_addr, res->ai_addrlen); if (res0 != &hints) freeaddrinfo(res0); ret->in = prot_new(sock, 0); ret->out = prot_new(sock, 1); ret->sock = sock; prot_settimeout(ret->in, config_getint(IMAPOPT_CLIENT_TIMEOUT)); prot_setflushonread(ret->in, ret->out); ret->prot = prot; /* use literal+ to send literals */ prot_setisclient(ret->in, 1); prot_setisclient(ret->out, 1); /* Start TLS if required */ if (do_tls) r = backend_starttls(ret, NULL, NULL, NULL); /* Login to the server */ if (prot->type == TYPE_SPEC) r = prot->u.spec.login(ret, userid, cb, auth_status, noauth); else r = backend_login(ret, userid, cb, auth_status, noauth); if (r) goto error; if (logfd >= 0) { prot_setlog(ret->in, logfd); prot_setlog(ret->out, logfd); } else prot_settimeout(ret->in, 0); return ret; error: forget_capabilities(ret); if (ret->in) { prot_free(ret->in); ret->in = NULL; } if (ret->out) { prot_free(ret->out); ret->out = NULL; } if (sock >= 0) close(sock); if (ret->saslconn) { sasl_dispose(&ret->saslconn); ret->saslconn = NULL; } if (!ret_backend) free(ret); return NULL; }
HIDDEN int ws_start_channel(struct transaction_t *txn, const char *protocol, int (*data_cb)(struct buf *inbuf, struct buf *outbuf, struct buf *logbuf, void **rock)) { int r; const char **hdr, *accept = NULL; wslay_event_context_ptr ev; struct ws_context *ctx; struct wslay_event_callbacks callbacks = { recv_cb, send_cb, NULL, NULL, NULL, NULL, on_msg_recv_cb }; /* Check for supported WebSocket version */ hdr = spool_getheader(txn->req_hdrs, "Sec-WebSocket-Version"); if (!hdr) { txn->error.desc = "Missing WebSocket version"; return HTTP_BAD_REQUEST; } else if (hdr[1]) { txn->error.desc = "Multiple WebSocket versions"; return HTTP_BAD_REQUEST; } else if (strcmp(hdr[0], WS_VERSION)) { txn->error.desc = "Unsupported WebSocket version"; return HTTP_UPGRADE; } if (protocol) { /* Check for supported WebSocket subprotocol */ int i, found = 0; hdr = spool_getheader(txn->req_hdrs, "Sec-WebSocket-Protocol"); if (!hdr) { txn->error.desc = "Missing WebSocket protocol"; return HTTP_BAD_REQUEST; } for (i = 0; !found && hdr[i]; i++) { tok_t tok = TOK_INITIALIZER(hdr[i], ",", TOK_TRIMLEFT|TOK_TRIMRIGHT); char *token; while ((token = tok_next(&tok))) { if (!strcmp(token, protocol)) { found = 1; break; } } tok_fini(&tok); } if (!found) { txn->error.desc = "Unsupported WebSocket protocol"; return HTTP_BAD_REQUEST; } } if (txn->flags.ver == VER_1_1) { unsigned char sha1buf[SHA1_DIGEST_LENGTH]; /* Check for WebSocket client key */ hdr = spool_getheader(txn->req_hdrs, "Sec-WebSocket-Key"); if (!hdr) { txn->error.desc = "Missing WebSocket client key"; return HTTP_BAD_REQUEST; } else if (hdr[1]) { txn->error.desc = "Multiple WebSocket client keys"; return HTTP_BAD_REQUEST; } else if (strlen(hdr[0]) != WS_CKEY_LEN) { txn->error.desc = "Invalid WebSocket client key"; return HTTP_BAD_REQUEST; } /* Create WebSocket accept key */ buf_setcstr(&txn->buf, hdr[0]); buf_appendcstr(&txn->buf, WS_GUID); xsha1((u_char *) buf_base(&txn->buf), buf_len(&txn->buf), sha1buf); buf_ensure(&txn->buf, WS_AKEY_LEN+1); accept = buf_base(&txn->buf); r = sasl_encode64((char *) sha1buf, SHA1_DIGEST_LENGTH, (char *) accept, WS_AKEY_LEN+1, NULL); if (r != SASL_OK) syslog(LOG_WARNING, "sasl_encode64: %d", r); } /* Create server context */ r = wslay_event_context_server_init(&ev, &callbacks, txn); if (r) { syslog(LOG_WARNING, "wslay_event_context_init: %s", wslay_strerror(r)); return HTTP_SERVER_ERROR; } /* Create channel context */ ctx = xzmalloc(sizeof(struct ws_context)); ctx->event = ev; ctx->accept = accept; ctx->protocol = protocol; ctx->data_cb = data_cb; txn->ws_ctx = ctx; /* Check for supported WebSocket extensions */ parse_extensions(txn); /* Prepare log buffer */ /* Add client data */ buf_printf(&ctx->log, "%s", txn->conn->clienthost); if (httpd_userid) buf_printf(&ctx->log, " as \"%s\"", httpd_userid); if ((hdr = spool_getheader(txn->req_hdrs, "User-Agent"))) { buf_printf(&ctx->log, " with \"%s\"", hdr[0]); if ((hdr = spool_getheader(txn->req_hdrs, "X-Client"))) buf_printf(&ctx->log, " by \"%s\"", hdr[0]); else if ((hdr = spool_getheader(txn->req_hdrs, "X-Requested-With"))) buf_printf(&ctx->log, " by \"%s\"", hdr[0]); } /* Add request-line */ buf_printf(&ctx->log, "; \"WebSocket/%s via %s\"", protocol ? protocol : "echo" , txn->req_line.ver); ctx->log_tail = buf_len(&ctx->log); /* Tell client that WebSocket negotiation has succeeded */ if (txn->conn->sess_ctx) { /* Treat WS data as chunked response */ txn->flags.te = TE_CHUNKED; response_header(HTTP_OK, txn); /* Force the response to the client immediately */ prot_flush(httpd_out); } else response_header(HTTP_SWITCH_PROT, txn); /* Set connection as non-blocking */ prot_NONBLOCK(txn->conn->pin); /* Don't do telemetry logging in prot layer */ prot_setlog(txn->conn->pin, PROT_NO_FD); prot_setlog(txn->conn->pout, PROT_NO_FD); return 0; }