/* initialize the network * we talk on unix sockets */ static struct backend *init_net(const char *unixpath) { int lmtpdsock; struct sockaddr_un addr; struct backend *conn; if ((lmtpdsock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { just_exit("socket failed"); } addr.sun_family = AF_UNIX; strlcpy(addr.sun_path, unixpath, sizeof(addr.sun_path)); if (connect(lmtpdsock, (struct sockaddr *) &addr, sizeof(addr.sun_family) + strlen(addr.sun_path) + 1) < 0) { just_exit("connect failed"); } conn = xzmalloc(sizeof(struct backend)); conn->timeout = NULL; conn->in = prot_new(lmtpdsock, 0); conn->out = prot_new(lmtpdsock, 1); conn->sock = lmtpdsock; prot_setflushonread(conn->in, conn->out); conn->prot = &lmtp_protocol; return conn; }
/* initialize the network */ int init_net(char *serverFQDN, int port, isieve_t **obj) { struct addrinfo hints, *res0, *res; int err; char portstr[6]; int sock = -1; snprintf(portstr, sizeof(portstr), "%d", port); memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; if ((err = getaddrinfo(serverFQDN, portstr, &hints, &res0)) != 0) { fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(err)); return -1; } for (res = res0; res; res = res->ai_next) { sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (sock < 0) continue; if (connect(sock, res->ai_addr, res->ai_addrlen) >= 0) break; close(sock); sock = -1; } freeaddrinfo(res0); if (sock < 0) { perror("connect"); return -1; } *obj = (isieve_t *) xzmalloc(sizeof(isieve_t)); (*obj)->sock = sock; (*obj)->serverFQDN = xstrdup(serverFQDN); (*obj)->port = port; /* set up the prot layer */ (*obj)->pin = prot_new(sock, 0); (*obj)->pout = prot_new(sock, 1); return 0; }
int init_net(const char *host, char *port, struct protstream **in, struct protstream **out) { int sock = -1, err; struct addrinfo hints, *res, *res0; memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = 0; if ((err = getaddrinfo(host, port, &hints, &res0)) != 0) { syslog(LOG_ERR, "getaddrinfo(%s, %s) failed: %m", host, port); return -1; } for (res = res0; res; res = res->ai_next) { if ((sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) < 0) continue; if (connect(sock, res->ai_addr, res->ai_addrlen) >= 0) break; close(sock); sock = -1; } freeaddrinfo(res0); if(sock < 0) { syslog(LOG_ERR, "connect(%s:%s) failed: %m", host, port); return -1; } *in = prot_new(sock, 0); *out = prot_new(sock, 1); prot_setflushonread(*in, *out); return sock; }
static void test_printstring(void) { PROLOG; struct protstream *p; int len; struct buf b = BUF_INITIALIZER; int i; char str[2600]; p = prot_new(_fd, 1); CU_ASSERT_PTR_NOT_NULL_FATAL(p); /* NULL string */ BEGIN; prot_printstring(p, NULL); prot_flush(p); END(str, len); CU_ASSERT_EQUAL(len, 3); CU_ASSERT_STRING_EQUAL(str, "NIL"); /* Zero length string */ BEGIN; prot_printstring(p, ""); prot_flush(p); END(str, len); CU_ASSERT_EQUAL(len, 2); CU_ASSERT_STRING_EQUAL(str, "\"\""); /* Boring string */ BEGIN; prot_printstring(p, "Hello"); prot_flush(p); END(str, len); CU_ASSERT_EQUAL(len, 7); CU_ASSERT_STRING_EQUAL(str, "\"Hello\""); /* String with non-dangerous whitespace */ BEGIN; prot_printstring(p, "Hello World\tagain"); prot_flush(p); END(str, len); CU_ASSERT_EQUAL(len, 19); CU_ASSERT_STRING_EQUAL(str, "\"Hello World\tagain\""); /* String with dangerous whitespace */ BEGIN; prot_printstring(p, "Good\rBye\nEarth"); prot_flush(p); END(str, len); CU_ASSERT_EQUAL(len, 20); CU_ASSERT_STRING_EQUAL(str, "{14}\r\nGood\rBye\nEarth"); /* String with embedded dquote */ BEGIN; prot_printstring(p, "Quot\"able"); prot_flush(p); END(str, len); CU_ASSERT_EQUAL(len, 14); CU_ASSERT_STRING_EQUAL(str, "{9}\r\nQuot\"able"); /* String with embedded percent */ BEGIN; prot_printstring(p, "per%ent"); prot_flush(p); END(str, len); CU_ASSERT_EQUAL(len, 12); CU_ASSERT(!strcmp(str, "{7}\r\nper%ent")); /* String with embedded backslash */ BEGIN; prot_printstring(p, "slash\\dot"); prot_flush(p); END(str, len); CU_ASSERT_EQUAL(len, 14); CU_ASSERT_STRING_EQUAL(str, "{9}\r\nslash\\dot"); /* String with embedded 8-bit chars */ BEGIN; prot_printstring(p, "Hi I'm \330l\345f"); prot_flush(p); END(str, len); CU_ASSERT_EQUAL(len, 17); CU_ASSERT_STRING_EQUAL(str, "{11}\r\nHi I'm \330l\345f"); /* Boring but overly long string */ for (i = 0 ; i<500 ; i++) buf_appendcstr(&b, "blah "); buf_cstring(&b); BEGIN; prot_printstring(p, b.s); prot_flush(p); END(str, len); CU_ASSERT_EQUAL(len, b.len+8); CU_ASSERT_STRING_EQUAL(str+8, b.s); str[8] = '\0'; CU_ASSERT_STRING_EQUAL(str, "{2500}\r\n"); buf_free(&b); prot_free(p); EPILOG; }
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; }
EXPORTED int command_popen(struct command **cmdp, const char *mode, const char *cwd, const char *argv0, ...) { va_list va; const char *p; strarray_t argv = STRARRAY_INITIALIZER; pid_t pid; int r = 0; struct command *cmd; int do_stdin = (strchr(mode, 'w') != NULL); int do_stdout = (strchr(mode, 'r') != NULL); int stdin_pipe[2] = { -1, -1 }; int stdout_pipe[2] = { -1, -1 }; strarray_append(&argv, argv0); va_start(va, argv0); while ((p = va_arg(va, const char *))) strarray_append(&argv, p); va_end(va); if (do_stdin) { r = pipe(stdin_pipe); if (r) { syslog(LOG_ERR, "Failed to pipe(): %m"); r = IMAP_SYS_ERROR; goto out; } } if (do_stdout) { r = pipe(stdout_pipe); if (r) { syslog(LOG_ERR, "Failed to pipe(): %m"); r = IMAP_SYS_ERROR; goto out; } } pid = fork(); if (pid < 0) { syslog(LOG_ERR, "Failed to fork: %m"); r = IMAP_SYS_ERROR; goto out; } if (!pid) { /* in child */ if (do_stdin) { close(stdin_pipe[PIPE_WRITE]); dup2(stdin_pipe[PIPE_READ], STDIN_FILENO); close(stdin_pipe[PIPE_READ]); } if (do_stdout) { close(stdout_pipe[PIPE_READ]); dup2(stdout_pipe[PIPE_WRITE], STDOUT_FILENO); close(stdout_pipe[PIPE_WRITE]); } if (cwd) { r = chdir(cwd); if (r) syslog(LOG_ERR, "Failed to chdir(%s): %m", cwd); } r = execv(argv0, argv.data); syslog(LOG_ERR, "Failed to execute %s: %m", argv0); exit(1); } /* in parent */ cmd = xzmalloc(sizeof(struct command)); cmd->argv0 = xstrdup(argv0); cmd->pid = pid; if (do_stdin) cmd->stdin_prot = prot_new(stdin_pipe[PIPE_WRITE], /*write*/1); if (do_stdout) cmd->stdout_prot = prot_new(stdout_pipe[PIPE_READ], /*write*/0); *cmdp = cmd; out: if (stdin_pipe[PIPE_READ] >= 0) close(stdin_pipe[PIPE_READ]); if (stdout_pipe[PIPE_WRITE] >= 0) close(stdout_pipe[PIPE_WRITE]); if (r) { if (stdin_pipe[PIPE_WRITE] >= 0) close(stdin_pipe[PIPE_WRITE]); if (stdout_pipe[PIPE_READ] >= 0) close(stdout_pipe[PIPE_READ]); } strarray_fini(&argv); return r; }
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) { /* need to (re)establish connection to server or create one */ int sock = -1; int r; int err = -1; int ask = 1; /* should we explicitly ask for capabilities? */ struct addrinfo hints, *res0 = NULL, *res; struct sockaddr_un sunsock; char buf[2048]; struct sigaction action; struct backend *ret; char rsessionid[MAX_SESSIONID_SIZE]; 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)); /* XXX set that we are preauthed */ /* change hostname to 'config_servername' */ strlcpy(ret->hostname, config_servername, sizeof(ret->hostname)); } else { /* inet socket */ memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; err = getaddrinfo(server, prot->service, &hints, &res0); if (err) { syslog(LOG_ERR, "getaddrinfo(%s) failed: %s", server, gai_strerror(err)); goto error; } } /* Setup timeout */ timedout = 0; action.sa_flags = 0; action.sa_handler = timed_out; sigemptyset(&action.sa_mask); if(sigaction(SIGALRM, &action, NULL) < 0) { syslog(LOG_ERR, "Setting timeout in backend_connect failed: sigaction: %m"); /* continue anyway */ } for (res = res0; res; res = res->ai_next) { sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (sock < 0) continue; alarm(config_getint(IMAPOPT_CLIENT_TIMEOUT)); if (connect(sock, res->ai_addr, res->ai_addrlen) >= 0) break; if(errno == EINTR && timedout == 1) errno = ETIMEDOUT; close(sock); sock = -1; } /* Remove timeout code */ alarm(0); signal(SIGALRM, SIG_IGN); if (sock < 0) { if (res0 != &hints) freeaddrinfo(res0); syslog(LOG_ERR, "connect(%s) failed: %m", server); goto error; } 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_setflushonread(ret->in, ret->out); ret->prot = prot; /* use literal+ to send literals */ prot_setisclient(ret->in, 1); prot_setisclient(ret->out, 1); if (prot->banner.auto_capa) { /* try to get the capabilities from the banner */ r = ask_capability(ret, /*dobanner*/1, AUTO_CAPA_BANNER); if (r) { /* found capabilities in banner -> don't ask */ ask = 0; } } else { do { /* read the initial greeting */ if (!prot_fgets(buf, sizeof(buf), ret->in)) { syslog(LOG_ERR, "backend_connect(): couldn't read initial greeting: %s", ret->in->error ? ret->in->error : "(null)"); goto error; } } while (strncasecmp(buf, prot->banner.resp, strlen(prot->banner.resp))); strncpy(ret->banner, buf, 2048); } if (ask) { /* get the capabilities */ ask_capability(ret, /*dobanner*/0, AUTO_CAPA_NO); } /* now need to authenticate to backend server, unless we're doing LMTP/CSYNC on a UNIX socket (deliver/sync_client) */ if ((server[0] != '/') || (strcmp(prot->sasl_service, "lmtp") && strcmp(prot->sasl_service, "csync"))) { char *old_mechlist = backend_get_cap_params(ret, CAPA_AUTH); const char *my_status; if ((r = backend_authenticate(ret, userid, cb, &my_status))) { syslog(LOG_ERR, "couldn't authenticate to backend server: %s", sasl_errstring(r, NULL, NULL)); free(old_mechlist); goto error; } else { const void *ssf; sasl_getprop(ret->saslconn, SASL_SSF, &ssf); if (*((sasl_ssf_t *) ssf)) { /* if we have a SASL security layer, compare SASL mech lists before/after AUTH to check for a MITM attack */ char *new_mechlist; int auto_capa = (prot->sasl_cmd.auto_capa == AUTO_CAPA_AUTH_SSF); if (!strcmp(prot->service, "sieve")) { /* XXX Hack to handle ManageSieve servers. * No way to tell from protocol if server will * automatically send capabilities, so we treat it * as optional. */ char ch; /* wait and probe for possible auto-capability response */ usleep(250000); prot_NONBLOCK(ret->in); if ((ch = prot_getc(ret->in)) != EOF) { prot_ungetc(ch, ret->in); } else { auto_capa = AUTO_CAPA_AUTH_NO; } prot_BLOCK(ret->in); } ask_capability(ret, /*dobanner*/0, auto_capa); new_mechlist = backend_get_cap_params(ret, CAPA_AUTH); if (new_mechlist && old_mechlist && strcmp(new_mechlist, old_mechlist)) { syslog(LOG_ERR, "possible MITM attack:" "list of available SASL mechanisms changed"); free(new_mechlist); free(old_mechlist); goto error; } free(new_mechlist); } else if (prot->sasl_cmd.auto_capa == AUTO_CAPA_AUTH_OK) { /* try to get the capabilities from the AUTH success response */ forget_capabilities(ret); parse_capability(ret, my_status); post_parse_capability(ret); } if (!(strcmp(prot->service, "imap") && (strcmp(prot->service, "pop3")))) { parse_sessionid(my_status, rsessionid); syslog(LOG_NOTICE, "proxy %s sessionid=<%s> remote=<%s>", userid, session_id(), rsessionid); } } if (auth_status) *auth_status = my_status; free(old_mechlist); } /* start compression if requested and both client/server support it */ if (config_getswitch(IMAPOPT_PROXY_COMPRESS) && ret && CAPA(ret, CAPA_COMPRESS) && prot->compress_cmd.cmd && do_compress(ret, &prot->compress_cmd)) { syslog(LOG_ERR, "couldn't enable compression on backend server"); goto error; } 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; }
/* * file in the message structure 'm' from 'pin', assuming a dot-stuffed * stream a la lmtp. * * returns 0 on success, imap error code on failure */ static int savemsg(struct clientdata *cd, const struct lmtp_func *func, message_data_t *m) { FILE *f; struct stat sbuf; const char **body; int r; int nrcpts = m->rcpt_num; time_t now = time(NULL); static unsigned msgid_count = 0; char datestr[RFC822_DATETIME_MAX+1], tls_info[250] = ""; const char *skipheaders[] = { "Return-Path", /* need to remove (we add our own) */ NULL }; char *addbody, *fold[5], *p; int addlen, nfold, i; /* Copy to spool file */ f = func->spoolfile(m); if (!f) { prot_printf(cd->pout, "451 4.3.%c cannot create temporary file: %s\r\n", ( #ifdef EDQUOT errno == EDQUOT || #endif errno == ENOSPC) ? '1' : '2', error_message(errno)); return IMAP_IOERROR; } prot_printf(cd->pout, "354 go ahead\r\n"); if (m->return_path && func->addretpath) { /* add the return path */ char *rpath = m->return_path; const char *hostname = 0; clean_retpath(rpath); /* Append our hostname if there's no domain in address */ hostname = NULL; if (!strchr(rpath, '@') && strlen(rpath) > 0) { hostname = config_servername; } addlen = 2 + strlen(rpath) + (hostname ? 1 + strlen(hostname) : 0); addbody = xmalloc(addlen + 1); sprintf(addbody, "<%s%s%s>", rpath, hostname ? "@" : "", hostname ? hostname : ""); fprintf(f, "Return-Path: %s\r\n", addbody); spool_cache_header(xstrdup("Return-Path"), addbody, m->hdrcache); } /* add a received header */ time_to_rfc822(now, datestr, sizeof(datestr)); addlen = 8 + strlen(cd->lhlo_param) + strlen(cd->clienthost); if (m->authuser) addlen += 28 + strlen(m->authuser) + 5; /* +5 for ssf */ addlen += 25 + strlen(config_servername) + strlen(cyrus_version()); #ifdef HAVE_SSL if (cd->tls_conn) { addlen += 3 + tls_get_info(cd->tls_conn, tls_info, sizeof(tls_info)); } #endif addlen += 2 + strlen(datestr); p = addbody = xmalloc(addlen + 1); nfold = 0; p += sprintf(p, "from %s (%s)", cd->lhlo_param, cd->clienthost); fold[nfold++] = p; if (m->authuser) { const void *ssfp; sasl_ssf_t ssf; sasl_getprop(cd->conn, SASL_SSF, &ssfp); ssf = *((sasl_ssf_t *) ssfp); p += sprintf(p, " (authenticated user=%s bits=%d)", m->authuser, ssf); fold[nfold++] = p; } /* We are always atleast "with LMTPA" -- no unauth delivery */ p += sprintf(p, " by %s", config_servername); if (config_serverinfo == IMAP_ENUM_SERVERINFO_ON) { p += sprintf(p, " (Cyrus %s)", cyrus_version()); } p += sprintf(p, " with LMTP%s%s", cd->starttls_done ? "S" : "", cd->authenticated != NOAUTH ? "A" : ""); if (*tls_info) { fold[nfold++] = p; p += sprintf(p, " (%s)", tls_info); } strcat(p++, ";"); fold[nfold++] = p; p += sprintf(p, " %s", datestr); fprintf(f, "Received: "); for (i = 0, p = addbody; i < nfold; p = fold[i], i++) { fprintf(f, "%.*s\r\n\t", (int) (fold[i] - p), p); } fprintf(f, "%s\r\n", p); spool_cache_header(xstrdup("Received"), addbody, m->hdrcache); /* add any requested headers */ if (func->addheaders) { struct addheader *h; for (h = func->addheaders; h && h->name; h++) { fprintf(f, "%s: %s\r\n", h->name, h->body); spool_cache_header(xstrdup(h->name), xstrdup(h->body), m->hdrcache); } } /* fill the cache */ r = spool_fill_hdrcache(cd->pin, f, m->hdrcache, skipheaders); /* now, using our header cache, fill in the data that we want */ /* first check resent-message-id */ if ((body = msg_getheader(m, "resent-message-id")) && body[0][0]) { m->id = xstrdup(body[0]); } else if ((body = msg_getheader(m, "message-id")) && body[0][0]) { m->id = xstrdup(body[0]); } else if (body) { r = IMAP_MESSAGE_BADHEADER; /* empty message-id */ } else { /* no message-id, create one */ pid_t p = getpid(); m->id = xmalloc(40 + strlen(config_servername)); sprintf(m->id, "<cmu-lmtpd-%d-%d-%u@%s>", p, (int) now, msgid_count++, config_servername); fprintf(f, "Message-ID: %s\r\n", m->id); spool_cache_header(xstrdup("Message-ID"), xstrdup(m->id), m->hdrcache); } /* get date */ if (!(body = spool_getheader(m->hdrcache, "date"))) { /* no date, create one */ addbody = xstrdup(datestr); m->date = xstrdup(datestr); fprintf(f, "Date: %s\r\n", addbody); spool_cache_header(xstrdup("Date"), addbody, m->hdrcache); } else { m->date = xstrdup(body[0]); } if (!m->return_path && (body = msg_getheader(m, "return-path"))) { /* let's grab return_path */ m->return_path = xstrdup(body[0]); clean822space(m->return_path); clean_retpath(m->return_path); } r |= spool_copy_msg(cd->pin, f); if (r) { fclose(f); if (func->removespool) { /* remove the spool'd message */ func->removespool(m); } while (nrcpts--) { send_lmtp_error(cd->pout, r); } return r; } fflush(f); if (ferror(f)) { while (nrcpts--) { prot_printf(cd->pout, "451 4.3.%c cannot copy message to temporary file: %s\r\n", ( #ifdef EDQUOT errno == EDQUOT || #endif errno == ENOSPC) ? '1' : '2', error_message(errno)); } fclose(f); if (func->removespool) func->removespool(m); return IMAP_IOERROR; } if (fstat(fileno(f), &sbuf) == -1) { while (nrcpts--) { prot_printf(cd->pout, "451 4.3.2 cannot stat message temporary file: %s\r\n", error_message(errno)); } fclose(f); if (func->removespool) func->removespool(m); return IMAP_IOERROR; } m->size = sbuf.st_size; m->f = f; m->data = prot_new(fileno(f), 0); return 0; }
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; }
/* * Begin reading a sync log file. If the reader is reading from a * channel, rename the current log file so it will not be appended to by * the write side code, and open the file. Otherwise, just open the file * (note this is still necessary even when the reader is reading from a * file descriptor). * * When sync_log_reader_begin() returns success, you should loop calling * sync_log_reader_getitem() and handling the items, until it returns * EOF, and then call sync_log_reader_end(). * * Returns zero on success, IMAP_AGAIN if reading from a channel and * there is no current log file, or an IMAP error code on failure. */ EXPORTED int sync_log_reader_begin(sync_log_reader_t *slr) { struct stat sbuf; int r; if (slr->input) { r = sync_log_reader_end(slr); if (r) return r; } if (stat(slr->work_file, &sbuf) == 0) { /* Existing work log file - process this first */ syslog(LOG_NOTICE, "Reprocessing sync log file %s", slr->work_file); } else if (!slr->log_file) { syslog(LOG_ERR, "Failed to stat %s: %m", slr->log_file); return IMAP_IOERROR; } else { /* Check for sync_log file */ if (stat(slr->log_file, &sbuf) < 0) { if (errno == ENOENT) return IMAP_AGAIN; /* no problem, try again later */ syslog(LOG_ERR, "Failed to stat %s: %m", slr->log_file); return IMAP_IOERROR; } /* Move sync_log to our work file */ if (rename(slr->log_file, slr->work_file) < 0) { syslog(LOG_ERR, "Rename %s -> %s failed: %m", slr->log_file, slr->work_file); return IMAP_IOERROR; } } if (slr->fd < 0) { int fd = open(slr->work_file, O_RDWR, 0); if (fd < 0) { syslog(LOG_ERR, "Failed to open %s: %m", slr->work_file); return IMAP_IOERROR; } if (lock_blocking(fd, slr->work_file) < 0) { syslog(LOG_ERR, "Failed to lock %s: %m", slr->work_file); close(fd); return IMAP_IOERROR; } slr->fd = fd; slr->fd_is_ours = 1; /* we can unlock immediately, since we have serialised * any process which held the lock over the rename. All * future attempts to lock this inode will stat and notice * the rename, so they won't write any more */ lock_unlock(slr->fd, slr->work_file); } slr->input = prot_new(slr->fd, /*write*/0); return 0; }