static HOST *host_init(const char *hostname) { const char *myname = "pgsql host_init"; HOST *host = (HOST *) mymalloc(sizeof(HOST)); const char *d = hostname; host->db = 0; host->hostname = mystrdup(hostname); host->stat = STATUNTRIED; host->ts = 0; /* * Ad-hoc parsing code. Expect "unix:pathname" or "inet:host:port", where * both "inet:" and ":port" are optional. */ if (strncmp(d, "unix:", 5) == 0 || strncmp(d, "inet:", 5) == 0) d += 5; host->name = mystrdup(d); host->port = split_at_right(host->name, ':'); /* This is how PgSQL distinguishes between UNIX and INET: */ if (host->name[0] && host->name[0] != '/') host->type = TYPEINET; else host->type = TYPEUNIX; if (msg_verbose > 1) msg_info("%s: host=%s, port=%s, type=%s", myname, host->name, host->port ? host->port : "", host->type == TYPEUNIX ? "unix" : "inet"); return host; }
const char *host_port(char *buf, char **host, char *def_host, char **port, char *def_service) { char *cp = buf; /* * [host]:port, [host]:, [host]. */ if (*cp == '[') { *host = ++cp; if ((cp = split_at(cp, ']')) == 0) return ("missing \"]\""); if (*cp && *cp++ != ':') return ("garbage after \"]\""); *port = *cp ? cp : def_service; } /* * host:port, host:, host, :port, port. */ else { if ((cp = split_at_right(buf, ':')) != 0) { *host = *buf ? buf : def_host; *port = *cp ? cp : def_service; } else { *host = def_host ? def_host : (*buf ? buf : 0); *port = def_service ? def_service : (*buf ? buf : 0); } } if (*host == 0) return ("missing host information"); if (*port == 0) return ("missing service information"); /* * Final sanity checks. We're still sloppy, allowing bare numerical * network addresses instead of requiring proper [ipaddress] forms. */ if (*host != def_host && !valid_hostname(*host, DONT_GRIPE) && !valid_hostaddr(*host, DONT_GRIPE)) return ("valid hostname or network address required"); if (*port != def_service && ISDIGIT(**port) && !alldig(*port)) return ("garbage after numerical service"); return (0); }
/* host_init - initialize HOST structure */ static HOST *host_init(const char *hostname) { const char *myname = "mysql host_init"; HOST *host = (HOST *) mymalloc(sizeof(HOST)); const char *d = hostname; char *s; host->db = 0; host->hostname = mystrdup(hostname); host->port = 0; host->stat = STATUNTRIED; host->ts = 0; /* * Ad-hoc parsing code. Expect "unix:pathname" or "inet:host:port", where * both "inet:" and ":port" are optional. */ if (strncmp(d, "unix:", 5) == 0) { d += 5; host->type = TYPEUNIX; } else { if (strncmp(d, "inet:", 5) == 0) d += 5; host->type = TYPEINET; } host->name = mystrdup(d); if ((s = split_at_right(host->name, ':')) != 0) host->port = ntohs(find_inet_port(s, "tcp")); if (strcasecmp(host->name, "localhost") == 0) { /* The MySQL way: this will actually connect over the UNIX socket */ myfree(host->name); host->name = 0; host->type = TYPEUNIX; } if (msg_verbose > 1) msg_info("%s: host=%s, port=%d, type=%s", myname, host->name ? host->name : "localhost", host->port, host->type == TYPEUNIX ? "unix" : "inet"); return host; }
int deliver_recipient(LOCAL_STATE state, USER_ATTR usr_attr) { const char *myname = "deliver_recipient"; int rcpt_stat; /* * Make verbose logging easier to understand. */ state.level++; if (msg_verbose) MSG_LOG_STATE(myname, state); /* * Duplicate filter. */ if (been_here(state.dup_filter, "recipient %d %s", state.level, state.msg_attr.rcpt.address)) return (0); /* * With each level of recursion, detect and break external message * forwarding loops. * * If the looping recipient address has an owner- alias, send the error * report there instead. * * XXX A delivery agent cannot change the envelope sender address for * bouncing. As a workaround we use a one-recipient bounce procedure. * * The proper fix would be to record in the bounce logfile an error return * address for each individual recipient. This would also eliminate the * need for VERP specific bouncing code, at the cost of complicating the * normal bounce sending procedure, but would simplify the code below. */ if (delivered_hdr_find(state.loop_info, state.msg_attr.rcpt.address)) { dsb_simple(state.msg_attr.why, "5.4.6", "mail forwarding loop for %s", state.msg_attr.rcpt.address); /* Account for possible owner- sender address override. */ return (bounce_workaround(state)); } /* * Set up the recipient-specific attributes. If this is forwarded mail, * leave the delivered attribute alone, so that the forwarded message * will show the correct forwarding recipient. */ if (state.msg_attr.delivered == 0) state.msg_attr.delivered = state.msg_attr.rcpt.address; state.msg_attr.local = mystrdup(state.msg_attr.rcpt.address); lowercase(state.msg_attr.local); if ((state.msg_attr.domain = split_at_right(state.msg_attr.local, '@')) == 0) msg_warn("no @ in recipient address: %s", state.msg_attr.local); /* * Address extension management. */ state.msg_attr.user = mystrdup(state.msg_attr.local); if (*var_rcpt_delim) { state.msg_attr.extension = split_addr(state.msg_attr.user, *var_rcpt_delim); if (state.msg_attr.extension && strchr(state.msg_attr.extension, '/')) { msg_warn("%s: address with illegal extension: %s", state.msg_attr.queue_id, state.msg_attr.local); state.msg_attr.extension = 0; } } else state.msg_attr.extension = 0; state.msg_attr.unmatched = state.msg_attr.extension; /* * Do not allow null usernames. */ if (state.msg_attr.user[0] == 0) { dsb_simple(state.msg_attr.why, "5.1.3", "null username in \"%s\"", state.msg_attr.rcpt.address); return (bounce_append(BOUNCE_FLAGS(state.request), BOUNCE_ATTR(state.msg_attr))); } /* * Run the recipient through the delivery switch. */ if (msg_verbose) deliver_attr_dump(&state.msg_attr); rcpt_stat = deliver_switch(state, usr_attr); /* * Clean up. */ myfree(state.msg_attr.local); myfree(state.msg_attr.user); return (rcpt_stat); }
DICT *dict_sockmap_open(const char *mapname, int open_flags, int dict_flags) { DICT_SOCKMAP *dp; char *saved_name = 0; char *sockmap; DICT_SOCKMAP_REFC_HANDLE *ref_handle; HTABLE_INFO *client_info; /* * Let the optimizer worry about eliminating redundant code. */ #define DICT_SOCKMAP_OPEN_RETURN(d) { \ DICT *__d = (d); \ if (saved_name != 0) \ myfree(saved_name); \ return (__d); \ } while (0) /* * Sanity checks. */ if (open_flags != O_RDONLY) DICT_SOCKMAP_OPEN_RETURN(dict_surrogate(DICT_TYPE_SOCKMAP, mapname, open_flags, dict_flags, "%s:%s map requires O_RDONLY access mode", DICT_TYPE_SOCKMAP, mapname)); if (dict_flags & DICT_FLAG_NO_UNAUTH) DICT_SOCKMAP_OPEN_RETURN(dict_surrogate(DICT_TYPE_SOCKMAP, mapname, open_flags, dict_flags, "%s:%s map is not allowed for security-sensitive data", DICT_TYPE_SOCKMAP, mapname)); /* * Separate the socketmap name from the socketmap server name. */ saved_name = mystrdup(mapname); if ((sockmap = split_at_right(saved_name, ':')) == 0) DICT_SOCKMAP_OPEN_RETURN(dict_surrogate(DICT_TYPE_SOCKMAP, mapname, open_flags, dict_flags, "%s requires server:socketmap argument", DICT_TYPE_SOCKMAP)); /* * Use one reference-counted client handle for all socketmaps with the * same inet:host:port or unix:pathname information. * * XXX Todo: graceful degradation after endpoint syntax error. */ if (dict_sockmap_handles == 0) dict_sockmap_handles = htable_create(1); if ((client_info = htable_locate(dict_sockmap_handles, saved_name)) == 0) { ref_handle = (DICT_SOCKMAP_REFC_HANDLE *) mymalloc(sizeof(*ref_handle)); client_info = htable_enter(dict_sockmap_handles, saved_name, (char *) ref_handle); /* XXX Late initialization, so we can reuse macros for consistency. */ DICT_SOCKMAP_RH_REFCOUNT(client_info) = 1; DICT_SOCKMAP_RH_HANDLE(client_info) = auto_clnt_create(saved_name, dict_sockmap_timeout, dict_sockmap_max_idle, dict_sockmap_max_ttl); } else DICT_SOCKMAP_RH_REFCOUNT(client_info) += 1; /* * Instantiate a socket map handle. */ dp = (DICT_SOCKMAP *) dict_alloc(DICT_TYPE_SOCKMAP, mapname, sizeof(*dp)); dp->rdwr_buf = vstring_alloc(100); dp->sockmap_name = mystrdup(sockmap); dp->client_info = client_info; dp->dict.lookup = dict_sockmap_lookup; dp->dict.close = dict_sockmap_close; /* Don't look up parent domains or network superblocks. */ dp->dict.flags = dict_flags | DICT_FLAG_PATTERN; DICT_SOCKMAP_OPEN_RETURN(DICT_DEBUG (&dp->dict)); }
static ARGV *expand_argv(const char *service, char **argv, RECIPIENT_LIST *rcpt_list, int flags) { VSTRING *buf = vstring_alloc(100); ARGV *result; char **cpp; PIPE_STATE state; int i; char *ext; char *dom; /* * This appears to be simple operation (replace $name by its expansion). * However, it becomes complex because a command-line argument that * references $recipient must expand to as many command-line arguments as * there are recipients (that's wat programs called by sendmail expect). * So we parse each command-line argument, and depending on what we find, * we either expand the argument just once, or we expand it once for each * recipient. In either case we end up parsing the command-line argument * twice. The amount of CPU time wasted will be negligible. * * Note: we can't use recursive macro expansion here, because recursion * would screw up mail addresses that contain $ characters. */ #define NO 0 #define EARLY_RETURN(x) { argv_free(result); vstring_free(buf); return (x); } result = argv_alloc(1); for (cpp = argv; *cpp; cpp++) { state.service = service; state.expand_flag = 0; if (mac_parse(*cpp, parse_callback, (char *) &state) & MAC_PARSE_ERROR) EARLY_RETURN(0); if (state.expand_flag == 0) { /* no $recipient etc. */ argv_add(result, dict_eval(PIPE_DICT_TABLE, *cpp, NO), ARGV_END); } else { /* contains $recipient etc. */ for (i = 0; i < rcpt_list->len; i++) { /* * This argument contains $recipient. */ if (state.expand_flag & PIPE_FLAG_RCPT) { morph_recipient(buf, rcpt_list->info[i].address, flags); dict_update(PIPE_DICT_TABLE, PIPE_DICT_RCPT, STR(buf)); } /* * This argument contains $original_recipient. */ if (state.expand_flag & PIPE_FLAG_ORIG_RCPT) { morph_recipient(buf, rcpt_list->info[i].orig_addr, flags); dict_update(PIPE_DICT_TABLE, PIPE_DICT_ORIG_RCPT, STR(buf)); } /* * This argument contains $user. Extract the plain user name. * Either anything to the left of the extension delimiter or, * in absence of the latter, anything to the left of the * rightmost @. * * Beware: if the user name is blank (e.g. +user@host), the * argument is suppressed. This is necessary to allow for * cyrus bulletin-board (global mailbox) delivery. XXX But, * skipping empty user parts will also prevent other * expansions of this specific command-line argument. */ if (state.expand_flag & PIPE_FLAG_USER) { morph_recipient(buf, rcpt_list->info[i].address, flags & PIPE_OPT_FOLD_ALL); if (split_at_right(STR(buf), '@') == 0) msg_warn("no @ in recipient address: %s", rcpt_list->info[i].address); if (*var_rcpt_delim) split_addr(STR(buf), var_rcpt_delim); if (*STR(buf) == 0) continue; dict_update(PIPE_DICT_TABLE, PIPE_DICT_USER, STR(buf)); } /* * This argument contains $extension. Extract the recipient * extension: anything between the leftmost extension * delimiter and the rightmost @. The extension may be blank. */ if (state.expand_flag & PIPE_FLAG_EXTENSION) { morph_recipient(buf, rcpt_list->info[i].address, flags & PIPE_OPT_FOLD_ALL); if (split_at_right(STR(buf), '@') == 0) msg_warn("no @ in recipient address: %s", rcpt_list->info[i].address); if (*var_rcpt_delim == 0 || (ext = split_addr(STR(buf), var_rcpt_delim)) == 0) ext = ""; /* insert null arg */ dict_update(PIPE_DICT_TABLE, PIPE_DICT_EXTENSION, ext); } /* * This argument contains $mailbox. Extract the mailbox name: * anything to the left of the rightmost @. */ if (state.expand_flag & PIPE_FLAG_MAILBOX) { morph_recipient(buf, rcpt_list->info[i].address, flags & PIPE_OPT_FOLD_ALL); if (split_at_right(STR(buf), '@') == 0) msg_warn("no @ in recipient address: %s", rcpt_list->info[i].address); dict_update(PIPE_DICT_TABLE, PIPE_DICT_MAILBOX, STR(buf)); } /* * This argument contains $domain. Extract the domain name: * anything to the right of the rightmost @. */ if (state.expand_flag & PIPE_FLAG_DOMAIN) { morph_recipient(buf, rcpt_list->info[i].address, flags & PIPE_OPT_FOLD_ALL); dom = split_at_right(STR(buf), '@'); if (dom == 0) { msg_warn("no @ in recipient address: %s", rcpt_list->info[i].address); dom = ""; /* insert null arg */ } dict_update(PIPE_DICT_TABLE, PIPE_DICT_DOMAIN, dom); } /* * Done. */ argv_add(result, dict_eval(PIPE_DICT_TABLE, *cpp, NO), ARGV_END); } } } argv_terminate(result); vstring_free(buf); return (result); }