int main(int argc, char **argv) { int r = 0; int opt; int lmtpflag = 0; int ignorequota = 0; char *mailboxname = NULL; char *authuser = NULL; char *return_path = NULL; char buf[1024]; char *alt_config = NULL; while ((opt = getopt(argc, argv, "C:df:r:m:a:F:eE:lqD")) != EOF) { switch(opt) { case 'C': /* alt config file */ alt_config = optarg; break; case 'd': /* Ignore -- /bin/mail compatibility flags */ break; case 'D': logdebug = 1; break; case 'r': case 'f': return_path = optarg; break; case 'm': if (mailboxname) { fprintf(stderr, "deliver: multiple -m options\n"); usage(); /* NOTREACHED */ } if (*optarg) mailboxname = optarg; break; case 'a': if (authuser) { fprintf(stderr, "deliver: multiple -a options\n"); usage(); /* NOTREACHED */ } authuser = optarg; break; case 'F': /* set IMAP flag. we no longer support this */ fprintf(stderr,"deliver: 'F' option no longer supported\n"); usage(); break; case 'e': /* duplicate delivery. ignore */ break; case 'E': fprintf(stderr,"deliver: 'E' option no longer supported\n"); usage(); break; case 'l': lmtpflag = 1; break; case 'q': ignorequota = 1; break; default: usage(); /* NOTREACHED */ } } deliver_in = prot_new(0, 0); deliver_out = prot_new(1, 1); prot_setflushonread(deliver_in, deliver_out); prot_settimeout(deliver_in, 300); cyrus_init(alt_config, "deliver", CYRUSINIT_NODB); sockaddr = config_getstring(IMAPOPT_LMTPSOCKET); if (!sockaddr) { strlcpy(buf, config_dir, sizeof(buf)); strlcat(buf, "/socket/lmtp", sizeof(buf)); sockaddr = buf; } if (lmtpflag == 1) { struct backend *conn = init_net(sockaddr); pipe_through(conn); backend_disconnect(conn); free(conn); } else { if (return_path == NULL) { uid_t me = getuid(); struct passwd *p = getpwuid(me); return_path = p->pw_name; } /* deliver to users or global mailbox */ r = deliver_msg(return_path,authuser, ignorequota, argv+optind, argc - optind, mailboxname); } cyrus_done(); return r; }
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); }
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; }
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; }