diff options
author | Daniel Baumann <daniel@debian.org> | 2024-11-21 17:49:05 +0100 |
---|---|---|
committer | Daniel Baumann <daniel@debian.org> | 2024-11-21 17:49:05 +0100 |
commit | 4a8e45026e0f5de7d9711cfe1ddd23518b1d17db (patch) | |
tree | 3f0f2bf5c60c901954649edcbf7611328575ff29 /src/scramble_ips.c | |
parent | Initial commit. (diff) | |
download | cryptopant-843d3904126ba91a6700024c549ee0e62744e822.tar.xz cryptopant-843d3904126ba91a6700024c549ee0e62744e822.zip |
Adding upstream version 1.3.1.upstream/1.3.1upstream
Signed-off-by: Daniel Baumann <daniel@debian.org>
Diffstat (limited to 'src/scramble_ips.c')
-rw-r--r-- | src/scramble_ips.c | 487 |
1 files changed, 487 insertions, 0 deletions
diff --git a/src/scramble_ips.c b/src/scramble_ips.c new file mode 100644 index 0000000..5222301 --- /dev/null +++ b/src/scramble_ips.c @@ -0,0 +1,487 @@ +/* -*- Mode:C; c-basic-offset:4; tab-width:8; indent-tabs-mode:t -*- */ +/* + * Copyright (C) 2004-2024 by the University of Southern California + * $Id: a91d5228a362749a1ccff71f0fbdc092ac7ebdcc $ + * + * 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, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + * + */ + +/* setup environment */ +#define _GNU_SOURCE 1 +#define _FILE_OFFSET_BITS 64 + +#include <stdio.h> + +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <getopt.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> +#include <regex.h> //xxx need to check in autoconf +#include <assert.h> +#include "cryptopANT.h" + +//autoconfigured +#include "config.h" + +//long options +#define PASS4 256 +#define PASS6 257 +#define KTYPE 258 +#define IPPREFIX 259 +#define IPSUFFIX 260 + +#define IP4MAXLEN 15 //strlen("xxx.xxx.xxx.xxx") +#define IP6MAXLEN 39 //strlen("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx") +#define CBUF_SZ (IP6MAXLEN*2) + +#define INITBUFSIZE 4096 //xxx need more space in case text expands due to IP scrambling + +#if HAVE__U6_ADDR32 +#define s6_addr32 __u6_addr.__u6_addr32 +#endif + +#if HAVE_REGEX_H +static const char REGEX4[]="((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))"; +static const char REGEX6[]="(((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:)))(%.+)?)"; +#endif //HAVE_REGEX_H + +static int pass_bits4 = 0; +static int pass_bits6 = 0; +static int reverse_mode = 0; + +//buffers for in/out processing +static char *linebuf1 = NULL,*linebuf2 = NULL; +static size_t linebuf1_sz = 0, linebuf2_sz = 0; + +static char cbuf[CBUF_SZ]; + +void +usage(const char *pname) { + fprintf(stderr, + "Read IP addresses from stdin and print scrambled addresses on stdout\n" + "USAGE:\n" + "\t%s [OPTIONS] [-r] key_file\n" + "\tWhere OPTIONS are:\n" + "\t\t--newkey|-G generate a new key key_file\n" + "\t\t--type=TYPE crypto to use for key generation (valid only with --newkey option)\n" + "\t\t supported choices are: blowfish (default), aes, sha1, md5\n" + "\t\t--pass4=<num> pass <num> higher bits of ipv4 addresses through unchanged\n" + "\t\t--pass6=<num> pass <num> higher bits of ipv6 addresses through unchanged\n" +#if HAVE_REGEX_H + "\t\t--ip-prefix=<regex> all ips must be prefixed by this regex prefix (both ipv4 and ipv6)\n" + "\t\t--ip-suffix=<regex> all ips must be followed by this regex suffix (both ipv4 and ipv6)\n" + "\t\t-t text mode: read text from stdin and scramble all addresses\n" + "\t\t that can be found using regex (use with caution)\n" +#endif //HAVE_REGEX_H + "\t\t-r reverse-mode i.e. for unscrambling ip addresses\n" + , pname); + exit(1); +} + +int +anon_ip4_txt(const char *oldip, char *newip) { + struct in_addr ip4, ip4s; + if (inet_pton(AF_INET, oldip, &ip4) <= 0) { + fprintf(stderr, "don't understand address (%s)\n", oldip); +#ifdef HAVE_STRLCPY + strlcpy(newip, oldip, INITBUFSIZE); +#else + strcpy(newip, oldip); //copy without changing +#endif + } else { + ip4s.s_addr = (reverse_mode) + ? unscramble_ip4(ip4.s_addr, pass_bits4) + : scramble_ip4(ip4.s_addr, pass_bits4); + if (newip != inet_ntop(AF_INET, &ip4s, newip, 256)) { + perror("Error: can't print new address"); + exit(1); + } + } + return strlen(newip); +} +int +anon_ip6_txt(const char *oldip, char *newip) { + struct in6_addr ip6; + if (inet_pton(AF_INET6, oldip, &ip6) <= 0) { + fprintf(stderr, "don't understand address (%s)\n", oldip); +#ifdef HAVE_STRLCPY + strlcpy(newip, oldip, INITBUFSIZE); +#else + strcpy(newip, oldip); //copy without changing +#endif + } else { + if (reverse_mode) { + unscramble_ip6(&ip6, pass_bits6); + } else { + scramble_ip6(&ip6, pass_bits6); + } + if (newip != inet_ntop(AF_INET6, &ip6, newip, 256)) { + perror("Error: can't print new address"); + exit(1); + } + } + return strlen(newip); +} + +//return the pointer in 'in' buffer where processing stopped +const char* +search_replace_ip(const char *in, char *out, const regex_t *r, + int (*anonf)(const char*, char*), size_t *outsz) { + const char *c = in; + char *c2 = out; + regmatch_t re_match[2]; + //0th element is the whole thing (including prefix and suffix) + //1st element is the IP address + while (0 == regexec(r, c, 2, re_match, 0)) { + const char *cc=c; + if (re_match[1].rm_so != -1) { + const char *ipbeg = cc + re_match[1].rm_so; + const char *ipend = cc + re_match[1].rm_eo; + size_t copylen = ipbeg - c; //from prev match to this one, pass-thru, including prefix + //copy from c..(beg-1), advance both pointers + while (c2 - out + copylen >= *outsz) { + *outsz *= 2; + out = realloc(out, *outsz); + if (out == NULL) { + fprintf(stderr, "Error: run out of buffer space\n"); + exit(1); + } + } + memcpy(c2, c, copylen); + c += copylen; + c2+= copylen; + char hold = *ipend; //store last character, we'll need temporarly replace it with '\0' + *(char*)ipend = '\0'; + size_t anoniplen = anonf(c, c2); + *(char*)ipend = hold; //restore + c = ipend; + c2 += anoniplen; + } + } + //copy the rest + size_t copylen = strlen(c) + 1; + while (c2 - out + copylen >= *outsz) { + *outsz *= 2; + out = realloc(out, *outsz); + if (out == NULL) { + fprintf(stderr, "Error: run out of buffer space\n"); + exit(1); + } + } + memcpy(c2, c, copylen); + + return out; +} + +int +main(int argc, char *argv[]) +{ + FILE *keyfile = NULL; + const char *keyfn = NULL; + const char *pname = argv[0]; + + int opt; + int text_mode = 0; + int opt_newkey = 0; + char *opt_keytype = NULL; +#if HAVE_REGEX_H + char *opt_ipprefix = NULL; + char *opt_ipsuffix = NULL; + char regex4[4096]; + char regex6[4096]; +#endif //HAVE_REGEX_H + + scramble_crypt_t key_crypto = SCRAMBLE_BLOWFISH; + + struct option long_options[] = { + {"newkey",0, NULL, 'G'}, + {"help", 0, NULL, 'h'}, + {"pass4", 1, NULL, PASS4}, + {"pass6", 1, NULL, PASS6}, + {"type", 1, NULL, KTYPE}, +#if HAVE_REGEX_H + {"text", 0, NULL, 't'}, + {"ip-prefix", 1, NULL, IPPREFIX}, + {"ip-suffix", 1, NULL, IPSUFFIX}, +#endif //HAVE_REGEX_H + }; + + while((opt = getopt_long(argc, argv, + "Ghr" +#if HAVE_REGEX_H + "t" +#endif //HAVE_REGEX_H + , long_options, NULL)) != EOF) { + switch(opt) { + /* long options first: */ + case PASS4: + pass_bits4 = atoi(optarg); + if (pass_bits4 < 0 || pass_bits4 > 32) { + fprintf(stderr, "Error: --pass4 option argument must be within [0..32]\n"); + exit(1); + } + break; + case PASS6: + pass_bits6 = atoi(optarg); + if (pass_bits6 < 0 || pass_bits6 > 128) { + fprintf(stderr, "Error: --pass6 option argument must be within [0..128]\n"); + exit(1); + } + break; + case KTYPE: + opt_keytype = optarg; + if (strcmp(opt_keytype, "blowfish") == 0) { + key_crypto = SCRAMBLE_BLOWFISH; + } else if (strcmp(opt_keytype, "aes") == 0) { + key_crypto = SCRAMBLE_AES; + } else if (strcmp(opt_keytype, "sha1") == 0) { + key_crypto = SCRAMBLE_SHA1; + } else if (strcmp(opt_keytype, "md5") == 0) { + key_crypto = SCRAMBLE_MD5; + } else { + fprintf(stderr, "Error: unsupported crypto key type: '%s' (can be one of: blowfish, aes, sha1, md5)\n", opt_keytype); + exit(1); + } + break; +#if HAVE_REGEX_H + case IPPREFIX: + opt_ipprefix = optarg; + break; + case IPSUFFIX: + opt_ipsuffix = optarg; + break; + case 't': + text_mode = 1; + break; +#endif //HAVE_REGEX_H + + /* short options: */ + case 'G': + opt_newkey = 1; + break; + case 'h': + usage(pname); + /* never returns */ + break; + case 'r': + reverse_mode = 1; + break; + + default: + usage(pname); + } + } + argc -= optind; + argv += optind; + if (argc != 1) { + usage(pname); + } + + keyfn = argv[0]; + + if (opt_newkey && (text_mode || reverse_mode || pass_bits4 || pass_bits6)) { + fprintf(stderr, "Error: --newkey or -G is mutually exclusive with other options.\n"); + exit(1); + } + if (opt_keytype && !opt_newkey) { + fprintf(stderr, "Error: --type requires --newkey (-G) option.\n"); + exit(1); + } +#if HAVE_REGEX_H + if (!text_mode && (opt_ipprefix != NULL || opt_ipsuffix != NULL)) { + fprintf(stderr, "--ip-prefix and --ipsuffix require text mode (-t).\n"); + exit(1); + } +#endif //HAVE_REGEX_H + if ((keyfile = fopen(keyfn, "r")) == NULL) { + if (!opt_newkey) { + /* no keyfile, but supposed to exist */ + fprintf(stderr, "Error: cannot open the key_file: '%s': %s\n", keyfn, strerror(errno)); + exit(1); + } + } else { + if (opt_newkey) { + /* keyfile exists, but asked to make new one */ + fprintf(stderr, "Error: keyfile '%s' already exists, remove it before trying to generate a new one.\n", + keyfn); + exit(1); + } + } + if (keyfile) fclose(keyfile); + + if (scramble_init_from_file(keyfn, key_crypto, key_crypto, NULL) < 0) { + fprintf(stderr, "Error: can't initialize from keyfile '%s'\n", keyfn); + exit(1); + } + if (opt_newkey) { + //only generating mode + exit(0); + } + + if (!text_mode && setvbuf(stdout, NULL, _IOLBF, 0) < 0) { + fprintf(stderr, "Error: setting line buffering: %s\n", strerror(errno)); + exit(1); + } + +#if 1 //HAVE_REGEX_H + if (text_mode) { + linebuf1_sz = INITBUFSIZE; + linebuf2_sz = INITBUFSIZE; + linebuf1 = malloc(linebuf1_sz); + linebuf2 = malloc(linebuf2_sz); + + memset(regex4, 0, sizeof(regex4)); + memset(regex6, 0, sizeof(regex6)); + if (opt_ipprefix != NULL) { + strncat(regex4, opt_ipprefix, sizeof(regex4)-1); + strncat(regex6, opt_ipprefix, sizeof(regex6)-1); + } + strncat(regex4, REGEX4, sizeof(regex4)-1); + strncat(regex6, REGEX6, sizeof(regex6)-1); + if (opt_ipsuffix != NULL) { + strncat(regex4, opt_ipsuffix, sizeof(regex4)-1); + strncat(regex6, opt_ipsuffix, sizeof(regex6)-1); + } + if (regex4[sizeof(regex4)-1] != '\0' || + regex6[sizeof(regex6)-1] != '\0') { + fprintf(stderr, "Error: regex for ip addresses is too long."); + exit(1); + } + + regex_t r4, r6; + if (0 != regcomp(&r4, regex4, REG_EXTENDED|REG_NEWLINE)) + exit(1); + if (0 != regcomp(&r6, regex6, REG_EXTENDED|REG_NEWLINE)) + exit(1); + linebuf1[linebuf1_sz-1]='x'; //anything but zero + //xxx todo: use buffered reading/writing + while (fgets(linebuf1, linebuf1_sz, stdin) != NULL) { + //fgets adds a terminating '\0', reading stops after an EOF or a newline + if (linebuf1[linebuf1_sz-1] == '\0' && linebuf1[linebuf1_sz-2] != '\n') { + //filled the buffer, but no newline; xxx remove newline dependency + fprintf(stderr, "Error: input lines are too long; maximum supported is %zd.\n", + linebuf1_sz-1); + exit(1); + } + //read a full line, can be anonymized in full + linebuf2 = (char*)search_replace_ip(linebuf1, linebuf2, &r4, &anon_ip4_txt, &linebuf2_sz); + linebuf1 = (char*)search_replace_ip(linebuf2, linebuf1, &r6, &anon_ip6_txt, &linebuf1_sz); + //output + fputs(linebuf1, stdout); + } + regfree(&r4); + regfree(&r6); + free(linebuf1); + free(linebuf2); + return 0; + } +#endif + + for (;;) { + int af; + int i; + int prefix = 0; + char *plen = NULL; + char *c; + + struct in_addr ip4, ip4s; + struct in6_addr ip6, ip6s; + void *new = NULL; + // char *c2; + if (fgets(cbuf, CBUF_SZ, stdin) == NULL) + break; + for (i = strnlen(cbuf, CBUF_SZ-1)-1; i >= 0; --i) { + if (!isgraph(cbuf[i])) + cbuf[i] = '\0'; + } + plen = NULL; + /* first see if this is a network */ + if (NULL != (c = strchr(cbuf, '/'))) { + /* this is a network */ + plen = c + 1; + if (!isdigit(*plen)) { + fprintf(stderr, "can't parse network prefix (%s)\n", cbuf); + continue; + } + *c = '\0'; + prefix = atoi(plen); + } + /* first try ipv4 */ + af= AF_INET; + if (inet_pton(af, cbuf, &ip4) <= 0) { + /* next try ipv6 */ + af= AF_INET6; + if (inet_pton(af, cbuf, &ip6) <= 0) { + fprintf(stderr, "don't understand address (%s)\n", cbuf); + continue; + } + ip6s = ip6; + if (reverse_mode) { + unscramble_ip6(&ip6s, pass_bits6); + } else { + scramble_ip6(&ip6s, pass_bits6); + } + new = &ip6s; + /* if it was a network, zero out host bits */ + if (plen && prefix < 128) { + int hostbits = 128 - prefix; + i = 3; + while (hostbits >= 32) { + ip6s.s6_addr32[i] = 0; + --i; + hostbits -= 32; + } + if (hostbits > 0) { + ip6s.s6_addr32[i] = htonl(ntohl(ip6s.s6_addr32[i]) + & (0xffffffffUL << hostbits)); + } + } + } else { + ip4s.s_addr = (reverse_mode) + ? unscramble_ip4(ip4.s_addr, pass_bits4) + : scramble_ip4(ip4.s_addr, pass_bits4); + new = &ip4s; + /* if it was a network, zero out host bits */ + if (plen && prefix < 32) { + int hostbits = 32 - prefix; + ip4s.s_addr = ntohl(ip4s.s_addr); + if (hostbits == 32) + ip4s.s_addr = 0; + else + ip4s.s_addr &= 0xffffffffUL << hostbits; + ip4s.s_addr = htonl(ip4s.s_addr); + } + } + + if (cbuf != inet_ntop(af, new, cbuf, CBUF_SZ)) { + perror("Error: can't print new address"); + exit(1); + } + + printf("%s", cbuf); + if (plen) + printf("/%d", prefix); + printf("\n"); + } + scramble_cleanup(); + return 0; +} |