/*-------------------------------------------------------------------------*/ string_t * struct_t_unique_name (struct_type_t *pSType) /* Also aliased to: struct_unique_name(struct_t *) * * Compose and return the unique name of struct type <pSType>. * The returned string reference is not counted. * * The value save/restore routines rely on the format of this string. */ { char name[MAXPATHLEN+256]; if (pSType->unique_name) return pSType->unique_name; snprintf(name, sizeof(name), "%s %s #%"PRId32 , get_txt(struct_t_name(pSType)) , get_txt(struct_t_pname(pSType)) , struct_t_pid(pSType) ); pSType->unique_name = new_mstring(name); return pSType->unique_name; } /* struct_t_unique_name() */
/*-------------------------------------------------------------------------*/ Bool dumpstat_dest(string_t *fname) /* this function dumps statistics about all destructed objects into the file * $MUDLIB/<fname>. It is called by dump_driver_info(). * Return TRUE on success, FALSE if <fname> can't be written. */ { FILE *f; object_t *ob; fname = check_valid_path(fname, current_object, STR_OBJDUMP, MY_TRUE); if (!fname) return MY_FALSE; f = fopen(get_txt(fname), "w"); if (!f) { free_mstring(fname); return MY_FALSE; } FCOUNT_WRITE(get_txt(fname)); for (ob = newly_destructed_objs; ob; ob = ob->next_all) { #ifdef DEBUG if (!(ob->flags & O_DESTRUCTED)) /* TODO: Can't happen */ continue; #endif fprintf(f, "%-20s ref %2"PRIdPINT" NEW\n" , get_txt(ob->name) , ob->ref ); } for (ob = destructed_objs; ob; ob = ob->next_all) { #ifdef DEBUG if (!(ob->flags & O_DESTRUCTED)) /* TODO: Can't happen */ continue; #endif fprintf(f, "%-20s ref %2"PRIdPINT"\n" , get_txt(ob->name) , ob->ref ); } fclose(f); free_mstring(fname); return MY_TRUE; } /* dumpstat_dest() */
int TB_handle_one(int cid) { term *trm, *result; TBbool sndvoid = TBfalse; assert_valid_cid(cid); trm = TB_receive(cid); if(!trm) { err_fatal("contact with ToolBus lost.\n"); } if(streq(get_txt(fun_sym(trm)), "rec-do")) { sndvoid = TBtrue; } result = connections[cid]->handler(cid, trm); if(result) { return TB_send(cid, result); } else if(sndvoid) { return TB_send(cid, TBmake("snd-void()")); } err_fatal("Unhandled case in TB_handle_one!\n"); return -1; }
/*-------------------------------------------------------------------------*/ string_t * trim_all_spaces (const string_t * txt) /* Trim the input string <txt> by removing all leading and trailing * space, and by folding embedded space runs into just one each. * Return the new string with one ref; the refcount of <txt> is not changed. * * Throw an error when out of memory. */ { char * dest; const char * src; size_t dest_ix, src_ix, srclen; string_t * rc; dest = alloca(mstrsize(txt)); if (dest == NULL) errorf("Stack overflow (%zu bytes)\n", mstrsize(txt)); src = get_txt((string_t *const)txt); srclen = mstrsize(txt); src_ix = 0; dest_ix = 0; /* Blank out trailing spaces */ while (srclen > 0 && src[srclen-1] == ' ') srclen--; /* Skip leading spaces */ while (src_ix < srclen && *src == ' ') src_ix++, src++; /* Copy characters, but fold embedded spaces. */ for ( ; src_ix < srclen; src_ix++, src++, dest_ix++) { dest[dest_ix] = *src; /* If this and the next character is a space, forward * src until the last space in this run. */ if (' ' == *src) { while (src_ix+1 < srclen && ' ' == src[1]) src_ix++, src++; } } memsafe(rc = new_n_mstring(dest, dest_ix), dest_ix, "trimmed result"); return rc; } /* trim_all_spaces() */
/*-------------------------------------------------------------------------*/ static INLINE hash32_t hash2 (string_t * const pName, string_t * const pProgName) /* Compute the hash from <pName> combined with <pProgName>. */ { hash32_t hash; hash = mstr_get_hash(pName); hash = hash_string_chained("\n", 1, hash); hash = hash_string_chained(get_txt(pProgName), mstrsize(pProgName), hash); return hash; } /* hash2() */
static term *tool_read_term(void) { int nelem; term *trm, *rtrm; inport *inp; TBbool sndvoid = TBfalse; while(TBtrue){ if(stand_alone){ fprintf(stdout, "%s", single_prompt); fflush(stdout); } inp = NULL; nelem = read_from_any_channel(&inp); if(nelem < 0){ err_warn("tool_read_term: cannot find ready input channel"); continue; } if(inp && inp->term_port){ if((trm = parse_buffer())){ /*TBmsg("tool_read_term: ***%t***\n", trm);*/ if(streq(get_txt(fun_sym(trm)), "rec-do")) sndvoid = TBtrue; rtrm = (*inp->callbackTerm)(trm); if(sndvoid) return Snd_Void; else return rtrm; } } else { if(inp) return (*inp->callbackChar)(inp->in); } } return NULL; /* <PO> missing return from lcc */ }
/*-------------------------------------------------------------------------*/ svalue_t * x_map_string (svalue_t *sp, int num_arg) /* EFUN map() for strings * * string map(string arg, string func, string|object ob, mixed extra...) * string map(string arg, closure cl, mixed extra...) * string map(string arg, mapping m) * * Call the function <ob>-><func>() resp. the closure <cl> for * every element of the array/struct/mapping/string <arg>, and return a result * made up from the returned values. * * It is also possible to map every entry through a lookup <m>[element]. If * the mapping entry doesn't exist, the original value is kept, otherwise the * result of the mapping lookup. * * Since <arg> is a string, only integer return values are allowed, of which * only the lower 8 bits are considered. * * If <ob> is omitted, or neither an object nor a string, then * this_object() is used. */ { string_t *res; string_t *str; svalue_t *arg; mp_int len; char *src, *dest; inter_sp = sp; arg = sp - num_arg + 1; str = arg->u.str; len = mstrsize(str); if (arg[1].type == T_MAPPING) { /* --- Map through mapping --- */ mapping_t *m; if (num_arg > 2) { inter_sp = sp; errorf("Too many arguments to map(string)\n"); } m = arg[1].u.map; res = alloc_mstring(len); if (!res) errorf("(map_string) Out of memory: string[%"PRIdMPINT "] for result\n", len); push_string(inter_sp, res); /* In case of errors */ for (src = get_txt(str), dest = get_txt(res); --len >= 0; src++, dest++) { svalue_t key, *v; put_number(&key, *src); v = get_map_value(m, &key); if (v == &const0) *dest = *src; else { if (v->type != T_NUMBER) { errorf("(map_string) Illegal value: %s, expected string\n" , typename(v->type) ); } *dest = (v->u.number & 0xFF); } }
void fill_header_from_mapping (svalue_t *key, svalue_t *val, void *extra) { psyc_modifier_t *m = extra; char oper = 0; char *name, *value; size_t namelen, valuelen, i; uint8_t type; svalue_t vsp, *lval; psycList list; psycString *elems = NULL; if (key->type != T_STRING) { errorf("fill_header_from_mapping: key type %d not supported\n", key->type); return; // not reached } name = get_txt(key->u.str); namelen = mstrsize(key->u.str); type = psyc_getVarType2(name, namelen); if (m->num_values > 1) oper = val[1].u.number; if (!oper) oper = C_GLYPH_OPERATOR_SET; switch (val->type) { case T_STRING: value = get_txt(val->u.str); valuelen = mstrsize(val->u.str); break; case T_NUMBER: case T_OBJECT: vsp.type = val->type; switch (val->type) { case T_NUMBER: if (type == PSYC_TYPE_DATE) vsp.u.number = val->u.number - PSYC_EPOCH; else vsp.u.number = val->u.number; break; case T_OBJECT: vsp.u.ob = val->u.ob; break; } f_to_string(&vsp); // generates an mstring value = get_txt(vsp.u.str); valuelen = mstrsize(vsp.u.str); break; case T_POINTER: if (VEC_SIZE(val->u.vec)) { elems = pxalloc(sizeof(psycString) * VEC_SIZE(val->u.vec)); if (!elems) { errorf("Out of memory in fill_header_from_mapping for elems\n"); return; // not reached } for (i = 0; i < VEC_SIZE(val->u.vec); i++) { lval = &(val->u.vec->item[i]); switch (lval->type) { case T_STRING: elems[i] = (psycString){mstrsize(lval->u.str), get_txt(lval->u.str)}; break; case T_NUMBER: case T_OBJECT: vsp.type = lval->type; switch (lval->type) { case T_NUMBER: vsp.u.number = lval->u.number; break; case T_OBJECT: vsp.u.ob = lval->u.ob; break; } f_to_string(&vsp); elems[i] = (psycString){mstrsize(vsp.u.str), get_txt(vsp.u.str)}; break; default: errorf("fill_header_from_mapping: list value type %d not supported\n", lval->type); return; // not reached } } } list = psyc_newList(elems, VEC_SIZE(val->u.vec), PSYC_LIST_CHECK_LENGTH); valuelen = list.length; value = pxalloc(valuelen); if (!value) { errorf("Out of memory in fill_header_from_mapping for list value\n"); return; // not reached } psyc_renderList(&list, value, valuelen); break; default: errorf("fill_header_from_mapping: value type %d not supported\n", val->type); return; // not reached } m->header->modifiers[m->header->lines++] = psyc_newModifier2(oper, name, namelen, value, valuelen, m->flag); }
/*-------------------------------------------------------------------------*/ Bool dumpstat (string_t *fname) /* This function dumps statistics about all listed objects into the file * $MUDLIB/<fname>. It is called from dump_driver_info. * Return TRUE on success, FALSE if <fname> can't be written. */ { FILE *f; object_t *ob; static char *swapstrings[] = {"", "PROG SWAPPED", "VAR SWAPPED", "SWAPPED", }; fname = check_valid_path(fname, current_object, STR_OBJDUMP, MY_TRUE); if (!fname) return MY_FALSE; f = fopen(get_txt(fname), "w"); if (!f) { free_mstring(fname); return MY_FALSE; } FCOUNT_WRITE(get_txt(fname)); for (ob = obj_list; ob; ob = ob->next_all) { mp_int compsize, totalsize, overhead; char timest[21]; struct tm *tm; #ifdef DEBUG if (ob->flags & O_DESTRUCTED) /* TODO: Can't happen */ continue; #endif compsize = data_size(ob, &totalsize); if (!O_PROG_SWAPPED(ob) && (ob->prog->ref == 1 || !(ob->flags & (O_CLONE|O_REPLACED)))) { overhead = ob->prog->total_size; } else { overhead = 0; } overhead += sizeof (object_t); fprintf(f, "%-20s %5"PRIdMPINT" (%5"PRIdMPINT") ref %2"PRIdPINT" %s " , get_txt(ob->name) , compsize + overhead, totalsize + overhead , ob->ref , ob->flags & O_HEART_BEAT ? "HB" : " " ); if (ob->super) fprintf(f, "%s ", get_txt(ob->super->name)); else fprintf(f, "-- "); if (ob->gigaticks) fprintf(f, " (%"PRIuMPINT"%09"PRIuMPINT")", (mp_uint)ob->gigaticks, (mp_uint)ob->ticks); else fprintf(f, " (%"PRIuMPINT")", (mp_uint)ob->ticks); fprintf(f, " %s", swapstrings[(O_PROG_SWAPPED(ob)?1:0) | (O_VAR_SWAPPED(ob)?2:0)] ); tm = localtime((time_t *)&ob->load_time); strftime(timest, sizeof(timest), "%Y.%m.%d-%H:%M:%S", tm); fprintf(f, " %s\n", timest); } fclose(f); free_mstring(fname); return MY_TRUE; } /* dumpstat() */
/*-------------------------------------------------------------------------*/ svalue_t * x_filter_string (svalue_t *sp, int num_arg) /* EFUN: filter() for strings. * * string filter(string arr, string fun, string|object obj, mixed extra, ...) * string filter(string arr, closure cl, mixed extra, ...) * string filter(string arr, mapping map) * * Filter the elements of <arr> through a filter defined by the other * arguments, and return an array of those elements, for which the * filter yields non-zero. * * The filter can be a function call: * * <obj>-><fun>(elem, <extra>...) * * or a mapping query: * * <map>[elem] * * <obj> can both be an object reference or a filename. If omitted, * this_object() is used (this also works if the third argument is * neither a string nor an object). */ { string_t *rc; /* Result string */ string_t *str; /* Argument string */ svalue_t *arg; /* First argument the vm stack */ mp_int slen; /* Argument string length */ char *src, *dest; /* String text work pointers */ char *flags; /* Flag array, one flag for each element of <str> * (in reverse order). */ mp_int res; /* Number of surviving elements */ res = 0; /* Locate the args on the stack, extract the string to filter * and allocate the flags vector. */ arg = sp - num_arg + 1; str = arg->u.str; slen = (mp_int)mstrsize(str); /* Every element in flags is associated by index number with an * element in the vector to filter. The filter function is evaluated * for every string character, and the associated flag is set to 0 * or 1 according to the result. * At the end, all 1-flagged elements are gathered and copied * into the result string. */ if (arg[1].type == T_MAPPING) { mp_int cnt; /* --- Filter by mapping query --- */ mapping_t *m; if (num_arg > 2) { errorf("Too many arguments to filter(array)\n"); } /* Allocate memory for the flag array. Simultaneously an error * handler is pushed onto the stack (after the arguments) for freeing * the buffer in case of runtime errors. */ flags = xalloc_with_error_handler((size_t)slen + 1); if (!flags) { errorf("Out of memory (%zu bytes) for temporary buffer in filter().\n", (size_t)slen + 1); } sp = inter_sp; m = arg[1].u.map; for (src = get_txt(str), cnt = slen; --cnt >= 0; src++) { svalue_t key; put_number(&key, *src); if (get_map_value(m, &key) == &const0) { flags[cnt] = 0; continue; } flags[cnt] = 1; res++; } } else { /* --- Filter by function call --- */ int error_index; callback_t cb; mp_int cnt; assign_eval_cost(); /* setup_efun_callback() will adopt and therefore remove the * arguments from arg+1 on to arg+num_arg from the stack and update * inter_sp. New top-of-stack will be arg. */ error_index = setup_efun_callback(&cb, arg+1, num_arg-1); if (error_index >= 0) { vefun_bad_arg(error_index+2, arg); /* NOTREACHED */ return arg; } /* push the callback structure onto the stack. */ sp = arg + 1; put_callback(sp, &cb); /* Allocate memory for the flag array. Simultaneously an error * handler is pushed onto the stack (after the arguments) for freeing * the buffer in case of runtime errors. */ inter_sp = sp; flags = xalloc_with_error_handler((size_t)slen + 1); if (!flags) { errorf("Out of memory (%"PRIdMPINT" bytes) for temporary buffer " "in filter().\n", slen + 1); } sp = inter_sp; /* Loop over all elements in p and call the filter. * w is the current element filtered. */ for (src = get_txt(str), cnt = slen; --cnt >= 0; src++) { svalue_t *v; flags[cnt] = 0; if (current_object->flags & O_DESTRUCTED) continue; /* Don't call the filter anymore, but fill the * flags array with 0es. */ if (!callback_object(&cb)) { inter_sp = sp; errorf("object used by filter(array) destructed"); } push_number(inter_sp, *src); v = apply_callback(&cb, 1); if (!v || (v->type == T_NUMBER && !v->u.number) ) continue; flags[cnt] = 1; res++; } } /* flags[] holds the filter results, res is the number of * elements to keep. Now create the result vector. */ rc = alloc_mstring(res); if (!rc) { errorf("Out of memory (%"PRIdMPINT" bytes) for result in filter().\n", slen+1); } for (src = get_txt(str), dest = get_txt(rc), flags = &flags[slen] ; res > 0 ; src++) { if (*--flags) { *dest++ = *src; res--; } } /* Cleanup. Arguments for the closure have already been removed. On the * stack are now the string, the mapping or callback structure and the * error handler. (Not using pop_n_elems() for 2 elements for saving loop * and function call overhead.) */ free_svalue(sp--); /* errorhandler, buffer and flags are freed by this. */ free_svalue(sp--); /* mapping or callback structure. */ free_mstring(str); /* string, at arg == sp */ sp->u.str = rc; /* put result here */ return sp; } /* x_filter_string() */
/*--------------------------------------------------------------------*/ string_t * intersect_strings (const string_t * p_left, const string_t * p_right, Bool bSubtract) /* !bSubtract: Intersect string <left> with string <right> and return * a newly allocated string with all those characters which are in * both strings. * bSubtract: Subtract string <right> from string <left> and return * a newly allocated string with all those characters which are in * <left> but not in <right>. * The order of the characters returned is their order of appearance * in <left>. */ { size_t len_left, len_right, len_out; size_t ix_left, ix_right; long * pos; CBool * matches; const char * left_txt; char * left, * right, * result_txt; string_t *result; len_left = mstrsize(p_left); len_right = mstrsize(p_right); xallocate(matches, len_left+1, "intersection matches"); /* +1 so that smalloc won't complain when given an empty left string */ for (ix_left = 0; ix_left < len_left; ix_left++) matches[ix_left] = bSubtract ? MY_TRUE : MY_FALSE; /* Sort the two strings */ left = sort_string(p_left, len_left, &pos); right = sort_string(p_right, len_right, NULL); /* Intersect the two strings by mutual comparison. * Each non-matched character in left gets is pos[] set to -1. */ len_out = bSubtract ? len_left : 0; for ( ix_left = 0, ix_right = 0 ; ix_left < len_left && ix_right < len_right ; ) { if (left[ix_left] < right[ix_right]) ix_left++; else if (left[ix_left] > right[ix_right]) ix_right++; else /* left[ix_left] == right[ix_right]) */ { if (!bSubtract) { matches[pos[ix_left]] = MY_TRUE; len_out++; } else { matches[pos[ix_left]] = MY_FALSE; len_out--; } ix_left++; } } /* Create the result: copy all flagged characters */ memsafe(result = alloc_mstring(len_out), len_out, "intersection result"); left_txt = get_txt((string_t *const)p_left); result_txt = get_txt(result); for (ix_left = 0, ix_right = 0; ix_left < len_left; ix_left++) if (matches[ix_left]) result_txt[ix_right++] = left_txt[ix_left]; /* Free intermediate results */ xfree(pos); xfree(matches); xfree(left); xfree(right); return result; } /* intersect_strings() */
/*--------------------------------------------------------------------*/ static char * sort_string (const string_t * p_in, size_t len, long ** pos) /* Sort the characters of string <in> (with length <len>) by their numeric * values and return a newly allocated memory block with the sorted string. * If <pos> is not NULL, it will be set to a newly allocated memory block * giving the original positions of the characters in the sorted string. * * We use Mergesort to sort the strings. * TODO: Use Quicksort instead of Mergesort? */ { const char * in; /* Input string */ char * out; /* Result string */ long * outpos; /* Result position array */ char * tmp; /* Temporary string */ long * tmppos; /* Temporary position array */ size_t step; size_t i, j; in = get_txt((string_t *const)p_in); out = xalloc(len+1); tmp = xalloc(len+1); if (!out || !tmp) { if (out) xfree(out); if (tmp) xfree(tmp); errorf("(sort_string) Out of memory (2 * %zu bytes) for temporaries.\n" , len+1); } out[len] = '\0'; tmp[len] = '\0'; if (pos) { outpos = xalloc(len * sizeof(*outpos) + 1); tmppos = xalloc(len * sizeof(*outpos) + 1); /* +1 so that smalloc won't complain when given an empty string */ if (!outpos || !tmppos) { if (out) xfree(out); if (tmp) xfree(tmp); if (outpos) xfree(outpos); if (tmppos) xfree(tmppos); errorf("(sort_string) Out of memory (2 * %zu bytes) for positions.\n" , len*sizeof(*outpos)+1); } } else { outpos = NULL; tmppos = NULL; } /* First Mergesort pass: comparison of adjacent characters * and initialisation of the out arrays. */ for (i = 0; i < len; i += 2) { if (i == len-1) { out[i] = in[i]; if (outpos) outpos[i] = i; } else if (in[i] <= in[i+1]) { out[i] = in[i]; out[i+1] = in[i+1]; if (outpos) { outpos[i] = i; outpos[i+1] = i+1; } } else /* (in[i] > in[i+1]) */ { out[i] = in[i+1]; out[i+1] = in[i]; if (outpos) { outpos[i] = i+1; outpos[i+1] = i; } } } /* for(initial pass) */ /* Mergesort loop: perform the mergesort passes with increasing steps. * Invariant: out is the (semi-sorted) data, tmp is the scratchspace. */ for (step = 2; step < len; step *= 2) { size_t start, dest, left; /* Exchange out and tmp */ { char *tmp2; long *tmp2pos; tmp2 = out; out = tmp; tmp = tmp2; if (outpos) { tmp2pos = outpos; outpos = tmppos; tmppos = tmp2pos; } } for (start = 0, dest = 0; start <= len; start += 2*step) { for ( i = start, j = start+step, left = 2 * step ; left && dest < len ; left--, dest++ ) { if (i >= start+step || i >= len) { if (j < len) { out[dest] = tmp[j]; if (outpos) outpos[dest] = tmppos[j]; j++; } } else if (j >= start+2*step || j >= len) { if (i < len) { out[dest] = tmp[i]; if (outpos) outpos[dest] = tmppos[i]; i++; } } else if (tmp[i] <= tmp[j]) { out[dest] = tmp[i]; if (outpos) outpos[dest] = tmppos[i]; i++; } else /* (tmp[i] > tmp[i+step]) */ { out[dest] = tmp[j]; if (outpos) outpos[dest] = tmppos[j]; j++; } } /* for (sort run) */ } /* for (start) */ } /* for(step) */ /* Free the temporary data */ if (tmppos) xfree(tmppos); xfree(tmp); /* Return the result */ if (pos) *pos = outpos; return out; } /* sort_string() */
svalue_t * f_psyc_parse (svalue_t *sp) { char *buffer = NULL; svalue_t *sv; vector_t *v, *list; mapping_t *map; char oper = 0; psycString name = {0,0}, value = {0,0}, elems[MAX_LIST_SIZE], elem; psycParseListState listState; int ret, retl, type = -1, error = 0; size_t size, i; ssize_t n; time_t timmy; if (!psyc_dispatch_callback) psyc_dispatch_callback = new_tabled("psyc_dispatch"); if (!psyc_error_callback) psyc_error_callback = new_tabled("psyc_error"); assert_shadow_sent(current_object); psyc_state_t *state = O_GET_PSYC_STATE(current_object); if (!state) { state = pxalloc(sizeof(psyc_state_t)); if (!state) { errorf("Out of memory for psyc state struct.\n"); return sp; // not reached } O_GET_PSYC_STATE(current_object) = state; memset(state, 0, sizeof(psyc_state_t)); state->parser = pxalloc(sizeof(psycParseState)); if (!state->parser) { errorf("Out of memory for psyc parse state struct.\n"); return sp; // not reached } psyc_initParseState(state->parser); } v = state->packet; if (sp->type == T_POINTER) { errorf("\npsyc_parse got %ld int* bytes... not supported yet\n", VEC_SIZE(sp->u.vec)); return sp; // not reached } else if (sp->type == T_STRING) { #ifdef DEBUG printf("\npsyc_parse got a %ld bytes long string...\n", mstrsize(sp->u.str)); #endif if (state->remaining) { // there are remaining bytes from the previous call to psyc_parse, // copy them together with the newly arrived data buffer = pxalloc(state->remaining_len + mstrsize(sp->u.str)); if (!buffer) { errorf("Out of memory for psyc_parse buffer.\n"); return sp; // not reached } memcpy(buffer, state->remaining, state->remaining_len); memcpy(buffer + state->remaining_len, get_txt(sp->u.str), mstrsize(sp->u.str)); psyc_setParseBuffer2(state->parser, buffer, state->remaining_len + mstrsize(sp->u.str)); pfree(state->remaining); state->remaining = NULL; state->remaining_len = 0; } else { psyc_setParseBuffer2(state->parser, get_txt(sp->u.str), mstrsize(sp->u.str)); } } else { errorf("\npsyc_parse got type %d, not supported\n", sp->type); return sp; // not reached } do { ret = psyc_parse(state->parser, &oper, &name, &value); #ifdef DEBUG printf("#%2d %c%.*s = %.*s\n", ret, oper ? oper : ' ', (int)name.length, name.ptr, (int)value.length, value.ptr); #endif if (!state->packet) { state->packet = allocate_array(4); if (!state->packet) { errorf("Out of memory for psyc_parse array.\n"); return sp; // not reached } v = state->packet; map = allocate_mapping(0, 2); // empty mapping if (!map) { errorf("Out of memory for psyc_parse routing header.\n"); return sp; // not reached } put_mapping(&v->item[PACKET_ROUTING], map); map = allocate_mapping(0, 2); // empty mapping if (!map) { errorf("Out of memory for psyc_parse entity header.\n"); return sp; // not reached } put_mapping(&v->item[PACKET_ENTITY], map); } switch (ret) { case PSYC_PARSE_ENTITY_START: case PSYC_PARSE_BODY_START: // save oper, name & value in state at the start of // incomplete entity or body state->oper = oper; state->name = mstring_alloc_string(name.length); memcpy(get_txt(state->name), name.ptr, name.length); if (!state->name) { errorf("Out of memory for name.\n"); return sp; // not reached } // allocate memory for the total length of the value state->value_len = 0; state->value = mstring_alloc_string(psyc_getParseValueLength(state->parser)); if (!state->value) { errorf("Out of memory for value.\n"); return sp; // not reached } // fall thru case PSYC_PARSE_ENTITY_CONT: case PSYC_PARSE_BODY_CONT: case PSYC_PARSE_ENTITY_END: case PSYC_PARSE_BODY_END: // append value to tmp buffer in state memcpy(get_txt(state->value) + state->value_len, value.ptr, value.length); state->value_len += value.length; } if (ret == PSYC_PARSE_ENTITY_END || ret == PSYC_PARSE_BODY_END) { // incomplete entity or body parsing done, // set oper/name/value to the ones saved in state oper = state->oper; name.ptr = get_txt(state->name); name.length = mstrsize(state->name); value.ptr = get_txt(state->value); value.length = mstrsize(state->value); } switch (ret) { case PSYC_PARSE_ROUTING: sv = pxalloc(sizeof(svalue_t)); // new_n_tabled fetches a reference of a probably existing // shared string put_string(sv, new_n_tabled(name.ptr, name.length)); sv = get_map_lvalue(v->item[PACKET_ROUTING].u.map, sv); put_number(&sv[1], oper); // strings are capable of containing 0 so we can do this // for binary data too. let's use a tabled string even // for values of routing variables as they repeat a lot put_string(sv, new_n_tabled(value.ptr, value.length)); break; case PSYC_PARSE_ENTITY_START: case PSYC_PARSE_ENTITY_CONT: break; case PSYC_PARSE_ENTITY_END: case PSYC_PARSE_ENTITY: sv = pxalloc(sizeof(svalue_t)); if (ret == PSYC_PARSE_ENTITY) put_string(sv, new_n_tabled(name.ptr, name.length)); else // PSYC_PARSE_ENTITY_END put_string(sv, make_tabled(state->name)); sv = get_map_lvalue(v->item[PACKET_ENTITY].u.map, sv); put_number(&sv[1], oper); type = psyc_getVarType(&name); switch (type) { case PSYC_TYPE_DATE: // number + PSYC_EPOCH if (psyc_parseDate(&value, &timmy)) put_number(sv, timmy); else error = PSYC_PARSE_ERROR_DATE; break; case PSYC_TYPE_TIME: // number if (psyc_parseTime(&value, &timmy)) put_number(sv, timmy); else error = PSYC_PARSE_ERROR_TIME; break; case PSYC_TYPE_AMOUNT: // number if (psyc_parseNumber(&value, &n)) put_number(sv, n); else error = PSYC_PARSE_ERROR_AMOUNT; break; case PSYC_TYPE_DEGREE: // first digit if (value.length && value.ptr[0] >= '0' && value.ptr[0] <= '9') put_number(sv, value.ptr[0] - '0'); else error = PSYC_PARSE_ERROR_DEGREE; break; case PSYC_TYPE_FLAG: // 0 or 1 if (value.length && value.ptr[0] >= '0' && value.ptr[0] <= '1') put_number(sv, value.ptr[0] - '0'); else error = PSYC_PARSE_ERROR_FLAG; break; case PSYC_TYPE_LIST: // array size = 0; if (value.length) { psyc_initParseListState(&listState); psyc_setParseListBuffer(&listState, value); elem = (psycString){0, 0}; do { retl = psyc_parseList(&listState, &elem); switch (retl) { case PSYC_PARSE_LIST_END: retl = 0; case PSYC_PARSE_LIST_ELEM: if (size >= MAX_LIST_SIZE) { error = PSYC_PARSE_ERROR_LIST_TOO_LARGE; break; } elems[size++] = elem; break; default: error = PSYC_PARSE_ERROR_LIST; } } while (retl > 0 && !error); } if (error) break; list = allocate_array(size); for (i = 0; i < size; i++) put_string(&list->item[i], new_n_tabled(elems[i].ptr, elems[i].length)); put_array(sv, list); break; default: // string if (ret == PSYC_PARSE_ENTITY) // is it good to put entity variable values into the // shared string table? probably yes.. but it's a guess //t_string(sv, new_n_mstring(value.ptr, value.length)); put_string(sv, new_n_tabled(value.ptr, value.length)); else // PSYC_PARSE_ENTITY_END put_string(sv, state->value); } break; case PSYC_PARSE_BODY_START: case PSYC_PARSE_BODY_CONT: break; case PSYC_PARSE_BODY_END: put_string(&v->item[PACKET_METHOD], make_tabled(state->name)); put_string(&v->item[PACKET_BODY], state->value); break; case PSYC_PARSE_BODY: // new_n_tabled gets the shared string for the method put_string(&v->item[PACKET_METHOD], new_n_tabled(name.ptr, name.length)); // allocate an untabled string for the packet body put_string(&v->item[PACKET_BODY], new_n_mstring(value.ptr, value.length)); break; case PSYC_PARSE_COMPLETE: put_array(inter_sp, v); sapply(psyc_dispatch_callback, current_object, 1); state->packet = NULL; break; case PSYC_PARSE_INSUFFICIENT: // insufficient data, save remaining bytes state->remaining_len = psyc_getParseRemainingLength(state->parser); if (state->remaining_len) { state->remaining = pxalloc(state->remaining_len); memcpy(state->remaining, psyc_getParseRemainingBuffer(state->parser), state->remaining_len); } else state->remaining = NULL; ret = 0; break; default: error = ret; } switch (ret) { case PSYC_PARSE_BODY_END: case PSYC_PARSE_ENTITY_END: // reset tmp buffers in state when incomplete // entity or body parsing is finished state->oper = 0; state->name = NULL; state->value = NULL; } } while (ret && !error); if (buffer) pfree(buffer); free_svalue(sp); put_number(sp, error); return sp; } /* f_psyc_parse */
/*-------------------------------------------------------------------------*/ svalue_t * v_sl_exec (svalue_t * sp, int num_arg) /* EFUN sl_exec() * * mixed* sl_exec(string statement, ...) * * Executes the SQL statement <statement> for the current * SQLite database. The SQL statement may contain wildcards like * '?' and '?nnn', where 'nnn' is an integer. These wildcards * can be given as further parameters to sl_exec. With '?nnn' * the number of a specific parameter can be given, the first * parameter has number 1. * * If the statement returns data, sl_exec returns an array * with each row (which is itself an array of columns) as * an element. */ { svalue_t *argp; sqlite_dbs_t *db; sqlite3_stmt *stmt; const char* tail; int err, rows, cols, num; struct sl_exec_cleanup_s * rec_data; vector_t * result; argp = sp - num_arg + 1; /* First argument: the SQL query */ db = find_db (current_object); if (!db) errorf("The current object doesn't have a database open.\n"); err = sqlite3_prepare(db->db, get_txt(argp->u.str), mstrsize(argp->u.str), &stmt, &tail); if(err) { const char* msg = sqlite3_errmsg(db->db); if(stmt) sqlite3_finalize(stmt); errorf("sl_exec: %s\n", msg); /* NOTREACHED */ } /* Now bind all parameters. */ for(argp++, num=1; argp <= sp; argp++, num++) { switch(argp->type) { default: sqlite3_finalize(stmt); errorf("Bad argument %d to sl_exec(): type %s\n", num+1, typename(argp->type)); break; /* NOTREACHED */ case T_FLOAT: sqlite3_bind_double(stmt, num, READ_DOUBLE(argp)); break; case T_NUMBER: if (sizeof(argp->u.number) > 4) sqlite3_bind_int64(stmt, num, argp->u.number); else sqlite3_bind_int(stmt, num, argp->u.number); break; case T_STRING: sqlite3_bind_text(stmt, num, get_txt(argp->u.str), mstrsize(argp->u.str), SQLITE_STATIC); break; } } rows = 0; cols = sqlite3_column_count(stmt); rec_data = xalloc(sizeof(*rec_data)); if(!rec_data) { sqlite3_finalize(stmt); errorf("(sl_exec) Out of memory: (%lu bytes) for cleanup structure\n", (unsigned long) sizeof(*rec_data)); } rec_data->rows = NULL; rec_data->stmt = stmt; sp = push_error_handler(sl_exec_cleanup, &(rec_data->head)); while((err = sqlite3_step(stmt)) == SQLITE_ROW) { int col; sqlite_rows_t *this_row; rows++; this_row = pxalloc(sizeof(*this_row)); if(!this_row) errorf("(sl_exec) Out of memory: (%lu bytes)\n", (unsigned long) sizeof(*this_row)); this_row->last = rec_data->rows; rec_data->rows = this_row; this_row->row = NULL; /* Because allocate_array may throw an error. */ this_row->row = allocate_array(cols); if(!this_row->row) errorf("(sl_exec) Out of memory: row vector\n"); for(col = 0; col < cols; col++) { svalue_t * entry; STORE_DOUBLE_USED; entry = this_row->row->item + col; switch(sqlite3_column_type(stmt, col)) { default: errorf( "sl_exec: Unknown type %d.\n" , sqlite3_column_type(stmt, col)); break; case SQLITE_BLOB: errorf("sl_exec: Blob columns are not supported.\n"); break; case SQLITE_INTEGER: if (sizeof(entry->u.number) >= 8) put_number(entry, sqlite3_column_int64(stmt, col)); else put_number(entry, sqlite3_column_int(stmt, col)); break; case SQLITE_FLOAT: entry->type = T_FLOAT; STORE_DOUBLE(entry, sqlite3_column_double(stmt, col)); break; case SQLITE_TEXT: put_c_n_string( entry , (char *)sqlite3_column_text(stmt, col) , sqlite3_column_bytes(stmt, col)); break; case SQLITE_NULL: /* All elements from this_row->row are initialized to 0. */ break; } } } sqlite3_finalize(stmt); rec_data->stmt = NULL; switch(err) { default: errorf("sl_exec: Unknown return code from sqlite3_step: %d.\n", err); break; case SQLITE_BUSY: errorf("sl_exec: Database is locked.\n"); break; case SQLITE_ERROR: errorf("sl_exec: %s\n", sqlite3_errmsg(db->db)); break; case SQLITE_MISUSE: errorf("sl_exec: sqlite3_step was called inappropriately.\n"); break; case SQLITE_DONE: break; } if(rows) { sqlite_rows_t *this_row; result = allocate_array(rows); if(!result) errorf("(sl_exec) Out of memory: result vector\n"); this_row = rec_data->rows; while(rows--) { put_array(result->item + rows, this_row->row); this_row->row = NULL; this_row = this_row->last; } } else result = NULL; // Pop arguments and our error handler. // Our error handler gets called and cleans the row stuff. sp = pop_n_elems(num_arg + 1, sp) + 1; if(rows) put_array(sp,result); else put_number(sp, 0); return sp; } /* v_sl_exec() */
int spf_check_host( struct spf *s, const yastr domain ) { int i, j, rc, qualifier, ret = SPF_RESULT_NONE; struct dnsr_result *dnsr_res, *dnsr_res_mech = NULL; struct dnsr_string *txt; yastr record = NULL, redirect = NULL, domain_spec, tmp; size_t tok_count = 0; yastr *split = NULL; char *p; unsigned long cidr, cidr6; int mech_queries = 0; /* RFC 7208 3.1 DNS Resource Records * SPF records MUST be published as a DNS TXT (type 16) Resource Record * (RR) [RFC1035] only. */ if (( dnsr_res = get_txt( domain )) == NULL ) { syslog( LOG_WARNING, "SPF %s [%s]: TXT lookup %s failed", s->spf_domain, domain, domain ); return( SPF_RESULT_TEMPERROR ); } for ( i = 0 ; i < dnsr_res->r_ancount ; i++ ) { if ( dnsr_res->r_answer[ i ].rr_type == DNSR_TYPE_TXT ) { txt = dnsr_res->r_answer[ i ].rr_txt.txt_data; /* RFC 7208 4.5 Selecting Records * Starting with the set of records that were returned by the * lookup, discard records that do not begin with a version section * of exactly "v=spf1". Note that the version section is * terminated by either an SP character or the end of the record. */ if (( strncasecmp( txt->s_string, "v=spf1", 6 ) == 0 ) && (( txt->s_string[ 6 ] == ' ' ) || ( txt->s_string[ 6 ] == '\0' ))) { if ( record != NULL ) { /* RFC 7208 3.2 Multiple DNS Records * A domain name MUST NOT have multiple records that would * cause an authorization check to select more than one * record. */ syslog( LOG_ERR, "SPF %s [%s]: multiple v=spf1 records found", s->spf_domain, domain ); ret = SPF_RESULT_PERMERROR; goto cleanup; } record = yaslempty( ); /* RFC 7208 3.3 Multiple Strings in a Single DNS Record * If a published record contains multiple character-strings, * then the record MUST be treated as if those strings are * concatenated together without adding spaces. */ for ( ; txt != NULL ; txt = txt->s_next ) { record = yaslcat( record, txt->s_string ); } } } } if ( record == NULL ) { simta_debuglog( 1, "SPF %s [%s]: no SPF record found", s->spf_domain, domain ); goto cleanup; } simta_debuglog( 2, "SPF %s [%s]: record: %s", s->spf_domain, domain, record ); split = yaslsplitlen( record, yasllen( record ), " ", 1, &tok_count ); /* Start at 1, 0 is v=spf1 */ for ( i = 1 ; i < tok_count ; i++ ) { /* multiple spaces in a record will result in empty elements */ if ( yasllen( split[ i ] ) == 0 ) { continue; } /* RFC 7208 4.6.4 DNS Lookup Limits * Some mechanisms and modifiers (collectively, "terms") cause DNS * queries at the time of evaluation [...] SPF implementations MUST * limit the total number of those terms to 10 during SPF evaluation, * to avoid unreasonable load on the DNS. If this limit is exceeded, * the implementation MUST return "permerror". */ /* In real life strictly enforcing a limit of ten will break SPF * evaluation of multiple major domains, so we use a higher limit. */ if ( s->spf_queries > 25 ) { syslog( LOG_WARNING, "SPF %s [%s]: DNS lookup limit exceeded", s->spf_domain, domain ); ret = SPF_RESULT_PERMERROR; goto cleanup; } /* RFC 7208 4.6.2 Mechanisms * The possible qualifiers, and the results they cause check_host() to * return, are as follows: * * "+" pass * "-" fail * "~" softfail * "?" neutral * * The qualifier is optional and defaults to "+". */ switch ( *split[ i ] ) { case '+': qualifier = SPF_RESULT_PASS; yaslrange( split[ i ], 1, -1 ); break; case '-': qualifier = SPF_RESULT_FAIL; yaslrange( split[ i ], 1, -1 ); break; case '~': qualifier = SPF_RESULT_SOFTFAIL; yaslrange( split[ i ], 1, -1 ); break; case '?': qualifier = SPF_RESULT_NEUTRAL; yaslrange( split[ i ], 1, -1 ); break; default: qualifier = SPF_RESULT_PASS; break; } if ( strncasecmp( split[ i ], "redirect=", 9 ) == 0 ) { s->spf_queries++; redirect = split[ i ]; yaslrange( redirect, 9, -1 ); simta_debuglog( 2, "SPF %s [%s]: redirect to %s", s->spf_domain, domain, redirect ); /* RFC 7208 5.1 "all" * The "all" mechanism is a test that always matches. */ } else if ( strcasecmp( split[ i ], "all" ) == 0 ) { simta_debuglog( 2, "SPF %s [%s]: matched all: %s", s->spf_domain, domain, spf_result_str( qualifier )); ret = qualifier; goto cleanup; /* RFC 7208 5.2 "include" * The "include" mechanism triggers a recursive evaluation of * check_host(). */ } else if ( strncasecmp( split[ i ], "include:", 8 ) == 0 ) { s->spf_queries++; yaslrange( split[ i ], 8, -1 ); simta_debuglog( 2, "SPF %s [%s]: include %s", s->spf_domain, domain, split[ i ] ); rc = spf_check_host( s, split[ i ] ); switch ( rc ) { case SPF_RESULT_NONE: ret = SPF_RESULT_PERMERROR; goto cleanup; case SPF_RESULT_PASS: ret = qualifier; goto cleanup; case SPF_RESULT_TEMPERROR: case SPF_RESULT_PERMERROR: ret = rc; goto cleanup; } /* RFC 7208 5.3 "a" */ } else if (( strcasecmp( split[ i ], "a" ) == 0 ) || ( strncasecmp( split[ i ], "a:", 2 ) == 0 ) || ( strncasecmp( split[ i ], "a/", 2 ) == 0 )) { s->spf_queries++; yaslrange( split[ i ], 1, -1 ); if (( domain_spec = spf_parse_domainspec_cidr( s, domain, split[ i ], &cidr, &cidr6 )) == NULL ) { /* Macro expansion failed, probably a syntax problem. */ ret = SPF_RESULT_PERMERROR; goto cleanup; } rc = spf_check_a( s, domain, cidr, cidr6, domain_spec ); switch( rc ) { case SPF_RESULT_PASS: simta_debuglog( 2, "SPF %s [%s]: matched a %s/%ld/%ld: %s", s->spf_domain, domain, domain_spec, cidr, cidr6, spf_result_str( qualifier )); yaslfree( domain_spec ); ret = qualifier; goto cleanup; case SPF_RESULT_TEMPERROR: yaslfree( domain_spec ); ret = rc; goto cleanup; default: break; } yaslfree( domain_spec ); /* RFC 7208 5.4 "mx" */ } else if (( strcasecmp( split[ i ], "mx" ) == 0 ) || ( strncasecmp( split[ i ], "mx:", 3 ) == 0 ) || ( strncasecmp( split[ i ], "mx/", 3 ) == 0 )) { s->spf_queries++; mech_queries = 0; yaslrange( split[ i ], 2, -1 ); if (( domain_spec = spf_parse_domainspec_cidr( s, domain, split[ i ], &cidr, &cidr6 )) == NULL ) { /* Macro expansion failed, probably a syntax problem. */ ret = SPF_RESULT_PERMERROR; goto cleanup; } if (( dnsr_res_mech = get_mx( domain_spec )) == NULL ) { syslog( LOG_WARNING, "SPF %s [%s]: MX lookup %s failed", s->spf_domain, domain, domain_spec ); yaslfree( domain_spec ); ret = SPF_RESULT_TEMPERROR; goto cleanup; } for ( j = 0 ; j < dnsr_res_mech->r_ancount ; j++ ) { if ( dnsr_res_mech->r_answer[ j ].rr_type == DNSR_TYPE_MX ) { /* RFC 7208 4.6.4 DNS Lookup Limits * When evaluating the "mx" mechanism, the number of "MX" * resource records queried is included in the overall * limit of 10 mechanisms/modifiers that cause DNS lookups */ s->spf_queries++; rc = spf_check_a( s, domain, cidr, cidr6, dnsr_res_mech->r_answer[ j ].rr_mx.mx_exchange ); switch( rc ) { case SPF_RESULT_PASS: simta_debuglog( 2, "SPF %s [%s]: matched mx %s/%ld/%ld: %s", s->spf_domain, domain, domain_spec, cidr, cidr6, spf_result_str( qualifier )); ret = qualifier; dnsr_free_result( dnsr_res_mech ); yaslfree( domain_spec ); goto cleanup; case SPF_RESULT_PERMERROR: case SPF_RESULT_TEMPERROR: ret = rc; dnsr_free_result( dnsr_res_mech ); yaslfree( domain_spec ); goto cleanup; default: break; } } } dnsr_free_result( dnsr_res_mech ); yaslfree( domain_spec ); /* RFC 7208 5.5 "ptr" (do not use) */ } else if (( strcasecmp( split[ i ], "ptr" ) == 0 ) || ( strncasecmp( split[ i ], "ptr:", 4 ) == 0 )) { s->spf_queries++; mech_queries = 0; if (( dnsr_res_mech = get_ptr( s->spf_sockaddr )) == NULL ) { /* RFC 7208 5.5 "ptr" (do not use ) * If a DNS error occurs while doing the PTR RR lookup, * then this mechanism fails to match. */ continue; } if ( dnsr_res_mech->r_ancount == 0 ) { dnsr_free_result( dnsr_res_mech ); continue; } if ( split[ i ][ 3 ] == ':' ) { domain_spec = yaslnew(( split[ i ] + 4 ), ( yasllen( split[ i ] ) - 4 )); } else { domain_spec = yasldup( domain ); } for ( j = 0 ; j < dnsr_res_mech->r_ancount ; j++ ) { if ( dnsr_res_mech->r_answer[ j ].rr_type != DNSR_TYPE_PTR ) { continue; } /* We only care if it's a pass; like the initial PTR query, * DNS errors are treated as a non-match rather than an error. */ /* RFC 7208 4.6.4 DNS Lookup Limits * the evaluation of each "PTR" record MUST NOT result in * querying more than 10 address records -- either "A" or * "AAAA" resource records. If this limit is exceeded, all * records other than the first 10 MUST be ignored. */ if (( mech_queries++ < 10 ) && ( spf_check_a( s, domain, 32, 128, dnsr_res_mech->r_answer[ j ].rr_dn.dn_name ) == SPF_RESULT_PASS )) { tmp = yaslauto( dnsr_res_mech->r_answer[ j ].rr_dn.dn_name ); while (( yasllen( tmp ) > yasllen( domain_spec )) && ( p = strchr( tmp, '.' ))) { yaslrange( tmp, ( p - tmp + 1 ), -1 ); } rc = strcasecmp( tmp, domain_spec ); yaslfree( tmp ); if ( rc == 0 ) { simta_debuglog( 2, "SPF %s [%s]: matched ptr %s (%s): %s", s->spf_domain, domain, domain_spec, dnsr_res_mech->r_answer[ j ].rr_dn.dn_name, spf_result_str( qualifier )); ret = qualifier; yaslfree( domain_spec ); dnsr_free_result( dnsr_res_mech ); goto cleanup; } } } yaslfree( domain_spec ); dnsr_free_result( dnsr_res_mech ); /* RFC 7208 5.6 "ip4" and "ip6" * These mechanisms test whether <ip> is contained within a given * IP network. */ } else if ( strncasecmp( split[ i ], "ip4:", 4 ) == 0 ) { if ( s->spf_sockaddr->sa_family != AF_INET ) { continue; } yaslrange( split[ i ], 4, -1 ); if (( p = strchr( split[ i ], '/' )) != NULL ) { errno = 0; cidr = strtoul( p + 1, NULL, 10 ); if ( errno ) { syslog( LOG_WARNING, "SPF %s [%s]: failed parsing CIDR mask %s: %m", s->spf_domain, domain, p + 1 ); ret = SPF_RESULT_PERMERROR; goto cleanup; } if ( cidr > 32 ) { syslog( LOG_WARNING, "SPF %s [%s]: invalid CIDR mask: %ld", s->spf_domain, domain, cidr ); ret = SPF_RESULT_PERMERROR; goto cleanup; } yaslrange( split[ i ], 0, p - split[ i ] - 1 ); } else { cidr = 32; } if (( rc = simta_cidr_compare( cidr, s->spf_sockaddr, NULL, split[ i ] )) < 0 ) { syslog( LOG_WARNING, "SPF %s [%s]: simta_cidr_compare failed for %s", s->spf_domain, domain, split[ i ] ); ret = SPF_RESULT_PERMERROR; goto cleanup; } else if ( rc == 0 ) { simta_debuglog( 2, "SPF %s [%s]: matched ip4 %s/%ld: %s", s->spf_domain, domain, split[ i ], cidr, spf_result_str( qualifier )); ret = qualifier; goto cleanup; } } else if ( strncasecmp( split[ i ], "ip6:", 4 ) == 0 ) { if ( s->spf_sockaddr->sa_family != AF_INET6 ) { continue; } yaslrange( split[ i ], 4, -1 ); if (( p = strchr( split[ i ], '/' )) != NULL ) { errno = 0; cidr = strtoul( p + 1, NULL, 10 ); if ( errno ) { syslog( LOG_WARNING, "SPF %s [%s]: failed parsing CIDR mask %s: %m", s->spf_domain, domain, p + 1 ); } if ( cidr > 128 ) { syslog( LOG_WARNING, "SPF %s [%s]: invalid CIDR mask: %ld", s->spf_domain, domain, cidr ); ret = SPF_RESULT_PERMERROR; goto cleanup; } yaslrange( split[ i ], 0, p - split[ i ] - 1 ); } else { cidr = 128; } if (( rc = simta_cidr_compare( cidr, s->spf_sockaddr, NULL, split[ i ] )) < 0 ) { syslog( LOG_WARNING, "SPF %s [%s]: simta_cidr_compare failed for %s", s->spf_domain, domain, split[ i ] ); ret = SPF_RESULT_PERMERROR; goto cleanup; } else if ( rc == 0 ) { simta_debuglog( 2, "SPF %s [%s]: matched ip6 %s/%ld: %s", s->spf_domain, domain, split[ i ], cidr, spf_result_str( qualifier )); ret = qualifier; goto cleanup; } /* RFC 7208 5.7 "exists" */ } else if ( strncasecmp( split[ i ], "exists:", 7 ) == 0 ) { s->spf_queries++; yaslrange( split[ i ], 7, -1 ); if (( domain_spec = spf_macro_expand( s, domain, split[ i ] )) == NULL ) { /* Macro expansion failed, probably a syntax problem. */ ret = SPF_RESULT_PERMERROR; goto cleanup; } if (( dnsr_res_mech = get_a( domain_spec )) == NULL ) { syslog( LOG_WARNING, "SPF %s [%s]: A lookup %s failed", s->spf_domain, domain, domain_spec ); yaslfree( domain_spec ); ret = SPF_RESULT_TEMPERROR; goto cleanup; } if ( dnsr_res_mech->r_ancount > 0 ) { simta_debuglog( 2, "SPF %s [%s]: matched exists %s: %s", s->spf_domain, domain, domain_spec, spf_result_str( qualifier )); dnsr_free_result( dnsr_res_mech ); yaslfree( domain_spec ); ret = qualifier; goto cleanup; } yaslfree( domain_spec ); dnsr_free_result( dnsr_res_mech ); } else { for ( p = split[ i ] ; isalnum( *p ) ; p++ ); if ( *p == '=' ) { /* RFC 7208 6 Modifier Definitions * Unrecognized modifiers MUST be ignored */ simta_debuglog( 1, "SPF %s [%s]: %s unknown modifier %s", s->spf_domain, domain, spf_result_str( qualifier ), split[ i ] ); } else { syslog( LOG_WARNING, "SPF %s [%s]: %s unknown mechanism %s", s->spf_domain, domain, spf_result_str( qualifier ), split[ i ] ); ret = SPF_RESULT_PERMERROR; goto cleanup; } } } if ( redirect != NULL ) { if (( domain_spec = spf_macro_expand( s, domain, redirect )) == NULL ) { /* Macro expansion failed, probably a syntax problem. */ ret = SPF_RESULT_PERMERROR; } else { ret = spf_check_host( s, domain_spec ); yaslfree( domain_spec ); } if ( ret == SPF_RESULT_NONE ) { ret = SPF_RESULT_PERMERROR; } } else { /* RFC 7208 4.7 Default Result * If none of the mechanisms match and there is no "redirect" modifier, * then the check_host() returns a result of "neutral", just as if * "?all" were specified as the last directive. */ ret = SPF_RESULT_NEUTRAL; simta_debuglog( 2, "SPF %s [%s]: default result: %s", s->spf_domain, domain, spf_result_str( ret )); } cleanup: if ( split != NULL ) { yaslfreesplitres( split, tok_count ); } yaslfree( record ); dnsr_free_result( dnsr_res ); return( ret ); }
/*-------------------------------------------------------------------------*/ static bool add_struct_name (struct_name_t * pSName) /* Add the struct name <pSName> to the hash table. * The <pSName>->hash must already been computed. * Returns false on error. */ { size_t ix; #ifdef DEBUG if (find_by_struct_name(pSName)) fatal("struct type %s (%s) already in table.\n" , get_txt(pSName->name) , get_txt(pSName->prog_name) ); #endif if (!table) { table = pxalloc(sizeof(*table)); if (table == NULL) return false; table_size = 1; table[0] = pSName; pSName->next = NULL; num_types = 1; return true; } ix = pSName->hash & (table_size-1); pSName->next = table[ix]; table[ix] = pSName; num_types++; /* If chain lengths grow too much (more than 2 entries per bucket * on average), increase the table size */ if (num_types > 2 * table_size && table_size * 2 < MAX_HASH32) { size_t new_size = 2 * table_size; struct_name_t ** table2; table2 = pxalloc(new_size * sizeof(*table2)); if (table2) { memset(table2, 0, new_size * sizeof(*table2)); /* Rehash all existing entries */ for (ix = 0; ix < table_size; ix++) { struct_name_t * this; while (NULL != (this = table[ix])) { size_t ix2; table[ix] = this->next; ix2 = this->hash & (new_size-1); this->next = table2[ix2]; table2[ix2] = this; } } /* for() */ pfree(table); table = table2; table_size = new_size; } /* if (table2) */ else return false; } /* if (check for rehash condition) */ return true; } /* add_struct_name() */
/*-------------------------------------------------------------------------*/ void remove_unreferenced_structs (void) /* Free all structs in the table which are not marked as referenced. * This function must be called before freeing all unreferenced strings! */ { size_t num; if (!table || !table_size) return; for (num = 0; num < table_size; num++) { struct_name_t * this, * prev; for (prev = NULL, this = table[num]; this != NULL; ) { if (!test_memory_reference(this)) { prev = this; this = this->next; } else { struct_name_t * next = this->next; if (prev) prev->next = next; else table[num] = next; num_types--; /* Now we deallocate the memory for the struct name * structure. */ size_struct_type -= STRUCT_NAME_MEMSIZE; dprintf2(gcollect_outfd, "struct name %x '%s' was left " "unreferenced, freeing now.\n" , (p_int) this , (p_int) get_txt(this->name) ); /* Reference all strings and free them, to avoid unnecessary * 'string unreferenced' diagnostics. */ count_ref_from_string(this->name); free_mstring(this->name); count_ref_from_string(this->prog_name); free_mstring(this->prog_name); /* Reference the memory (to update its flags) and free it */ note_malloced_block_ref(this); xfree(this); this = next; } } } /* for (num) */ } /* remove_unreferenced_structs() */
/*-------------------------------------------------------------------------*/ svalue_t * f_sl_open (svalue_t *sp) /* EFUN sl_open * * int sl_open(string filename) * * Opens the file <filename> for use as a SQLite database. * If the file doesn't exists it will be created. * Only one open file per object is allowed. On success this * function returns 1, otherwise usually an error is thrown. */ { string_t *file; sqlite3 *db; sqlite_dbs_t *tmp; int err; file = check_valid_path(sp->u.str, current_object, STR_SQLITE_OPEN , MY_TRUE); if (!file) errorf("Illegal use of sl_open('%s')\n", get_txt(sp->u.str)); tmp = find_db (current_object); if (tmp) { free_mstring(file); errorf("The current object already has a database open.\n"); } err = sqlite3_open (get_txt(file), &db); free_mstring(file); if (err) { const char* msg = sqlite3_errmsg(db); sqlite3_close(db); errorf("sl_open: %s\n", msg ); /* NOTREACHED */ } /* create a new chain link and hang on the old chain */ tmp = new_db(); if(!tmp) { sqlite3_close(db); errorf("(sl_open) Out of memory: (%lu bytes)\n", (unsigned long) sizeof(*tmp)); } tmp->db = db; tmp->obj = current_object; current_object->open_sqlite_db = MY_TRUE; /* Synchronous is damn slow. Forget it. */ sqlite3_exec(db, "PRAGMA synchronous = OFF", NULL, NULL, NULL); sqlite3_set_authorizer(db, my_sqlite3_authorizer, NULL); free_string_svalue (sp); put_number (sp, 1); return sp; } /* f_sl_open() */
svalue_t * f_convert_charset (svalue_t *sp) /* EFUN convert_charset() * * string convert_charset(string str, string from_cs, string to_cs) * * Convert the string <str> from charset <from_cs> to charset <to_cs> * and return the converted string. * * The efun is only available on systems with libiconv. */ { iconv_t context; string_t *from_cs, *to_cs, *in_str, *out_str; #if HAS_ICONV_NONCONST_IN # define ICONV_IN_CAST (char**) #else # define ICONV_IN_CAST #endif const char *pIn; /* Input string pointer */ size_t in_len; /* Input length */ size_t in_left; /* Input length left */ char * out_buf; /* Output buffer */ size_t out_size; /* Size of the output buffer */ size_t out_left; /* Size left in output buffer */ char *pOut; /* Output string pointer */ in_str = sp[-2].u.str; from_cs = sp[-1].u.str; to_cs = sp->u.str; pIn = get_txt(in_str); in_len = mstrsize(in_str); in_left = in_len; /* If the input string is empty, we can return immediately * (and in fact must since the allocator will balk at allocating 0 bytes) */ if (!in_len) { sp -= 2; free_string_svalue(sp); free_string_svalue(sp+1); put_string(sp, sp[2].u.str); return sp; } /* Allocate a temporary output string */ out_size = in_len > 65536 ? (in_len + 33) : (2 * in_len); out_left = out_size; xallocate(out_buf, out_size, "iconv buffer"); pOut = out_buf; /* Open the iconv context */ context = iconv_open(get_txt(to_cs), get_txt(from_cs)); if (context == (iconv_t) -1) { xfree(out_buf); if (errno == EINVAL) errorf("convert_charset(): Conversion '%s' -> '%s' not supported.\n" , get_txt(from_cs), get_txt(to_cs) ); else errorf("convert_charset(): Error %d.\n", errno); /* NOTREACHED */ return sp; } /* Convert the string, reallocating the output buffer where necessary */ while (in_left) { size_t rc; rc = iconv(context, ICONV_IN_CAST &pIn, &in_left, &pOut, &out_left); if (rc == (size_t)-1) { if (errno == E2BIG) { /* Reallocate output buffer */ size_t newsize; char * tmp; newsize = out_size + (in_len > 128 ? in_len : 128); tmp = rexalloc(out_buf, newsize); if (!tmp) { iconv_close(context); xfree(out_buf); outofmem(newsize, "iconv buffer"); /* NOTREACHED */ return sp; } out_buf = tmp; pOut = out_buf + out_size; out_left = newsize - out_size; out_size = newsize; continue; } /* Other error: clean up */ iconv_close(context); xfree(out_buf); if (errno == EILSEQ) { errorf("convert_charset(): Invalid character sequence at " "index %td\n", (ptrdiff_t)(pIn - get_txt(in_str))); /* NOTREACHED */ return sp; } if (errno == EINVAL) { errorf("convert_charset(): Incomplete character sequence at " "index %td\n", (ptrdiff_t)(pIn - get_txt(in_str))); /* NOTREACHED */ return sp; } errorf("convert_charset(): Error %d at index %td\n" , errno, (ptrdiff_t)(pIn - get_txt(in_str)) ); /* NOTREACHED */ return sp; } /* if (rc < 0) */ } /* while (in_left) */ /* While the actual conversion is complete, the output stream may now * be in a non-base state. Add the necessary epilogue to get back * to the base state. */ while(1) { size_t rc; rc = iconv(context, NULL, NULL, &pOut, &out_left); if (rc == (size_t)-1) { if (errno == E2BIG) { /* Reallocate output buffer */ size_t newsize; char * tmp; newsize = out_size + (in_len > 128 ? in_len : 128); tmp = rexalloc(out_buf, newsize); if (!tmp) { iconv_close(context); xfree(out_buf); outofmem(newsize, "iconv buffer"); /* NOTREACHED */ return sp; } out_buf = tmp; pOut = out_buf + out_size; out_left = newsize - out_size; out_size = newsize; continue; } /* Other error: clean up */ iconv_close(context); xfree(out_buf); if (errno == EILSEQ) { errorf("convert_charset(): Invalid character sequence at " "index %td\n", (ptrdiff_t)(pIn - get_txt(in_str))); /* NOTREACHED */ return sp; } if (errno == EINVAL) { errorf("convert_charset(): Incomplete character sequence at " "index %td\n", (ptrdiff_t)(pIn - get_txt(in_str))); /* NOTREACHED */ return sp; } errorf("convert_charset(): Error %d at index %td\n" , errno, (ptrdiff_t)(pIn - get_txt(in_str)) ); /* NOTREACHED */ return sp; } /* if (rc < 0) */ /* At this point, the iconv() succeeded: we're done */ break; } /* while(1) */ iconv_close(context); /* Get the return string and prepare the return arguments */ out_str = new_n_mstring(out_buf, out_size - out_left); xfree(out_buf); if (!out_str) { outofmem(out_size - out_left, "convert_charset() result"); /* NOTREACHED */ return sp; } free_string_svalue(sp--); free_string_svalue(sp--); free_string_svalue(sp); put_string(sp, out_str); return sp; } /* f_convert_charset() */
/*-------------------------------------------------------------------------*/ Bool assert_simul_efun_object (void) /* (Re)load the simul_efun object and extract all information we need. * Result is TRUE if either the simul_efun object could be loaded, or if * master::get_simul_efun() did not return a string/string vector to * name the simul efun object. The result is FALSE if master::get_simul_efun() * specified a simul efun object, which couldn't be found. * * In other words: after calling assert_simul_efun_object(), the caller * still has to check if simul_efun_object is NULL. * * At the time of call, simul_efun_object must be NULL. */ { svalue_t *svp; object_t *ob; program_t *progp; CBool *visible; /* Flag for every function: visible or not */ string_t *name; int i, j, num_fun; invalidate_simul_efuns(); /* Invalidate the simul_efun information */ free_defines(); /* to prevent #defines hideing places for globals */ /* Get the name(s) of the simul_efun object. */ svp = apply_master(STR_GET_SEFUN, 0); /* If a simul_efun_object appears during the GET_SEFUN call, it * might have been due to a recursive get_simul_efun() call which may * have gotten an old backup copy. This can lead to hard-to-debug * variable and function definition inconsistencies. */ if (simul_efun_object) { printf("%s simul_efun object appeared while asking for it.\n", time_stamp()); return MY_TRUE; } if (svp == NULL) { printf("%s No simul_efun\n", time_stamp()); return MY_TRUE; } if (svp->type == T_POINTER) { simul_efun_vector = svp->u.vec; svp->type = T_NUMBER; if (VEC_SIZE(svp->u.vec)) svp = svp->u.vec->item; } if (svp->type != T_STRING) { printf("%s No simul_efun\n", time_stamp()); return MY_TRUE; } /* Make the (primary) simul_efun name */ name = del_slash(svp->u.str); if (simul_efun_file_name) free_mstring(simul_efun_file_name); simul_efun_file_name = make_tabled(name); /* Get the object and load the program */ ob = find_object(simul_efun_file_name); if (ob == NULL) { fprintf(stderr, "%s The simul_efun file %s was not loaded.\n" , time_stamp(), get_txt(simul_efun_file_name)); fprintf(stderr, "%s The function get_simul_efun() in the master must load it.\n" , time_stamp()); return MY_FALSE; } if (O_PROG_SWAPPED(ob) && load_ob_from_swap(ob) < 0) { fprintf(stderr, "%s Out of memory (unswap object '%s') ==> " "No simul_efun\n", time_stamp(), get_txt(ob->name)); return MY_TRUE; } reference_prog( (simul_efun_program = ob->prog), "get_simul_efun"); num_fun = ob->prog->num_function_names; if (num_fun == 0) return MY_TRUE; if (!simul_efunp) { simul_efunp = xalloc(sizeof (function_t) * num_fun); } else num_fun = total_simul_efun; free_defines(); /* to prevent #defines hideing places for globals */ /* locals and defines are freed now. There are still reserved words, * but it is impossible to define a function with the name being * a reserved word, thus, there will be no clashes with higher-priority * shared identifiers. */ progp = ob->prog; visible = alloca((i = ob->prog->num_functions) * sizeof(*visible)); memset(visible, 0, i); i = ob->prog->num_function_names; while (--i >= 0) visible[progp->function_names[i]] = MY_TRUE; /* The functions .num_function_names+1 .. .num_functions are not * visible by definition. */ /* Loop over the functions in the simul_efun object and * copy the salient information. */ for (i = 0; i < ob->prog->num_functions; i++) { int ix; funflag_t flags, flags2; bytecode_p funstart; mp_int fun_ix_offs, var_ix_offs; program_t *inherit_progp; function_t*funheader; if (!visible[i]) continue; ix = i; flags2 = flags = progp->functions[ix]; flags &= ~FUNSTART_MASK; /* Pinpoint the function, resolving inheritance where * necessary. */ fun_ix_offs = ix; var_ix_offs = 0; inherit_progp = progp; while (flags2 & NAME_INHERITED) { inherit_t *inheritp; inheritp = &inherit_progp->inherit[flags2 & INHERIT_MASK]; ix -= inheritp->function_index_offset; var_ix_offs += inheritp->variable_index_offset; inherit_progp = inheritp->prog; flags2 = inherit_progp->functions[ix]; } fun_ix_offs -= ix; funstart = inherit_progp->program + (flags2 & FUNSTART_MASK); funheader = inherit_progp->function_headers + FUNCTION_HEADER_INDEX(funstart); /* Don't stumble over undefined functions */ if (is_undef_function(funstart)) { flags |= NAME_UNDEFINED; } /* If the function is __INIT, pretend it's a private function */ if ( !(flags & (TYPE_MOD_STATIC|TYPE_MOD_PRIVATE|NAME_UNDEFINED)) ) { if (mstreq(funheader->name, STR_VARINIT)) flags |= TYPE_MOD_PRIVATE; } /* If the function is indeed visible, get its information */ if ( !(flags & (TYPE_MOD_STATIC|TYPE_MOD_PROTECTED|TYPE_MOD_PRIVATE|NAME_UNDEFINED)) ) { string_t *function_name; ident_t *p; unsigned char num_arg; function_name = funheader->name; num_arg = funheader->num_arg; /* Find or make the identifier for the function */ p = make_shared_identifier_mstr(function_name, I_TYPE_GLOBAL, 0); if (p->type == I_TYPE_UNKNOWN) { init_global_identifier(p, /* bVariable: */ MY_FALSE); p->next_all = all_simul_efuns; all_simul_efuns = p; } if (flags & TYPE_MOD_VARARGS) num_arg = SIMUL_EFUN_VARARGS; /* Find the proper index in simul_efunp[] */ switch(0) { default: /* TRY... */ /* Try to find a discarded sefun entry with matching * name, number of arguments and XVARARGS flag to reuse. */ if (all_discarded_simul_efun >= 0) { int last; j = all_discarded_simul_efun; while ( (j = simul_efunp[last = j].offset.next_sefun) >= 0) { if (num_arg != simul_efunp[j].num_arg || 0 != ((simul_efunp[j].flags ^ flags) & TYPE_MOD_XVARARGS) ) continue; if (!mstreq(function_name, simul_efunp[j].name)) continue; /* Found one: remove it from the 'discarded' list */ simul_efunp[last].offset.next_sefun = simul_efunp[j].offset.next_sefun; break; } if (j >= 0) break; /* switch */ } /* New simul_efun: make a new entry */ (void)ref_mstring(function_name); j = num_simul_efun++; if (num_simul_efun > num_fun) { num_fun = num_simul_efun + 12; simul_efunp = rexalloc(simul_efunp , sizeof (function_t) * num_fun ); } simul_efunp[j].name = function_name; simul_efunp[j].num_arg = num_arg; } /* switch() */ /* j now indexes the simul_efunp[] entry to use */ p->u.global.sim_efun = j; simul_efunp[j].flags = funheader->flags; simul_efunp[j].type = funheader->type; simul_efunp[j].num_locals = funheader->num_locals; /* If possible, make an entry in the simul_efun table */ if ((size_t)j < SEFUN_TABLE_SIZE) { simul_efun_table[j].funstart = funstart; simul_efun_table[j].program = inherit_progp; simul_efun_table[j].function_index_offset = fun_ix_offs; simul_efun_table[j].variable_index_offset = var_ix_offs; } } /* if (function visible) */ } /* for ( all functions) */ total_simul_efun = num_fun; simul_efun_object = ob; return MY_TRUE; } /* get_simul_efun_object() */
svalue_t * f_psyc_render(svalue_t *sp) { uint8_t i; vector_t *v; string_t *out; char *meth, *body; size_t mlen, blen; mapping_t *map; psycPacket packet; psycHeader headers[2]; // unless (sp->type == T_POINTER) return sp; v = sp->u.vec; if (VEC_SIZE(v) == PACKET_BODY + 1) { for (i = PACKET_ROUTING; i <= PACKET_ENTITY; i++) { headers[i].lines = 0; if (v->item[i].type == T_MAPPING) { map = v->item[i].u.map; if (!MAP_SIZE(map)) continue; headers[i].modifiers = malloc(sizeof(psycModifier) * MAP_SIZE(v->item[i].u.map)); if (!headers[i].modifiers) { errorf("Out of memory in psyc_render for modifier table.\n"); return sp; // not reached } walk_mapping(map, &fill_header_from_mapping, &(psyc_modifier_t) { &headers[i], map->num_values, i == PACKET_ROUTING ? PSYC_MODIFIER_ROUTING : PSYC_MODIFIER_CHECK_LENGTH }); } // else ... ignoring possibly invalid args } } else { errorf("Wrong number of elements (%" PRIdMPINT ") " "in array argument to psyc_render()\n", VEC_SIZE(v)); return sp; // not reached } if (v->item[PACKET_METHOD].type == T_STRING) { meth = get_txt(v->item[PACKET_METHOD].u.str); mlen = mstrsize(v->item[PACKET_METHOD].u.str); } else { meth = NULL; mlen = 0; } if (v->item[PACKET_BODY].type == T_STRING) { body = get_txt(v->item[PACKET_BODY].u.str); blen = mstrsize(v->item[PACKET_BODY].u.str); } else { body = NULL; blen = 0; } packet = psyc_newPacket2(headers[PACKET_ROUTING].modifiers, headers[PACKET_ROUTING].lines, headers[PACKET_ENTITY].modifiers, headers[PACKET_ENTITY].lines, meth, mlen, body, blen, PSYC_PACKET_CHECK_LENGTH); #ifdef DEBUG printf("rendering... packet.length = %ld\n", packet.length); #endif // alloc_mstring creates an *untabled* string suitable for tmp data memsafe(out = alloc_mstring(packet.length), packet.length, "f_psyc_render"); psyc_render(&packet, get_txt(out), packet.length); free_svalue(sp); put_string(sp, out); // stack should take care of freeing the string after use return sp; } /* f_psyc_render */