/* ARISA - Loading and Saving Functions * Copyright (C) 2003, 2004 Carl Ritson * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #include "arisa.h" #include #include #include #include "script-eng.h" #include "base64.h" #include /* Structures/Type Definitions */ typedef struct field_t field_t; typedef struct section_t section_t; typedef struct config_t config_t; struct field_t { char *name; char *data; }; struct section_t { char *id; field_t **fields; int no_fields; void *ptr; int ptr_ref; config_t *config; }; struct config_t { section_t **sections; int no_sections; }; /* Functions */ static char *get_line(FILE *fp, char **buffer, size_t *bufsize) { size_t tmp, pos = 0; char *ret; do { if(pos > 0) { D(""); (*bufsize) *= 2; (*buffer) = xreloc(*buffer,*bufsize); } ret = readline_file_nt(fp,(*buffer)+pos,(*bufsize)-pos,&tmp); //D("ret: %p, tmp: %d, pos: %d",ret,tmp,pos); //D("buffer: \"%s\"",*buffer); if(ret == NULL || tmp == 0) { if(pos == 0) return NULL; else return *buffer; } pos += tmp; } while((*buffer)[pos] != '\n'); (*buffer)[pos] = '\0'; trim(*buffer); return *buffer; } static char *decode_field_data(char *data) { size_t len; int wpos, rpos; char *ret; if(data[0] == '"' && data[(len = strlen(data))-1] == '"') { if(len == 1) return data; ret = data+1; wpos = 1; rpos = 1; data[len-1] = '\0'; } else { return data; } while(data[rpos] != '\0') { if(data[rpos] == '\\') { unsigned long ul; char *p; switch (data[rpos+1]) { case 'n': data[wpos++] = '\n'; rpos += 2; break; case '"': data[wpos++] = '"'; rpos += 2; break; case '\\': data[wpos++] = '\\'; rpos += 2; break; case 'x': ul = strtoul(data+rpos+2,&p,16); data[wpos++] = (char) ul; rpos = (p - data); break; case '0': ul = strtoul(data+rpos+2,&p,8); data[wpos++] = (char) ul; rpos = (p - data); break; default: data[wpos++] = '\\'; rpos++; break; } } else if(wpos != rpos) { data[wpos++] = data[rpos++]; } else { wpos++; rpos++; } } return ret; } static char *encode_field_data(char *field, char *buffer, size_t bufsize) { size_t len = strlen(field); int wpos,rpos; assert(bufsize >= 3); if(!(isspace(field[0]) || isspace(field[len-1])) && strchr(field,'\n') == 0) return field; if(bufsize < (len*2)+3) { bufsize = (len*2) + 3; buffer = xalloc(bufsize); } buffer[0] = '"'; for(wpos = 1, rpos = 0; wpos < (bufsize-3) && field[rpos] != '\0'; ++rpos) { if(field[rpos] == '\\') { buffer[wpos++] = '\\'; buffer[wpos++] = '\\'; } else if(field[rpos] == '"') { buffer[wpos++] = '\\'; buffer[wpos++] = '"'; } else if(field[rpos] == '\n') { buffer[wpos++] = '\\'; buffer[wpos++] = 'n'; } } buffer[wpos++] = '"'; buffer[wpos] = '\0'; return buffer; } static void load_section(FILE *fp, section_t *s, char **buffer_ptr, size_t *bufsize_ptr) { char *buffer; char *p1,*p2; field_t *field; while((buffer = get_line(fp,buffer_ptr,bufsize_ptr)) != NULL) { if(buffer[0] == '#' || buffer[0] == '\0') continue; p1 = buffer; splitline(p1,&p2); if(p1[0] == '}') break; if(p2 == NULL) continue; field = xalloc(sizeof(field_t)); PTRARR_ADD(&s->fields,&s->no_fields,field); field->name = xstrdup(p1); field->data = xstrdup(decode_field_data(p2)); } } static config_t *load_config(const char *filename) { section_t *s; config_t *c; size_t bufsize = 1024; char *buffer = xalloc(bufsize); char *p1,*p2; FILE *fp; fp = fopen(filename,"r"); if(fp == NULL) return NULL; c = xalloc(sizeof(config_t)); c->sections = NULL; c->no_sections = 0; while(get_line(fp,&buffer,&bufsize) != NULL) { if(buffer[0] == '#' || buffer[0] == '\0') continue; p1 = buffer; splitline(p1,&p2); if(p2 == NULL) continue; if(p2[0] != '{') continue; s = xalloc(sizeof(section_t)); PTRARR_ADD(&c->sections,&c->no_sections,s); s->id = xstrdup(p1); s->fields = NULL; s->no_fields = 0; s->ptr = NULL; s->ptr_ref = 0; s->config = c; load_section(fp,s,&buffer,&bufsize); } fclose(fp); xfree(buffer); return c; } static int save_config(char *filename, config_t *config) { char buffer[1024]; FILE *fp; int i,j,ret = 0; fp = fopen(filename,"w"); if(fp == NULL) return -1; for(i = 0; i < config->no_sections; ++i) { section_t *s = config->sections[i]; fprintf(fp,"%s {\n",s->id); for(j = 0; j < s->no_fields; ++j) { char *data = encode_field_data(s->fields[j]->data, buffer,sizeof(buffer)); ret = fprintf(fp,"\t%s %s\n",s->fields[j]->name,data); if(data != s->fields[j]->data && data != buffer) xfree(data); if(ret < 0) goto out; } ret = fprintf(fp,"}\n"); if(ret < 0) goto out; } ret = 0; out: fclose(fp); return ret; } static void free_field(field_t *field) { if(field->name != NULL) xfree(field->name); if(field->data != NULL) xfree(field->data); xfree(field); } static void free_section(section_t *section) { if(section->fields != NULL) { int i; for(i = 0; i < section->no_fields; ++i) free_field(section->fields[i]); xfree(section->fields); } if(section->id != NULL) xfree(section->id); xfree(section); } static void free_config(config_t *config) { if(config->sections != NULL) { int i; for(i = 0; i < config->no_sections; ++i) free_section(config->sections[i]); xfree(config->sections); } xfree(config); } static section_t *new_section(const char *type, void *p) { section_t *s = xalloc(sizeof(section_t)); if(type != NULL) { char buffer[128]; snprintf(buffer,sizeof(buffer),"%s:%p",type,p); s->id = xstrdup(buffer); } else { s->id = NULL; } s->fields = NULL; s->no_fields = 0; s->ptr = NULL; s->ptr_ref = 0; return s; } static void add_section(config_t *config, section_t *section) { int i,found; for(i = 0, found = 0; i < config->no_sections && !found; ++i) { if(strcmp(config->sections[i]->id,section->id) == 0) found = 1; } if(!found) PTRARR_ADD(&config->sections,&config->no_sections,section); else free_section(section); } static void fail_loading(section_t *section, const char *name, const char *value) { if(section != NULL) { if(value == NULL) fprintf(stderr,"Error While Loading Section: %s, Critical Field: %s is Missing\n",section->id,name); else fprintf(stderr,"Error While Loading Section: %s, Unable to locate Critical Structure %s from reference %s\n",section->id,name,value); } else fprintf(stderr,"Error While Loading, Currupt or Invalid Configuration File\n"); exit(1); } /* add_field functions */ static void add_field(section_t *section, const char *name, const char *data) { field_t *f = xalloc(sizeof(field_t)); f->name = xstrdup(name); f->data = xstrdup(data); PTRARR_ADD(§ion->fields,§ion->no_fields,f); } static void add_field_int(section_t *s, const char *n, int d) { char buffer[16]; snprintf(buffer,sizeof(buffer),"%d",d); add_field(s,n,buffer); } static void add_field_unsigned_int(section_t *s, const char *n, unsigned int d) { char buffer[16]; snprintf(buffer,sizeof(buffer),"%u",d); add_field(s,n,buffer); } #if 0 static void add_field_long(section_t *s, const char *n, long d) { char buffer[16]; snprintf(buffer,sizeof(buffer),"%ld",d); add_field(s,n,buffer); } #endif #if 0 static void add_field_unsigned_long(section_t *s, const char *n, unsigned long d) { char buffer[16]; snprintf(buffer,sizeof(buffer),"%lu",d); add_field(s,n,buffer); } #endif static void add_field_float(section_t *s, const char *n, float d) { char buffer[32]; /* do we need more precision than this? */ snprintf(buffer,sizeof(buffer),"%.5f",d); add_field(s,n,buffer); } static void add_field_time_t(section_t *s, const char *n, time_t d) { char buffer[16]; snprintf(buffer,sizeof(buffer),"%lu",(unsigned long)d); add_field(s,n,buffer); } static void add_field_char(section_t *s, const char *n, const char *d) { if(d != NULL) add_field(s,n,d); } static void add_field_ptr(section_t *s, const char *n, const char *type, void *ptr) { char buffer[32]; if(ptr != NULL) { snprintf(buffer,sizeof(buffer),"%s:%p",type,ptr); add_field(s,n,buffer); } } static void add_field_off_t(section_t *s, const char *n, off_t d) { char buffer[64]; snprintf(buffer,sizeof(buffer),"%lld",(long long)d); add_field(s,n,buffer); } static void add_field_size_t(section_t *s, const char *n, size_t d) { char buffer[32]; snprintf(buffer,sizeof(buffer),"%ld",(long)d); add_field(s,n,buffer); } #if 0 static void add_field_int32_t(section_t *s, const char *n, int32_t d) { char buffer[32]; snprintf(buffer,sizeof(buffer),"%ld",(long)d); add_field(s,n,buffer); } #endif static void add_field_uint32_t(section_t *s, const char *n, uint32_t d) { char buffer[32]; snprintf(buffer,sizeof(buffer),"%lu",(unsigned long)d); add_field(s,n,buffer); } /* find_ functions */ static char *find_field(section_t *section, const char *name) { int i; for(i = 0; i < section->no_fields; ++i) { if(strcmp(section->fields[i]->name,name) == 0) return section->fields[i]->data; } return NULL; } static char **find_fields(section_t *section, const char *name, int *j) { char **fields = NULL; int i; for(i = 0, *j = 0; i < section->no_fields; ++i) { if(strcmp(section->fields[i]->name,name) == 0) PTRARR_ADD(&fields,j,section->fields[i]->data); } return fields; } static void *find_ptr(section_t *section, const char *id) { config_t *c = section->config; int i; for(i = 0; i < c->no_sections; ++i) { if(strcmp(c->sections[i]->id,id) == 0) { c->sections[i]->ptr_ref++; return c->sections[i]->ptr; } } return NULL; } /* remove_ functions */ static void remove_field(section_t *section, const char *name) { int i; for(i = 0; i < section->no_fields; ++i) { if(strcmp(section->fields[i]->name,name) == 0) { free_field(section->fields[i]); PTRARR_DEL(&(section->fields),&(section->no_fields), section->fields[i]); return; } } } /* load_ functions (those used by dynamic code) */ static int load_int(const char *d) { return atoi(d); } static unsigned int load_unsigned_int(const char *d) { return (unsigned int)strtoul(d,NULL,10); } #if 0 static long load_long(const char *d) { return atol(d); } #endif #if 0 static unsigned long load_unsigned_long(const char *d) { return strtoul(d,NULL,10); } #endif static float load_float(const char *d) { return (float)strtod(d,NULL); } static time_t load_time_t(const char *d) { return (time_t)strtoul(d,NULL,10); } static off_t load_off_t(const char *d) { return (off_t)strtoll(d,NULL,10); } #if 0 static size_t load_size_t(const char *d) { return (size_t) strtol(d,NULL,10); } #endif static uint32_t load_uint32_t(const char *d) { return (uint32_t)strtoul(d,NULL,10); } /* Include the actual loading and saving code which is automatically * generated based on the contents of structs.h. */ #include "loadsave-autogen.c" /* Now linkage into main routines */ static char *type_of_id(const char *id, char *buffer, size_t bufsize) { int i; for(i = 0; id[i] != ':' && id[i] != '\0' && i < (bufsize-1); ++i) { buffer[i] = id[i]; } buffer[i] = '\0'; return buffer; } /* Don't look at this function it's one *big* hack! */ static void fix_access_structures(config_t *config) { section_t *s, *sf, **to_add = NULL, **interfaces = NULL, **packlists = NULL; field_t *f, **chan_to_net = NULL; char buffer[64]; long unique = 31337; int no_to_add = 0, no_interfaces = 0, no_packlists = 0, no_chan_to_net = 0; int i,j,k,do_fix; for(i = 0; i < config->no_sections; ++i) { type_of_id(config->sections[i]->id,buffer,sizeof(buffer)); if(strcmp(buffer,"packlist_t") == 0) { PTRARR_ADD(&packlists,&no_packlists,config->sections[i]); } else if(strcmp(buffer,"interface_t") == 0) { s = config->sections[i]; for(j = 0; j < s->no_fields; ++j) { if(strcmp(s->fields[j]->name,"type") == 0) { if(strcmp(s->fields[j]->data,"INTERFACE_RECV") == 0) PTRARR_ADD(&interfaces,&no_interfaces,s); } } } else if(strcmp(buffer,"network_t") == 0) { s = config->sections[i]; for(j = 0; j < s->no_fields; ++j) { if(strcmp(s->fields[j]->name,"channels") == 0) { f = xalloc(sizeof(field_t)); f->name = xstrdup(s->fields[j]->data); f->data = xstrdup(s->id); PTRARR_ADD(&chan_to_net,&no_chan_to_net,f); } } } } for(i = 0; i < no_packlists; ++i) { s = packlists[i]; for(j = 0, do_fix = 0; j < s->no_fields && !do_fix; ++j) { if(strcmp(s->fields[j]->name,"access") == 0 && strncmp(s->fields[j]->data,"channel_t",9) == 0) do_fix = 1; } s = new_section("access_t",(void *)(unique++)); add_field_int(s,"flags",ACCESS_SMART); if(do_fix) { for(j = 0; j < packlists[i]->no_fields; ++j) { if(strcmp(packlists[i]->fields[j]->name,"access") == 0) { sf = new_section("access_entry_t",(void *)(unique++)); add_field_int(sf,"flags",ACCESS_ALLOW); add_field(sf,"channel",packlists[i]->fields[j]->data); for(k = 0, do_fix = 0; k < no_chan_to_net && !do_fix; ++k) { if(strcmp(chan_to_net[k]->name,packlists[i]->fields[j]->data) == 0) { add_field(sf,"network",chan_to_net[k]->data); do_fix = 1; } } if(do_fix) { add_field(s,"entries",sf->id); PTRARR_ADD(&to_add,&no_to_add,sf); } } } for(j = 0; j < packlists[i]->no_fields; ++j) { if(strcmp(packlists[i]->fields[j]->name,"access") == 0) { free_field(packlists[i]->fields[j]); packlists[i]->fields[j] = NULL; } } PTRARR_DEL(&(packlists[i]->fields),&(packlists[i]->no_fields),NULL); } PTRARR_ADD(&to_add,&no_to_add,s); add_field(packlists[i],"access",s->id); } for(i = 0; i < no_interfaces; ++i) { s = interfaces[i]; for(j = 0, do_fix = 0; j < s->no_fields && !do_fix; ++j) { if(strcmp(interfaces[i]->fields[j]->name,"recv.hosts") == 0) do_fix = 1; } s = new_section("access_t",(void *)(unique++)); add_field_int(s,"flags",ACCESS_DENY); if(do_fix) { for(j = 0; j < interfaces[i]->no_fields; ++j) { if(strcmp(interfaces[i]->fields[j]->name,"host") == 0) { sf = new_section("access_entry_t",(void *)(unique++)); add_field_int(sf,"flags",ACCESS_ALLOW); add_field(sf,"host",interfaces[i]->fields[j]->data); add_field(s,"entries",sf->id); PTRARR_ADD(&to_add,&no_to_add,sf); } } for(j = 0; j < interfaces[i]->no_fields; ++j) { if(strcmp(interfaces[i]->fields[j]->name,"access") == 0) { free_field(interfaces[i]->fields[j]); interfaces[i]->fields[j] = NULL; } } PTRARR_DEL(&(interfaces[i]->fields),&(interfaces[i]->no_fields),NULL); } PTRARR_ADD(&to_add,&no_to_add,s); add_field(interfaces[i],"recv.access",s->id); } for(i = 0; i < no_to_add; ++i) { to_add[i]->config = config; add_section(config,to_add[i]); } if(to_add != NULL) xfree(to_add); if(packlists != NULL) xfree(packlists); if(interfaces != NULL) xfree(interfaces); for(i = 0; i < no_chan_to_net; ++i) free_field(chan_to_net[i]); if(chan_to_net != NULL) xfree(chan_to_net); } static void fix_pack_structures(config_t *config) { char buffer[64]; int i; for(i = 0; i < config->no_sections; ++i) { section_t *s = config->sections[i]; type_of_id(s->id,buffer,sizeof(buffer)); if(strcmp(buffer,"pack_t") == 0) { char *hashed = find_field(s,"hashed"); if(hashed == NULL) { remove_field(s,"crc"); remove_field(s,"md5"); } else { int hashed_val = atoi(hashed); if(hashed_val == 0) { remove_field(s,"crc"); remove_field(s,"md5"); } else { if(find_field(s,"crc") == NULL || find_field(s,"md5") == NULL) { remove_field(s,"hashed"); remove_field(s,"crc"); remove_field(s,"md5"); } } } } } } static void load_se_data(config_t *config) { section_t *s = NULL; int i; for(i = config->no_sections - 1; i >= 0 && s == NULL; --i) { if(strcmp(config->sections[i]->id,"se_data") == 0) s = config->sections[i]; } if(s == NULL) return; for(i = 0; i < s->no_fields; ++i) { field_t *f = s->fields[i]; char buffer[128]; char *cdata, *data, *len_field; size_t bclen, clen; unsigned long len; if((len = strlen(f->name)) >= 6) { if(strcmp(f->name + len - 5,"_size") == 0) continue; } snprintf(buffer,sizeof(buffer),"%s_size",f->name); len_field = find_field(s,buffer); if(len_field == NULL) continue; bclen = strlen(f->data); clen = bclen; cdata = xalloc(clen); len = (size_t) strtoul(len_field,NULL,10) + 24; data = xalloc(len); if(base64_dec(cdata,&clen,f->data,bclen) != 0) { xfree(cdata); xfree(data); continue; } if(uncompress(data,&len,cdata,clen) != Z_OK) { xfree(cdata); xfree(data); continue; } xfree(cdata); if(se_load_data(f->name,data,len) != 0) xfree(data); } } global_t *load_global(const char *filename, int sends) { config_t *config = load_config(filename); char buffer[64]; global_t *ret = NULL; int i,j; if(config == NULL) return NULL; if(sends == 0) { for(i = 0; i < config->no_sections; ++i) { type_of_id(config->sections[i]->id,buffer,sizeof(buffer)); if(strcmp(buffer,"send_t") == 0) { free_section(config->sections[i]); config->sections[i] = NULL; } } PTRARR_DEL(&config->sections,&config->no_sections,NULL); } /* This calls a fix to convert old access lists to new enhanced access lists, should be removed before beta/release. Only affects early alpha testers. */ fix_access_structures(config); // This one isn't temporary fix_pack_structures(config); /* Init all the Data Structures */ for(i = 0; i < config->no_sections; ++i) { type_of_id(config->sections[i]->id,buffer,sizeof(buffer)); for(j = 0; _section_funcs[j].type != NULL && config->sections[i]->ptr == NULL; ++j) { if(strcmp(_section_funcs[j].type,buffer) == 0) _section_funcs[j].init(config->sections[i]); } if(config->sections[i]->ptr == NULL) { xfree(config->sections[i]->id); /* effectively NULL the id */ config->sections[i]->id = xstrdup(buffer); } } /* Load them with Data */ for(i = 0; i < config->no_sections; ++i) { type_of_id(config->sections[i]->id,buffer,sizeof(buffer)); if(config->sections[i]->ptr != NULL) { for(j = 0; _section_funcs[j].type != NULL; ++j) { if(strcmp(_section_funcs[j].type,buffer) == 0) _section_funcs[j].load(config->sections[i]); } } } /* Load reference count, has to be done after all loads complete */ for(i = 0; i < config->no_sections; ++i) { type_of_id(config->sections[i]->id,buffer,sizeof(buffer)); if(config->sections[i]->ptr != NULL) { for(j = 0; _section_funcs[j].type != NULL; ++j) { if(_section_funcs[j].ref_count != NULL && strcmp(_section_funcs[j].type,buffer) == 0) _section_funcs[j].ref_count(config->sections[i]); } } } /* Free any unused ones */ for(i = 0; i < config->no_sections; ++i) { type_of_id(config->sections[i]->id,buffer,sizeof(buffer)); if(config->sections[i]->ptr != NULL && config->sections[i]->ptr_ref == 0 && strcmp(buffer,"global_t") != 0) { for(j = 0; _section_funcs[j].type != NULL; ++j) { if(strcmp(_section_funcs[j].type,buffer) == 0) _section_funcs[j].deinit(config->sections[i]); } } else if(strcmp(buffer,"global_t") == 0) ret = (global_t *)config->sections[i]->ptr; } load_se_data(config); free_config(config); if(ret == NULL) fail_loading(NULL,NULL,NULL); if(ret->settings == NULL) fail_loading(NULL,NULL,NULL); ret->settings->config = xstrdup(filename); return ret; } static void save_se_data(config_t *config) { se_save_data_t **data; section_t *s; int i,no_data; data = se_save_data(&no_data); if(data == NULL) return; s = new_section(NULL,NULL); s->id = xstrdup("se_data"); for(i = 0; i < no_data; ++i) { char buffer[128]; char *cdata, *bcdata; unsigned long clen; size_t bclen; field_t *f; clen = data[i]->size*2; cdata = xalloc(clen); compress2(cdata,&clen,data[i]->data,data[i]->size, Z_BEST_COMPRESSION); xfree(data[i]->data); bclen = clen * 2; bcdata = xalloc(bclen); base64_enc(bcdata,&bclen,cdata,clen); xfree(cdata); snprintf(buffer,sizeof(buffer),"%s_size",data[i]->name); add_field_size_t(s,buffer,data[i]->size); f = xalloc(sizeof(field_t)); f->name = data[i]->name; // no need to copy it f->data = bcdata; xfree(data[i]); PTRARR_ADD(&s->fields,&s->no_fields,f); } xfree(data); add_section(config,s); } int save_global(global_t *gptr) { config_t *config; char *fn,*bakfn; int ret; LOCK(gptr->settings); if(gptr->settings->config != NULL) fn = xstrdup(gptr->settings->config); else fn = NULL; UNLOCK(gptr->settings); if(fn == NULL) { LOGP(L_ERR,"Save: Failed, No Config File Specified."); return -1; } bakfn = xalloc(strlen(fn)+5); sprintf(bakfn,"%s.bak",fn); ret = rename(fn,bakfn); if(ret == -1) { char errbuf[96]; LOGP(L_INF,"Save: Ignoring Error \"%s\" while attempting to rename \"%s\" to \"%s\".", lstrerror_r(errno,errbuf,sizeof(errbuf)) == 0 ? errbuf : "Unknown", fn,bakfn); } xfree(bakfn); config = xalloc(sizeof(config_t)); config->sections = NULL; config->no_sections = 0; LOGP(L_INF,"Save: Building Configuration Snapshot."); save_global_t(config,gptr); LOGP(L_INF,"Save: Snapshot Built, %d objects captured.", config->no_sections); LOGP(L_INF,"Save: Capturing Scripting Engine Data."); save_se_data(config); LOGP(L_INF,"Save: Captured Scripting Engine Data."); ret = save_config(fn,config); if(ret == -1) { char errbuf[96]; LOGP(L_ERR,"Save: while writing to \"%s\", Error: %s",fn, lstrerror_r(errno,errbuf,sizeof(errbuf)) == 0 ? errbuf : "Unknown"); unlink(fn); } else LOGP(L_INF,"Save: configuration successfully written to \"%s\".",fn); free_config(config); xfree(fn); return ret; }