/** * Configure the user supplied buffer. */ static ag_bool fmem_config_user_buf(fmem_cookie_t * pFMC, void * buf, ssize_t len) { /* * User allocated buffer. User responsible for disposal. */ if (len == 0) { free(pFMC); errno = EINVAL; return AG_FALSE; } pFMC->buffer = (buf_bytes_t*)buf; /* Figure out where our "next byte" and EOF are. * Truncated files start at the beginning. */ if (pFMC->mode & FLAG_BIT(truncate)) { /* * "write" mode */ pFMC->eof = \ pFMC->next_ix = 0; } else if (pFMC->mode & FLAG_BIT(binary)) { pFMC->eof = len; pFMC->next_ix = (pFMC->mode & FLAG_BIT(append)) ? len : 0; } else { /* * append or read text mode -- find the end of the buffer * (the first NUL character) */ buf_bytes_t *p = (buf_bytes_t*)buf; pFMC->eof = 0; while ((*p != NUL) && (++(pFMC->eof) < len)) p++; pFMC->next_ix = (pFMC->mode & FLAG_BIT(append)) ? pFMC->eof : 0; } /* * text mode - NUL terminate buffer, if it fits. */ if ( ((pFMC->mode & FLAG_BIT(binary)) == 0) && (pFMC->next_ix < len)) { pFMC->buffer[pFMC->next_ix] = NUL; } pFMC->buf_size = len; return AG_TRUE; }
/** * Convert a mode string into mode bits. */ static int fmem_getmode(char const * mode, mode_bits_t * pRes) { if (mode == NULL) return 1; switch (*mode) { case 'a': *pRes = FLAG_BIT(write) | FLAG_BIT(append); break; case 'w': *pRes = FLAG_BIT(write) | FLAG_BIT(truncate); break; case 'r': *pRes = FLAG_BIT(read); break; default: return EINVAL; } /* * If someone wants to supply a "wxxbxbbxbb+" mode string, I don't care. */ for (;;) { switch (*++mode) { case '+': *pRes |= FLAG_BIT(read) | FLAG_BIT(write); if (mode[1] != NUL) return EINVAL; break; case NUL: break; case 'b': *pRes |= FLAG_BIT(binary); continue; case 'x': continue; default: return EINVAL; } break; } return 0; }
/** * Allocate an initial buffer for fmem. */ static ag_bool fmem_alloc_buf(fmem_cookie_t * pFMC, ssize_t len) { /* * We must allocate the buffer. If "len" is zero, set it to page size. */ pFMC->mode |= FLAG_BIT(allocated); if (len == 0) len = pFMC->pg_size; /* * Unallocated file space is set to NULs. Emulate that. */ pFMC->buffer = calloc((size_t)1, (size_t)len); if (pFMC->buffer == NULL) { errno = ENOMEM; free(pFMC); return AG_FALSE; } /* * We've allocated the buffer. The end of file and next entry * are both zero. */ pFMC->next_ix = 0; pFMC->eof = 0; pFMC->buf_size = len; return AG_TRUE; }
int main(int argc, char **argv) { int flags = 0; int c = 0; extern char *optarg; char *filter = NULL; while ((c = getopt(argc, argv, "pf:")) != -1) { switch (c) { case 'p': flags |= FLAG_BIT(FLAG_PROMISC); break; case 'f': filter = strdup(optarg); break; default: usage(argv[0]); exit(-1); break; /* not reached */ } } if (filter == NULL) { filter = "ip"; } process(flags, filter); return (0); }
/** * Extend the space associated with an fmem file. */ static int fmem_extend(fmem_cookie_t *pFMC, size_t new_size) { size_t ns = (new_size + (pFMC->pg_size - 1)) & (~(pFMC->pg_size - 1)); /* * We can expand the buffer only if we are in append mode. */ if (pFMC->mode & FLAG_BIT(fixed_size)) goto no_space; if ((pFMC->mode & FLAG_BIT(allocated)) == 0) { /* * Previously, this was a user supplied buffer. We now move to one * of our own. The user is responsible for the earlier memory. */ void* bf = malloc(ns); if (bf == NULL) goto no_space; memcpy(bf, pFMC->buffer, pFMC->buf_size); pFMC->buffer = bf; pFMC->mode |= FLAG_BIT(allocated); } else { void* bf = realloc(pFMC->buffer, ns); if (bf == NULL) goto no_space; pFMC->buffer = bf; } /* * Unallocated file space is set to zeros. Emulate that. */ memset(pFMC->buffer + pFMC->buf_size, 0, ns - pFMC->buf_size); pFMC->buf_size = ns; return 0; no_space: errno = ENOSPC; return -1; }
/** * Free up the memory associated with an fmem file. * If the user is managing the space, then the allocated bit is set. */ static int fmem_close(void * cookie) { fmem_cookie_t * pFMC = cookie; cookie_fp_map_t * pmap = (void *)map; unsigned int mct = map_ct; while (mct-- != 0) { if (pmap->cookie == cookie) { *pmap = map[--map_ct]; break; } pmap++; } if (mct > map_ct) errno = EINVAL; if (pFMC->mode & FLAG_BIT(allocated)) free(pFMC->buffer); free(pFMC); return 0; }
/*=export_func ag_fmemopen * * what: Open a stream to a string * * arg: + void* + buf + buffer to use for i/o + * arg: + ssize_t + len + size of the buffer + * arg: + char* + mode + mode string, a la fopen(3C) + * * ret-type: FILE* * ret-desc: a stdio FILE* pointer * * err: NULL is returned and errno is set to @code{EINVAL} or @code{ENOSPC}. * * doc: * * This function requires underlying @var{libc} functionality: * either @code{fopencookie(3GNU)} or @code{funopen(3BSD)}. * * If @var{buf} is @code{NULL}, then a buffer is allocated. The initial * allocation is @var{len} bytes. If @var{len} is less than zero, then the * buffer will be reallocated as more space is needed. Any allocated * memory is @code{free()}-ed when @code{fclose(3C)} is called. * * If @code{buf} is not @code{NULL}, then @code{len} must not be zero. * It may still be less than zero to indicate that the buffer may * be reallocated. * * The mode string is interpreted as follows. If the first character of * the mode is: * * @table @code * @item a * Then the string is opened in "append" mode. In binary mode, "appending" * will begin from the end of the initial buffer. Otherwise, appending will * start at the first NUL character in the initial buffer (or the end of the * buffer if there is no NUL character). Do not use fixed size buffers * (negative @var{len} lengths) in append mode. * * @item w * Then the string is opened in "write" mode. Any initial buffer is presumed * to be empty. * * @item r * Then the string is opened in "read" mode. * @end table * * @noindent * If it is not one of these three, the open fails and @code{errno} is * set to @code{EINVAL}. These initial characters may be followed by: * * @table @code * @item + * The buffer is marked as updatable and both reading and writing is enabled. * * @item b * The I/O is marked as "binary" and a trailing NUL will not be inserted * into the buffer. Without this mode flag, one will be inserted after the * @code{EOF}, if it fits. It will fit if the buffer is extensible (the * provided @var{len} was negative). This mode flag has no effect if * the buffer is opened in read-only mode. * * @item x * This is ignored. * @end table * * @noindent * Any other letters following the inital 'a', 'w' or 'r' will cause an error. =*/ FILE * ag_fmemopen(void * buf, ssize_t len, char const * mode) { fmem_cookie_t *pFMC; { mode_bits_t bits; if (fmem_getmode(mode, &bits) != 0) { return NULL; } pFMC = malloc(sizeof(fmem_cookie_t)); if (pFMC == NULL) { errno = ENOMEM; return NULL; } pFMC->mode = bits; } /* * Two more mode bits that do not come from the mode string: * a negative size implies fixed size buffer and a NULL * buffer pointer means we must allocate (and free) it. */ if (len <= 0) { /* * We only need page size if we might extend an allocation. */ len = -len; pFMC->pg_size = getpagesize(); } else { pFMC->mode |= FLAG_BIT(fixed_size); } if (buf != NULL) { if (! fmem_config_user_buf(pFMC, buf, len)) return NULL; } else if ((pFMC->mode & (FLAG_BIT(append) | FLAG_BIT(truncate))) == 0) { /* * Not appending and not truncating. We must be reading. * We also have no user supplied buffer. Nonsense. */ errno = EINVAL; free(pFMC); return NULL; } else if (! fmem_alloc_buf(pFMC, len)) return NULL; #ifdef TEST_FMEMOPEN saved_cookie = pFMC; #endif { FILE * res; cookie_read_function_t* pRd = (pFMC->mode & FLAG_BIT(read)) ? (cookie_read_function_t*)fmem_read : NULL; cookie_write_function_t* pWr = (pFMC->mode & FLAG_BIT(write)) ? (cookie_write_function_t*)fmem_write : NULL; #ifdef HAVE_FOPENCOOKIE cookie_io_functions_t iof; iof.read = pRd; iof.write = pWr; iof.seek = fmem_seek; iof.close = fmem_close; res = fopencookie(pFMC, mode, iof); #else res = funopen(pFMC, pRd, pWr, fmem_seek, fmem_close); #endif if (res == NULL) return res; if (++map_ct >= map_alloc_ct) { void * p = (map_alloc_ct > 0) ? realloc((void *)map, (map_alloc_ct += 4) * sizeof(*map)) : malloc((map_alloc_ct = 4) * sizeof(*map)); if (p == NULL) { fclose(res); errno = ENOMEM; /* "fclose" set it to "EINVAL". */ return NULL; } map = p; } { cookie_fp_map_t * p = (void *)(map + map_ct - 1); p->fp = res; p->cookie = pFMC; } return res; } }
/** * Handle file system callback to write data to our string */ static ssize_t fmem_write(void *cookie, const void *pBuf, size_t sz) { fmem_cookie_t *pFMC = cookie; int add_nul_char; /* * In append mode, always seek to the end before writing. */ if (pFMC->mode & FLAG_BIT(append)) pFMC->next_ix = pFMC->eof; /* * Only add a NUL character if: * * * we are not in binary mode * * there are data to write * * the last character to write is not already NUL */ add_nul_char = ((pFMC->mode & FLAG_BIT(binary)) != 0) && (sz > 0) && (((char*)pBuf)[sz - 1] != NUL); { size_t next_pos = pFMC->next_ix + sz + add_nul_char; if (next_pos > pFMC->buf_size) { if (fmem_extend(pFMC, next_pos) != 0) { /* * We could not extend the memory. Try to write some data. * Fail if we are either at the end or not writing data. */ if ((pFMC->next_ix >= pFMC->buf_size) || (sz == 0)) return -1; /* no space at all. errno is set. */ /* * Never add the NUL for a truncated write. "sz" may be * unchanged or limited here. */ add_nul_char = 0; sz = pFMC->buf_size - pFMC->next_ix; } } } memcpy(pFMC->buffer + pFMC->next_ix, pBuf, sz); pFMC->next_ix += sz; /* * Check for new high water mark and remember it. Add a NUL if * we do that and if we have a new high water mark. */ if (pFMC->next_ix > pFMC->eof) { pFMC->eof = pFMC->next_ix; if (add_nul_char) /* * There is space for this NUL. The "add_nul_char" is not part of * the "sz" that was added to "next_ix". */ pFMC->buffer[ pFMC->eof ] = NUL; } return sz; }
void process(int flags) { char *interface=NULL; char errbuf[PCAP_ERRBUF_SIZE]; struct bpf_program prog; bpf_u_int32 network, netmask; char *filter=NULL; int flagdef; filter="tcp"; signal(SIGHUP, SIG_IGN); signal(SIGINT, signal_handler); signal(SIGQUIT, signal_handler); signal(SIGTERM, signal_handler); /* Attempt to find the interface */ interface=pcap_lookupdev(errbuf); if(interface==NULL) { fprintf(stderr, "pcap_lookupdev: %s\n", errbuf); exit(-1); } if(pcap_lookupnet(interface, &network, &netmask, errbuf) < 0) { fprintf(stderr, "pcap_lookupnet: %s\n", errbuf); exit(-1); } if(flags&FLAG_BIT(FLAG_PROMISC)) /* find out if we want to be promisc */ flagdef=1; else flagdef=0; pcap_socket=pcap_open_live(interface, 1024, flagdef, 1024, errbuf); if(pcap_socket==NULL) { fprintf(stderr, "pcap_open_live: %s\n", errbuf); exit(-1); } switch(pcap_datalink(pcap_socket)) { case DLT_EN10MB: dlt_len=14; break; case DLT_SLIP: dlt_len=16; break; case DLT_PPP: dlt_len=4; break; case DLT_FDDI: fprintf(stderr, "Sorry, can't do FDDI\n"); signal_handler(-1); break; default: dlt_len=4; } if(pcap_compile(pcap_socket, &prog, filter, 1, netmask) < 0) { fprintf(stderr, "pcap_compile: %s\n", errbuf); signal_handler(-1); } if(pcap_setfilter(pcap_socket, &prog) < 0) { fprintf(stderr, "pcap_setfilter: %s\n", errbuf); signal_handler(-1); } fprintf(stderr, "interface: %s, filter: %s, promiscuous: %s\n", interface, filter, (flags&FLAG_BIT(FLAG_PROMISC))?"yes":"no"); for(;;) pcap_loop(pcap_socket, -1, (pcap_handler)filter_packet, NULL); }