int runcmd(bool force) { char **argv; static char *envp[] = { "HOME=/", "PATH=/sbin:/bin:/usr/sbin:/usr/bin", NULL }; int ret; argv = argv_split(GFP_KERNEL, restart_cmd, NULL); if (argv) { ret = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC); argv_free(argv); } else { printk(KERN_WARNING "ak47 %s failed to allocate memory for \"%s\"\n", __func__, restart_cmd); ret = -ENOMEM; } if (ret && force) { printk(KERN_WARNING "ak47 Failed to start \n "); } printk("AK47 in kthread runcmd exit\n"); return ret; }
static void erlang_parse_config(DICT_ERLANG *dict_erlang, const char *erlangcf) { CFG_PARSER *p; char *nodes; p = dict_erlang->parser = cfg_parser_alloc(erlangcf); nodes = cfg_get_str(p, "nodes", "", 0, 0); dict_erlang->nodes = argv_split(nodes, " ,\t\r\n"); myfree(nodes); dict_erlang->cookie = cfg_get_str(p, "cookie", "", 1, 0); dict_erlang->mod = cfg_get_str(p, "module", "", 1, 0); dict_erlang->fun = cfg_get_str(p, "function", "", 1, 0); dict_erlang->ctx = NULL; db_common_parse(&dict_erlang->dict, &dict_erlang->ctx, "%s", 1); db_common_parse_domain(p, dict_erlang->ctx); if (db_common_dict_partial(dict_erlang->ctx)) dict_erlang->dict.flags |= DICT_FLAG_PATTERN; else dict_erlang->dict.flags |= DICT_FLAG_FIXED; if (dict_erlang->dict.flags & DICT_FLAG_FOLD_FIX) dict_erlang->dict.fold_buf = vstring_alloc(10); }
static ssize_t lcd_reg_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int argc; char **args; int r,val; struct spi_device *spi = to_spi_device(dev); args = argv_split(GFP_KERNEL, buf, &argc); if (args == NULL) { dev_err(dev, "error getting arguments\n"); return count; } if (argc==2) { r=simple_strtoul(*args, NULL, 0); args++; val=simple_strtoul(*args, NULL, 0); printk("set lcd panel spi reg %x = %x\n",r,val); spi_send(spi, r, val); } argv_free(args); return count; }
static int __orderly_poweroff(bool force) { char **argv; static char *envp[] = { "HOME=/", "PATH=/sbin:/bin:/usr/sbin:/usr/bin", NULL }; int ret; argv = argv_split(GFP_KERNEL, poweroff_cmd, NULL); if (argv) { ret = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC); argv_free(argv); } else { ret = -ENOMEM; } if (ret && force) { pr_warn("Failed to start orderly shutdown: forcing the issue\n"); /* * I guess this should try to kick off some daemon to sync and * poweroff asap. Or not even bother syncing if we're doing an * emergency shutdown? */ emergency_sync(); kernel_power_off(); } return ret; }
static const char *parse_master_line(PC_MASTER_ENT *masterp, const char *buf) { ARGV *argv; /* * We can't use the master daemon's master_ent routines in their current * form. They convert everything to internal form, and they skip disabled * services. * * The postconf command needs to show default fields as "-", and needs to * know about all service names so that it can generate service-dependent * parameter names (transport-dependent etc.). */ #define MASTER_BLANKS " \t\r\n" /* XXX */ argv = argv_split(buf, MASTER_BLANKS); if (argv->argc < PC_MASTER_MIN_FIELDS) { argv_free(argv); return ("bad field count"); } normalize_options(argv); masterp->name_space = concatenate(argv->argv[0], ".", argv->argv[1], (char *) 0); masterp->argv = argv; masterp->valid_names = 0; masterp->all_params = 0; return (0); }
ARGV *pcf_parse_service_pattern(const char *pattern, int min_expr, int max_expr) { ARGV *argv; char **cpp; /* * Work around argv_split() lameness. */ if (*pattern == '/') return (0); argv = argv_split(pattern, PCF_NAMESP_SEP_STR); if (argv->argc < min_expr || argv->argc > max_expr) { argv_free(argv); return (0); } /* * Allow '*' only all by itself. */ for (cpp = argv->argv; *cpp; cpp++) { if (!PCF_MATCH_ANY(*cpp) && strchr(*cpp, PCF_MATCH_WILDC_STR[0]) != 0) { argv_free(argv); return (0); } } /* * Provide defaults for missing fields. */ while (argv->argc < max_expr) argv_add(argv, PCF_MATCH_WILDC_STR, ARGV_END); return (argv); }
int main(int argc, char **argv) { ARGV *types_argv; unsigned *types; char *name; VSTRING *fqdn = vstring_alloc(100); VSTRING *why = vstring_alloc(100); int rcode; DNS_RR *rr; int i; msg_vstream_init(argv[0], VSTREAM_ERR); if (argc != 3) msg_fatal("usage: %s types name", argv[0]); types_argv = argv_split(argv[1], ", \t\r\n"); types = (unsigned *) mymalloc(sizeof(*types) * (types_argv->argc + 1)); for (i = 0; i < types_argv->argc; i++) if ((types[i] = dns_type(types_argv->argv[i])) == 0) msg_fatal("invalid query type: %s", types_argv->argv[i]); types[i] = 0; argv_free(types_argv); name = argv[2]; msg_verbose = 1; switch (dns_lookup_rv(name, RES_DEBUG | RES_USE_DNSSEC, &rr, fqdn, why, &rcode, DNS_REQ_FLAG_NONE, types)) { default: msg_fatal("%s (rcode=%d)", vstring_str(why), rcode); case DNS_OK: printf("%s: fqdn: %s\n", name, vstring_str(fqdn)); print_rr(rr); dns_rr_free(rr); } myfree((char *) types); exit(0); }
/* * allocate resources for a job. * * The job will consist of at least one app, e.g., "allocate * jobid=100 return=all timeout=10:app=0 np=5 N=2 * node_list=vm2,vm3 flag=mandatory:app=1 N=2". * * IN: * new_fd: send allocation result to socket_fd * msg: resource requirement cmd */ extern void allocate_job_op(slurm_fd_t new_fd, const char *msg) { char orte_jobid[16] = ""; char return_flag[16] = ""; size_t job_timeout = 15; /* if not specified, by default */ char send_buf[SIZE]; char **app_argv = NULL, **tmp_app_argv; size_t app_timeout; uint32_t app_count = 1; char app_resp_msg[SIZE]; char **all_resp_msg_argv = NULL, **tmp_all_resp_msg_argv; app_argv = argv_split(msg, ':'); /* app_count dose not include the first part (job info) */ app_count = argv_count(app_argv) - 1; /* app_argv will be freed */ tmp_app_argv = app_argv; while (*tmp_app_argv) { if (strstr(*tmp_app_argv, "allocate")) { _parse_job_params(*tmp_app_argv, orte_jobid, return_flag, &job_timeout); } else if (strstr(*tmp_app_argv, "app")) { app_timeout = job_timeout / app_count; _allocate_app_op(*tmp_app_argv, app_timeout, app_resp_msg); if (0 == strcmp(return_flag, "all") && 0 != strlen(app_resp_msg)) { argv_append_nosize(&all_resp_msg_argv, app_resp_msg); } else if (0 != strlen(app_resp_msg)) { /* if return_flag != "all", * each app's allocation will be sent individually */ sprintf(send_buf, "jobid=%s:%s", orte_jobid, app_resp_msg); info("BBB: send to client: %s", send_buf); send_reply(new_fd, send_buf); } } tmp_app_argv++; } /* free app_argv */ argv_free(app_argv); if (0 == strcmp(return_flag, "all")) { sprintf(send_buf, "jobid=%s", orte_jobid); /* all_resp_msg_argv will be freed */ tmp_all_resp_msg_argv = all_resp_msg_argv; while (*tmp_all_resp_msg_argv) { sprintf(send_buf, "%s:%s", send_buf, *tmp_all_resp_msg_argv); tmp_all_resp_msg_argv++; } /* free all_resp_msg_argv */ argv_free(all_resp_msg_argv); info("BBB: send to client: %s", send_buf); send_reply(new_fd, send_buf); } }
/* Ktap Main Entry */ static int ktap_main(struct file *file, struct ktap_user_parm *uparm_ptr) { unsigned long *buff = NULL; ktap_State *ks; Closure *cl; int argc; char **argv, *argstr; int ret; argstr = kmalloc(uparm_ptr->arglen, GFP_KERNEL); if (!argstr) return -ENOMEM; ret = copy_from_user(argstr, (void __user *)uparm_ptr->argstr, uparm_ptr->arglen); if (ret < 0) { kfree(argstr); return -EFAULT; } argv = argv_split(GFP_KERNEL, argstr, &argc); if (!argv) { kfree(argstr); pr_err("out of memory"); return -ENOMEM; } kfree(argstr); ret = load_trunk(uparm_ptr, &buff); if (ret) { pr_err("cannot load file %s\n", argv[0]); argv_free(argv); return ret; } ks = kp_newstate((ktap_State **)&file->private_data, argc, argv); argv_free(argv); if (unlikely(!ks)) { vfree(buff); return -ENOEXEC; } cl = kp_load(ks, (unsigned char *)buff); vfree(buff); if (cl) { /* optimize bytecode before excuting */ kp_optimize_code(ks, 0, cl->l.p); kp_call(ks, ks->top - 1, 0); } kp_exit(ks); return 0; }
int http_transport_init(struct c2_transport *t) { struct http_ctx *ctx = calloc(1, sizeof *ctx); if (ctx == NULL) { return -1; } ctx->t = t; ctx->uri = strdup(c2_transport_uri(t)); if (ctx->uri == NULL) { goto err; } ctx->data.content_type = "application/octet-stream"; ctx->opts.flags = HTTP_OPTS_SKIP_TLS_VALIDATION; char *ua = "Mozilla/5.0 (Windows NT 6.1; Trident/7.0; rv:11.0) like Gecko"; char *args = strchr(ctx->uri, '|'); if (args) { *args = '\0'; if (strlen(++args)) { size_t argc = 0; char **argv = argv_split(args, NULL, &argc); for (size_t i = 0; i < argc; i++) { if (strcmp(argv[i], "--ua") == 0 && argv[i + 1]) { ua = argv[i + 1]; } } } } ctx->data.num_headers = 1; ctx->headers[0] = strdup("Connection: close"); if (asprintf(&ctx->headers[ctx->data.num_headers], "User-Agent: %s", ua) > 0) { ctx->data.num_headers++; } ctx->data.headers = ctx->headers; ctx->first_packet = 1; ev_init(&ctx->poll_timer, http_poll_timer_cb); ctx->poll_timer.data = ctx; ctx->egress = buffer_queue_new(); if (ctx->egress == NULL) { goto err; } c2_transport_set_ctx(t, ctx); return 0; err: http_ctx_free(ctx); return -1; }
int main(int argc, char * argv[]) { peopleHead = NULL; allMahHashes_employees = ht_create(65536); allMahHashes_guests = ht_create(65536); int32_t fileSize = 0; FILE* file = NULL; size_t bytes = 0; ssize_t read = 0; char * line = NULL; char interString[MAX * 4]; //get Logreader arguements logread_args args = opt_parser(argc, argv, 1); //Verify integrity and hopefully the syntax should be right fileSize = fsize(args.logName); if (fileSize > 15) { unsigned int salt[] = { 12345, 54321 }; FILE * encrypted_file = fopen(args.logName, "r"); FILE * decrypted = fopen("tempblahman", "w+"); do_crypt(encrypted_file, decrypted, DECRYPT, args.token, strlen(args.token), (unsigned char *) salt); rename("tempblahman", args.logName); } else { invalid(); } file = fopen(args.logName, "r"); //Line by line apply the options while ((read = getline(&line, &bytes, file)) != -1 && fileSize > 10) { int len = strlen(line); fileSize = fileSize - len; // RERUN COMMANDS CAUZE LOGIC! sprintf(interString, "./logappend %s", line); int tempc; char ** tempv = argv_split(interString, &tempc); logappend_args temp = opt_parser_log(tempc, tempv); buildDataStructs(&temp); bzero(interString, MAX * 4); argv_free(tempv); // FINISH LOGICZ } doBadThings(&args); unsigned int salt[] = { 12345, 54321 }; FILE * decrypted_file = fopen(args.logName, "r"); FILE * encrypted = fopen("tempblahman", "w+"); do_crypt(decrypted_file, encrypted, ENCRYPT, args.token, strlen(args.token), (unsigned char *) salt); rename("tempblahman", args.logName); return 0; }
static int command_trace_probe(const char *buf) { char **argv; int argc = 0, ret = 0; argv = argv_split(GFP_KERNEL, buf, &argc); if (!argv) return -ENOMEM; if (argc) ret = create_trace_probe(argc, argv); argv_free(argv); return ret; }
/* Parse kprobe_events event into struct probe_point */ void parse_trace_kprobe_event(const char *str, struct probe_point *pp) { char pr; char *p; int ret, i, argc; char **argv; pr_debug("Parsing kprobe_events: %s\n", str); argv = argv_split(str, &argc); if (!argv) die("argv_split failed."); if (argc < 2) semantic_error("Too less arguments."); /* Scan event and group name. */ ret = sscanf(argv[0], "%c:%a[^/ \t]/%a[^ \t]", &pr, (float *)(void *)&pp->group, (float *)(void *)&pp->event); if (ret != 3) semantic_error("Failed to parse event name: %s", argv[0]); pr_debug("Group:%s Event:%s probe:%c\n", pp->group, pp->event, pr); pp->retprobe = (pr == 'r'); /* Scan function name and offset */ ret = sscanf(argv[1], "%a[^+]+%d", (float *)(void *)&pp->function, &pp->offset); if (ret == 1) pp->offset = 0; /* kprobe_events doesn't have this information */ pp->line = 0; pp->file = NULL; pp->nr_args = argc - 2; pp->args = zalloc(sizeof(char *) * pp->nr_args); for (i = 0; i < pp->nr_args; i++) { p = strchr(argv[i + 2], '='); if (p) /* We don't need which register is assigned. */ *p = '\0'; pp->args[i] = strdup(argv[i + 2]); if (!pp->args[i]) die("Failed to copy argument."); } argv_free(argv); }
NORETURN exec_command(const char *command) { ARGV *argv; /* * Character filter. In this particular case, we allow space and tab in * addition to the regular character set. */ static char ok_chars[] = "1234567890!@%-_=+:,./\ abcdefghijklmnopqrstuvwxyz\ ABCDEFGHIJKLMNOPQRSTUVWXYZ" SPACE_TAB; /* * See if this command contains any shell magic characters. */ if (command[strspn(command, ok_chars)] == 0 && command[strspn(command, SPACE_TAB)] != 0) { /* * No shell meta characters found, so we can try to avoid the overhead * of running a shell. Just split the command on whitespace and exec * the result directly. */ argv = argv_split(command, SPACE_TAB); (void) execvp(argv->argv[0], argv->argv); /* * Auch. Perhaps they're using some shell built-in command. */ if (errno != ENOENT || strchr(argv->argv[0], '/') != 0) msg_fatal("execvp %s: %m", argv->argv[0]); /* * Not really necessary, but... */ argv_free(argv); } /* * Pass the command to a shell. */ (void) execl(_PATH_BSHELL, "sh", "-c", command, (char *) 0); msg_fatal("execl %s: %m", _PATH_BSHELL); }
static int run_cmd(const char *cmd) { char **argv; static char *envp[] = { "HOME=/", "PATH=/sbin:/bin:/usr/sbin:/usr/bin", NULL }; int ret; argv = argv_split(GFP_KERNEL, cmd, NULL); if (argv) { ret = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC); argv_free(argv); } else { ret = -ENOMEM; } return ret; }
/** * orderly_poweroff - Trigger an orderly system poweroff * @force: force poweroff if command execution fails * * This may be called from any context to trigger a system shutdown. * If the orderly shutdown fails, it will force an immediate shutdown. */ int orderly_poweroff(bool force) { int argc; char **argv = argv_split(GFP_ATOMIC, poweroff_cmd, &argc); static char *envp[] = { "HOME=/", "PATH=/sbin:/bin:/usr/sbin:/usr/bin", NULL }; int ret = -ENOMEM; struct subprocess_info *info; if (argv == NULL) { printk(KERN_WARNING "%s failed to allocate memory for \"%s\"\n", __func__, poweroff_cmd); goto out; } info = call_usermodehelper_setup(argv[0], argv, envp); if (info == NULL) { argv_free(argv); goto out; } call_usermodehelper_setcleanup(info, argv_cleanup); ret = call_usermodehelper_exec(info, UMH_NO_WAIT); out: if (ret && force) { printk(KERN_WARNING "Failed to start orderly shutdown: " "forcing the issue\n"); /* I guess this should try to kick off some daemon to sync and poweroff asap. Or not even bother syncing if we're doing an emergency shutdown? */ emergency_sync(); kernel_power_off(); } return ret; }
void psc_dnsbl_init(void) { const char *myname = "psc_dnsbl_init"; ARGV *dnsbl_site = argv_split(var_psc_dnsbl_sites, CHARS_COMMA_SP); char **cpp; /* * Sanity check. */ if (dnsbl_site_cache != 0) msg_panic("%s: called more than once", myname); /* * pre-compute the DNSBLOG socket name. */ psc_dnsbl_service = concatenate(MAIL_CLASS_PRIVATE, "/", var_dnsblog_service, (char *) 0); /* * Prepare for quick iteration when sending out queries to all DNSBL * servers, and for quick lookup when a reply arrives from a specific * DNSBL server. */ dnsbl_site_cache = htable_create(13); for (cpp = dnsbl_site->argv; *cpp; cpp++) psc_dnsbl_add_site(*cpp); argv_free(dnsbl_site); dnsbl_site_list = htable_list(dnsbl_site_cache); /* * The per-client blocklist score. */ dnsbl_score_cache = htable_create(13); /* * Space for ad-hoc DNSBLOG server request/reply parameters. */ reply_client = vstring_alloc(100); reply_dnsbl = vstring_alloc(100); reply_addr = vstring_alloc(100); }
int main(int unused_argc, char **unused_argv) { VSTRING *buf = vstring_alloc(1); ARGV *argv; struct action *ap; int interactive = isatty(0); endp_prop = vstring_alloc(1); dest_prop = vstring_alloc(1); vstream_fileno(VSTREAM_ERR) = 1; while (get_buffer(buf, VSTREAM_IN, interactive) != VSTREAM_EOF) { argv = argv_split(STR(buf), CHARS_SPACE); if (argv->argc > 0 && argv->argv[0][0] != '#') { msg_verbose = verbose_level; for (ap = actions; ap->command != 0; ap++) { if (strcmp(ap->command, argv->argv[0]) == 0) { if ((ap->flags & FLAG_NEED_CACHE) != 0 && scache == 0) msg_error("no session cache"); else ap->action(argv); break; } } msg_verbose = 0; if (ap->command == 0) msg_error("bad command: %s", argv->argv[0]); } argv_free(argv); } scache_free(scache); vstring_free(endp_prop); vstring_free(dest_prop); vstring_free(buf); exit(0); }
/* Parse perf-probe event definition */ void parse_perf_probe_event(const char *str, struct probe_point *pp, bool *need_dwarf) { char **argv; int argc, i; *need_dwarf = false; argv = argv_split(str, &argc); if (!argv) die("argv_split failed."); if (argc > MAX_PROBE_ARGS + 1) semantic_error("Too many arguments"); /* Parse probe point */ parse_perf_probe_probepoint(argv[0], pp); if (pp->file || pp->line || pp->lazy_line) *need_dwarf = true; /* Copy arguments and ensure return probe has no C argument */ pp->nr_args = argc - 1; pp->args = zalloc(sizeof(char *) * pp->nr_args); for (i = 0; i < pp->nr_args; i++) { pp->args[i] = strdup(argv[i + 1]); if (!pp->args[i]) die("Failed to copy argument."); if (is_c_varname(pp->args[i])) { if (pp->retprobe) semantic_error("You can't specify local" " variable for kretprobe"); *need_dwarf = true; } } argv_free(argv); }
void read_master(int fail_on_open_error) { const char *myname = "read_master"; char *path; VSTRING *buf; ARGV *argv; VSTREAM *fp; int entry_count = 0; int line_count = 0; /* * Sanity check. */ if (master_table != 0) msg_panic("%s: master table is already initialized", myname); /* * Get the location of master.cf. */ if (var_config_dir == 0) set_config_dir(); path = concatenate(var_config_dir, "/", MASTER_CONF_FILE, (char *) 0); /* * We can't use the master daemon's master_ent routines in their current * form. They convert everything to internal form, and they skip disabled * services. * * The postconf command needs to show default fields as "-", and needs to * know about all service names so that it can generate service-dependent * parameter names (transport-dependent etc.). */ #define MASTER_BLANKS " \t\r\n" /* XXX */ /* * Initialize the in-memory master table. */ master_table = (PC_MASTER_ENT *) mymalloc(sizeof(*master_table)); /* * Skip blank lines and comment lines. Degrade gracefully if master.cf is * not available, and master.cf is not the primary target. */ if ((fp = vstream_fopen(path, O_RDONLY, 0)) == 0) { if (fail_on_open_error) msg_fatal("open %s: %m", path); msg_warn("open %s: %m", path); } else { buf = vstring_alloc(100); while (readlline(buf, fp, &line_count) != 0) { master_table = (PC_MASTER_ENT *) myrealloc((char *) master_table, (entry_count + 2) * sizeof(*master_table)); argv = argv_split(STR(buf), MASTER_BLANKS); if (argv->argc < PC_MASTER_MIN_FIELDS) msg_fatal("file %s: line %d: bad field count", path, line_count); normalize_options(argv); master_table[entry_count].name_space = concatenate(argv->argv[0], ".", argv->argv[1], (char *) 0); master_table[entry_count].argv = argv; master_table[entry_count].valid_names = 0; master_table[entry_count].all_params = 0; entry_count += 1; } vstream_fclose(fp); vstring_free(buf); } /* * Null-terminate the master table and clean up. */ master_table[entry_count].argv = 0; myfree(path); }
int main(int unused_ac, char **av) { VSTRING *inbuf = vstring_alloc(10); int status; ARGV *argv = 0; msg_vstream_init(av[0], VSTREAM_ERR); msg_verbose = 3; mail_conf_read(); msg_info("using config files in %s", var_config_dir); if (chdir(var_queue_dir) < 0) msg_fatal("chdir %s: %m", var_queue_dir); while (vstring_fgets_nonl(inbuf, VSTREAM_IN)) { argv = argv_split(STR(inbuf), " \t\r\n"); if (argv->argc == 0) { argv_free(argv); continue; } #define COMMAND(argv, str, len) \ (strcasecmp(argv->argv[0], str) == 0 && argv->argc == len) if (COMMAND(argv, "policy", 2)) { int cachable; int timeout; status = tls_mgr_policy(argv->argv[1], &cachable, &timeout); vstream_printf("status=%d cachable=%d timeout=%d\n", status, cachable, timeout); } else if (COMMAND(argv, "seed", 2)) { VSTRING *buf = vstring_alloc(10); VSTRING *hex = vstring_alloc(10); int len = atoi(argv->argv[1]); status = tls_mgr_seed(buf, len); hex_encode(hex, STR(buf), LEN(buf)); vstream_printf("status=%d seed=%s\n", status, STR(hex)); vstring_free(hex); vstring_free(buf); } else if (COMMAND(argv, "lookup", 3)) { VSTRING *buf = vstring_alloc(10); status = tls_mgr_lookup(argv->argv[1], argv->argv[2], buf); vstream_printf("status=%d session=%.*s\n", status, LEN(buf), STR(buf)); vstring_free(buf); } else if (COMMAND(argv, "update", 4)) { status = tls_mgr_update(argv->argv[1], argv->argv[2], argv->argv[3], strlen(argv->argv[3])); vstream_printf("status=%d\n", status); } else if (COMMAND(argv, "delete", 3)) { status = tls_mgr_delete(argv->argv[1], argv->argv[2]); vstream_printf("status=%d\n", status); } else { vstream_printf("usage:\n" "seed byte_count\n" "policy smtpd|smtp|lmtp\n" "lookup smtpd|smtp|lmtp cache_id\n" "update smtpd|smtp|lmtp cache_id session\n" "delete smtpd|smtp|lmtp cache_id\n"); } vstream_fflush(VSTREAM_OUT); argv_free(argv); } vstring_free(inbuf); return (0); }
int main(int argc, char **argv) { MILTERS *milters = 0; char *conn_macros, *helo_macros, *mail_macros, *rcpt_macros; char *data_macros, *eoh_macros, *eod_macros, *unk_macros; VSTRING *inbuf = vstring_alloc(100); char *bufp; char *cmd; int ch; int istty = isatty(vstream_fileno(VSTREAM_IN)); conn_macros = helo_macros = mail_macros = rcpt_macros = data_macros = eoh_macros = eod_macros = unk_macros = ""; msg_vstream_init(argv[0], VSTREAM_ERR); while ((ch = GETOPT(argc, argv, "V:v")) > 0) { switch (ch) { default: msg_fatal("usage: %s [-a action] [-p protocol] [-v]", argv[0]); case 'a': var_milt_def_action = optarg; break; case 'p': var_milt_protocol = optarg; break; case 'v': msg_verbose++; break; } } optind = OPTIND; for (;;) { const char *resp = 0; ARGV *argv; char **args; if (istty) { vstream_printf("- "); vstream_fflush(VSTREAM_OUT); } if (vstring_fgets_nonl(inbuf, VSTREAM_IN) <= 0) break; bufp = vstring_str(inbuf); if (!istty) { vstream_printf("> %s\n", bufp); vstream_fflush(VSTREAM_OUT); } if (*bufp == '#') continue; cmd = mystrtok(&bufp, " "); if (cmd == 0) { usage(); continue; } argv = argv_split(bufp, " "); args = argv->argv; if (strcmp(cmd, "create") == 0 && argv->argc == 1) { if (milters != 0) { msg_warn("deleting existing milters"); milter_free(milters); } milters = milter_create(args[0], var_milt_conn_time, var_milt_cmd_time, var_milt_msg_time, var_milt_protocol, var_milt_def_action, conn_macros, helo_macros, mail_macros, rcpt_macros, data_macros, eoh_macros, eod_macros, unk_macros); } else if (strcmp(cmd, "free") == 0 && argv->argc == 0) { if (milters == 0) { msg_warn("no milters"); continue; } milter_free(milters); milters = 0; } else if (strcmp(cmd, "connect") == 0 && argv->argc == 4) { if (milters == 0) { msg_warn("no milters"); continue; } resp = milter_conn_event(milters, args[0], args[1], args[2], strcmp(args[3], "AF_INET") == 0 ? AF_INET : strcmp(args[3], "AF_INET6") == 0 ? AF_INET6 : strcmp(args[3], "AF_UNIX") == 0 ? AF_UNIX : AF_UNSPEC); } else if (strcmp(cmd, "helo") == 0 && argv->argc == 1) { if (milters == 0) { msg_warn("no milters"); continue; } resp = milter_helo_event(milters, args[0], 0); } else if (strcmp(cmd, "ehlo") == 0 && argv->argc == 1) { if (milters == 0) { msg_warn("no milters"); continue; } resp = milter_helo_event(milters, args[0], 1); } else if (strcmp(cmd, "mail") == 0 && argv->argc > 0) { if (milters == 0) { msg_warn("no milters"); continue; } resp = milter_mail_event(milters, (const char **) args); } else if (strcmp(cmd, "rcpt") == 0 && argv->argc > 0) { if (milters == 0) { msg_warn("no milters"); continue; } resp = milter_rcpt_event(milters, 0, (const char **) args); } else if (strcmp(cmd, "unknown") == 0 && argv->argc > 0) { if (milters == 0) { msg_warn("no milters"); continue; } resp = milter_unknown_event(milters, args[0]); } else if (strcmp(cmd, "data") == 0 && argv->argc == 0) { if (milters == 0) { msg_warn("no milters"); continue; } resp = milter_data_event(milters); } else if (strcmp(cmd, "disconnect") == 0 && argv->argc == 0) { if (milters == 0) { msg_warn("no milters"); continue; } milter_disc_event(milters); } else { usage(); } if (resp != 0) msg_info("%s", resp); argv_free(argv); } if (milters != 0) milter_free(milters); vstring_free(inbuf); return (0); }
void dict_open_dlinfo(const char *path) { char *myname="dict_open_dlinfo"; VSTREAM *conf_fp=vstream_fopen(path,O_RDONLY,0); VSTRING *buf = vstring_alloc(100); char *cp; ARGV *argv; MVECT vector; int nelm=0; int linenum=0; dict_dlinfo=(DLINFO*)mvect_alloc(&vector,sizeof(DLINFO),3,NULL,NULL); if (!conf_fp) { msg_warn("%s: cannot open %s. No dynamic maps will be allowed.", myname, path); } else { while (vstring_get_nonl(buf,conf_fp) != VSTREAM_EOF) { cp = vstring_str(buf); linenum++; if (*cp == '#' || *cp == '\0') continue; argv = argv_split(cp, " \t"); if (argv->argc != 3 && argv->argc != 4) { msg_fatal("%s: Expected \"pattern .so-name open-function [mkmap-function]\" at line %d", myname, linenum); } if (STREQ(argv->argv[0],"*")) { msg_warn("%s: wildcard dynamic map entry no longer supported.", myname); continue; } if (argv->argv[1][0] != '/') { msg_fatal("%s: .so name must begin with a \"/\" at line %d", myname, linenum); } if (nelm >= vector.nelm) { dict_dlinfo=(DLINFO*)mvect_realloc(&vector,vector.nelm+3); } dict_dlinfo[nelm].pattern = mystrdup(argv->argv[0]); dict_dlinfo[nelm].soname = mystrdup(argv->argv[1]); dict_dlinfo[nelm].openfunc = mystrdup(argv->argv[2]); if (argv->argc==4) dict_dlinfo[nelm].mkmapfunc = mystrdup(argv->argv[3]); else dict_dlinfo[nelm].mkmapfunc = NULL; nelm++; argv_free(argv); } } if (nelm >= vector.nelm) { dict_dlinfo=(DLINFO*)mvect_realloc(&vector,vector.nelm+1); } dict_dlinfo[nelm].pattern = NULL; dict_dlinfo[nelm].soname = NULL; dict_dlinfo[nelm].openfunc = NULL; dict_dlinfo[nelm].mkmapfunc = NULL; if (conf_fp) vstream_fclose(conf_fp); vstring_free(buf); }
int main(int argc, char **argv) { DICT_CACHE_TEST *test_job; VSTRING *inbuf = vstring_alloc(100); char *bufp; ARGV *args; DICT_CACHE *cache = 0; int stdin_is_tty; msg_vstream_init(argv[0], VSTREAM_ERR); if (argc != 1) usage(argv[0]); test_job = create_requests(DICT_CACHE_SREQ_LIMIT); stdin_is_tty = isatty(0); for (;;) { if (stdin_is_tty) { vstream_printf("> "); vstream_fflush(VSTREAM_OUT); } if (vstring_fgets_nonl(inbuf, VSTREAM_IN) == 0) break; bufp = vstring_str(inbuf); if (!stdin_is_tty) { vstream_printf("> %s\n", bufp); vstream_fflush(VSTREAM_OUT); } if (*bufp == '#') continue; args = argv_split(bufp, DELIMS); if (argc == 0) { vstream_printf("usage: %s\n", USAGE); vstream_fflush(VSTREAM_OUT); continue; } if (strcmp(args->argv[0], "verbose") == 0 && args->argc == 2) { msg_verbose = atoi(args->argv[1]); } else if (strcmp(args->argv[0], "elapsed") == 0 && args->argc == 2) { show_elapsed = atoi(args->argv[1]); #ifdef HAS_LMDB } else if (strcmp(args->argv[0], "lmdb_map_size") == 0 && args->argc == 2) { dict_lmdb_map_size = atol(args->argv[1]); #endif } else if (strcmp(args->argv[0], "cache") == 0 && args->argc == 2) { if (cache) dict_cache_close(cache); cache = dict_cache_open(args->argv[1], O_CREAT | O_RDWR, DICT_CACHE_OPEN_FLAGS); } else if (strcmp(args->argv[0], "reset") == 0 && args->argc == 1) { reset_requests(test_job); } else if (strcmp(args->argv[0], "run") == 0 && args->argc == 1) { run_requests(test_job, cache, inbuf); } else if (strcmp(args->argv[0], "status") == 0 && args->argc == 1) { show_status(test_job, cache); } else { add_request(test_job, args); } vstream_fflush(VSTREAM_OUT); argv_free(args); } vstring_free(inbuf); free_requests(test_job); if (cache) dict_cache_close(cache); return (0); }
int main(int argc, char **argv) { static VSTREAM *lock_fp; static VSTREAM *data_lock_fp; VSTRING *lock_path; VSTRING *data_lock_path; off_t inherited_limit; int debug_me = 0; int ch; int fd; int n; int test_lock = 0; VSTRING *why; WATCHDOG *watchdog; ARGV *import_env; /* * Fingerprint executables and core dumps. */ MAIL_VERSION_STAMP_ALLOCATE; /* * Initialize. */ umask(077); /* never fails! */ /* * Process environment options as early as we can. */ if (getenv(CONF_ENV_VERB)) msg_verbose = 1; if (getenv(CONF_ENV_DEBUG)) debug_me = 1; /* * Don't die when a process goes away unexpectedly. */ signal(SIGPIPE, SIG_IGN); /* * Strip and save the process name for diagnostics etc. */ var_procname = mystrdup(basename(argv[0])); /* * When running a child process, don't leak any open files that were * leaked to us by our own (privileged) parent process. Descriptors 0-2 * are taken care of after we have initialized error logging. * * Some systems such as AIX have a huge per-process open file limit. In * those cases, limit the search for potential file descriptor leaks to * just the first couple hundred. * * The Debian post-installation script passes an open file descriptor into * the master process and waits forever for someone to close it. Because * of this we have to close descriptors > 2, and pray that doing so does * not break things. */ closefrom(3); /* * Initialize logging and exit handler. */ msg_syslog_init(mail_task(var_procname), LOG_PID, LOG_FACILITY); /* * Check the Postfix library version as soon as we enable logging. */ MAIL_VERSION_CHECK; /* * The mail system must be run by the superuser so it can revoke * privileges for selected operations. That's right - it takes privileges * to toss privileges. */ if (getuid() != 0) msg_fatal("the master command is reserved for the superuser"); if (unsafe() != 0) msg_fatal("the master command must not run as a set-uid process"); /* * Process JCL. */ while ((ch = GETOPT(argc, argv, "c:Dde:tv")) > 0) { switch (ch) { case 'c': if (setenv(CONF_ENV_PATH, optarg, 1) < 0) msg_fatal("out of memory"); break; case 'd': master_detach = 0; break; case 'e': event_request_timer(master_exit_event, (char *) 0, atoi(optarg)); break; case 'D': debug_me = 1; break; case 't': test_lock = 1; break; case 'v': msg_verbose++; break; default: usage(argv[0]); /* NOTREACHED */ } } /* * This program takes no other arguments. */ if (argc > optind) usage(argv[0]); /* * If started from a terminal, get rid of any tty association. This also * means that all errors and warnings must go to the syslog daemon. */ if (master_detach) for (fd = 0; fd < 3; fd++) { (void) close(fd); if (open("/dev/null", O_RDWR, 0) != fd) msg_fatal("open /dev/null: %m"); } /* * Run in a separate process group, so that "postfix stop" can terminate * all MTA processes cleanly. Give up if we can't separate from our * parent process. We're not supposed to blow away the parent. */ if (debug_me == 0 && master_detach != 0 && setsid() == -1 && getsid(0) != getpid()) msg_fatal("unable to set session and process group ID: %m"); /* * Make some room for plumbing with file descriptors. XXX This breaks * when a service listens on many ports. In order to do this right we * must change the master-child interface so that descriptors do not need * to have fixed numbers. * * In a child we need two descriptors for the flow control pipe, one for * child->master status updates and at least one for listening. */ for (n = 0; n < 5; n++) { if (close_on_exec(dup(0), CLOSE_ON_EXEC) < 0) msg_fatal("dup(0): %m"); } /* * Final initializations. Unfortunately, we must read the global Postfix * configuration file after doing command-line processing, so that we get * consistent results when we SIGHUP the server to reload configuration * files. */ master_vars_init(); /* * In case of multi-protocol support. This needs to be done because * master does not invoke mail_params_init() (it was written before that * code existed). */ (void) inet_proto_init(VAR_INET_PROTOCOLS, var_inet_protocols); /* * Environment import filter, to enforce consistent behavior whether * Postfix is started by hand, or at system boot time. */ import_env = argv_split(var_import_environ, ", \t\r\n"); clean_env(import_env->argv); argv_free(import_env); if ((inherited_limit = get_file_limit()) < 0) set_file_limit(OFF_T_MAX); if (chdir(var_queue_dir)) msg_fatal("chdir %s: %m", var_queue_dir); /* * Lock down the master.pid file. In test mode, no file means that it * isn't locked. */ lock_path = vstring_alloc(10); data_lock_path = vstring_alloc(10); why = vstring_alloc(10); vstring_sprintf(lock_path, "%s/%s.pid", DEF_PID_DIR, var_procname); if (test_lock && access(vstring_str(lock_path), F_OK) < 0) exit(0); lock_fp = open_lock(vstring_str(lock_path), O_RDWR | O_CREAT, 0644, why); if (test_lock) exit(lock_fp ? 0 : 1); if (lock_fp == 0) msg_fatal("open lock file %s: %s", vstring_str(lock_path), vstring_str(why)); vstream_fprintf(lock_fp, "%*lu\n", (int) sizeof(unsigned long) * 4, (unsigned long) var_pid); if (vstream_fflush(lock_fp)) msg_fatal("cannot update lock file %s: %m", vstring_str(lock_path)); close_on_exec(vstream_fileno(lock_fp), CLOSE_ON_EXEC); /* * Lock down the Postfix-writable data directory. */ vstring_sprintf(data_lock_path, "%s/%s.lock", var_data_dir, var_procname); set_eugid(var_owner_uid, var_owner_gid); data_lock_fp = open_lock(vstring_str(data_lock_path), O_RDWR | O_CREAT, 0644, why); set_ugid(getuid(), getgid()); if (data_lock_fp == 0) msg_fatal("open lock file %s: %s", vstring_str(data_lock_path), vstring_str(why)); vstream_fprintf(data_lock_fp, "%*lu\n", (int) sizeof(unsigned long) * 4, (unsigned long) var_pid); if (vstream_fflush(data_lock_fp)) msg_fatal("cannot update lock file %s: %m", vstring_str(data_lock_path)); close_on_exec(vstream_fileno(data_lock_fp), CLOSE_ON_EXEC); /* * Clean up. */ vstring_free(why); vstring_free(lock_path); vstring_free(data_lock_path); /* * Optionally start the debugger on ourself. */ if (debug_me) debug_process(); /* * Finish initialization, last part. We must process configuration files * after processing command-line parameters, so that we get consistent * results when we SIGHUP the server to reload configuration files. */ master_config(); master_sigsetup(); master_flow_init(); msg_info("daemon started -- version %s, configuration %s", var_mail_version, var_config_dir); /* * Process events. The event handler will execute the read/write/timer * action routines. Whenever something has happened, see if we received * any signal in the mean time. Although the master process appears to do * multiple things at the same time, it really is all a single thread, so * that there are no concurrency conflicts within the master process. */ #define MASTER_WATCHDOG_TIME 1000 watchdog = watchdog_create(MASTER_WATCHDOG_TIME, (WATCHDOG_FN) 0, (char *) 0); for (;;) { #ifdef HAS_VOLATILE_LOCKS if (myflock(vstream_fileno(lock_fp), INTERNAL_LOCK, MYFLOCK_OP_EXCLUSIVE) < 0) msg_fatal("refresh exclusive lock: %m"); if (myflock(vstream_fileno(data_lock_fp), INTERNAL_LOCK, MYFLOCK_OP_EXCLUSIVE) < 0) msg_fatal("refresh exclusive lock: %m"); #endif watchdog_start(watchdog); /* same as trigger servers */ event_loop(MASTER_WATCHDOG_TIME / 2); if (master_gotsighup) { msg_info("reload -- version %s, configuration %s", var_mail_version, var_config_dir); master_gotsighup = 0; /* this first */ master_vars_init(); /* then this */ master_refresh(); /* then this */ } if (master_gotsigchld) { if (msg_verbose) msg_info("got sigchld"); master_gotsigchld = 0; /* this first */ master_reap_child(); /* then this */ } } }
int main(int unused_ac, char **av) { ACL_VSTRING *inbuf = acl_vstring_alloc(10); int status; ARGV *argv = 0; ACL_EVENT *eventp = acl_event_new_select(1, 0); acl_msg_verbose = 3; mail_conf_read(); acl_msg_info("using config files in %s", var_config_dir); if (chdir(var_queue_dir) < 0) acl_msg_fatal("chdir %s: %s", var_queue_dir, acl_last_serror()); tls_mgr_open(eventp); while (acl_vstring_fgets_nonl(inbuf, ACL_VSTREAM_IN)) { argv = argv_split(STR(inbuf), " \t\r\n"); if (argv->argc == 0) { argv_free(argv); continue; } #define COMMAND(argv, str, len) \ (strcasecmp(argv->argv[0], str) == 0 && argv->argc == len) if (COMMAND(argv, "policy", 2)) { int cachable; status = tls_mgr_policy(argv->argv[1], &cachable); acl_vstream_printf("status=%d cachable=%d\n", status, cachable); } else if (COMMAND(argv, "seed", 2)) { ACL_VSTRING *buf = acl_vstring_alloc(10); ACL_VSTRING *hex = acl_vstring_alloc(10); int len = atoi(argv->argv[1]); status = tls_mgr_seed(buf, len); hex_encode(hex, STR(buf), LEN(buf)); acl_vstream_printf("status=%d seed=%s\n", status, STR(hex)); acl_vstring_free(hex); acl_vstring_free(buf); } else if (COMMAND(argv, "lookup", 3)) { ACL_VSTRING *buf = acl_vstring_alloc(10); status = tls_mgr_lookup(argv->argv[1], argv->argv[2], buf); acl_vstream_printf("status=%d session=%.*s\n", status, LEN(buf), STR(buf)); acl_vstring_free(buf); } else if (COMMAND(argv, "update", 4)) { status = tls_mgr_update(argv->argv[1], argv->argv[2], argv->argv[3], strlen(argv->argv[3])); acl_vstream_printf("status=%d\n", status); } else if (COMMAND(argv, "delete", 3)) { status = tls_mgr_delete(argv->argv[1], argv->argv[2]); acl_vstream_printf("status=%d\n", status); } else { acl_vstream_printf("usage:\n" "seed byte_count\n" "policy smtpd|smtp|lmtp\n" "lookup smtpd|smtp|lmtp cache_id\n" "update smtpd|smtp|lmtp cache_id session\n" "delete smtpd|smtp|lmtp cache_id\n"); } acl_vstream_fflush(ACL_VSTREAM_OUT); argv_free(argv); } acl_vstring_free(inbuf); acl_event_free(eventp); return (0); }
static void qmgr_message_resolve(QMGR_MESSAGE *message) { static ARGV *defer_xport_argv; RECIPIENT_LIST list = message->rcpt_list; RECIPIENT *recipient; QMGR_TRANSPORT *transport = 0; QMGR_QUEUE *queue = 0; RESOLVE_REPLY reply; VSTRING *queue_name; char *at; char **cpp; char *nexthop; ssize_t len; int status; DSN dsn; MSG_STATS stats; DSN *saved_dsn; #define STREQ(x,y) (strcmp(x,y) == 0) #define STR vstring_str #define LEN VSTRING_LEN resolve_clnt_init(&reply); queue_name = vstring_alloc(1); for (recipient = list.info; recipient < list.info + list.len; recipient++) { /* * Redirect overrides all else. But only once (per entire message). * For consistency with the remainder of Postfix, rewrite the address * to canonical form before resolving it. */ if (message->redirect_addr) { if (recipient > list.info) { recipient->u.queue = 0; continue; } message->rcpt_offset = 0; message->rcpt_unread = 0; rewrite_clnt_internal(REWRITE_CANON, message->redirect_addr, reply.recipient); RECIPIENT_UPDATE(recipient->address, STR(reply.recipient)); if (qmgr_resolve_one(message, recipient, recipient->address, &reply) < 0) continue; if (!STREQ(recipient->address, STR(reply.recipient))) RECIPIENT_UPDATE(recipient->address, STR(reply.recipient)); } /* * Content filtering overrides the address resolver. * * XXX Bypass content_filter inspection for user-generated probes * (sendmail -bv). MTA-generated probes never have the "please filter * me" bits turned on, but we handle them here anyway for the sake of * future proofing. */ #define FILTER_WITHOUT_NEXTHOP(filter, next) \ (((next) = split_at((filter), ':')) == 0 || *(next) == 0) #define RCPT_WITHOUT_DOMAIN(rcpt, next) \ ((next = strrchr(rcpt, '@')) == 0 || *++(next) == 0) else if (message->filter_xport && (message->tflags & DEL_REQ_TRACE_ONLY_MASK) == 0) { reply.flags = 0; vstring_strcpy(reply.transport, message->filter_xport); if (FILTER_WITHOUT_NEXTHOP(STR(reply.transport), nexthop) && *(nexthop = var_def_filter_nexthop) == 0 && RCPT_WITHOUT_DOMAIN(recipient->address, nexthop)) nexthop = var_myhostname; vstring_strcpy(reply.nexthop, nexthop); vstring_strcpy(reply.recipient, recipient->address); } /* * Resolve the destination to (transport, nexthop, address). The * result address may differ from the one specified by the sender. */ else { if (qmgr_resolve_one(message, recipient, recipient->address, &reply) < 0) continue; if (!STREQ(recipient->address, STR(reply.recipient))) RECIPIENT_UPDATE(recipient->address, STR(reply.recipient)); } /* * Bounce null recipients. This should never happen, but is most * likely the result of a fault in a different program, so aborting * the queue manager process does not help. */ if (recipient->address[0] == 0) { QMGR_REDIRECT(&reply, MAIL_SERVICE_ERROR, "5.1.3 null recipient address"); } /* * Discard mail to the local double bounce address here, so this * system can run without a local delivery agent. They'd still have * to configure something for mail directed to the local postmaster, * though, but that is an RFC requirement anyway. * * XXX This lookup should be done in the resolver, and the mail should * be directed to a general-purpose null delivery agent. */ if (reply.flags & RESOLVE_CLASS_LOCAL) { at = strrchr(STR(reply.recipient), '@'); len = (at ? (at - STR(reply.recipient)) : strlen(STR(reply.recipient))); if (strncasecmp(STR(reply.recipient), var_double_bounce_sender, len) == 0 && !var_double_bounce_sender[len]) { status = sent(message->tflags, message->queue_id, QMGR_MSG_STATS(&stats, message), recipient, "none", DSN_SIMPLE(&dsn, "2.0.0", "undeliverable postmaster notification discarded")); if (status == 0) { deliver_completed(message->fp, recipient->offset); #if 0 /* It's the default verification probe sender address. */ msg_warn("%s: undeliverable postmaster notification discarded", message->queue_id); #endif } else message->flags |= status; continue; } } /* * Optionally defer deliveries over specific transports, unless the * restriction is lifted temporarily. */ if (*var_defer_xports && (message->qflags & QMGR_FLUSH_DFXP) == 0) { if (defer_xport_argv == 0) defer_xport_argv = argv_split(var_defer_xports, CHARS_COMMA_SP); for (cpp = defer_xport_argv->argv; *cpp; cpp++) if (strcmp(*cpp, STR(reply.transport)) == 0) break; if (*cpp) { QMGR_REDIRECT(&reply, MAIL_SERVICE_RETRY, "4.3.2 deferred transport"); } } /* * Look up or instantiate the proper transport. */ if (transport == 0 || !STREQ(transport->name, STR(reply.transport))) { if ((transport = qmgr_transport_find(STR(reply.transport))) == 0) transport = qmgr_transport_create(STR(reply.transport)); queue = 0; } /* * This message is being flushed. If need-be unthrottle the * transport. */ if ((message->qflags & QMGR_FLUSH_EACH) != 0 && QMGR_TRANSPORT_THROTTLED(transport)) qmgr_transport_unthrottle(transport); /* * This transport is dead. Defer delivery to this recipient. */ if (QMGR_TRANSPORT_THROTTLED(transport)) { saved_dsn = transport->dsn; if ((transport = qmgr_error_transport(MAIL_SERVICE_RETRY)) != 0) { nexthop = qmgr_error_nexthop(saved_dsn); vstring_strcpy(reply.nexthop, nexthop); myfree(nexthop); queue = 0; } else { qmgr_defer_recipient(message, recipient, saved_dsn); continue; } } /* * The nexthop destination provides the default name for the * per-destination queue. When the delivery agent accepts only one * recipient per delivery, give each recipient its own queue, so that * deliveries to different recipients of the same message can happen * in parallel, and so that we can enforce per-recipient concurrency * limits and prevent one recipient from tying up all the delivery * agent resources. We use recipient@nexthop as queue name rather * than the actual recipient domain name, so that one recipient in * multiple equivalent domains cannot evade the per-recipient * concurrency limit. Split the address on the recipient delimiter if * one is defined, so that extended addresses don't get extra * delivery slots. * * Fold the result to lower case so that we don't have multiple queues * for the same name. * * Important! All recipients in a queue must have the same nexthop * value. It is OK to have multiple queues with the same nexthop * value, but only when those queues are named after recipients. * * The single-recipient code below was written for local(8) like * delivery agents, and assumes that all domains that deliver to the * same (transport + nexthop) are aliases for $nexthop. Delivery * concurrency is changed from per-domain into per-recipient, by * changing the queue name from nexthop into localpart@nexthop. * * XXX This assumption is incorrect when different destinations share * the same (transport + nexthop). In reality, such transports are * rarely configured to use single-recipient deliveries. The fix is * to decouple the per-destination recipient limit from the * per-destination concurrency. */ vstring_strcpy(queue_name, STR(reply.nexthop)); if (strcmp(transport->name, MAIL_SERVICE_ERROR) != 0 && strcmp(transport->name, MAIL_SERVICE_RETRY) != 0 && transport->recipient_limit == 1) { /* Copy the recipient localpart. */ at = strrchr(STR(reply.recipient), '@'); len = (at ? (at - STR(reply.recipient)) : strlen(STR(reply.recipient))); vstring_strncpy(queue_name, STR(reply.recipient), len); /* Remove the address extension from the recipient localpart. */ if (*var_rcpt_delim && split_addr(STR(queue_name), var_rcpt_delim)) vstring_truncate(queue_name, strlen(STR(queue_name))); /* Assume the recipient domain is equivalent to nexthop. */ vstring_sprintf_append(queue_name, "@%s", STR(reply.nexthop)); } lowercase(STR(queue_name)); /* * This transport is alive. Find or instantiate a queue for this * recipient. */ if (queue == 0 || !STREQ(queue->name, STR(queue_name))) { if ((queue = qmgr_queue_find(transport, STR(queue_name))) == 0) queue = qmgr_queue_create(transport, STR(queue_name), STR(reply.nexthop)); } /* * This message is being flushed. If need-be unthrottle the queue. */ if ((message->qflags & QMGR_FLUSH_EACH) != 0 && QMGR_QUEUE_THROTTLED(queue)) qmgr_queue_unthrottle(queue); /* * This queue is dead. Defer delivery to this recipient. */ if (QMGR_QUEUE_THROTTLED(queue)) { saved_dsn = queue->dsn; if ((queue = qmgr_error_queue(MAIL_SERVICE_RETRY, saved_dsn)) == 0) { qmgr_defer_recipient(message, recipient, saved_dsn); continue; } } /* * This queue is alive. Bind this recipient to this queue instance. */ recipient->u.queue = queue; } resolve_clnt_free(&reply); vstring_free(queue_name); }
static void psc_dnsbl_receive(int event, void *context) { const char *myname = "psc_dnsbl_receive"; VSTREAM *stream = (VSTREAM *) context; PSC_DNSBL_SCORE *score; PSC_DNSBL_HEAD *head; PSC_DNSBL_SITE *site; ARGV *reply_argv; int request_id; int dnsbl_ttl; PSC_CLEAR_EVENT_REQUEST(vstream_fileno(stream), psc_dnsbl_receive, context); /* * Receive the DNSBL lookup result. * * This is preliminary code to explore the field. Later, DNSBL lookup will * be handled by an UDP-based DNS client that is built directly into some * Postfix daemon. * * Don't bother looking up the blocklist score when the client IP address is * not listed at the DNSBL. * * Don't panic when the blocklist score no longer exists. It may be deleted * when the client triggers a "drop" action after pregreet, when the * client does not pregreet and the DNSBL reply arrives late, or when the * client triggers a "drop" action after hanging up. */ if (event == EVENT_READ && attr_scan(stream, ATTR_FLAG_STRICT, RECV_ATTR_STR(MAIL_ATTR_RBL_DOMAIN, reply_dnsbl), RECV_ATTR_STR(MAIL_ATTR_ACT_CLIENT_ADDR, reply_client), RECV_ATTR_INT(MAIL_ATTR_LABEL, &request_id), RECV_ATTR_STR(MAIL_ATTR_RBL_ADDR, reply_addr), RECV_ATTR_INT(MAIL_ATTR_TTL, &dnsbl_ttl), ATTR_TYPE_END) == 5 && (score = (PSC_DNSBL_SCORE *) htable_find(dnsbl_score_cache, STR(reply_client))) != 0 && score->request_id == request_id) { /* * Run this response past all applicable DNSBL filters and update the * blocklist score for this client IP address. * * Don't panic when the DNSBL domain name is not found. The DNSBLOG * server may be messed up. */ if (msg_verbose > 1) msg_info("%s: client=\"%s\" score=%d domain=\"%s\" reply=\"%d %s\"", myname, STR(reply_client), score->total, STR(reply_dnsbl), dnsbl_ttl, STR(reply_addr)); head = (PSC_DNSBL_HEAD *) htable_find(dnsbl_site_cache, STR(reply_dnsbl)); if (head == 0) { /* Bogus domain. Do nothing. */ } else if (*STR(reply_addr) != 0) { /* DNS reputation record(s) found. */ reply_argv = 0; for (site = head->first; site != 0; site = site->next) { if (site->byte_codes == 0 || psc_dnsbl_match(site->byte_codes, reply_argv ? reply_argv : (reply_argv = argv_split(STR(reply_addr), " ")))) { if (score->dnsbl_name == 0 || score->dnsbl_weight < site->weight) { score->dnsbl_name = head->safe_dnsbl; score->dnsbl_weight = site->weight; } score->total += site->weight; if (msg_verbose > 1) msg_info("%s: filter=\"%s\" weight=%d score=%d", myname, site->filter ? site->filter : "null", site->weight, score->total); } /* As with dnsblog(8), a value < 0 means no reply TTL. */ if (site->weight > 0) { if (score->fail_ttl < 0 || score->fail_ttl > dnsbl_ttl) score->fail_ttl = dnsbl_ttl; } else { if (score->pass_ttl < 0 || score->pass_ttl > dnsbl_ttl) score->pass_ttl = dnsbl_ttl; } } if (reply_argv != 0) argv_free(reply_argv); } else { /* No DNS reputation record found. */ for (site = head->first; site != 0; site = site->next) { /* As with dnsblog(8), a value < 0 means no reply TTL. */ if (site->weight > 0) { if (score->pass_ttl < 0 || score->pass_ttl > dnsbl_ttl) score->pass_ttl = dnsbl_ttl; } else { if (score->fail_ttl < 0 || score->fail_ttl > dnsbl_ttl) score->fail_ttl = dnsbl_ttl; } } } /* * Notify the requestor(s) that the result is ready to be picked up. * If this call isn't made, clients have to sit out the entire * pre-handshake delay. */ score->pending_lookups -= 1; if (score->pending_lookups == 0) PSC_CALL_BACK_NOTIFY(score, PSC_NULL_EVENT); } else if (event == EVENT_TIME) { msg_warn("dnsblog reply timeout %ds for %s", var_psc_dnsbl_tmout, (char *) vstream_context(stream)); } /* Here, score may be a null pointer. */ vstream_fclose(stream); }
static int tls_policy_lookup_one(SMTP_SESSION *session, int *site_level, const char *site_name, const char *site_class) { const char *lookup; char *policy; char *saved_policy; char *tok; const char *err; char *name; char *val; static VSTRING *cbuf; #undef FREE_RETURN #define FREE_RETURN(x) do { myfree(saved_policy); return (x); } while (0) if ((lookup = maps_find(tls_policy, site_name, 0)) == 0) { if (tls_policy->error) { msg_fatal("%s: %s lookup error for %s", session->state->request->queue_id, tls_policy->title, site_name); /* XXX session->stream has no longjmp context yet. */ } return (0); } if (cbuf == 0) cbuf = vstring_alloc(10); #define WHERE \ vstring_str(vstring_sprintf(cbuf, "TLS policy table, %s \"%s\"", \ site_class, site_name)) saved_policy = policy = mystrdup(lookup); if ((tok = mystrtok(&policy, "\t\n\r ,")) == 0) { msg_warn("%s: invalid empty policy", WHERE); *site_level = TLS_LEV_INVALID; FREE_RETURN(1); /* No further lookups */ } *site_level = tls_level_lookup(tok); if (*site_level == TLS_LEV_INVALID) { /* tls_level_lookup() logs no warning. */ msg_warn("%s: invalid security level \"%s\"", WHERE, tok); FREE_RETURN(1); /* No further lookups */ } /* * Warn about ignored attributes when TLS is disabled. */ if (*site_level < TLS_LEV_MAY) { while ((tok = mystrtok(&policy, "\t\n\r ,")) != 0) msg_warn("%s: ignoring attribute \"%s\" with TLS disabled", WHERE, tok); FREE_RETURN(1); } /* * Errors in attributes may have security consequences, don't ignore * errors that can degrade security. */ while ((tok = mystrtok(&policy, "\t\n\r ,")) != 0) { if ((err = split_nameval(tok, &name, &val)) != 0) { *site_level = TLS_LEV_INVALID; msg_warn("%s: malformed attribute/value pair \"%s\": %s", WHERE, tok, err); break; } /* Only one instance per policy. */ if (!strcasecmp(name, "ciphers")) { if (*val == 0) { msg_warn("%s: attribute \"%s\" has empty value", WHERE, name); *site_level = TLS_LEV_INVALID; break; } if (session->tls_grade) { msg_warn("%s: attribute \"%s\" is specified multiple times", WHERE, name); *site_level = TLS_LEV_INVALID; break; } session->tls_grade = mystrdup(val); continue; } /* Only one instance per policy. */ if (!strcasecmp(name, "protocols")) { if (session->tls_protocols) { msg_warn("%s: attribute \"%s\" is specified multiple times", WHERE, name); *site_level = TLS_LEV_INVALID; break; } session->tls_protocols = mystrdup(val); continue; } /* Multiple instance(s) per policy. */ if (!strcasecmp(name, "match")) { char *delim = *site_level == TLS_LEV_FPRINT ? "|" : ":"; if (*site_level <= TLS_LEV_ENCRYPT) { msg_warn("%s: attribute \"%s\" invalid at security level \"%s\"", WHERE, name, policy_name(*site_level)); *site_level = TLS_LEV_INVALID; break; } if (*val == 0) { msg_warn("%s: attribute \"%s\" has empty value", WHERE, name); *site_level = TLS_LEV_INVALID; break; } if (session->tls_matchargv == 0) session->tls_matchargv = argv_split(val, delim); else argv_split_append(session->tls_matchargv, val, delim); continue; } /* Only one instance per policy. */ if (!strcasecmp(name, "exclude")) { if (session->tls_exclusions) { msg_warn("%s: attribute \"%s\" is specified multiple times", WHERE, name); *site_level = TLS_LEV_INVALID; break; } session->tls_exclusions = vstring_strcpy(vstring_alloc(10), val); continue; } else { msg_warn("%s: invalid attribute name: \"%s\"", WHERE, name); *site_level = TLS_LEV_INVALID; break; } } FREE_RETURN(1); }
static void session_tls_init(SMTP_SESSION *session, const char *dest, const char *host, int flags) { const char *myname = "session_tls_init"; int global_level; int site_level; /* * Initialize all TLS related session properties. */ session->tls_context = 0; session->tls_nexthop = 0; session->tls_level = TLS_LEV_NONE; session->tls_retry_plain = 0; session->tls_protocols = 0; session->tls_grade = 0; session->tls_exclusions = 0; session->tls_matchargv = 0; /* * Compute the global TLS policy. This is the default policy level when * no per-site policy exists. It also is used to override a wild-card * per-site policy. */ if (*var_smtp_tls_level) { /* Require that var_smtp_tls_level is sanitized upon startup. */ global_level = tls_level_lookup(var_smtp_tls_level); if (global_level == TLS_LEV_INVALID) msg_panic("%s: invalid TLS security level: \"%s\"", myname, var_smtp_tls_level); } else if (var_smtp_enforce_tls) { global_level = var_smtp_tls_enforce_peername ? TLS_LEV_VERIFY : TLS_LEV_ENCRYPT; } else { global_level = var_smtp_use_tls ? TLS_LEV_MAY : TLS_LEV_NONE; } if (msg_verbose) msg_info("%s TLS level: %s", "global", policy_name(global_level)); /* * Compute the per-site TLS enforcement level. For compatibility with the * original TLS patch, this algorithm is gives equal precedence to host * and next-hop policies. */ site_level = TLS_LEV_NOTFOUND; if (tls_policy) { tls_policy_lookup(session, &site_level, dest, "next-hop destination"); } else if (tls_per_site) { tls_site_lookup(&site_level, dest, "next-hop destination"); if (strcasecmp(dest, host) != 0) tls_site_lookup(&site_level, host, "server hostname"); if (msg_verbose) msg_info("%s TLS level: %s", "site", policy_name(site_level)); /* * Override a wild-card per-site policy with a more specific global * policy. * * With the original TLS patch, 1) a per-site ENCRYPT could not override * a global VERIFY, and 2) a combined per-site (NONE+MAY) policy * produced inconsistent results: it changed a global VERIFY into * NONE, while producing MAY with all weaker global policy settings. * * With the current implementation, a combined per-site (NONE+MAY) * consistently overrides global policy with NONE, and global policy * can override only a per-site MAY wildcard. That is, specific * policies consistently override wildcard policies, and * (non-wildcard) per-site policies consistently override global * policies. */ if (site_level == TLS_LEV_MAY && global_level > TLS_LEV_MAY) site_level = global_level; } if (site_level == TLS_LEV_NOTFOUND) session->tls_level = global_level; else session->tls_level = site_level; /* * Use main.cf protocols setting if not set in per-destination table. */ if (session->tls_level > TLS_LEV_NONE && session->tls_protocols == 0) session->tls_protocols = mystrdup((session->tls_level == TLS_LEV_MAY) ? var_smtp_tls_proto : var_smtp_tls_mand_proto); /* * Compute cipher grade (if set in per-destination table, else * set_cipher() uses main.cf settings) and security level dependent * cipher exclusion list. */ set_cipher_grade(session); /* * Use main.cf cert_match setting if not set in per-destination table. */ if (session->tls_matchargv == 0) { switch (session->tls_level) { case TLS_LEV_INVALID: case TLS_LEV_NONE: case TLS_LEV_MAY: case TLS_LEV_ENCRYPT: break; case TLS_LEV_FPRINT: session->tls_matchargv = argv_split(var_smtp_tls_fpt_cmatch, "\t\n\r, |"); break; case TLS_LEV_VERIFY: session->tls_matchargv = argv_split(var_smtp_tls_vfy_cmatch, "\t\n\r, :"); break; case TLS_LEV_SECURE: session->tls_matchargv = argv_split(var_smtp_tls_sec_cmatch, "\t\n\r, :"); break; default: msg_panic("unexpected TLS security level: %d", session->tls_level); } } if (msg_verbose && (tls_policy || tls_per_site)) msg_info("%s TLS level: %s", "effective", policy_name(session->tls_level)); }