static int str_catfromby(str* s, const char* helo_domain, const char* host, const char* ip) { int c; if (helo_domain == 0) helo_domain = (host != 0 && *host != 0) ? host : (ip != 0) ? ip : UNKNOWN; while((c = *helo_domain++) != 0) { if(!str_catc(s, (isalnum(c) || (c == '.') || (c == '-'))? c:'_')) return 0; } /* if (!str_cats(s, helo_domain)) return 0; */ if (host != 0 || ip != 0) { if (!str_cats(s, " (")) return 0; if (host != 0) { if (!str_cats(s, host)) return 0; if (ip != 0) if (!str_catc(s, ' ')) return 0; } if (ip != 0) { if (!str_catc(s, '[')) return 0; if(strchr(ip, ':') && !str_cats(s, "IPV6:")) return 0; if( !str_cats(s, ip) || !str_catc(s, ']')) return 0; } if (!str_catc(s, ')')) return 0; } return 1; }
static const response* check_fqdn(str* s) { int at; int dot; const char* name; if ((at = str_findlast(s, '@')) <= 0) { if ((name = session_getenv("DEFAULTHOST")) != 0) { at = s->len; if (!str_catc(s, '@') || !str_cats(s, name)) return &resp_oom; } else return &resp_nodomain; } if ((dot = str_findlast(s, '.')) < at) { if ((name = session_getenv("DEFAULTDOMAIN")) != 0) { if (!str_catc(s, '.') || !str_cats(s, name)) return &resp_oom; } else return &resp_nofqdn; } return 0; }
const char* temppath(const char* prefix, str* path) { struct timeval tv; gettimeofday(&tv, 0); if (!str_copy2s(path, prefix, ".tmp.barch.") || !str_catu(path, pid) || !str_catc(path, '.') || !str_catu(path, tv.tv_sec) || !str_catc(path, '.') || !str_catuw(path, tv.tv_usec, 6, '0')) die_oom(1); return path->s; }
/* ------------------------------------------------------------------------- */ const char* format_connection(const struct connections_entry* c) { static str s; if (!str_copys(&s, ipv4_format(&c->key.ip))) return 0; if (!str_catc(&s, '/')) return 0; if (!str_catu(&s, c->key.port)) return 0; if (!str_catc(&s, '/')) return 0; if (!str_cat(&s, &c->data.service->key.sender)) return 0; if (!str_catc(&s, '/')) return 0; if (!str_cat(&s, &c->data.service->key.service)) return 0; if (!str_cats(&s, ": ")) return 0; return s.s; }
/** Sort a string. \param s The string to sort. \param sep The character which delimits the substrings. \param count The number of substrings within \c s (set to \c -1 if not known). \param fn The comparison function. Defaults to a function that works like \c memcmp. \note This function allocates a temporary array of substring pointers, and so may return \c 0 if memory allocation fails. The string itself is not reallocated. */ int str_sort(str* s, char sep, long count, int (*fn)(const str_sortentry* a, const str_sortentry* b)) { str_sortentry* ptrs; const char* ptr; const char* end; long i; str tmp = {0,0,0}; if (count == -1) { for (count = 0, ptr = s->s, end = s->s+s->len; ptr != 0 && ptr < end; ++count) { ptr = memchr(ptr, sep, end-ptr); if (ptr) ++ptr; } } if ((ptrs = alloca(count * sizeof *ptrs)) == 0) return 0; if (!str_copy(&tmp, s)) { str_free(&tmp); return 0; } if (fn == 0) fn = default_cmp; for (i = 0, ptr = tmp.s, end = tmp.s+tmp.len; i < count; ++i) { const char* ptrend = memchr(ptr, sep, end-ptr); if (ptrend == 0) ptrend = end; ptrs[i].str = ptr; ptrs[i].len = ptrend - ptr; ptr = ptrend + 1; } qsort(ptrs, count, sizeof(*ptrs), (int (*)(const void*,const void*))fn); str_truncate(s, 0); for (i = 0; i < count; i++) { str_catb(s, ptrs[i].str, ptrs[i].len); str_catc(s, sep); } str_free(&tmp); return 1; }
int session_putenv(const char* s) { if (session.env.len > 0) if (!str_catc(&session.env, 0)) return 0; return str_catb(&session.env, s, strlen(s) + 1); }
static void parse_env(const char* ptr, str* out) { while (ptr && *ptr != 0) if ((ptr = parse_str(ptr, ',', out)) != 0) { str_catc(out, 0); if (*ptr == ',') ++ptr; } }
static const response* sq_recipient(str* recipient, str* params) { str_cat(&qrecips, recipient); str_catc(&qrecips, 0); return 0; (void)params; }
void make_username(const char* start, ssize_t len, const char* msgprefix) { str_copyb(&username, start, len); if (local_name && str_findfirst(&username, AT) < 0) { str_catc(&username, AT); str_cats(&username, local_name); } str_copy2s(&msgstr, msgprefix, username.s); log_line(msgstr.s, msgstr.len); }
/** Create a temporary file. This function creates a temporary file by adding an difficult-to-predict suffix (using the PID and microsecond timestamp) to the given prefix. If this filename does not exist, it is opened in read/write mode. */ int path_mktemp(const char* prefix, str* filename) { struct stat s; static long pid = 0; struct timeval tv; if (pid == 0) pid = getpid(); do { if (!str_copys(filename, prefix)) return -1; if (!str_catc(filename, '.')) return -1; if (!str_catu(filename, pid)) return -1; gettimeofday(&tv, 0); if (!str_catc(filename, '.')) return -1; if (!str_catu(filename, tv.tv_sec)) return -1; if (!str_catc(filename, '.')) return -1; if (!str_catuw(filename, tv.tv_usec, 6, '0')) return -1; } while (lstat(filename->s, &s) != -1); return open(filename->s, O_RDWR | O_EXCL | O_CREAT, 0600); }
static void cmd_stat(void) { if (!str_copys(&tmp, "+OK ") || !str_catu(&tmp, msg_count - del_count) || !str_catc(&tmp, SPACE) || !str_catu(&tmp, msg_bytes - del_bytes)) respond(err_internal); else respond(tmp.s); }
int open_file(const char* prefix, const char* filename) { static str fullname; if (!str_truncate(&fullname, 0)) oom(); if (prefix != 0) { if (!str_copys(&fullname, prefix)) oom(); if (!str_catc(&fullname, '/')) oom(); } if (!str_cats(&fullname, filename)) oom(); return open(fullname.s, O_RDONLY); }
static const char* parse_str(const char* ptr, char sep, str* out) { char ch = 0; /* str_truncate(out, 0); */ for (;;) { if (*ptr == sep || *ptr == NUL) return ptr; ptr = parse_char(ptr, &ch); str_catc(out, ch); } return ptr; }
static void cmd_list_one(const str* arg) { long i; if ((i = msgnum(arg)) == 0) return; if (!str_copys(&tmp, "+OK ") || !str_catu(&tmp, i) || !str_catc(&tmp, SPACE) || !str_catu(&tmp, msgs[i-1].size)) respond(err_internal); else respond(tmp.s); }
/** Put an assignment, in \c NAME=value format, into the environment * string. * \note Unlike putenv, a copy of the assignment is made instead of * keeping a copy of the given pointer. */ int envstr_put(struct str* env, const char* asgn, int overwrite) { long varlen; const char* found; found = strchr(asgn, '='); varlen = (found == 0) ? (long)strlen(asgn) : found - asgn; if ((found = envstr_find(env, asgn, varlen)) != 0) { if (!overwrite) return 1; str_spliceb(env, found - env->s, strlen(found) + 1, 0, 0); } return str_cats(env, asgn) && str_catc(env, 0); }
int sendpacket(int fd, const str* s) { const char* ptr; long wr; long len; packet.len = 0; if (!str_catu(&packet, s->len) || !str_catc(&packet, ':') || !str_cat(&packet, s) || !str_catc(&packet, ',')) { errno = ENOMEM; return -1; } len = packet.len; ptr = packet.s; while (len > 0) { if ((wr = write(fd, ptr, len)) <= 0) return wr; len -= wr; ptr += wr; } return packet.len; }
static void cmd_uidl_one(const str* arg) { long i; if ((i = msgnum(arg)) != 0) { msg* m = &msgs[i-1]; const char* fn = m->filename + 4; if (!str_copys(&tmp, "+OK ") || !str_catu(&tmp, i) || !str_catc(&tmp, SPACE) || !str_catb(&tmp, fn, m->uid_len)) respond(err_internal); else respond(tmp.s); } }
static long scan_dir(const char* subdir, str* list, long* countptr, long max) { DIR* dir; direntry* entry; long count; if ((dir = opendir(subdir)) == 0) return 0; count = 0; while (!(max_count > 0 && msg_count >= max_count) && !(max > 0 && count >= max) && (entry = readdir(dir)) != 0) { if (entry->d_name[0] == '.') continue; if (!str_copys(&tmp, subdir)) return 0; if (!str_catc(&tmp, '/')) return 0; if (!str_cats(&tmp, entry->d_name)) return 0; if (!str_cat(list, &tmp)) return 0; if (!str_catc(list, 0)) return 0; ++count; ++msg_count; } closedir(dir); *countptr = count; return 1; }
static void make_prq(void) { brandom_fill(nonce, sizeof nonce); pkt_start(&packet, PRQ1); pkt_add_b(&packet, nonce, sizeof nonce); pkt_add_s1c(&packet, AUTHENTICATOR_NAME); wrap_str(str_copys(&keyex_name, nistp224_cb.name)); if (keylist_get(&shared_secrets, &curve25519_cb) != 0) { wrap_str(str_catc(&keyex_name, 0)); wrap_str(str_cats(&keyex_name, curve25519_cb.name)); } pkt_add_s1(&packet, &keyex_name); pkt_add_s1c(&packet, KEYHASH_NAME); pkt_add_s1c(&packet, ENCRYPTOR_NAME); pkt_add_s1c(&packet, "null"); }
/* Mark a maildir filename with the named flag */ static int add_flag(str* fn, char flag) { int c; /* If the filename has no flags, append them */ if ((c = str_findfirst(fn, ':')) == -1) { if (!str_cats(fn, ":2,")) return 0; } else { /* If it has a colon (start of flags), see if they are a type we * recognize, and bail out if they aren't */ if (fn->s[c+1] != '2' || fn->s[c+2] != ',') return 1; /* Scan through the flag characters and return success * if the message is already marked with the flag */ if (strchr(fn->s+c+3, flag) != 0) return 1; } return str_catc(fn, flag); }
static const response* parse_addr_arg(void) { unsigned i; char term; int quoted; if (!str_truncate(&addr, 0)) return &resp_oom; if (!str_truncate(¶ms, 0)) return &resp_oom; addr.len = 0; if ((i = str_findfirst(&arg, LBRACE) + 1) != 0) term = RBRACE; else { term = SPACE; if ((i = str_findfirst(&arg, COLON) + 1) == 0) if ((i = str_findfirst(&arg, SPACE) + 1) == 0) return &resp_badaddr; while (i < arg.len && arg.s[i] == SPACE) ++i; } for (quoted = 0; i < arg.len && (quoted || arg.s[i] != term); ++i) { switch (arg.s[i]) { case QUOTE: quoted = !quoted; break; case ESCAPE: ++i; /* fall through */ default: if (!str_catc(&addr, arg.s[i])) return &resp_oom; } } ++i; if (i > arg.len) return &resp_badaddr; while (i < arg.len && arg.s[i] == SPACE) ++i; if (!str_copyb(¶ms, arg.s+i, arg.len-i)) return &resp_oom; str_subst(¶ms, ' ', 0); /* strip source routing */ if (addr.s[0] == AT && (i = str_findfirst(&addr, COLON) + 1) != 0) str_lcut(&addr, i); return 0; }
static int dblchk(str *domain, str *dbltxt) { str dblstr; char *dbl = getenv("DBLLOOKUP"); int l, i; unsigned char ansbuf[512]; if(!dbl || domain->len == 0) return 0; if(session_getnum("sump",0)) return 0; /* no point */ str_init(&dblstr); str_copy(&dblstr, domain); str_catc(&dblstr, '.'); str_cats(&dblstr, dbl); l = res_query(dblstr.s, C_IN, T_TXT, ansbuf, sizeof(ansbuf)); if(l > 0 && ((HEADER *)ansbuf)->ancount != 0) { /* something in the answer */ unsigned char *recbuf = ansbuf+NS_HFIXEDSZ; /* skip over questions, why am I still writing stuff * like this? */ for(i = ns_get16(ansbuf+4); i != 0; --i) recbuf += dn_skipname(recbuf, ansbuf+l)+4; for(i = ns_get16(ansbuf+6); i != 0; --i) { recbuf += dn_skipname(recbuf, ansbuf+l); if(ns_get16(recbuf) != T_TXT) { /* CNAME or something */ recbuf += 10 + ns_get16(recbuf+8); continue; } /* it's a TXT record, wow */ str_init(dbltxt); str_copyb(dbltxt, (char*)recbuf+11, recbuf[10]); str_free(&dblstr); return 1; } } /* didn't find anything */ str_free(&dblstr); return 0; }
static void handle_packet(struct connection* c) { str* packet; const char* id; const char* runas; const char* command; const char* envstart; long slot; unsigned int i; struct passwd* pw; packet = &c->packet; id = packet->s; if (*id == NUL) FAIL(id, "DInvalid ID"); if ((slot = pick_slot()) < 0) FAIL(id, "DCould not allocate a slot"); wrap_str(str_copys(&slots[slot].id, id)); if ((i = str_findfirst(packet, NUL) + 1) >= packet->len) FAIL(id, "DInvalid packet"); runas = packet->s + i; if (*runas == NUL || (pw = getpwnam(runas)) == 0) FAIL(id, "DInvalid username"); if ((i = str_findnext(packet, NUL, i) + 1) >= packet->len) FAIL(id, "DInvalid packet"); command = packet->s + i; if ((i = str_findnext(packet, NUL, i) + 1) >= packet->len) envstart = 0; else { envstart = packet->s + i; wrap_str(str_catc(packet, 0)); } if (!init_slot(slot, pw)) FAIL(id, "ZOut of memory"); start_slot(slot, command, envstart); }
static void end_slot(int slot, int status) { struct stat st; slots[slot].pid = 0; --slots_used; if (slots[slot].sending_email) { slots[slot].sending_email = 0; if (status) report_slot(slot, "ZJob complete, sending email failed"); else report_slot(slot, "KJob complete, email sent"); } else { /* No header, no possible way to send email. */ if (slots[slot].headerlen == 0) report_slot(slot, "KJob complete, no MAILTO"); else { /* If the job crashed, make sure it is noted. */ if (WIFSIGNALED(status)) { debugf(DEBUG_EXEC, "{slot }d{ Job was killed by signal #}d", slot, WTERMSIG(status)); wrap_str(str_copys(&tmp, "\n\nJob was killed by signal #")); wrap_str(str_cati(&tmp, WTERMSIG(status))); wrap_str(str_catc(&tmp, '\n')); write(slots[slot].tmpfd, tmp.s, tmp.len); } if (fstat(slots[slot].tmpfd, &st) == -1) failsys_slot(slot, "ZCould not fstat"); else if (st.st_size > slots[slot].headerlen) send_email(slot); else report_slot(slot, "KJob complete, no mail sent"); } /* To simplify the procedure, close the temporary file early. * The email sender still has it open, and will effect the final * deletion of the file when it completes. */ if (slots[slot].tmpfd != devnull) close(slots[slot].tmpfd); slots[slot].tmpfd = -1; } }
static void cmd_listsys(void) { DIR* dir; direntry* entry; str data = {0,0,0}; if ((dir = opendir(CRONTAB_DIR)) == 0) respond("ZCould not open crontabs directory"); while ((entry = readdir(dir)) != 0) { if (entry->d_name[0] != ':') continue; make_filename(entry->d_name); if (!str_cat3s(&data, "==> ", entry->d_name, " <==\n")) respond("ZOut of memory"); if (ibuf_openreadclose(filename.s, &data) == -1) respond("ZCould not read crontab"); if (!str_catc(&data, '\n')) respond("ZOut of memory"); } closedir(dir); respond_okstr(&data); }
/** Read a line from the \c ibuf into a dynamic string. */ int ibuf_getstr(ibuf* in, struct str* s, char boundary) { iobuf* io; int ch; io = &in->io; in->count = 0; str_truncate(s, 0); if (ibuf_eof(in) || ibuf_error(in) || ibuf_timedout(in)) return 0; for (;;) { if (io->bufstart >= io->buflen && !ibuf_refill(in)) { if (ibuf_eof(in)) break; return 0; } in->count++; ch = io->buffer[io->bufstart++]; if (!str_catc(s, ch)) return 0; if (ch == boundary) break; } return in->count > 0; }
/* This parser is rather liberal in what it accepts: The IMAP standard mandates seperate character sets for tags, commands, unquoted strings. Since they all must be seperated by spaces, this parser allows all non-whitespace characters for each of the above. */ static int parse_line(void) { #define RESPOND(T,S) do{ respond(T,S); return 0; }while(0) unsigned i; unsigned len; const char* ptr; str* arg; /* Parse out the command tag */ str_truncate(&tag, 0); for (i = 0, ptr = line.s; i < line.len && isspace(*ptr); ++i, ++ptr) ; for (; i < line.len && !isspace(*ptr); ++i, ++ptr) str_catc(&tag, line.s[i]); if (!tag.len) RESPOND(NOTAG, "BAD Syntax error"); if (i >= line.len) RESPOND(0, "BAD Syntax error"); /* Parse out the command itself */ str_truncate(&cmd, 0); for (; i < line.len && isspace(*ptr); ++i, ++ptr) ; for (; i < line.len && !isspace(*ptr); ++i, ++ptr) str_catc(&cmd, line.s[i]); if (!cmd.len) RESPOND(0, "BAD Syntax error"); /* Parse out the command-line args */ for (line_argc = 0; line_argc < MAX_ARGC; ++line_argc) { arg = &line_args[line_argc]; str_truncate(arg, 0); for (; i < line.len && isspace(*ptr); ++i, ++ptr) ; if (i >= line.len) break; switch (*ptr) { case LBRACE: /* Handle a string literal */ ++i, ++ptr; if (!isdigit(*ptr)) RESPOND(0, "BAD Syntax error: missing integer"); for (len = 0; i < line.len && *ptr != RBRACE; ++i, ++ptr) { if (!isdigit(*ptr)) RESPOND(0, "BAD Syntax error: invalid integer"); len = len * 10 + *ptr - '0'; } ++i, ++ptr; if (*ptr != 0) RESPOND(0, "BAD Syntax error: missing LF after integer"); str_ready(arg, len); respond(CONT, "OK"); if (len > 0) ibuf_read(&inbuf, arg->s, len); arg->s[arg->len = len] = 0; ibuf_getstr_crlf(&inbuf, &line); i = 0; ptr = line.s; break; case QUOTE: /* Handle a quoted string */ for (++i, ++ptr; i < line.len && *ptr != QUOTE; ++i, ++ptr) { if (*ptr == ESCAPE) { if (++i >= line.len) break; ++ptr; } str_catc(arg, *ptr); } if (i >= line.len || *ptr != QUOTE) RESPOND(0, "BAD Syntax error: unterminated quoted string"); ++i, ++ptr; break; default: /* Normal case is very simple */ for (; i < line.len && !isspace(*ptr); ++i, ++ptr) str_catc(arg, *ptr); } } for (; i < line.len && isspace(*ptr); ++i, ++ptr) ; if (i < line.len) RESPOND(0, "BAD Too many command arguments"); return 1; }