static DICT_CIDR_ENTRY *dict_cidr_parse_rule(char *p, VSTRING *why) { DICT_CIDR_ENTRY *rule; char *pattern; char *value; CIDR_MATCH cidr_info; MAI_HOSTADDR_STR hostaddr; /* * Split the rule into key and value. We already eliminated leading * whitespace, comments, empty lines or lines with whitespace only. This * means a null key can't happen but we will handle this anyway. */ pattern = p; while (*p && !ISSPACE(*p)) /* Skip over key */ p++; if (*p) /* Terminate key */ *p++ = 0; while (*p && ISSPACE(*p)) /* Skip whitespace */ p++; value = p; trimblanks(value, 0)[0] = 0; /* Trim trailing blanks */ if (*pattern == 0) { vstring_sprintf(why, "no address pattern"); return (0); } if (*value == 0) { vstring_sprintf(why, "no lookup result"); return (0); } /* * Parse the pattern, destroying it in the process. */ if (cidr_match_parse(&cidr_info, pattern, why) != 0) return (0); /* * Bundle up the result. */ rule = (DICT_CIDR_ENTRY *) mymalloc(sizeof(DICT_CIDR_ENTRY)); rule->cidr_info = cidr_info; rule->value = mystrdup(value); if (msg_verbose) { if (inet_ntop(cidr_info.addr_family, cidr_info.net_bytes, hostaddr.buf, sizeof(hostaddr.buf)) == 0) msg_fatal("inet_ntop: %m"); msg_info("dict_cidr_open: add %s/%d %s", hostaddr.buf, cidr_info.mask_shift, rule->value); } return (rule); }
const HEADER_OPTS *header_opts_find(const char *string) { const char *cp; if (header_hash == 0) header_opts_init(); /* * Look up the lower-cased version of the header name. */ VSTRING_RESET(header_key); for (cp = string; *cp != ':'; cp++) { if (*cp == 0) msg_panic("header_opts_find: no colon in header: %.30s", string); VSTRING_ADDCH(header_key, TOLOWER(*cp)); } vstring_truncate(header_key, trimblanks(vstring_str(header_key), cp - string) - vstring_str(header_key)); VSTRING_TERMINATE(header_key); return ((const HEADER_OPTS *) htable_find(header_hash, vstring_str(header_key))); }
DICT *dict_thash_open(const char *path, int open_flags, int dict_flags) { DICT_THASH *dict_thash; VSTREAM *fp = 0; struct stat st; time_t before; time_t after; VSTRING *line_buffer = 0; int lineno; char *key; char *value; HTABLE *table; HTABLE_INFO *ht; /* * Let the optimizer worry about eliminating redundant code. */ #define DICT_THASH_OPEN_RETURN(d) { \ DICT *__d = (d); \ if (fp != 0) \ vstream_fclose(fp); \ if (line_buffer != 0) \ vstring_free(line_buffer); \ return (__d); \ } while (0) /* * Sanity checks. */ if (open_flags != O_RDONLY) DICT_THASH_OPEN_RETURN(dict_surrogate(DICT_TYPE_THASH, path, open_flags, dict_flags, "%s:%s map requires O_RDONLY access mode", DICT_TYPE_THASH, path)); /* * Read the flat text file into in-memory hash. Read the file again if it * may have changed while we were reading. */ for (before = time((time_t *) 0); /* see below */ ; before = after) { if ((fp = vstream_fopen(path, open_flags, 0644)) == 0) { DICT_THASH_OPEN_RETURN(dict_surrogate(DICT_TYPE_THASH, path, open_flags, dict_flags, "open database %s: %m", path)); } if (line_buffer == 0) line_buffer = vstring_alloc(100); lineno = 0; table = htable_create(13); while (readlline(line_buffer, fp, &lineno)) { /* * Split on the first whitespace character, then trim leading and * trailing whitespace from key and value. */ key = STR(line_buffer); value = key + strcspn(key, " \t\r\n"); if (*value) *value++ = 0; while (ISSPACE(*value)) value++; trimblanks(key, 0)[0] = 0; trimblanks(value, 0)[0] = 0; /* * Enforce the "key whitespace value" format. Disallow missing * keys or missing values. */ if (*key == 0 || *value == 0) { msg_warn("%s, line %d: expected format: key whitespace value" " -- ignoring this line", path, lineno); continue; } if (key[strlen(key) - 1] == ':') msg_warn("%s, line %d: record is in \"key: value\" format;" " is this an alias file?", path, lineno); /* * Optionally fold the key. */ if (dict_flags & DICT_FLAG_FOLD_FIX) lowercase(key); /* * Store the value under the key. Handle duplicates * appropriately. */ if ((ht = htable_locate(table, key)) != 0) { if (dict_flags & DICT_FLAG_DUP_IGNORE) { /* void */ ; } else if (dict_flags & DICT_FLAG_DUP_REPLACE) { myfree(ht->value); ht->value = mystrdup(value); } else if (dict_flags & DICT_FLAG_DUP_WARN) { msg_warn("%s, line %d: duplicate entry: \"%s\"", path, lineno, key); } else { msg_fatal("%s, line %d: duplicate entry: \"%s\"", path, lineno, key); } } else { htable_enter(table, key, mystrdup(value)); } } /* * See if the source file is hot. */ if (fstat(vstream_fileno(fp), &st) < 0) msg_fatal("fstat %s: %m", path); if (vstream_fclose(fp)) msg_fatal("read %s: %m", path); fp = 0; /* DICT_THASH_OPEN_RETURN() */ after = time((time_t *) 0); if (st.st_mtime < before - 1 || st.st_mtime > after) break; /* * Yes, it is hot. Discard the result and read the file again. */ htable_free(table, myfree); if (msg_verbose > 1) msg_info("pausing to let file %s cool down", path); doze(300000); } /* * Create the in-memory table. */ dict_thash = (DICT_THASH *) dict_alloc(DICT_TYPE_THASH, path, sizeof(*dict_thash)); dict_thash->dict.lookup = dict_thash_lookup; dict_thash->dict.sequence = dict_thash_sequence; dict_thash->dict.close = dict_thash_close; dict_thash->dict.flags = dict_flags | DICT_FLAG_DUP_WARN | DICT_FLAG_FIXED; if (dict_flags & DICT_FLAG_FOLD_FIX) dict_thash->dict.fold_buf = vstring_alloc(10); dict_thash->info = 0; dict_thash->table = table; dict_thash->dict.owner.uid = st.st_uid; dict_thash->dict.owner.status = (st.st_uid != 0); DICT_THASH_OPEN_RETURN(DICT_DEBUG (&dict_thash->dict)); }
/* ARGSUSED */ int main(int argc, char **argv) { #ifdef LTD_STATS printf("mergescores: this program cannot be used with LTD_STATS\n"); exit(1); #else int fd; int i; char buf[MAXBUFFER]; float fplanets, farmsbomb, fkills, flosses; float temp; int delta; int fcount = 0; int dupcount = 0; int foldcount = 0; char namebuf[NAME_LEN+1]; if (argc>1) usage(); getpath(); fprintf(stderr, "Warning: If you do not know how to use this program, break it now!\n"); lstatus=(struct status *) malloc(sizeof(struct status)); fstatus=(struct status *) malloc(sizeof(struct status)); scanf("%10ld %10d %10d %10d %10d %10lf\n", (long int *) &fstatus->time, &fstatus->planets, &fstatus->armsbomb, &fstatus->kills, &fstatus->losses, &fstatus->timeprod); fd = open(Global, O_RDONLY); if (fd < 0) { fprintf(stderr, "Cannot open the global file!\n"); exit(0); } read(fd, (char *) lstatus, sizeof(struct status)); close(fd); fprintf(stderr, " Ticks Planets Armsbomb Kills Losses Timeprod\n"); fprintf(stderr, "Loc: %10ld %10d %10d %10d %10d %10lf\n", (long int) lstatus->time, lstatus->planets, lstatus->armsbomb, lstatus->kills, lstatus->losses, lstatus->timeprod); fprintf(stderr, "For: %10ld %10d %10d %10d %10d %10lf\n", (long int) fstatus->time, fstatus->planets, fstatus->armsbomb, fstatus->kills, fstatus->losses, fstatus->timeprod); /* want factors such that (For. rating)*factor == (Local rating) * (to scale t-mode kills). * foreign->timeprod * local->kills * therefore, factor is local/foreign == ----------------------------------- * foreign->kills * local->timeprod * (for defense, use reciprocal) */ fplanets = (((float) fstatus->timeprod)*((float) lstatus->planets))/ (((float) fstatus->planets)*((float) lstatus->timeprod)); farmsbomb = (((float) fstatus->timeprod)*((float) lstatus->armsbomb))/ (((float) fstatus->armsbomb)*((float) lstatus->timeprod)); fkills = (((float) fstatus->timeprod)*((float) lstatus->kills))/ (((float) fstatus->kills)*((float) lstatus->timeprod)); flosses = (((float) fstatus->losses)*((float) lstatus->timeprod))/ (((float) fstatus->timeprod)*((float) lstatus->losses)); fprintf(stderr, "Convert Factors:%10.3f %10.3f %10.3f %10.3f\n", fplanets, farmsbomb, fkills, flosses); fprintf(stderr, "(foreign rating * factor) = local rating\n\n"); read_lplayers(); fd = open(PlayerFile, O_WRONLY|O_CREAT|O_TRUNC, 0600); if (fd < 0) { perror(PlayerFile); exit(1); } fprintf(stderr, "Processing foreign players...\n"); while (fgets(buf, MAXBUFFER, stdin)) { if (strlen(buf) > 0) buf[strlen(buf)-1] = '\0'; trimblanks2(buf+16); trimblanks(buf+33); trimblanks(buf+129); strcpy(play_entry.name, buf); fcount++; strcpy(play_entry.password, buf+17); strcpy(play_entry.stats.st_keymap, buf+34); sscanf(buf+130, " %d %lf %d %d %d %d %d %d %d %d %d %d %d %d %d %lf %ld %d\n", &play_entry.stats.st_rank, &play_entry.stats.st_maxkills, &play_entry.stats.st_kills, &play_entry.stats.st_losses, &play_entry.stats.st_armsbomb, &play_entry.stats.st_planets, &play_entry.stats.st_ticks, &play_entry.stats.st_tkills, &play_entry.stats.st_tlosses, &play_entry.stats.st_tarmsbomb, &play_entry.stats.st_tplanets, &play_entry.stats.st_tticks, &play_entry.stats.st_sbkills, &play_entry.stats.st_sblosses, &play_entry.stats.st_sbticks, &play_entry.stats.st_sbmaxkills, &play_entry.stats.st_lastlogin, &play_entry.stats.st_flags); temp = play_entry.stats.st_tplanets * fplanets; delta = temp - play_entry.stats.st_tplanets; /* increase/decrease */ play_entry.stats.st_tplanets = temp; play_entry.stats.st_planets -= delta; if (play_entry.stats.st_planets < 0) play_entry.stats.st_planets = 0; temp = play_entry.stats.st_tarmsbomb * farmsbomb; delta = temp - play_entry.stats.st_tarmsbomb; /* increase/decrease */ play_entry.stats.st_tarmsbomb = temp; play_entry.stats.st_armsbomb -= delta; if (play_entry.stats.st_armsbomb < 0) play_entry.stats.st_armsbomb = 0; temp = play_entry.stats.st_tkills * fkills; delta = temp - play_entry.stats.st_tkills; /* increase/decrease */ play_entry.stats.st_tkills = temp; play_entry.stats.st_kills -= delta; if (play_entry.stats.st_kills < 0) play_entry.stats.st_kills = 0; temp = play_entry.stats.st_tlosses * flosses; delta = temp - play_entry.stats.st_tlosses; /* increase/decrease */ play_entry.stats.st_tlosses = temp; play_entry.stats.st_losses -= delta; if (play_entry.stats.st_losses < 0) play_entry.stats.st_losses = 0; play_entry.stats.st_lastlogin = time(NULL); i = dumbsearch(&play_entry); switch (i) { case DUP_NAME_DIFF_PASSWORD: dupcount++; strcpy(namebuf, play_entry.name); strcat(namebuf, "_"); printf("%-16.16s %-16.16s %-96.96s %1d %9.2lf %7d %7d %7d %7d %7d %7d %7d %7d %7d %7d %7d %7d %7d %9.2lf %9ld %7d\n", namebuf, play_entry.password, play_entry.stats.st_keymap, play_entry.stats.st_rank, play_entry.stats.st_maxkills, play_entry.stats.st_kills, play_entry.stats.st_losses, play_entry.stats.st_armsbomb, play_entry.stats.st_planets, play_entry.stats.st_ticks, play_entry.stats.st_tkills, play_entry.stats.st_tlosses, play_entry.stats.st_tarmsbomb, play_entry.stats.st_tplanets, play_entry.stats.st_tticks, play_entry.stats.st_sbkills, play_entry.stats.st_sblosses, play_entry.stats.st_sbticks, play_entry.stats.st_sbmaxkills, play_entry.stats.st_lastlogin, play_entry.stats.st_flags); break; case DIFF_NAME: /* append (actually, prepend) */ write(fd, (char *) &play_entry, sizeof(struct statentry)); break; default: /* fold stats together */ foldcount++; lplayers[i]->stats.st_maxkills = MAX(lplayers[i]->stats.st_maxkills, play_entry.stats.st_maxkills); lplayers[i]->stats.st_kills +=play_entry.stats.st_kills; lplayers[i]->stats.st_losses += play_entry.stats.st_losses; lplayers[i]->stats.st_armsbomb += play_entry.stats.st_armsbomb; lplayers[i]->stats.st_planets += play_entry.stats.st_planets; lplayers[i]->stats.st_ticks += play_entry.stats.st_ticks; lplayers[i]->stats.st_tkills += play_entry.stats.st_tkills; lplayers[i]->stats.st_tlosses += play_entry.stats.st_tlosses; lplayers[i]->stats.st_tarmsbomb += play_entry.stats.st_tarmsbomb; lplayers[i]->stats.st_tplanets += play_entry.stats.st_tplanets; lplayers[i]->stats.st_tticks += play_entry.stats.st_tticks; lplayers[i]->stats.st_sbkills += play_entry.stats.st_sbkills; lplayers[i]->stats.st_sblosses += play_entry.stats.st_sblosses; lplayers[i]->stats.st_sbticks += play_entry.stats.st_sbticks; lplayers[i]->stats.st_lastlogin = play_entry.stats.st_lastlogin; lplayers[i]->stats.st_sbmaxkills = MAX(lplayers[i]->stats.st_sbmaxkills, play_entry.stats.st_sbmaxkills); lplayers[i]->stats.st_rank = MAX(lplayers[i]->stats.st_rank, play_entry.stats.st_rank); break; } /* end switch */ } /* end while */ fprintf(stderr, "Rewriting local/folded players...\n"); for (i = 0; i < lplayercount; i++) { write(fd, (char *) lplayers[i], sizeof(struct statentry)); } fprintf(stderr, "Done! %d entries appended/folded (%d folded).\n", fcount - dupcount , foldcount); fprintf(stderr, "%d entries not appended, written to stdout.\n", dupcount); close(fd); #endif /* LTD_STATS */ return 1; /* satify lint */ }
DICT *dict_pcre_open(const char *mapname, int open_flags, int dict_flags) { DICT_PCRE *dict_pcre; VSTREAM *map_fp; struct stat st; VSTRING *line_buffer; DICT_PCRE_RULE *last_rule = 0; DICT_PCRE_RULE *rule; int lineno = 0; int nesting = 0; char *p; /* * Sanity checks. */ if (open_flags != O_RDONLY) return (dict_surrogate(DICT_TYPE_PCRE, mapname, open_flags, dict_flags, "%s:%s map requires O_RDONLY access mode", DICT_TYPE_PCRE, mapname)); /* * Open the configuration file. */ if ((map_fp = vstream_fopen(mapname, O_RDONLY, 0)) == 0) return (dict_surrogate(DICT_TYPE_PCRE, mapname, open_flags, dict_flags, "open %s: %m", mapname)); if (fstat(vstream_fileno(map_fp), &st) < 0) msg_fatal("fstat %s: %m", mapname); line_buffer = vstring_alloc(100); dict_pcre = (DICT_PCRE *) dict_alloc(DICT_TYPE_PCRE, mapname, sizeof(*dict_pcre)); dict_pcre->dict.lookup = dict_pcre_lookup; dict_pcre->dict.close = dict_pcre_close; dict_pcre->dict.flags = dict_flags | DICT_FLAG_PATTERN; if (dict_flags & DICT_FLAG_FOLD_MUL) dict_pcre->dict.fold_buf = vstring_alloc(10); dict_pcre->head = 0; dict_pcre->expansion_buf = 0; if (dict_pcre_init == 0) { pcre_malloc = (void *(*) (size_t)) mymalloc; pcre_free = (void (*) (void *)) myfree; dict_pcre_init = 1; } dict_pcre->dict.owner.uid = st.st_uid; dict_pcre->dict.owner.status = (st.st_uid != 0); /* * Parse the pcre table. */ while (readlline(line_buffer, map_fp, &lineno)) { p = vstring_str(line_buffer); trimblanks(p, 0)[0] = 0; /* Trim space at end */ if (*p == 0) continue; rule = dict_pcre_parse_rule(mapname, lineno, p, nesting, dict_flags); if (rule == 0) continue; if (rule->op == DICT_PCRE_OP_IF) { nesting++; } else if (rule->op == DICT_PCRE_OP_ENDIF) { nesting--; } if (last_rule == 0) dict_pcre->head = rule; else last_rule->next = rule; last_rule = rule; } if (nesting) msg_warn("pcre map %s, line %d: more IFs than ENDIFs", mapname, lineno); vstring_free(line_buffer); vstream_fclose(map_fp); return (DICT_DEBUG (&dict_pcre->dict)); }
static int eval_command_status(int command_status, char *service, DELIVER_REQUEST *request, PIPE_ATTR *attr, DSN_BUF *why) { RECIPIENT *rcpt; int status; int result = 0; int n; char *saved_text; /* * Depending on the result, bounce or defer the message, and mark the * recipient as done where appropriate. */ switch (command_status) { case PIPE_STAT_OK: /* Save the command output before dsb_update() clobbers it. */ vstring_truncate(why->reason, trimblanks(STR(why->reason), VSTRING_LEN(why->reason)) - STR(why->reason)); if (VSTRING_LEN(why->reason) > 0) { VSTRING_TERMINATE(why->reason); saved_text = vstring_export(vstring_sprintf( vstring_alloc(VSTRING_LEN(why->reason)), " (%.100s)", STR(why->reason))); } else saved_text = mystrdup(""); /* uses shared R/O storage */ dsb_update(why, "2.0.0", (attr->flags & PIPE_OPT_FINAL_DELIVERY) ? "delivered" : "relayed", DSB_SKIP_RMTA, DSB_SKIP_REPLY, "delivered via %s service%s", service, saved_text); myfree(saved_text); (void) DSN_FROM_DSN_BUF(why); for (n = 0; n < request->rcpt_list.len; n++) { rcpt = request->rcpt_list.info + n; status = sent(DEL_REQ_TRACE_FLAGS(request->flags), request->queue_id, &request->msg_stats, rcpt, service, &why->dsn); if (status == 0 && (request->flags & DEL_REQ_FLAG_SUCCESS)) deliver_completed(request->fp, rcpt->offset); result |= status; } break; case PIPE_STAT_BOUNCE: case PIPE_STAT_DEFER: (void) DSN_FROM_DSN_BUF(why); for (n = 0; n < request->rcpt_list.len; n++) { rcpt = request->rcpt_list.info + n; /* XXX Maybe encapsulate this with ndr_append(). */ status = (STR(why->status)[0] != '4' ? bounce_append : defer_append) (DEL_REQ_TRACE_FLAGS(request->flags), request->queue_id, &request->msg_stats, rcpt, service, &why->dsn); if (status == 0) deliver_completed(request->fp, rcpt->offset); result |= status; } break; case PIPE_STAT_CORRUPT: /* XXX DSN should we send something? */ result |= DEL_STAT_DEFER; break; default: msg_panic("eval_command_status: bad status %d", command_status); /* NOTREACHED */ } return (result); }
static void postmap(char *map_type, char *path_name, int postmap_flags, int open_flags, int dict_flags) { VSTREAM *NOCLOBBER source_fp; VSTRING *line_buffer; MKMAP *mkmap; int lineno; int last_line; char *key; char *value; struct stat st; mode_t saved_mask; /* * Initialize. */ line_buffer = vstring_alloc(100); if ((open_flags & O_TRUNC) == 0) { /* Incremental mode. */ source_fp = VSTREAM_IN; vstream_control(source_fp, CA_VSTREAM_CTL_PATH("stdin"), CA_VSTREAM_CTL_END); } else { /* Create database. */ if (strcmp(map_type, DICT_TYPE_PROXY) == 0) msg_fatal("can't create maps via the proxy service"); dict_flags |= DICT_FLAG_BULK_UPDATE; if ((source_fp = vstream_fopen(path_name, O_RDONLY, 0)) == 0) msg_fatal("open %s: %m", path_name); } if (fstat(vstream_fileno(source_fp), &st) < 0) msg_fatal("fstat %s: %m", path_name); /* * Turn off group/other read permissions as indicated in the source file. */ if ((postmap_flags & POSTMAP_FLAG_SAVE_PERM) && S_ISREG(st.st_mode)) saved_mask = umask(022 | (~st.st_mode & 077)); /* * If running as root, run as the owner of the source file, so that the * result shows proper ownership, and so that a bug in postmap does not * allow privilege escalation. */ if ((postmap_flags & POSTMAP_FLAG_AS_OWNER) && getuid() == 0 && (st.st_uid != geteuid() || st.st_gid != getegid())) set_eugid(st.st_uid, st.st_gid); /* * Open the database, optionally create it when it does not exist, * optionally truncate it when it does exist, and lock out any * spectators. */ mkmap = mkmap_open(map_type, path_name, open_flags, dict_flags); /* * And restore the umask, in case it matters. */ if ((postmap_flags & POSTMAP_FLAG_SAVE_PERM) && S_ISREG(st.st_mode)) umask(saved_mask); /* * Trap "exceptions" so that we can restart a bulk-mode update after a * recoverable error. */ for (;;) { if (dict_isjmp(mkmap->dict) != 0 && dict_setjmp(mkmap->dict) != 0 && vstream_fseek(source_fp, SEEK_SET, 0) < 0) msg_fatal("seek %s: %m", VSTREAM_PATH(source_fp)); /* * Add records to the database. */ last_line = 0; while (readllines(line_buffer, source_fp, &last_line, &lineno)) { /* * First some UTF-8 checks sans casefolding. */ if ((mkmap->dict->flags & DICT_FLAG_UTF8_ACTIVE) && !allascii(STR(line_buffer)) && !valid_utf8_string(STR(line_buffer), LEN(line_buffer))) { msg_warn("%s, line %d: non-UTF-8 input \"%s\"" " -- ignoring this line", VSTREAM_PATH(source_fp), lineno, STR(line_buffer)); continue; } /* * Split on the first whitespace character, then trim leading and * trailing whitespace from key and value. */ key = STR(line_buffer); value = key + strcspn(key, CHARS_SPACE); if (*value) *value++ = 0; while (ISSPACE(*value)) value++; trimblanks(key, 0)[0] = 0; trimblanks(value, 0)[0] = 0; /* * Enforce the "key whitespace value" format. Disallow missing * keys or missing values. */ if (*key == 0 || *value == 0) { msg_warn("%s, line %d: expected format: key whitespace value", VSTREAM_PATH(source_fp), lineno); continue; } if (key[strlen(key) - 1] == ':') msg_warn("%s, line %d: record is in \"key: value\" format; is this an alias file?", VSTREAM_PATH(source_fp), lineno); /* * Store the value under a case-insensitive key. */ mkmap_append(mkmap, key, value); if (mkmap->dict->error) msg_fatal("table %s:%s: write error: %m", mkmap->dict->type, mkmap->dict->name); } break; } /* * Close the mapping database, and release the lock. */ mkmap_close(mkmap); /* * Cleanup. We're about to terminate, but it is a good sanity check. */ vstring_free(line_buffer); if (source_fp != VSTREAM_IN) vstream_fclose(source_fp); }
void edit_parameters(int mode, int argc, char **argv) { char *path; EDIT_FILE *ep; VSTREAM *src; VSTREAM *dst; VSTRING *buf = vstring_alloc(100); VSTRING *key = vstring_alloc(10); char *cp; char *edit_key; char *edit_val; HTABLE *table; struct cvalue { char *value; int found; }; struct cvalue *cvalue; HTABLE_INFO **ht_info; HTABLE_INFO **ht; int interesting; const char *err; /* * Store command-line parameters for quick lookup. */ table = htable_create(argc); while ((cp = *argv++) != 0) { if (strchr(cp, '\n') != 0) msg_fatal("-e or -# accepts no multi-line input"); while (ISSPACE(*cp)) cp++; if (*cp == '#') msg_fatal("-e or -# accepts no comment input"); if (mode & EDIT_MAIN) { if ((err = split_nameval(cp, &edit_key, &edit_val)) != 0) msg_fatal("%s: \"%s\"", err, cp); } else if (mode & COMMENT_OUT) { if (*cp == 0) msg_fatal("-# requires non-blank parameter names"); if (strchr(cp, '=') != 0) msg_fatal("-# requires parameter names only"); edit_key = mystrdup(cp); trimblanks(edit_key, 0); edit_val = 0; } else { msg_panic("edit_parameters: unknown mode %d", mode); } cvalue = (struct cvalue *) mymalloc(sizeof(*cvalue)); cvalue->value = edit_val; cvalue->found = 0; htable_enter(table, edit_key, (char *) cvalue); } /* * Open a temp file for the result. This uses a deterministic name so we * don't leave behind thrash with random names. */ set_config_dir(); path = concatenate(var_config_dir, "/", MAIN_CONF_FILE, (char *) 0); if ((ep = edit_file_open(path, O_CREAT | O_WRONLY, 0644)) == 0) msg_fatal("open %s%s: %m", path, EDIT_FILE_SUFFIX); dst = ep->tmp_fp; /* * Open the original file for input. */ if ((src = vstream_fopen(path, O_RDONLY, 0)) == 0) { /* OK to delete, since we control the temp file name exclusively. */ (void) unlink(ep->tmp_path); msg_fatal("open %s for reading: %m", path); } /* * Copy original file to temp file, while replacing parameters on the * fly. Issue warnings for names found multiple times. */ #define STR(x) vstring_str(x) interesting = 0; while (vstring_get(buf, src) != VSTREAM_EOF) { for (cp = STR(buf); ISSPACE(*cp) /* including newline */ ; cp++) /* void */ ; /* Copy comment, all-whitespace, or empty line. */ if (*cp == '#' || *cp == 0) { vstream_fputs(STR(buf), dst); } /* Copy, skip or replace continued text. */ else if (cp > STR(buf)) { if (interesting == 0) vstream_fputs(STR(buf), dst); else if (mode & COMMENT_OUT) vstream_fprintf(dst, "#%s", STR(buf)); } /* Copy or replace start of logical line. */ else { vstring_strncpy(key, cp, strcspn(cp, " \t\r\n=")); cvalue = (struct cvalue *) htable_find(table, STR(key)); if ((interesting = !!cvalue) != 0) { if (cvalue->found++ == 1) msg_warn("%s: multiple entries for \"%s\"", path, STR(key)); if (mode & EDIT_MAIN) vstream_fprintf(dst, "%s = %s\n", STR(key), cvalue->value); else if (mode & COMMENT_OUT) vstream_fprintf(dst, "#%s", cp); else msg_panic("edit_parameters: unknown mode %d", mode); } else { vstream_fputs(STR(buf), dst); } } } /* * Generate new entries for parameters that were not found. */ if (mode & EDIT_MAIN) { for (ht_info = ht = htable_list(table); *ht; ht++) { cvalue = (struct cvalue *) ht[0]->value; if (cvalue->found == 0) vstream_fprintf(dst, "%s = %s\n", ht[0]->key, cvalue->value); } myfree((char *) ht_info); } /* * When all is well, rename the temp file to the original one. */ if (vstream_fclose(src)) msg_fatal("read %s: %m", path); if (edit_file_close(ep) != 0) msg_fatal("close %s%s: %m", path, EDIT_FILE_SUFFIX); /* * Cleanup. */ myfree(path); vstring_free(buf); vstring_free(key); htable_free(table, myfree); }
static void postmap(char *map_type, char *path_name, int postmap_flags, int open_flags, int dict_flags) { VSTREAM *source_fp; VSTRING *line_buffer; MKMAP *mkmap; int lineno; char *key; char *value; struct stat st; mode_t saved_mask; /* * Initialize. */ line_buffer = vstring_alloc(100); if ((open_flags & O_TRUNC) == 0) { source_fp = VSTREAM_IN; vstream_control(source_fp, VSTREAM_CTL_PATH, "stdin", VSTREAM_CTL_END); } else if (strcmp(map_type, DICT_TYPE_PROXY) == 0) { msg_fatal("can't create maps via the proxy service"); } else if ((source_fp = vstream_fopen(path_name, O_RDONLY, 0)) == 0) { msg_fatal("open %s: %m", path_name); } if (fstat(vstream_fileno(source_fp), &st) < 0) msg_fatal("fstat %s: %m", path_name); /* * Turn off group/other read permissions as indicated in the source file. */ if ((postmap_flags & POSTMAP_FLAG_SAVE_PERM) && S_ISREG(st.st_mode)) saved_mask = umask(022 | (~st.st_mode & 077)); /* * If running as root, run as the owner of the source file, so that the * result shows proper ownership, and so that a bug in postmap does not * allow privilege escalation. */ if ((postmap_flags & POSTMAP_FLAG_AS_OWNER) && getuid() == 0 && (st.st_uid != geteuid() || st.st_gid != getegid())) set_eugid(st.st_uid, st.st_gid); /* * Open the database, optionally create it when it does not exist, * optionally truncate it when it does exist, and lock out any * spectators. */ mkmap = mkmap_open(map_type, path_name, open_flags, dict_flags); /* * And restore the umask, in case it matters. */ if ((postmap_flags & POSTMAP_FLAG_SAVE_PERM) && S_ISREG(st.st_mode)) umask(saved_mask); /* * Add records to the database. */ lineno = 0; while (readlline(line_buffer, source_fp, &lineno)) { /* * Split on the first whitespace character, then trim leading and * trailing whitespace from key and value. */ key = STR(line_buffer); value = key + strcspn(key, " \t\r\n"); if (*value) *value++ = 0; while (ISSPACE(*value)) value++; trimblanks(key, 0)[0] = 0; trimblanks(value, 0)[0] = 0; /* * Enforce the "key whitespace value" format. Disallow missing keys * or missing values. */ if (*key == 0 || *value == 0) { msg_warn("%s, line %d: expected format: key whitespace value", VSTREAM_PATH(source_fp), lineno); continue; } if (key[strlen(key) - 1] == ':') msg_warn("%s, line %d: record is in \"key: value\" format; is this an alias file?", VSTREAM_PATH(source_fp), lineno); /* * Store the value under a case-insensitive key. */ mkmap_append(mkmap, key, value); if (mkmap->dict->error) msg_fatal("table %s:%s: write error: %m", mkmap->dict->type, mkmap->dict->name); } /* * Close the mapping database, and release the lock. */ mkmap_close(mkmap); /* * Cleanup. We're about to terminate, but it is a good sanity check. */ vstring_free(line_buffer); if (source_fp != VSTREAM_IN) vstream_fclose(source_fp); }
void pcf_edit_master(int mode, int argc, char **argv) { const char *myname = "pcf_edit_master"; char *path; EDIT_FILE *ep; VSTREAM *src; VSTREAM *dst; VSTRING *line_buf = vstring_alloc(100); VSTRING *parse_buf = vstring_alloc(100); int lineno; PCF_MASTER_ENT *new_entry; VSTRING *full_entry_buf = vstring_alloc(100); char *cp; char *pattern; int service_name_type_matched; const char *err; PCF_MASTER_EDIT_REQ *edit_reqs; PCF_MASTER_EDIT_REQ *req; int num_reqs = argc; const char *edit_opts = "-Me, -Fe, -Pe, -X, or -#"; char *service_name; char *service_type; /* * Sanity check. */ if (num_reqs <= 0) msg_panic("%s: empty argument list", myname); /* * Preprocessing: split pattern=value, then split the pattern components. */ edit_reqs = (PCF_MASTER_EDIT_REQ *) mymalloc(sizeof(*edit_reqs) * num_reqs); for (req = edit_reqs; *argv != 0; req++, argv++) { req->match_count = 0; req->raw_text = *argv; cp = req->parsed_text = mystrdup(req->raw_text); if (strchr(cp, '\n') != 0) msg_fatal("%s accept no multi-line input", edit_opts); while (ISSPACE(*cp)) cp++; if (*cp == '#') msg_fatal("%s accept no comment input", edit_opts); /* Separate the pattern from the value. */ if (mode & PCF_EDIT_CONF) { if ((err = split_nameval(cp, &pattern, &req->edit_value)) != 0) msg_fatal("%s: \"%s\"", err, req->raw_text); if ((mode & PCF_MASTER_PARAM) && req->edit_value[strcspn(req->edit_value, PCF_MASTER_BLANKS)]) msg_fatal("whitespace in parameter value: \"%s\"", req->raw_text); } else if (mode & (PCF_COMMENT_OUT | PCF_EDIT_EXCL)) { if (strchr(cp, '=') != 0) msg_fatal("-X or -# requires names without value"); pattern = cp; trimblanks(pattern, 0); req->edit_value = 0; } else { msg_panic("%s: unknown mode %d", myname, mode); } #define PCF_MASTER_MASK (PCF_MASTER_ENTRY | PCF_MASTER_FLD | PCF_MASTER_PARAM) /* * Split name/type or name/type/whatever pattern into components. */ switch (mode & PCF_MASTER_MASK) { case PCF_MASTER_ENTRY: if ((req->service_pattern = pcf_parse_service_pattern(pattern, 2, 2)) == 0) msg_fatal("-Me, -MX or -M# requires service_name/type"); break; case PCF_MASTER_FLD: if ((req->service_pattern = pcf_parse_service_pattern(pattern, 3, 3)) == 0) msg_fatal("-Fe or -FX requires service_name/type/field_name"); req->field_number = pcf_parse_field_pattern(req->service_pattern->argv[2]); if (pcf_is_magic_field_pattern(req->field_number)) msg_fatal("-Fe does not accept wild-card field name"); if ((mode & PCF_EDIT_CONF) && req->field_number < PCF_MASTER_FLD_CMD && req->edit_value[strcspn(req->edit_value, PCF_MASTER_BLANKS)]) msg_fatal("-Fe does not accept whitespace in non-command field"); break; case PCF_MASTER_PARAM: if ((req->service_pattern = pcf_parse_service_pattern(pattern, 3, 3)) == 0) msg_fatal("-Pe or -PX requires service_name/type/parameter"); req->param_pattern = req->service_pattern->argv[2]; if (PCF_IS_MAGIC_PARAM_PATTERN(req->param_pattern)) msg_fatal("-Pe does not accept wild-card parameter name"); if ((mode & PCF_EDIT_CONF) && req->edit_value[strcspn(req->edit_value, PCF_MASTER_BLANKS)]) msg_fatal("-Pe does not accept whitespace in parameter value"); break; default: msg_panic("%s: unknown edit mode %d", myname, mode); } } /* * Open a temp file for the result. This uses a deterministic name so we * don't leave behind thrash with random names. */ pcf_set_config_dir(); path = concatenate(var_config_dir, "/", MASTER_CONF_FILE, (char *) 0); if ((ep = edit_file_open(path, O_CREAT | O_WRONLY, 0644)) == 0) msg_fatal("open %s%s: %m", path, EDIT_FILE_SUFFIX); dst = ep->tmp_fp; /* * Open the original file for input. */ if ((src = vstream_fopen(path, O_RDONLY, 0)) == 0) { /* OK to delete, since we control the temp file name exclusively. */ (void) unlink(ep->tmp_path); msg_fatal("open %s for reading: %m", path); } /* * Copy original file to temp file, while replacing service entries on * the fly. */ service_name_type_matched = 0; new_entry = 0; lineno = 0; while ((cp = pcf_next_cf_line(parse_buf, src, dst, &lineno)) != 0) { vstring_strcpy(line_buf, STR(parse_buf)); /* * Copy, skip or replace continued text. */ if (cp > STR(parse_buf)) { if (service_name_type_matched == 0) vstream_fputs(STR(line_buf), dst); else if (mode & PCF_COMMENT_OUT) vstream_fprintf(dst, "#%s", STR(line_buf)); } /* * Copy or replace (start of) logical line. */ else { service_name_type_matched = 0; /* * Parse out the service name and type. */ if ((service_name = mystrtok(&cp, PCF_MASTER_BLANKS)) == 0 || (service_type = mystrtok(&cp, PCF_MASTER_BLANKS)) == 0) msg_fatal("file %s: line %d: specify service name and type " "on the same line", path, lineno); if (strchr(service_name, '=')) msg_fatal("file %s: line %d: service name syntax \"%s\" is " "unsupported with %s", path, lineno, service_name, edit_opts); if (service_type[strcspn(service_type, "=/")] != 0) msg_fatal("file %s: line %d: " "service type syntax \"%s\" is unsupported with %s", path, lineno, service_type, edit_opts); /* * Match each service pattern. */ for (req = edit_reqs; req < edit_reqs + num_reqs; req++) { if (PCF_MATCH_SERVICE_PATTERN(req->service_pattern, service_name, service_type)) { service_name_type_matched = 1; /* Sticky flag */ req->match_count += 1; /* * Generate replacement master.cf entries. */ if ((mode & PCF_EDIT_CONF) || ((mode & PCF_MASTER_PARAM) && (mode & PCF_EDIT_EXCL))) { switch (mode & PCF_MASTER_MASK) { /* * Replace master.cf entry field or parameter * value. */ case PCF_MASTER_FLD: case PCF_MASTER_PARAM: if (new_entry == 0) { /* Gobble up any continuation lines. */ pcf_gobble_cf_line(full_entry_buf, line_buf, src, dst, &lineno); new_entry = (PCF_MASTER_ENT *) mymalloc(sizeof(*new_entry)); if ((err = pcf_parse_master_entry(new_entry, STR(full_entry_buf))) != 0) msg_fatal("file %s: line %d: %s", path, lineno, err); } if (mode & PCF_MASTER_FLD) { pcf_edit_master_field(new_entry, req->field_number, req->edit_value); } else { pcf_edit_master_param(new_entry, mode, req->param_pattern, req->edit_value); } break; /* * Replace entire master.cf entry. */ case PCF_MASTER_ENTRY: if (new_entry != 0) pcf_free_master_entry(new_entry); new_entry = (PCF_MASTER_ENT *) mymalloc(sizeof(*new_entry)); if ((err = pcf_parse_master_entry(new_entry, req->edit_value)) != 0) msg_fatal("%s: \"%s\"", err, req->raw_text); break; default: msg_panic("%s: unknown edit mode %d", myname, mode); } } } } /* * Pass through or replace the current input line. */ if (new_entry) { pcf_print_master_entry(dst, PCF_FOLD_LINE, new_entry); pcf_free_master_entry(new_entry); new_entry = 0; } else if (service_name_type_matched == 0) { vstream_fputs(STR(line_buf), dst); } else if (mode & PCF_COMMENT_OUT) { vstream_fprintf(dst, "#%s", STR(line_buf)); } } } /* * Postprocessing: when editing entire service entries, generate new * entries for services not found. Otherwise (editing fields or * parameters), "service not found" is a fatal error. */ for (req = edit_reqs; req < edit_reqs + num_reqs; req++) { if (req->match_count == 0) { if ((mode & PCF_MASTER_ENTRY) && (mode & PCF_EDIT_CONF)) { new_entry = (PCF_MASTER_ENT *) mymalloc(sizeof(*new_entry)); if ((err = pcf_parse_master_entry(new_entry, req->edit_value)) != 0) msg_fatal("%s: \"%s\"", err, req->raw_text); pcf_print_master_entry(dst, PCF_FOLD_LINE, new_entry); pcf_free_master_entry(new_entry); } else if ((mode & PCF_MASTER_ENTRY) == 0) { msg_warn("unmatched service_name/type: \"%s\"", req->raw_text); } } } /* * When all is well, rename the temp file to the original one. */ if (vstream_fclose(src)) msg_fatal("read %s: %m", path); if (edit_file_close(ep) != 0) msg_fatal("close %s%s: %m", path, EDIT_FILE_SUFFIX); /* * Cleanup. */ myfree(path); vstring_free(line_buf); vstring_free(parse_buf); vstring_free(full_entry_buf); for (req = edit_reqs; req < edit_reqs + num_reqs; req++) { argv_free(req->service_pattern); myfree(req->parsed_text); } myfree((char *) edit_reqs); }
static DICT_CIDR_ENTRY *dict_cidr_parse_rule(const char *mapname, int lineno, char *p) { DICT_CIDR_ENTRY *rule; char *key; char *value; char *mask; int mask_shift; unsigned long net_bits; unsigned long mask_bits; struct in_addr net_addr; /* * Split the rule into key and value. We already eliminated leading * whitespace, comments, empty lines or lines with whitespace only. This * means a null key can't happen but we will handle this anyway. */ key = p; while (*p && !ISSPACE(*p)) /* Skip over key */ p++; if (*p) /* Terminate key */ *p++ = 0; while (*p && ISSPACE(*p)) /* Skip whitespace */ p++; value = p; trimblanks(value, 0)[0] = 0; /* Trim trailing blanks */ if (*key == 0) { msg_warn("cidr map %s, line %d: no address pattern: skipping this rule", mapname, lineno); return (0); } if (*value == 0) { msg_warn("cidr map %s, line %d: no lookup result: skipping this rule", mapname, lineno); return (0); } /* * Parse the key into network and mask, and destroy the key. Treat a bare * network address as /32. * * We need explicit code for /0. The result of << is undefined when the * shift is greater or equal to the number of bits in the shifted * operand. */ if ((mask = split_at(key, '/')) != 0) { if (!alldig(mask) || (mask_shift = atoi(mask)) > BITS_PER_ADDR || (net_bits = inet_addr(key)) == INADDR_NONE) { msg_warn("cidr map %s, line %d: bad net/mask pattern: \"%s/%s\": " "skipping this rule", mapname, lineno, key, mask); return (0); } mask_bits = mask_shift > 0 ? htonl((0xffffffff) << (BITS_PER_ADDR - mask_shift)) : 0; if (net_bits & ~mask_bits) { net_addr.s_addr = (net_bits & mask_bits); msg_warn("cidr map %s, line %d: net/mask pattern \"%s/%s\" with " "non-null host portion: skipping this rule", mapname, lineno, key, mask); msg_warn("specify \"%s/%d\" if this is really what you want", inet_ntoa(net_addr), mask_shift); return (0); } } else { if ((net_bits = inet_addr(key)) == INADDR_NONE) { msg_warn("cidr map %s, line %d: bad address pattern: \"%s\": " "skipping this rule", mapname, lineno, key); return (0); } mask_shift = 32; mask_bits = htonl(0xffffffff); } /* * Bundle up the result. */ rule = (DICT_CIDR_ENTRY *) mymalloc(sizeof(DICT_CIDR_ENTRY)); rule->net_bits = net_bits; rule->mask_bits = mask_bits; rule->value = mystrdup(value); rule->next = 0; if (msg_verbose) msg_info("dict_cidr_open: %s: %lu/%d %s", mapname, rule->net_bits, mask_shift, rule->value); return (rule); }
static DICT_CIDR_ENTRY *dict_cidr_parse_rule(char *p, int lineno, int nesting, VSTRING *why) { DICT_CIDR_ENTRY *rule; char *pattern; char *value; CIDR_MATCH cidr_info; MAI_HOSTADDR_STR hostaddr; int match = 1; /* * IF must be followed by a pattern. */ if (strncasecmp(p, "IF", 2) == 0 && !ISALNUM(p[2])) { p += 2; for (;;) { if (*p == '!') match = !match; else if (!ISSPACE(*p)) break; p++; } if (*p == 0) { vstring_sprintf(why, "no address pattern"); return (0); } trimblanks(p, 0)[0] = 0; /* Trim trailing blanks */ if (cidr_match_parse_if(&cidr_info, p, match, why) != 0) return (0); value = ""; } /* * ENDIF must not be followed by other text. */ else if (strncasecmp(p, "ENDIF", 5) == 0 && !ISALNUM(p[5])) { p += 5; while (*p && ISSPACE(*p)) /* Skip whitespace */ p++; if (*p != 0) { vstring_sprintf(why, "garbage after ENDIF"); return (0); } if (nesting == 0) { vstring_sprintf(why, "ENDIF without IF"); return (0); } cidr_match_endif(&cidr_info); value = ""; } /* * An address pattern. */ else { /* * Process negation operators. */ for (;;) { if (*p == '!') match = !match; else if (!ISSPACE(*p)) break; p++; } /* * Split the rule into key and value. We already eliminated leading * whitespace, comments, empty lines or lines with whitespace only. * This means a null key can't happen but we will handle this anyway. */ pattern = p; while (*p && !ISSPACE(*p)) /* Skip over key */ p++; if (*p) /* Terminate key */ *p++ = 0; while (*p && ISSPACE(*p)) /* Skip whitespace */ p++; value = p; trimblanks(value, 0)[0] = 0; /* Trim trailing blanks */ if (*pattern == 0) { vstring_sprintf(why, "no address pattern"); return (0); } /* * Parse the pattern, destroying it in the process. */ if (cidr_match_parse(&cidr_info, pattern, match, why) != 0) return (0); if (*value == 0) { vstring_sprintf(why, "no lookup result"); return (0); } } /* * Bundle up the result. */ rule = (DICT_CIDR_ENTRY *) mymalloc(sizeof(DICT_CIDR_ENTRY)); rule->cidr_info = cidr_info; rule->value = mystrdup(value); rule->lineno = lineno; if (msg_verbose) { if (inet_ntop(cidr_info.addr_family, cidr_info.net_bytes, hostaddr.buf, sizeof(hostaddr.buf)) == 0) msg_fatal("inet_ntop: %m"); msg_info("dict_cidr_open: add %s/%d %s", hostaddr.buf, cidr_info.mask_shift, rule->value); } return (rule); }