summaryrefslogtreecommitdiffstats
path: root/src/scramble_ips.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel@debian.org>2024-11-21 17:49:05 +0100
committerDaniel Baumann <daniel@debian.org>2024-11-21 17:49:05 +0100
commit4a8e45026e0f5de7d9711cfe1ddd23518b1d17db (patch)
tree3f0f2bf5c60c901954649edcbf7611328575ff29 /src/scramble_ips.c
parentInitial commit. (diff)
downloadcryptopant-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.c487
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;
+}