/* * Mausezahn - A fast versatile traffic generator * Copyright (C) 2008-2010 Herbert Haas * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License version 2 as published by the * Free Software Foundation. * * 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, see http://www.gnu.org/licenses/gpl-2.0.html * */ #include "mz.h" #include "mops.h" // Creates first element, aka "head" element // This element can also be used! See automops_alloc_protocol! // struct automops * automops_init(void) { // Create initial automops element: struct automops *new_automops = (struct automops*) calloc(1, sizeof(struct automops)); new_automops->next = new_automops; new_automops->prev = new_automops; automops_set_defaults (new_automops); new_automops->used = -1; // freshly created, no valid data in it return new_automops; } // (Re-)sets anything within the specified automops element void automops_set_defaults(struct automops * cur) { int i; mz_strncpy(cur->name, "user_proto", 16); mz_strncpy(cur->desc, "undefined", 16); cur->layers_on = 0; cur->layers_off = 0; cur->etype = 0; cur->proto = 0; for (i=0; i<6; i++) { cur->sa[i] = 0x00; cur->da[i] = 0xff; // bcast (silly?) } cur->SA = cur->DA = 0; cur->sp = cur->dp = 0; cur->payload_type = 0; // both ascii or hex cur->payload = NULL; cur->payload_s = 0; cur->defined_externally = -1; // undefined if (cur->field != NULL) automops_delete_fields (cur); cur->field = NULL; } // Returns pointer to new automops element: // 1) either insert a new automops element in list // 2) or returns same pointer again if current automops element is empty // Note that new element N is always PREPENDED to cur: // ... = N-2 = N-1 = N = cur = 1 = 2 = ... // Therefore, cur should be typically a pointer to the head element // struct automops * automops_alloc_protocol(struct automops *cur) { struct automops *new_automops; if (cur->used == -1) // allows to use head element in list { new_automops = cur; // current automops was unused => no need to insert a new one! } else // create new automops element { new_automops = (struct automops *) malloc(sizeof(struct automops)); if (new_automops==NULL) { fprintf(stderr, "MZ alert: cannot create new automops entry - memory full?\n"); return NULL; // memory full? } new_automops->field=NULL; // don't remove this! See automops_set_defaults() to understand. automops_set_defaults(new_automops); } new_automops->used=0; // append to doubly linked list new_automops->prev = cur->prev; new_automops->next = cur; cur->prev = new_automops; new_automops->prev->next = new_automops; return new_automops; } // Delete particular protocol (remove it from list or mops). // // If amp_head is deleted, makes previous element amp_head. // Note that the global amp_head must exist but within MOPS this // is always the case. // // RETURN VALUE: // // - pointer to previous element in the list // - NULL if current automops is used by some mops(es) // (in this case, we may still need it, maybe the user wants // to modify data or wants other information...?) // // - NULL if current element is a single element attached to a mops // struct automops * automops_delete_protocol(struct automops *cur) { struct automops *last; // Maybe the following is not really practical? ///// if (cur->used>0) { return NULL; } ///////////////////////////////////////////////////// // delete fields list: automops_delete_fields (cur); if (cur->payload_s) free (cur->payload); if ((cur!=amp_head) && (cur->prev==NULL) && (cur->next==NULL)) { // this one is attached to a mops if (cur!=NULL) free (cur); return NULL; } // part of linked list last = cur->prev; cur->next->prev = cur->prev; cur->prev->next = cur->next; if (cur==amp_head) { amp_head = last; } if (cur!=NULL) free (cur); return last; } // Search automops element for a given protocol name // // Returns pointer to that automops element // or NULL if not found // struct automops * automops_search_protocol(struct automops *list, char *name) { struct automops *head = list; struct automops *cur = list; do { if ( (strncasecmp(name, cur->name, AUTOMOPS_MAX_NAME_LEN) == 0)) { return cur; // FOUND! } cur = cur->next; } while (head != cur); return NULL; // NOT FOUND! } // Runs through all automops entries and dumps some basic info // Returns the number of used protocols // int automops_dump_all(struct automops* list) { struct automops *head = list; struct automops *cur = list; struct fields *f=NULL; int anzmops=0, used=0; char str[64], ft[32]; uint32_t SA=0, DA=0; uint8_t *x, *y; char bits_on[18], bits_off[18]; int i=0, j=0; do { if (cur->used==-1) { fprintf(stderr, "AUTOMOPS: Initial element\n"); if ((cur->next==cur)&&(cur->prev==cur)) fprintf(stderr, " No other elements found.\n"); break; } if (cur->used>0) used++; anzmops++; SA=ntohl(cur->SA); x = (uint8_t*) &SA; DA=ntohl(cur->DA); y = (uint8_t*) &DA; char2bits(cur->layers_on, bits_on); char2bits(cur->layers_off, bits_off); fprintf(stderr, "Protocol %i: %s -- %s\n" " Layercodes: X T U I M Q S E\n" " requires %s (0x%02x)\n" " conflicts %s (0x%02x)\n" " L2: EtherType=%04x, sa=%02x:%02x:%02x:%02x:%02x:%02x, da=%02x:%02x:%02x:%02x:%02x:%02x\n" , anzmops, cur->name, cur->desc, bits_on, cur->layers_on, bits_off, cur->layers_off, cur->etype, cur->sa[0], cur->sa[1], cur->sa[2], cur->sa[3], cur->sa[4], cur->sa[5], cur->da[0], cur->da[1], cur->da[2], cur->da[3], cur->da[4], cur->da[5]); if (cur->layers_on&MOPS_IP) { fprintf(stderr, " IP: proto=%i, SA=%u.%u.%u.%u, DA=%u.%u.%u.%u\n", cur->proto, *x, *(x+1), *(x+2), *(x+3), *y, *(y+1), *(y+2), *(y+3)); } else { fprintf(stderr, " IP: ---\n"); } // Walk through field data: f=cur->field; j=0; while (f!=NULL) { j++; // count number of fields if (verbose) { i=0; if (f->longdesc!=NULL) { mz_strncpy(str, f->longdesc, 60); if (strnlen(str,60)>=59) i=1; } else { mz_strncpy(str, "-- no long description specified --", 60); } amp_type2str(f->type, ft); fprintf(stderr, " %02i Field [%i] '%s' -- %s\n" " Description: %s%s\n" " Type: %s %s %s (%lu/%lu) {%lu..%lu..%lu} shift: %i; %i chars\n" ,f->i, f->index, f->name, f->shortdesc, str, (i) ? "..." : "", ft, (f->constant) ? "FIXED" : "", (f->valname!=NULL) ? f->valname : "(no value name)" , (long unsigned int) f->tlv_type, (long unsigned int) f->tlv_len, (long unsigned int) f->min, (long unsigned int) f->val, (long unsigned int) f->max, f->leftshift, f->str_s); } f=f->next; } if (verbose==0) fprintf(stderr, " %i fields defined.\n", j); //--------------------------------- cur = cur->next; } while (head != cur); return used; } // Creates an independent automops element for mops // (it will be not part of any linked list so, next=prev=NULL) // // RETURN VALUE: - Pointer to the cloned automops // - NULL upon failure // struct automops * automops_clone_automops(struct automops * amp) { struct automops *new_automops; struct fields *f, *g, *h=NULL; int i; // Allocate memory new_automops = (struct automops *) malloc(sizeof(struct automops)); if (new_automops==NULL) { fprintf(stderr, "MZ alert: cannot create new automops element - memory full?\n"); return NULL; // memory full? } // Copy the automops items new_automops->next = NULL; new_automops->prev = NULL; strncpy(new_automops->name, amp->name, AUTOMOPS_MAX_NAME_LEN); strncpy(new_automops->desc, amp->desc, AUTOMOPS_MAX_SHORTDESC_LEN); new_automops->layers_on = amp->layers_on; new_automops->layers_off = amp->layers_off; new_automops->etype = amp->etype; new_automops->proto = amp->proto; for (i=0; i<6; i++) { new_automops->da[i] = amp->da[i]; // dst mac new_automops->sa[i] = amp->sa[i]; // src mac } new_automops->DA = amp->DA; // dst IP new_automops->SA = amp->SA; // src IP new_automops->dp = amp->dp; // dst port new_automops->sp = amp->sp; // src port new_automops->defined_externally = amp->defined_externally; new_automops->payload_type = amp->payload_type; if (amp->payload_s) { new_automops->payload = (char*) malloc(amp->payload_s); if (new_automops->payload==NULL) { fprintf(stderr, "MZ alert: cannot create new automops payload element - memory full?\n"); return NULL; // memory full? } memcpy((void*) new_automops->payload, amp->payload, amp->payload_s); } new_automops->used = amp->used; //////////////////////////////////////////////////////////////////////////////////////////////// // // Copy the fields list // new_automops->field = NULL; for (f=amp->field; f!=NULL; f=f->next) { g = (struct fields *) malloc(sizeof(struct fields)); if (g==NULL) { fprintf(stderr, "MZ alert: cannot create new field element - memory full?\n"); return NULL; // memory full? } if (new_automops->field==NULL) { // first element new_automops->field = g; h = g; } else { // next elements h->next = g; h = g; } // copy all data. From here on 'h' is the new one, 'f' is the existing one mz_strncpy(h->name, f->name, AUTOMOPS_MAX_NAME_LEN); mz_strncpy(h->shortdesc, f->shortdesc, AUTOMOPS_MAX_SHORTDESC_LEN); mz_strncpy(h->valname, f->valname, AUTOMOPS_MAX_NAME_LEN); if (f->longdesc!=NULL) { h->longdesc = (char*) malloc(strnlen(f->longdesc, 1600)); // 80 chars x 20 lines should be enough if (h->longdesc == NULL) { fprintf(stderr, "MZ alert: cannot allocate memory!\n"); return NULL; // memory full? } strncpy(h->longdesc, f->longdesc, 1600); } if (f->str_s) { h->str_s = f->str_s; h->str = (u_int8_t *) malloc(f->str_s); if (h->str == NULL) { fprintf(stderr, "MZ alert: cannot allocate memory!\n"); return NULL; // memory full? } memcpy((void*) h->str, (void*) f->str, f->str_s); } h->constant = f->constant; h->type = f->type; h->tlv_type = f->tlv_type; h->tlv_len = f->tlv_len; h->val = f->val; h->min = f->min; h->max = f->max; h->leftshift = f->leftshift; h->index = f->index; } return new_automops; } // Add a new field object struct fields * automops_add_field (struct automops *amp) { struct fields *f, *f_prev=NULL, *g; int i=0; // jump to the end of the fields list f=amp->field; while (f!=NULL) { f_prev=f; ++i; f=f->next; } g = (struct fields *) malloc(sizeof(struct fields)); if (g==NULL) { if (verbose) fprintf(stderr, "MZ alert: cannot create new field element - memory full?\n"); return NULL; // memory full? } if (amp->field==NULL) { // is is first element in amp amp->field = g; } else { // it is just another element in the fields list f_prev->next = g; } g->next=NULL; // 'pointing to NULL' identifies the last element g->i=i; // each field element has a unique internal number g->index=0; // indicates 'empty' field automops_field_set_defaults(g); return g; } // Typically only used by automops_add_field() // Only call this function after creating a new field element void automops_field_set_defaults(struct fields *f) { f->name[0]=0x00; f->shortdesc[0]=0x00; f->longdesc=NULL; f->constant=0; //NOTE: f->i MUST NOT be reset! f->index=0; f->valname[0]=0x00; f->tlv_type=0; f->tlv_len=0; f->val=0; f->min=0; f->max=0; f->leftshift=0; f->str=NULL; f->str_s=0; f->next=NULL; } // Returns integer equivalent for a string of basic protocols. // For example returns MOPS_ETH | MOPS_IP for d="eth ip". // See the definitions in mops.h. // // NOTE: Does (and must) NOT verify whether items are conflicting // such as "udp tcp". This task MUST be done by callee, otherwise // this function's purpose would be not generic enough. // // RETURN VALUE: // The sum of basic protocols // or -1 upon failure. int mops_str2layers(char *d) { int ret=0; char *tok; // dissalow too long strings. if (strlen(d)>50) return -1; // TODO: replace 100 to a more reasonable value tok=strtok(d, " "); while (tok!=NULL) { if (strncasecmp("eth", d, 10)==0) ret |= MOPS_ETH; else if (strncasecmp("snap", d, 10)==0) ret |= MOPS_SNAP; else if (strncasecmp("dot1q", d, 10)==0) ret |= MOPS_dot1Q; else if (strncasecmp("mpls", d, 10)==0) ret |= MOPS_MPLS; else if (strncasecmp("ip", d, 10)==0) ret |= MOPS_IP; else if (strncasecmp("udp", d, 10)==0) ret |= MOPS_UDP; else if (strncasecmp("tcp", d, 10)==0) ret |= MOPS_TCP; else return -1; // unknown tok=strtok(NULL, " "); } return ret; } // Returns one of 'enum fieldtypes' for a given ascii string // or -1 if unknown field type given. int amp_str2type(char *d) { if (strncasecmp("byte8", d, 10)==0) return Byte8; if (strncasecmp("byte16", d, 10)==0) return Byte16; if (strncasecmp("byte32", d, 10)==0) return Byte32; if (strncasecmp("flaginbyte", d, 16)==0) return Flag_in_Byte; if (strncasecmp("multibytes", d, 16)==0) return MultiBytes; if (strncasecmp("multibyteshex", d, 16)==0) return MultiBytesHex; if (strncasecmp("tlv", d, 10)==0) return TLV; return -1; } // Converts integer field types into ascii string s[32]. // Returns 0 upon success, 1 if unknown type int amp_type2str(int t, char *s) { switch (t) { case Byte8: mz_strncpy(s, "Byte8", 32); break; case Byte16: mz_strncpy(s, "Byte16", 32); break; case Byte32: mz_strncpy(s, "Byte32", 32); break; case Flag_in_Byte: mz_strncpy(s, "FlagInByte", 32); break; case MultiBytes: mz_strncpy(s, "MultiBytes", 32); break; case MultiBytesHex: mz_strncpy(s, "MultiBytesHex", 32); break; case TLV: mz_strncpy(s, "TLV", 32); break; default: mz_strncpy(s, "[unknown/same]", 32); return 1; } return 0; } // Searches the automops object with specified name 'd'. // NOTE: The names are case insensitive! // // RETURN VALUE: pointer to that object // or NULL if not found // struct automops * amp_getamp_byname(struct automops *head, char *d) { struct automops *a; a = head; do { if (strncasecmp(a->name, d, AUTOMOPS_MAX_NAME_LEN)==0) return a; a=a->next; } while (a!=head); return NULL; // not found } // Add data 'd' identified by tag 'xntag' to the automops entry 'amp'. // // RETURN VALUE: 0 upon success, 1 upon failure // int amp_add_pentry (struct automops *amp, int xntag, char *d) { int i=0; char *tok; u_int8_t x[MAX_MOPS_MSG_SIZE]; struct automops *g; switch (xntag) { case xml_name: if (strpbrk(d," \t")!=NULL) return ampInvalidName; // name must not consist of multiple words! g = amp_getamp_byname(amp_head, d); if (g!=NULL) return ampDuplicateName; // name already exists! mz_strncpy(amp->name, d, AUTOMOPS_MAX_NAME_LEN); if (verbose==2) { fprintf(stderr, "Adding protocol '%s'\n", amp->name); } break; case xml_desc: mz_strncpy(amp->desc, d, AUTOMOPS_MAX_SHORTDESC_LEN); break; case xml_requires: i = mops_str2layers(d); if (i==-1) return ampInvalidLayer; if ((i&MOPS_UDP) && (i&MOPS_TCP)) return ampTCPandUDP; // cannot require both! amp->layers_on |= i; // must be ORed because several same-tags allowed break; case xml_conflicts: i = mops_str2layers(d); if (i==-1) return ampInvalidLayer; amp->layers_off |= i; // must be ORed because several same-tags allowed break; case xml_payloadtype: // 0=none, 1=ascii, 2=hex, 3=any tok = strtok (d," "); while (tok!=NULL) { if (strncasecmp("allowed", d, 10)==0) { // only change if payload_type is still zero if (amp->payload_type==0) amp->payload_type=3; } else if (strncasecmp("ascii", d, 10)==0) amp->payload_type|=1; else if (strncasecmp("hex", d, 10)==0) amp->payload_type|=2; else if (strncasecmp("any", d, 10)==0) amp->payload_type=3; else if (strncasecmp("none", d, 10)==0) amp->payload_type=0; else return ampPayloadType; // unknown tok=strtok(NULL, " "); } break; case xml_payload: i=strnlen(d,MAX_MOPS_MSG_SIZE); if (i==MAX_MOPS_MSG_SIZE) return ampPayloadLen; amp->payload = (char*) malloc (i+1); mz_strncpy(amp->payload, d, i+1); amp->payload_s = i; break; case xml_payloadhex: i=str2hex(d,x,MAX_MOPS_MSG_SIZE); if (i==MAX_MOPS_MSG_SIZE) return ampPayloadLen; if (i==-1) return 1; amp->payload = (char*) malloc (i+1); memcpy((void*)amp->payload, (void*) x, i); amp->payload_s = i; break; default: return ampUnknownTag; } return 0; } // Checks if given index value would be valid for the specified amp. // (Index values must increase monotonic, successive same-values are // allowed, step size is 0 or 1 but not greater. First index value // must be 1. Example: 1,2,2,2,3,4,5,5,5,5,5,6,7,7,7.) // // RETURN VALUE: 0 if ok, 1 if wrong // int amp_checkindex(struct automops *amp, int i) { int last_i=0; struct fields *g, *h=NULL; g=amp->field; while (g!=NULL) { // jump to last field object P->F1->F2->NULL if (g->index==0) break; // stop if empty field object found h=g; g=g->next; } // now h is the penultimate object // printf("CHECKINDEX: try for %i, amp='%s' -- field '%s', index %i, [%i]\n", // i, amp->name, h->name, h->index, h->i); if (h==NULL) return 0; // first element, so any i is ok last_i=h->index; if (i1) return 1; // index increase step larger 1! return 0; } // Searches the field object with specified name 'd'. // NOTE: The names ar case insensitive! // // RETURN VALUE: pointer to that object // or NULL if not found // struct fields * amp_getfield_byname(struct automops *amp, char *d) { struct fields *f; f = amp->field; while (f!=NULL) { if (strncasecmp(f->name, d, AUTOMOPS_MAX_NAME_LEN)==0) return f; f=f->next; } return NULL; // not found } // This strange function ensures that 'w' consists of a single word. // If 'w' consists of multiple words, it removes all but the first // word. Additionally surrounding spaces are removed. // // RETURN VALUE: number of words found // // EXAMPLE: "Hello world" => "Hello" // (return value = 2) // int ensure_single_word(char *w) { char *t, *t0; int i=0; t=strtok(w," "); t0=t; while (t!=NULL) { i++; t=strtok(NULL, " "); } mz_strncpy(w, t0, AUTOMOPS_MAX_NAME_LEN); return i; } // Add data 'd' identified by tag 'xntag' to the field entry 'f' int amp_add_fentry (struct automops *amp, struct fields *f, int xntag, char *d) { int i=0; unsigned long long int ulli=0; struct fields *g=NULL; switch(xntag) { case xml_index: i = (int) str2int(d); if (amp_checkindex(amp, i)) return ampInvalidIndex; // invalid index f->index = (int) i; break; case xml_name: if (ensure_single_word(d)>1) return ampInvalidName; // name must be single word g = amp_getfield_byname(amp, d); if (g!=NULL) return 1; // name already exists mz_strncpy(f->name, d, AUTOMOPS_MAX_NAME_LEN); break; case xml_desc: mz_strncpy(f->shortdesc, d, AUTOMOPS_MAX_SHORTDESC_LEN); break; case xml_longdesc: i = strnlen(d, 400); if (i==400) return ampDescTooLong; f->longdesc = (char*) malloc(i+1); mz_strncpy(f->longdesc, d, i+1); break; case xml_type: i = amp_str2type(d); if (i==-1) return ampInvalidType; f->type = i; break; case xml_constant: if (strncasecmp(d, "yes", 6)==0) f->constant=1; else if (strncasecmp(d, "no", 6)==0) f->constant=0; else return ampUnknownKeyword; // unknown keyword break; case xml_valname: if (ensure_single_word(d)>1) return ampSingleWordRequired; // name must be single word i = strnlen(d, AUTOMOPS_MAX_NAME_LEN); if (i==AUTOMOPS_MAX_NAME_LEN) return 1; // too long mz_strncpy(f->valname, d, AUTOMOPS_MAX_NAME_LEN); break; case xml_value: ulli = str2lint(d); if (ulli>0xffffffff) return ampRangeError; f->val = (u_int32_t) ulli; break; case xml_min: ulli = str2lint(d); if (ulli>0xffffffff) return ampRangeError; f->min = (u_int32_t) ulli; break; case xml_max: ulli = str2lint(d); if (ulli>0xffffffff) return ampRangeError; if (ullimin) return 1; // max must be greater or equal min f->max = (u_int32_t) ulli; break; case xml_tlvt: ulli = str2lint(d); if (ulli>0xffffffff) return ampRangeError; f->tlv_type = (u_int32_t) ulli; break; case xml_tlvl: ulli = str2lint(d); if (ulli>0xffffffff) return ampRangeError; f->tlv_len = (u_int32_t) ulli; break; case xml_lshift: i = (int) str2int(d); if (i>7) return ampRangeError; f->leftshift=i; break; default: return ampUnknownTag; // unknown tag } return 0; } // Delete linked list of field elements for a given automops // Returns the number of deleted elements int automops_delete_fields (struct automops *amp) { struct fields * cur = amp->field; struct fields * tmp; int i=0; if (cur==NULL) return 0; do { tmp = cur; cur = cur->next; if (tmp->str_s) { if (tmp->str!=NULL) { free (tmp->str); tmp->str=NULL; } } if (tmp->longdesc!=NULL) { free(tmp->longdesc); tmp->longdesc=NULL; } if (tmp!=NULL) { free(tmp); tmp=NULL; } i++; } while (cur!=NULL); return i; } // Deletes all elements except the specified element which us usually // the head element. Also 'used' elements will be removed! // void automops_delete_all (struct automops *list) { struct automops *head = list; struct automops *cur = list->next; struct automops *tmp; // Delete all but head element: while (head != cur) { tmp = cur->next; if (verbose) { fprintf(stderr, " Deleting '%s'\n",cur->name); } automops_delete_protocol(cur); cur = tmp; } head->next = head; head->prev = head; if (verbose) { fprintf(stderr, " Deleting '%s'\n",head->name); } if (head->payload_s) { if (head->payload!=NULL) { free (head->payload); head->payload=NULL; } } automops_set_defaults(head); } // Completely clean up. // After that, there is no automops list anymore. // You only need this function when stopping mausezahn. // void automops_cleanup (struct automops *list) { // 1) delete all elements except head: automops_delete_all(list); // 2) delete head: automops_delete_fields (list); if (list->payload_s) { if (list->payload!=NULL) { free (list->payload); list->payload=NULL; } } if (list!=NULL) { free(list); list=NULL; } } // Converts amperr error values in 'e' to string messages 's' // which must be at least 64 bytes in size. // // RETURN VALUE: 0 if convertable, 1 else // int amperr2str (int e, char *s) { switch (e) { case ampSuccess: break; case ampInvalidIndex: mz_strncpy(s, "invalid index", 64); break; case ampInvalidName: mz_strncpy(s, "invalid name", 64); break; case ampDuplicateName: mz_strncpy(s, "duplicate name", 64); break; case ampDescTooLong: mz_strncpy(s, "description too long", 64); break; case ampInvalidLayer: mz_strncpy(s, "invalid layer", 64); break; case ampTCPandUDP: mz_strncpy(s, "either TCP or UDP", 64); break; case ampInvalidType: mz_strncpy(s, "invalid type", 64); break; case ampUnknownKeyword: mz_strncpy(s, "unknown keyword", 64); break; case ampSingleWordRequired: mz_strncpy(s, "single word required", 64); break; case ampRangeError: mz_strncpy(s, "invalid range", 64); break; case ampPayloadType: mz_strncpy(s, "invalid payload type", 64); break; case ampPayloadLen: mz_strncpy(s, "payload length exceeded", 64); break; case ampUnknownTag: mz_strncpy(s, "unknown tag (check mausezahn version?)", 64); break; default: mz_strncpy(s, "completely unknown cause", 64); return 1; } return 0; } // Open amp file (containing XML data describing one or more protocols for automops) // and copy the data into a char array. // // NOTE that the char array must be free'd by the caller. // // RETURN VALUE: - pointer to char array with the XML data // - NULL upon failure // char * mapfile (char *fn) { int i, c; long fn_s; FILE *fd; char *blob; fd = fopen (fn, "r"); if (fd==NULL) return NULL; // Determine length of file (void) fseek(fd, 0L, SEEK_END); fn_s = ftell(fd); if (fn_s > AUTOMOPS_MAX_FILE_SIZE) { fprintf(stderr, " File '%s' exceeds max allowed length (%lu>%i)\n", fn, fn_s, AUTOMOPS_MAX_FILE_SIZE); fclose(fd); return NULL; } if (verbose) fprintf(stderr, " Parsing %lu bytes from '%s'...\n", fn_s, fn); rewind(fd); blob = (char*) malloc(fn_s+1); if (blob==NULL) { fclose(fd); return NULL; } i=0; while ((c=fgetc(fd)) != EOF) { blob[i]=(char) c; i++; if (i>fn_s) { fprintf(stderr, " WARNING: parsing '%s' exceeded EOF\n", fn); break; // should not reach here } } fclose(fd); blob[i]='\0'; return blob; } // Create automops PDU within *mp based on data in *amp // int automops_update (struct mops *mp, struct automops *amp) { return 0; }