int testDAD_init_suite() { char *macdata[] = { "ec:ac:24:70:4c:f6", "EC:AC:24:70:4C:F7", "Ec:aC:24:70:4c:F8", "38:45:63:f6:8e:83", "f7:d5:9c:38:8f:db", "bf:de:9e:0e:6e:eb", "b6:f5:bf:ff:c2:32", "83:23:3d:f2:17:31", "26:3e:f2:4e:0d:ff", "d4:fc:84:dc:81:a9", NULL }; char *ipdata[] = { "ffe5:1838:afd7:2472:b3e7:3ae6:a228:12b4", "0E51:A030:C113:3838:C080:DD09:4D6C:189D", "9af2:2354:ecd1:f412:b9e3:648C:519D:DDDF", "69df:0c97:aaff:ef86:7cc0:ede5:2a7b:6cc0", "4605:da5a:9f0f:8a36:f63a:e40a:1614:0554", "6956:2caa:6abf:f6b2:3c57:a99c:d88d:ff7d", "4a7d:e4d7:54ea:48ed:8467:6b59:3670:a941", "1d24:c623:219a:0aa1:7628:51ce:870a:898f", "5e0f:1e4b:e619:d99f:dd65:caf1:12a5:01fc", "8693:39c0:36de:d326:cb63:a585:c63e:2f04", NULL }; char *ipstr = "5338:41ab:64f7:a598:39b6:e0f8:5a6e:5ec8"; IP_t ip; int i; time_t ts = 1234567890; ip_parse(&ip, ipstr); for (i = 0; i < EXAMPLE_LEN; i++) { host_set(&h[i], mac_parse(NULL, macdata[i]), ip_parse(NULL, ipdata[i]), ts++); } for (i = 0; i < EXAMPLE_LEN; i++) { host_set(&g[i], mac_parse(NULL, macdata[i]), &ip, ts++); } return 0; }
int mac_expand(VSTRING *result, const char *pattern, int flags, const char *filter, MAC_EXP_LOOKUP_FN lookup, char *context) { MAC_EXP mc; int status; /* * Bundle up the request and do the substitutions. */ mc.result = result; mc.flags = flags; mc.filter = filter; mc.lookup = lookup; mc.context = context; mc.status = 0; mc.level = 0; if ((flags & (MAC_EXP_FLAG_APPEND | MAC_EXP_FLAG_SCAN)) == 0) VSTRING_RESET(result); status = mac_parse(pattern, mac_expand_callback, (char *) &mc); if ((flags & MAC_EXP_FLAG_SCAN) == 0) VSTRING_TERMINATE(result); return (status); }
int main(int unused_argc, char **unused_argv) { VSTRING *buf = vstring_alloc(1); while (vstring_fgets_nonl(buf, VSTREAM_IN)) { mac_parse(vstring_str(buf), mac_parse_print, (char *) 0); vstream_fflush(VSTREAM_OUT); } vstring_free(buf); return (0); }
static int mac_expand_callback(int type, VSTRING *buf, char *ptr) { char *myname = "mac_expand_callback"; MAC_EXP *mc = (MAC_EXP *) ptr; int lookup_mode; const char *text; char *cp; int ch; int len; /* * Sanity check. */ if (mc->level++ > 100) { msg_warn("unreasonable macro call nesting: \"%s\"", vstring_str(buf)); mc->status |= MAC_PARSE_ERROR; } if (mc->status & MAC_PARSE_ERROR) return (mc->status); /* * $Name etc. reference. */ if (type == MAC_PARSE_VARNAME) { /* * Look for the ? or : delimiter. In case of a syntax error, return * without doing damage, and issue a warning instead. */ for (cp = vstring_str(buf); /* void */ ; cp++) { if ((ch = *cp) == 0) { lookup_mode = MAC_EXP_MODE_USE; break; } if (ch == '?' || ch == ':') { *cp++ = 0; lookup_mode = MAC_EXP_MODE_TEST; break; } if (!ISALNUM(ch) && ch != '_') { msg_warn("macro name syntax error: \"%s\"", vstring_str(buf)); mc->status |= MAC_PARSE_ERROR; return (mc->status); } } /* * Look up the named parameter. */ text = mc->lookup(vstring_str(buf), lookup_mode, mc->context); /* * Perform the requested substitution. */ switch (ch) { case '?': if (text != 0 && *text != 0) mac_parse(cp, mac_expand_callback, (char *) mc); break; case ':': if (text == 0 || *text == 0) mac_parse(cp, mac_expand_callback, (char *) mc); break; default: if (text == 0) { mc->status |= MAC_PARSE_UNDEF; } else if (*text == 0) { /* void */ ; } else if (mc->flags & MAC_EXP_FLAG_RECURSE) { mac_parse(text, mac_expand_callback, (char *) mc); } else { len = VSTRING_LEN(mc->result); vstring_strcat(mc->result, text); if (mc->filter) { cp = vstring_str(mc->result) + len; while (*(cp += strspn(cp, mc->filter))) *cp++ = '_'; } } break; } } /* * Literal text. */ else { text = vstring_str(buf); vstring_strcat(mc->result, text); } /* * Give the poor tester a clue of what is going on. */ if (msg_verbose) msg_info("%s: %s = %s", myname, vstring_str(buf), text ? text : "(undef)"); mc->level--; return (mc->status); }
static int mac_expand_callback(int type, VSTRING *buf, char *ptr) { MAC_EXP *mc = (MAC_EXP *) ptr; int lookup_mode; const char *text; char *cp; int ch; ssize_t len; /* * Sanity check. */ if (mc->level++ > 100) { msg_warn("unreasonable macro call nesting: \"%s\"", vstring_str(buf)); mc->status |= MAC_PARSE_ERROR; } if (mc->status & MAC_PARSE_ERROR) return (mc->status); /* * $Name etc. reference. * * In order to support expansion of lookup results, we must save the lookup * result. We use the input buffer since it will not be needed anymore. */ if (type == MAC_PARSE_EXPR) { /* * Look for the ? or : delimiter. In case of a syntax error, return * without doing damage, and issue a warning instead. */ for (cp = vstring_str(buf); /* void */ ; cp++) { if ((ch = *cp) == 0) { lookup_mode = MAC_EXP_MODE_USE; break; } if (ch == '?' || ch == ':') { *cp++ = 0; lookup_mode = MAC_EXP_MODE_TEST; break; } if (!ISALNUM(ch) && ch != '_') { msg_warn("macro name syntax error: \"%s\"", vstring_str(buf)); mc->status |= MAC_PARSE_ERROR; return (mc->status); } } /* * Look up the named parameter. */ text = mc->lookup(vstring_str(buf), lookup_mode, mc->context); /* * Perform the requested substitution. */ switch (ch) { case '?': if ((text != 0 && *text != 0) || (mc->flags & MAC_EXP_FLAG_SCAN)) mac_parse(cp, mac_expand_callback, (char *) mc); break; case ':': if (text == 0 || *text == 0 || (mc->flags & MAC_EXP_FLAG_SCAN)) mac_parse(cp, mac_expand_callback, (char *) mc); break; default: if (text == 0) { mc->status |= MAC_PARSE_UNDEF; } else if (*text == 0 || (mc->flags & MAC_EXP_FLAG_SCAN)) { /* void */ ; } else if (mc->flags & MAC_EXP_FLAG_RECURSE) { vstring_strcpy(buf, text); mac_parse(vstring_str(buf), mac_expand_callback, (char *) mc); } else { len = VSTRING_LEN(mc->result); vstring_strcat(mc->result, text); if (mc->filter) { cp = vstring_str(mc->result) + len; while (*(cp += strspn(cp, mc->filter))) *cp++ = '_'; } } break; } } /* * Literal text. */ else if ((mc->flags & MAC_EXP_FLAG_SCAN) == 0) { vstring_strcat(mc->result, vstring_str(buf)); } mc->level--; return (mc->status); }
static DICT_PCRE_RULE *dict_pcre_parse_rule(const char *mapname, int lineno, char *line, int nesting, int dict_flags) { char *p; int actual_sub; p = line; /* * An ordinary match rule takes one pattern and replacement text. */ if (!ISALNUM(*p)) { DICT_PCRE_REGEXP regexp; DICT_PCRE_ENGINE engine; DICT_PCRE_PRESCAN_CONTEXT prescan_context; DICT_PCRE_MATCH_RULE *match_rule; /* * Get the pattern string and options. */ if (dict_pcre_get_pattern(mapname, lineno, &p, ®exp) == 0) return (0); /* * Get the replacement text. */ while (*p && ISSPACE(*p)) ++p; if (!*p) msg_warn("%s, line %d: no replacement text: using empty string", mapname, lineno); /* * Sanity check the $number instances in the replacement text. */ prescan_context.mapname = mapname; prescan_context.lineno = lineno; prescan_context.max_sub = 0; prescan_context.literal = 0; /* * The optimizer will eliminate code duplication and/or dead code. */ #define CREATE_MATCHOP_ERROR_RETURN(rval) do { \ if (prescan_context.literal) \ myfree(prescan_context.literal); \ return (rval); \ } while (0) if (mac_parse(p, dict_pcre_prescan, (char *) &prescan_context) & MAC_PARSE_ERROR) { msg_warn("pcre map %s, line %d: bad replacement syntax: " "skipping this rule", mapname, lineno); CREATE_MATCHOP_ERROR_RETURN(0); } /* * Substring replacement not possible with negative regexps. */ if (prescan_context.max_sub > 0 && regexp.match == 0) { msg_warn("pcre map %s, line %d: $number found in negative match " "replacement text: skipping this rule", mapname, lineno); CREATE_MATCHOP_ERROR_RETURN(0); } if (prescan_context.max_sub > 0 && (dict_flags & DICT_FLAG_NO_REGSUB)) { msg_warn("pcre map %s, line %d: " "regular expression substitution is not allowed: " "skipping this rule", mapname, lineno); CREATE_MATCHOP_ERROR_RETURN(0); } /* * Compile the pattern. */ if (dict_pcre_compile(mapname, lineno, ®exp, &engine) == 0) CREATE_MATCHOP_ERROR_RETURN(0); #ifdef PCRE_INFO_CAPTURECOUNT if (pcre_fullinfo(engine.pattern, engine.hints, PCRE_INFO_CAPTURECOUNT, (void *) &actual_sub) != 0) msg_panic("pcre map %s, line %d: pcre_fullinfo failed", mapname, lineno); if (prescan_context.max_sub > actual_sub) { msg_warn("pcre map %s, line %d: out of range replacement index \"%d\": " "skipping this rule", mapname, lineno, (int) prescan_context.max_sub); if (engine.pattern) myfree((char *) engine.pattern); if (engine.hints) myfree((char *) engine.hints); CREATE_MATCHOP_ERROR_RETURN(0); } #endif /* * Save the result. */ match_rule = (DICT_PCRE_MATCH_RULE *) dict_pcre_rule_alloc(DICT_PCRE_OP_MATCH, nesting, lineno, sizeof(DICT_PCRE_MATCH_RULE)); match_rule->match = regexp.match; match_rule->max_sub = prescan_context.max_sub; if (prescan_context.literal) match_rule->replacement = prescan_context.literal; else match_rule->replacement = mystrdup(p); match_rule->pattern = engine.pattern; match_rule->hints = engine.hints; return ((DICT_PCRE_RULE *) match_rule); } /* * The IF operator takes one pattern but no replacement text. */ else if (strncasecmp(p, "IF", 2) == 0 && !ISALNUM(p[2])) { DICT_PCRE_REGEXP regexp; DICT_PCRE_ENGINE engine; DICT_PCRE_IF_RULE *if_rule; p += 2; /* * Get the pattern. */ while (*p && ISSPACE(*p)) p++; if (!dict_pcre_get_pattern(mapname, lineno, &p, ®exp)) return (0); /* * Warn about out-of-place text. */ while (*p && ISSPACE(*p)) ++p; if (*p) { msg_warn("pcre map %s, line %d: ignoring extra text after " "IF statement: \"%s\"", mapname, lineno, p); msg_warn("pcre map %s, line %d: do not prepend whitespace" " to statements between IF and ENDIF", mapname, lineno); } /* * Compile the pattern. */ if (dict_pcre_compile(mapname, lineno, ®exp, &engine) == 0) return (0); /* * Save the result. */ if_rule = (DICT_PCRE_IF_RULE *) dict_pcre_rule_alloc(DICT_PCRE_OP_IF, nesting, lineno, sizeof(DICT_PCRE_IF_RULE)); if_rule->match = regexp.match; if_rule->pattern = engine.pattern; if_rule->hints = engine.hints; return ((DICT_PCRE_RULE *) if_rule); } /* * The ENDIF operator takes no patterns and no replacement text. */ else if (strncasecmp(p, "ENDIF", 5) == 0 && !ISALNUM(p[5])) { DICT_PCRE_RULE *rule; p += 5; /* * Warn about out-of-place ENDIFs. */ if (nesting == 0) { msg_warn("pcre map %s, line %d: ignoring ENDIF without matching IF", mapname, lineno); return (0); } /* * Warn about out-of-place text. */ while (*p && ISSPACE(*p)) ++p; if (*p) msg_warn("pcre map %s, line %d: ignoring extra text after ENDIF", mapname, lineno); /* * Save the result. */ rule = dict_pcre_rule_alloc(DICT_PCRE_OP_ENDIF, nesting, lineno, sizeof(DICT_PCRE_RULE)); return (rule); } /* * Unrecognized input. */ else { msg_warn("pcre map %s, line %d: ignoring unrecognized request", mapname, lineno); return (0); } }
static const char *dict_pcre_lookup(DICT *dict, const char *lookup_string) { DICT_PCRE *dict_pcre = (DICT_PCRE *) dict; DICT_PCRE_RULE *rule; DICT_PCRE_IF_RULE *if_rule; DICT_PCRE_MATCH_RULE *match_rule; int lookup_len = strlen(lookup_string); DICT_PCRE_EXPAND_CONTEXT ctxt; int nesting = 0; dict->error = 0; if (msg_verbose) msg_info("dict_pcre_lookup: %s: %s", dict->name, lookup_string); /* * Optionally fold the key. */ if (dict->flags & DICT_FLAG_FOLD_MUL) { if (dict->fold_buf == 0) dict->fold_buf = vstring_alloc(10); vstring_strcpy(dict->fold_buf, lookup_string); lookup_string = lowercase(vstring_str(dict->fold_buf)); } for (rule = dict_pcre->head; rule; rule = rule->next) { /* * Skip rules inside failed IF/ENDIF. */ if (nesting < rule->nesting) continue; switch (rule->op) { /* * Search for a matching expression. */ case DICT_PCRE_OP_MATCH: match_rule = (DICT_PCRE_MATCH_RULE *) rule; ctxt.matches = pcre_exec(match_rule->pattern, match_rule->hints, lookup_string, lookup_len, NULL_STARTOFFSET, NULL_EXEC_OPTIONS, ctxt.offsets, PCRE_MAX_CAPTURE * 3); if (ctxt.matches > 0) { if (!match_rule->match) continue; /* Negative rule matched */ } else if (ctxt.matches == PCRE_ERROR_NOMATCH) { if (match_rule->match) continue; /* Positive rule did not * match */ } else { dict_pcre_exec_error(dict->name, rule->lineno, ctxt.matches); continue; /* pcre_exec failed */ } /* * Skip $number substitutions when the replacement text contains * no $number strings, as learned during the compile time * pre-scan. The pre-scan already replaced $$ by $. */ if (match_rule->max_sub == 0) return match_rule->replacement; /* * We've got a match. Perform substitution on replacement string. */ if (dict_pcre->expansion_buf == 0) dict_pcre->expansion_buf = vstring_alloc(10); VSTRING_RESET(dict_pcre->expansion_buf); ctxt.dict_pcre = dict_pcre; ctxt.match_rule = match_rule; ctxt.lookup_string = lookup_string; if (mac_parse(match_rule->replacement, dict_pcre_expand, (char *) &ctxt) & MAC_PARSE_ERROR) msg_fatal("pcre map %s, line %d: bad replacement syntax", dict->name, rule->lineno); VSTRING_TERMINATE(dict_pcre->expansion_buf); return (vstring_str(dict_pcre->expansion_buf)); /* * Conditional. XXX We provide space for matched substring info * because PCRE uses part of it as workspace for backtracking. * PCRE will allocate memory if it runs out of backtracking * storage. */ case DICT_PCRE_OP_IF: if_rule = (DICT_PCRE_IF_RULE *) rule; ctxt.matches = pcre_exec(if_rule->pattern, if_rule->hints, lookup_string, lookup_len, NULL_STARTOFFSET, NULL_EXEC_OPTIONS, ctxt.offsets, PCRE_MAX_CAPTURE * 3); if (ctxt.matches > 0) { if (!if_rule->match) continue; /* Negative rule matched */ } else if (ctxt.matches == PCRE_ERROR_NOMATCH) { if (if_rule->match) continue; /* Positive rule did not * match */ } else { dict_pcre_exec_error(dict->name, rule->lineno, ctxt.matches); continue; /* pcre_exec failed */ } nesting++; continue; /* * ENDIF after successful IF. */ case DICT_PCRE_OP_ENDIF: nesting--; continue; default: msg_panic("dict_pcre_lookup: impossible operation %d", rule->op); } } return (0); }
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); }
static int mac_expand_callback(int type, VSTRING *buf, void *ptr) { static const char myname[] = "mac_expand_callback"; MAC_EXP_CONTEXT *mc = (MAC_EXP_CONTEXT *) ptr; int lookup_mode; const char *lookup; char *cp; int ch; ssize_t res_len; ssize_t tmp_len; const char *res_iftrue; const char *res_iffalse; /* * Sanity check. */ if (mc->level++ > 100) mac_exp_parse_error(mc, "unreasonable macro call nesting: \"%s\"", vstring_str(buf)); if (mc->status & MAC_PARSE_ERROR) return (mc->status); /* * Named parameter or relational expression. In case of a syntax error, * return without doing damage, and issue a warning instead. */ if (type == MAC_PARSE_EXPR) { cp = vstring_str(buf); /* * Relational expression. If recursion is disabled, perform only one * level of $name expansion. */ if (MAC_EXP_FIND_LEFT_CURLY(tmp_len, cp)) { if (mac_exp_parse_relational(mc, &lookup, &cp) != 0) return (mc->status); /* * Look for the ? or : operator. */ if ((ch = *cp) != 0) { if (ch != '?' && ch != ':') MAC_EXP_ERR_RETURN(mc, "\"?\" or \":\" expected at: " "\"...}>>>%.20s\"", cp); cp++; } } /* * Named parameter. */ else { /* * Look for the ? or : operator. In case of a syntax error, * return without doing damage, and issue a warning instead. */ for ( /* void */ ; /* void */ ; cp++) { if ((ch = *cp) == 0) { lookup_mode = MAC_EXP_MODE_USE; break; } if (ch == '?' || ch == ':') { *cp++ = 0; lookup_mode = MAC_EXP_MODE_TEST; break; } if (!ISALNUM(ch) && ch != '_') { MAC_EXP_ERR_RETURN(mc, "attribute name syntax error at: " "\"...%.*s>>>%.20s\"", (int) (cp - vstring_str(buf)), vstring_str(buf), cp); } } /* * Look up the named parameter. Todo: allow the lookup function * to specify if the result is safe for $name expanson. */ lookup = mc->lookup(vstring_str(buf), lookup_mode, mc->context); } /* * Return the requested result. After parsing the result operand * following ?, we fall through to parse the result operand following * :. This is necessary with the ternary ?: operator: first, with * MAC_EXP_FLAG_SCAN to parse both result operands with mac_parse(), * and second, to find garbage after any result operand. Without * MAC_EXP_FLAG_SCAN the content of only one of the ?: result * operands will be parsed with mac_parse(); syntax errors in the * other operand will be missed. */ switch (ch) { case '?': if (MAC_EXP_FIND_LEFT_CURLY(tmp_len, cp)) { if ((res_iftrue = mac_exp_extract_curly_payload(mc, &cp)) == 0) return (mc->status); } else { res_iftrue = cp; cp = ""; /* no left-over text */ } if ((lookup != 0 && *lookup != 0) || (mc->flags & MAC_EXP_FLAG_SCAN)) mc->status |= mac_parse(res_iftrue, mac_expand_callback, (void *) mc); if (*cp == 0) /* end of input, OK */ break; if (*cp != ':') /* garbage */ MAC_EXP_ERR_RETURN(mc, "\":\" expected at: " "\"...%s}>>>%.20s\"", res_iftrue, cp); cp += 1; /* FALLTHROUGH: do not remove, see comment above. */ case ':': if (MAC_EXP_FIND_LEFT_CURLY(tmp_len, cp)) { if ((res_iffalse = mac_exp_extract_curly_payload(mc, &cp)) == 0) return (mc->status); } else { res_iffalse = cp; cp = ""; /* no left-over text */ } if (lookup == 0 || *lookup == 0 || (mc->flags & MAC_EXP_FLAG_SCAN)) mc->status |= mac_parse(res_iffalse, mac_expand_callback, (void *) mc); if (*cp != 0) /* garbage */ MAC_EXP_ERR_RETURN(mc, "unexpected input at: " "\"...%s}>>>%.20s\"", res_iffalse, cp); break; case 0: if (lookup == 0) { mc->status |= MAC_PARSE_UNDEF; } else if (*lookup == 0 || (mc->flags & MAC_EXP_FLAG_SCAN)) { /* void */ ; } else if (mc->flags & MAC_EXP_FLAG_RECURSE) { vstring_strcpy(buf, lookup); mc->status |= mac_parse(vstring_str(buf), mac_expand_callback, (void *) mc); } else { res_len = VSTRING_LEN(mc->result); vstring_strcat(mc->result, lookup); if (mc->flags & MAC_EXP_FLAG_PRINTABLE) { printable(vstring_str(mc->result) + res_len, '_'); } else if (mc->filter) { cp = vstring_str(mc->result) + res_len; while (*(cp += strspn(cp, mc->filter))) *cp++ = '_'; } } break; default: msg_panic("%s: unknown operator code %d", myname, ch); } } /* * Literal text. */ else if ((mc->flags & MAC_EXP_FLAG_SCAN) == 0) { vstring_strcat(mc->result, vstring_str(buf)); } mc->level--; return (mc->status); }
int nmrp_do(struct nmrpd_args *args) { struct nmrp_pkt tx, rx; uint8_t *src, dest[6]; uint16_t len, region; char *filename; time_t beg; int i, status, ulreqs, expect, upload_ok, autoip; struct ethsock *sock; uint32_t intf_addr; void (*sigh_orig)(int); struct { struct in_addr addr; struct in_addr mask; } PACKED ipconf; if (args->op != NMRP_UPLOAD_FW) { fprintf(stderr, "Operation not implemented.\n"); return 1; } if (!mac_parse(args->mac, dest)) { fprintf(stderr, "Invalid MAC address '%s'.\n", args->mac); return 1; } if ((ipconf.mask.s_addr = inet_addr(args->ipmask)) == INADDR_NONE) { fprintf(stderr, "Invalid subnet mask '%s'.\n", args->ipmask); return 1; } if (!args->ipaddr) { autoip = true; /* The MAC of the device that was used to test this utility starts * with a4:2b:8c, hence 164 (0xa4) and 183 (0x2b + 0x8c) */ args->ipaddr = "10.164.183.252"; if (!args->ipaddr_intf) { args->ipaddr_intf = "10.164.183.253"; } } else if (args->ipaddr_intf) { autoip = true; } else { autoip = false; } if ((ipconf.addr.s_addr = inet_addr(args->ipaddr)) == INADDR_NONE) { fprintf(stderr, "Invalid IP address '%s'.\n", args->ipaddr); return 1; } if (args->ipaddr_intf && (intf_addr = inet_addr(args->ipaddr_intf)) == INADDR_NONE) { fprintf(stderr, "Invalid IP address '%s'.\n", args->ipaddr_intf); return 1; } if (args->file_local && strcmp(args->file_local, "-") && access(args->file_local, R_OK) == -1) { fprintf(stderr, "Error accessing file '%s'.\n", args->file_local); return 1; } if (args->file_remote) { if (!tftp_is_valid_filename(args->file_remote)) { fprintf(stderr, "Invalid remote filename '%s'.\n", args->file_remote); return 1; } } if (args->region) { region = htons(to_region_code(args->region)); if (!region) { fprintf(stderr, "Invalid region code '%s'.\n", args->region); return 1; } } else { region = 0; } status = 1; sock = ethsock_create(args->intf, ETH_P_NMRP); if (!sock) { return 1; } gsock = sock; garp = 0; sigh_orig = signal(SIGINT, sigh); if (!autoip) { status = is_valid_ip(sock, &ipconf.addr, &ipconf.mask); if (status <= 0) { if (!status) { fprintf(stderr, "Address %s/%s cannot be used on interface %s.\n", args->ipaddr, args->ipmask, args->intf); } goto out; } } else { if (verbosity) { printf("Adding %s to interface %s.\n", args->ipaddr_intf, args->intf); } if (ethsock_ip_add(sock, intf_addr, ipconf.mask.s_addr, &gundo) != 0) { goto out; } } if (ethsock_set_timeout(sock, args->rx_timeout)) { goto out; } src = ethsock_get_hwaddr(sock); if (!src) { goto out; } memcpy(tx.eh.ether_shost, src, 6); memcpy(tx.eh.ether_dhost, dest, 6); tx.eh.ether_type = htons(ETH_P_NMRP); msg_init(&tx.msg, NMRP_C_ADVERTISE); msg_opt_add(&tx.msg, NMRP_O_MAGIC_NO, "NTGR", 4); msg_hton(&tx.msg); i = 0; upload_ok = 0; beg = time(NULL); while (1) { printf("\rAdvertising NMRP server on %s ... %c", args->intf, spinner[i]); fflush(stdout); i = (i + 1) & 3; if (pkt_send(sock, &tx) < 0) { perror("sendto"); goto out; } status = pkt_recv(sock, &rx); if (status == 0 && memcmp(rx.eh.ether_dhost, src, 6) == 0) { break; } else if (status == 1) { goto out; } else { if ((time(NULL) - beg) >= 60) { printf("\nNo response after 60 seconds. Bailing out.\n"); goto out; } } } printf("\n"); expect = NMRP_C_CONF_REQ; ulreqs = 0; do { if (expect != NMRP_C_NONE && rx.msg.code != expect) { fprintf(stderr, "Received %s while waiting for %s!\n", msg_code_str(rx.msg.code), msg_code_str(expect)); } msg_init(&tx.msg, NMRP_C_NONE); status = 1; switch (rx.msg.code) { case NMRP_C_ADVERTISE: printf("Received NMRP advertisement from %s.\n", mac_to_str(rx.eh.ether_shost)); status = 1; goto out; case NMRP_C_CONF_REQ: tx.msg.code = NMRP_C_CONF_ACK; msg_opt_add(&tx.msg, NMRP_O_DEV_IP, &ipconf, 8); msg_opt_add(&tx.msg, NMRP_O_FW_UP, NULL, 0); #ifdef NMRPFLASH_SET_REGION if (region) { msg_opt_add(&tx.msg, NMRP_O_DEV_REGION, ®ion, 2); } #endif expect = NMRP_C_TFTP_UL_REQ; printf("Received configuration request from %s.\n", mac_to_str(rx.eh.ether_shost)); memcpy(tx.eh.ether_dhost, rx.eh.ether_shost, 6); printf("Sending configuration: ip %s, mask %s.\n", args->ipaddr, args->ipmask); memcpy(arpmac, rx.eh.ether_shost, 6); memcpy(&arpip, &ipconf.addr, sizeof(ipconf.addr)); if (ethsock_arp_add(sock, arpmac, &arpip) != 0) { goto out; } garp = 1; break; case NMRP_C_TFTP_UL_REQ: if (!upload_ok) { if (++ulreqs > 5) { printf("Bailing out after %d upload requests.\n", ulreqs); tx.msg.code = NMRP_C_CLOSE_REQ; break; } } else { if (verbosity) { printf("Ignoring extra upload request.\n"); } ethsock_set_timeout(sock, args->ul_timeout); tx.msg.code = NMRP_C_KEEP_ALIVE_REQ; break; } len = 0; filename = msg_opt_data(&rx.msg, NMRP_O_FILE_NAME, &len); if (filename) { if (!args->file_remote) { args->file_remote = filename; } printf("Received upload request: filename '%.*s'.\n", len, filename); } else if (!args->file_remote) { args->file_remote = args->file_local; printf("Received upload request with empty filename.\n"); } status = 0; if (args->tftpcmd) { printf("Executing '%s' ... \n", args->tftpcmd); setenv("IP", inet_ntoa(ipconf.addr), 1); setenv("MAC", mac_to_str(rx.eh.ether_shost), 1); setenv("NETMASK", inet_ntoa(ipconf.mask), 1); status = system(args->tftpcmd); } if (!status && args->file_local) { if (!autoip) { status = is_valid_ip(sock, &ipconf.addr, &ipconf.mask); if (status < 0) { goto out; } else if (!status) { printf("IP address of %s has changed. Please assign a " "static ip to the interface.\n", args->intf); tx.msg.code = NMRP_C_CLOSE_REQ; break; } } if (verbosity) { printf("Using remote filename '%s'.\n", args->file_remote); } if (!strcmp(args->file_local, "-")) { printf("Uploading from stdin ... "); } else { printf("Uploading %s ... ", leafname(args->file_local)); } fflush(stdout); status = tftp_put(args); } if (!status) { printf("OK\nWaiting for remote to respond.\n"); upload_ok = 1; ethsock_set_timeout(sock, args->ul_timeout); tx.msg.code = NMRP_C_KEEP_ALIVE_REQ; expect = NMRP_C_NONE; } else if (status == -2) { expect = NMRP_C_TFTP_UL_REQ; } else { goto out; } break; case NMRP_C_KEEP_ALIVE_REQ: tx.msg.code = NMRP_C_KEEP_ALIVE_ACK; ethsock_set_timeout(sock, args->ul_timeout); printf("Received keep-alive request.\n"); break; case NMRP_C_CLOSE_REQ: tx.msg.code = NMRP_C_CLOSE_ACK; break; case NMRP_C_CLOSE_ACK: status = 0; goto out; default: fprintf(stderr, "Unknown message code 0x%02x!\n", rx.msg.code); msg_dump(&rx.msg, 0); } if (tx.msg.code != NMRP_C_NONE) { msg_hton(&tx.msg); if (pkt_send(sock, &tx) < 0) { perror("sendto"); goto out; } if (tx.msg.code == NMRP_C_CLOSE_REQ) { goto out; } } if (rx.msg.code == NMRP_C_CLOSE_REQ) { printf("Remote finished. Closing connection.\n"); break; } status = pkt_recv(sock, &rx); if (status) { if (status == 2) { fprintf(stderr, "Timeout while waiting for %s.\n", msg_code_str(expect)); } goto out; } ethsock_set_timeout(sock, args->rx_timeout); } while (1); status = 0; if (ulreqs) { printf("Reboot your device now.\n"); } else { printf("No upload request received.\n"); } out: signal(SIGINT, sigh_orig); gsock = NULL; ethsock_arp_del(sock, arpmac, &arpip); ethsock_ip_del(sock, &gundo); ethsock_close(sock); return status; }