summaryrefslogtreecommitdiffstats
path: root/src
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
parentInitial commit. (diff)
downloadcryptopant-4a8e45026e0f5de7d9711cfe1ddd23518b1d17db.tar.xz
cryptopant-4a8e45026e0f5de7d9711cfe1ddd23518b1d17db.zip
Adding upstream version 1.3.1.upstream/1.3.1upstream
Signed-off-by: Daniel Baumann <daniel@debian.org>
Diffstat (limited to 'src')
-rw-r--r--src/config.h.in138
-rw-r--r--src/cryptopANT.c847
-rw-r--r--src/cryptopANT.h92
-rw-r--r--src/scramble_ips.c487
4 files changed, 1564 insertions, 0 deletions
diff --git a/src/config.h.in b/src/config.h.in
new file mode 100644
index 0000000..dfbc598
--- /dev/null
+++ b/src/config.h.in
@@ -0,0 +1,138 @@
+/* src/config.h.in. Generated from configure.ac by autoheader. */
+
+/* Define if building universal (internal helper macro) */
+#undef AC_APPLE_UNIVERSAL_BUILD
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#undef HAVE_DLFCN_H
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#undef HAVE_INTTYPES_H
+
+/* Define to 1 if you have the `crypto' library (-lcrypto). */
+#undef HAVE_LIBCRYPTO
+
+/* Define to 1 if you have the <openssl/aes.h> header file. */
+#undef HAVE_OPENSSL_AES_H
+
+/* Define to 1 if you have the <openssl/blowfish.h> header file. */
+#undef HAVE_OPENSSL_BLOWFISH_H
+
+/* Define to 1 if you have the <openssl/core_names.h> header file. */
+#undef HAVE_OPENSSL_CORE_NAMES_H
+
+/* Define to 1 if you have the <openssl/err.h> header file. */
+#undef HAVE_OPENSSL_ERR_H
+
+/* Define to 1 if you have the <openssl/evp.h> header file. */
+#undef HAVE_OPENSSL_EVP_H
+
+/* Define to 1 if you have the <openssl/md5.h> header file. */
+#undef HAVE_OPENSSL_MD5_H
+
+/* Define to 1 if you have the <openssl/provider.h> header file. */
+#undef HAVE_OPENSSL_PROVIDER_H
+
+/* Define to 1 if you have the <openssl/sha.h> header file. */
+#undef HAVE_OPENSSL_SHA_H
+
+/* Define to 1 if you have the `regcomp' function. */
+#undef HAVE_REGCOMP
+
+/* Define to 1 if you have the `regexec' function. */
+#undef HAVE_REGEXEC
+
+/* Define to 1 if you have the <regex.h> header file. */
+#undef HAVE_REGEX_H
+
+/* S6_ADDR32 is present in in6_addr when defined */
+#undef HAVE_S6_ADDR32
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#undef HAVE_STDINT_H
+
+/* Define to 1 if you have the <stdio.h> header file. */
+#undef HAVE_STDIO_H
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#undef HAVE_STDLIB_H
+
+/* Define to 1 if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define to 1 if you have the <string.h> header file. */
+#undef HAVE_STRING_H
+
+/* Define to 1 if you have the `strlcpy' function. */
+#undef HAVE_STRLCPY
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#undef HAVE_SYS_STAT_H
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#undef HAVE_SYS_TYPES_H
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* U6_ADDR32 is present in in6_addr when defined */
+#undef HAVE__U6_ADDR32
+
+/* Define to the sub-directory where libtool stores uninstalled libraries. */
+#undef LT_OBJDIR
+
+/* Name of package */
+#undef PACKAGE
+
+/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#undef PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the home page for this package. */
+#undef PACKAGE_URL
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
+
+/* Define to 1 if all of the C90 standard headers exist (not just the ones
+ required in a freestanding environment). This macro is provided for
+ backward compatibility; new code need not use it. */
+#undef STDC_HEADERS
+
+/* Version number of package */
+#undef VERSION
+
+/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
+ significant byte first (like Motorola and SPARC, unlike Intel). */
+#if defined AC_APPLE_UNIVERSAL_BUILD
+# if defined __BIG_ENDIAN__
+# define WORDS_BIGENDIAN 1
+# endif
+#else
+# ifndef WORDS_BIGENDIAN
+# undef WORDS_BIGENDIAN
+# endif
+#endif
+
+/* Define for Solaris 2.5.1 so the uint32_t typedef from <sys/synch.h>,
+ <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
+ #define below would cause a syntax error. */
+#undef _UINT32_T
+
+/* Define to empty if `const' does not conform to ANSI C. */
+#undef const
+
+/* Define to `unsigned int' if <sys/types.h> does not define. */
+#undef size_t
+
+/* Define to the type of an unsigned integer type of width exactly 32 bits if
+ such a type exists and the standard includes do not define it. */
+#undef uint32_t
diff --git a/src/cryptopANT.c b/src/cryptopANT.c
new file mode 100644
index 0000000..e140f24
--- /dev/null
+++ b/src/cryptopANT.c
@@ -0,0 +1,847 @@
+/* -*- Mode:C; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
+/*
+ * Copyright (C) 2004-2024 by the University of Southern California
+ * $Id: cdc84b9fca5b7bc01d665de67bbe6358d0a8131f $
+ *
+ * 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.
+ *
+ */
+
+#include <stdio.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <netinet/in.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <ctype.h>
+#include <stdlib.h>
+
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <assert.h>
+
+#include <openssl/evp.h>
+#include <openssl/md5.h>
+#include <openssl/blowfish.h>
+#include <openssl/sha.h>
+#include <openssl/aes.h>
+#include <openssl/err.h>
+#include <openssl/core_names.h>
+#include <openssl/provider.h>
+
+#include "cryptopANT.h"
+#include "config.h"
+
+#define MAX_BLK_LENGTH 32
+#define CACHE_BITS 24 /* How many bits of IPv4 we cache, cannot be zero */
+#define BF_KEYLEN 16 /* bytes */
+
+#define TEST_CACHE 0
+
+#define RESET_ETHER_MCAST(p) (*(char*)(p) &= 0xfe)
+
+#ifndef MAX
+#define MAX(a,b) ((a) > (b) ? (a) : (b))
+#endif
+
+#if HAVE__U6_ADDR32
+#define s6_addr32 __u6_addr.__u6_addr32
+#endif
+
+//determined by autoconf
+#ifdef WORDS_BIGENDIAN
+//sigh, older version of the code was not byte-order safe; this is needed
+//to ensure backward compatibility AND compatibility with BE-systems.
+#include <byteswap.h>
+#define cryptopant_swap32(x) bswap_32(x)
+#else
+#define cryptopant_swap32(x) (x)
+#endif
+
+typedef struct ipv4_hash_blk_ {
+ uint32_t ip4;
+ uint8_t pad[MAX_BLK_LENGTH - sizeof(uint32_t)];
+} ipv4_hash_blk_t;
+
+typedef struct ipv6_hash_blk_ {
+ struct in6_addr ip6;
+ uint8_t pad[MAX_BLK_LENGTH - sizeof(struct in6_addr)];
+} ipv6_hash_blk_t;
+
+
+uint8_t scramble_ether_addr[ETHER_ADDR_LEN];
+uint16_t scramble_ether_vlan;
+int scramble_mac;
+
+static int readhexstring (FILE *, u_char *, int *);
+
+static uint32_t ip4cache[1<<CACHE_BITS];
+static uint32_t ip4pad; /* first 4 bytes of pad */
+static uint32_t ip6pad[4];
+static u_char scramble_mac_buf[MAX_BLK_LENGTH];
+static u_char scramble_key[MAX_BLK_LENGTH];
+
+static uint8_t ivec[64];
+
+/* statistics */
+static long ipv4_cache_hits = 0;
+static long ipv4_anon_calls = 0;
+static long ipv6_anon_calls = 0;
+
+
+static ipv4_hash_blk_t b4_in, b4_out;
+static ipv6_hash_blk_t b6_in, b6_out;
+
+static scramble_crypt_t scramble_crypto4 = SCRAMBLE_AES;
+static scramble_crypt_t scramble_crypto6 = SCRAMBLE_AES;
+
+/* openssl EVP pointers */
+static EVP_CIPHER_CTX *ctx4, *ctx6;
+static EVP_CIPHER *cipher4, *cipher6;
+static EVP_MD_CTX *mdctx4, *mdctx6;
+static EVP_MD *md4, *md6;
+
+static struct {
+ char *name;
+ scramble_crypt_t type;
+} scramble_names[] = {
+ { "md5", SCRAMBLE_MD5 },
+ { "blowfish", SCRAMBLE_BLOWFISH },
+ { "aes", SCRAMBLE_AES },
+ { "sha", SCRAMBLE_SHA1 },
+};
+
+const char *
+scramble_type2name(scramble_crypt_t t)
+{
+ int i;
+ for (i = 0; i < sizeof(scramble_names)/sizeof(scramble_names[0]); ++i)
+ if (scramble_names[i].type == t)
+ return scramble_names[i].name;
+ return NULL;
+}
+
+scramble_crypt_t
+scramble_name2type(const char *name)
+{
+ int i;
+ for (i = 0; i < sizeof(scramble_names)/sizeof(scramble_names[0]); ++i)
+ if (strcasecmp(name, scramble_names[i].name) == 0)
+ return scramble_names[i].type;
+ return SCRAMBLE_NONE;
+}
+
+scramble_crypt_t
+scramble_crypto_ip4(void)
+{
+ return scramble_crypto4;
+}
+
+scramble_crypt_t
+scramble_crypto_ip6(void)
+{
+ return scramble_crypto6;
+}
+
+int
+scramble_newkey(u_char *key, int klen)
+{
+ FILE *rnd = fopen(SCRAMBLE_RANDOM_DEV, "r");
+ if (rnd == NULL) {
+ perror("scramble_newkey(): fopen");
+ return -1;
+ }
+ if (fread(key, 1, klen, rnd) != klen) {
+ perror("scramble_newkey(): fread");
+ fclose(rnd);
+ return -1;
+ }
+ fclose(rnd);
+ return 0;
+}
+
+int
+scramble_newpad(u_char *pad, int plen)
+{
+ FILE *rnd = fopen(SCRAMBLE_RANDOM_DEV, "r");
+ if (rnd == NULL) {
+ perror("scramble_newpad(): fopen");
+ return -1;
+ }
+ if (fread(pad, 1, plen, rnd) != plen) {
+ perror("scramble_newpad(): fread");
+ fclose(rnd);
+ return -1;
+ }
+ fclose(rnd);
+ return 0;
+}
+
+int
+scramble_newmac(u_char *mac, int mlen)
+{
+ FILE *rnd = fopen(SCRAMBLE_RANDOM_DEV, "r");
+ if (rnd == NULL) {
+ perror("scramble_newkey(): fopen");
+ return -1;
+ }
+ if (fread(mac, 1, mlen, rnd) != mlen) {
+ perror("scramble_newkey(): fread");
+ fclose(rnd);
+ return -1;
+ }
+ fclose(rnd);
+ return 0;
+}
+
+int
+scramble_newiv(u_char *iv, int ivlen)
+{
+ FILE *rnd = fopen(SCRAMBLE_RANDOM_DEV, "r");
+ if (rnd == NULL) {
+ perror("scramble_newiv(): fopen");
+ return -1;
+ }
+ if (fread(iv, 1, ivlen, rnd) != ivlen) {
+ perror("scramble_newiv(): fread");
+ fclose(rnd);
+ return -1;
+ }
+ fclose(rnd);
+ return 0;
+}
+
+/* read a hex string from fd at current position and store it in s */
+static int
+readhexstring(FILE *f, u_char *s, int *len)
+{
+ char c = 0;
+ int i;
+ for (i = 0; i < *len + 1; ++i) {
+ switch (fread(&c, 1, 1, f)) {
+ case 0:
+ *len = i;
+ return 0;
+ case 1:
+ break;
+ default:
+ return -1;
+ }
+ if (!isxdigit(c)) {
+ *len = i;
+ return 0;
+ }
+ s[i] = ((isdigit(c)) ? c - '0' : tolower(c) - 'a' + 10) << 4;
+ if (fread(&c, 1, 1, f) != 1) {
+ *len = i;
+ return -1; /* error: a byte has 2 digits */
+ }
+ if (!isxdigit(c)) {
+ *len = i;
+ return -1;
+ }
+ s[i] |= (isdigit(c)) ? c - '0' : tolower(c) - 'a' + 10;
+ }
+ if (i == *len + 1)
+ return -1; /* means buffer is too short */
+ return 0;
+}
+
+int
+scramble_readstate(const char *fn, scramble_state_t *s)
+{
+ u_char c4, c6;
+ int l4 = 1, l6 = 1;
+ FILE *f = fopen(fn, "r");
+ if (f == NULL) {
+ perror("scramble_readstate(): fopen");
+ return -1;
+ }
+ if (readhexstring(f, (u_char*)&c4, &l4) != 0) {
+ fprintf(stderr, "scramble_readstate(): error reading c4");
+ fclose(f);
+ return -1;
+ }
+ assert(l4 == 1);
+ s->c4 = (scramble_crypt_t)c4;
+ if (readhexstring(f, (u_char*)&c6, &l6) != 0) {
+ fprintf(stderr, "scramble_readstate(): error reading c6");
+ fclose(f);
+ return -1;
+ }
+ assert(l6 == 1);
+ s->c6 = (scramble_crypt_t)c6;
+ if (readhexstring(f, s->key, &s->klen) != 0) {
+ fprintf(stderr, "scramble_readstate(): error reading key");
+ fclose(f);
+ return -1;
+ }
+ if (readhexstring(f, s->pad, &s->plen) != 0) {
+ fprintf(stderr, "scramble_readstate(): error reading pad");
+ fclose(f);
+ return -1;
+ }
+ if (readhexstring(f, s->mac, &s->mlen) != 0) {
+ fprintf(stderr, "scramble_readstate(): error reading mac");
+ fclose(f);
+ return -1;
+ }
+ if (readhexstring(f, s->iv, &s->ivlen) != 0) {
+ fprintf(stderr, "scramble_readstate(): error reading iv");
+ fclose(f);
+ return -1;
+ }
+ fclose(f);
+ return 0;
+}
+
+int
+scramble_savestate(const char *fn, const scramble_state_t *s)
+{
+ int i;
+ /* set restrictive mode */
+ int fd = creat(fn, S_IRUSR|S_IWUSR);
+ if (fd < 0) {
+ perror("scramble_savestate(): open");
+ return -1;
+ }
+ FILE *f = fdopen(fd, "w");
+ if (f == NULL) {
+ perror("scramble_savestate(): fopen");
+ return -1;
+ }
+ if (fprintf(f, "%02x:%02x:", (unsigned)s->c4, (unsigned)s->c6) < 0) {
+ perror("scramble_savestate(): error saving cryptos");
+ fclose(f);
+ return -1;
+ }
+ for (i = 0; i < s->klen; ++i) {
+ if (fprintf(f, "%02x", s->key[i]) < 0) {
+ perror("scramble_savestate(): error saving key");
+ fclose(f);
+ return -1;
+ }
+ }
+ fprintf(f, ":");
+ for (i = 0; i < s->plen; ++i) {
+ if (fprintf(f, "%02x", s->pad[i]) < 0) {
+ perror("scramble_savestate(): error saving pad");
+ fclose(f);
+ return -1;
+ }
+ }
+ fprintf(f, ":");
+ for (i = 0; i < s->mlen; ++i) {
+ if (fprintf(f, "%02x", s->mac[i]) < 0) {
+ perror("scramble_savestate(): error saving mac");
+ fclose(f);
+ return -1;
+ }
+ }
+
+ fprintf(f, ":");
+ for (i = 0; i < s->ivlen; ++i) {
+ if (fprintf(f, "%02x", s->iv[i]) < 0) {
+ perror("scramble_savestate(): error saving lv");
+ fclose(f);
+ return -1;
+ }
+ }
+ fprintf(f, "\n");
+ fclose(f);
+ return 0;
+}
+
+void
+scramble_cleanup()
+{
+ // openssl evp cleanup
+ if (ctx4 != NULL) {
+ EVP_CIPHER_CTX_free(ctx4);
+ ctx4 = NULL;
+ }
+ if (ctx6 != NULL) {
+ EVP_CIPHER_CTX_free(ctx6);
+ ctx6 = NULL;
+ }
+ if (mdctx4 != NULL) {
+ EVP_MD_CTX_free(mdctx4);
+ mdctx4 = NULL;
+ }
+ if (mdctx6 != NULL) {
+ EVP_MD_CTX_free(mdctx6);
+ mdctx6 = NULL;
+ }
+ if (cipher4 != NULL) {
+ EVP_CIPHER_free(cipher4);
+ cipher4 = NULL;
+ }
+ if (cipher6 != NULL) {
+ EVP_CIPHER_free(cipher6);
+ cipher6 = NULL;
+ }
+ if (md4 != NULL) {
+ EVP_MD_free(md4);
+ md4 = NULL;
+ }
+ if (md6 != NULL) {
+ EVP_MD_free(md6);
+ md6 = NULL;
+ }
+}
+
+
+int
+scramble_init(const scramble_state_t *s)
+{
+ int plen;
+ if (s->plen > MAX_BLK_LENGTH)
+ plen = MAX_BLK_LENGTH;
+ else
+ plen = s->plen;
+
+ scramble_crypto4 = s->c4;
+ scramble_crypto6 = s->c6;
+
+ memcpy(&b4_in, s->pad, plen);
+ ip4pad = cryptopant_swap32(b4_in.ip4);
+
+ memcpy(&b6_in, s->pad, s->plen);
+ ip6pad[0] = b6_in.ip6.s6_addr32[0];
+ ip6pad[1] = b6_in.ip6.s6_addr32[1];
+ ip6pad[2] = b6_in.ip6.s6_addr32[2];
+ ip6pad[3] = b6_in.ip6.s6_addr32[3];
+
+ memcpy(scramble_key, s->key, s->klen);
+
+ // create contexts (will not need them all)
+ ctx4 = EVP_CIPHER_CTX_new();
+ ctx6 = EVP_CIPHER_CTX_new();
+ mdctx4 = EVP_MD_CTX_new();
+ mdctx6 = EVP_MD_CTX_new();
+
+ // fetch ciphers and digests
+ void *res_ctx = NULL, *res_crypt = NULL;
+ switch(s->c4) {
+ case SCRAMBLE_AES:
+ OSSL_PROVIDER_load(NULL, "default");
+ cipher4 = EVP_CIPHER_fetch(NULL, "AES-128-ECB", "provider=default");
+ res_ctx = ctx4;
+ res_crypt = cipher4;
+ break;
+ case SCRAMBLE_BLOWFISH:
+ OSSL_PROVIDER_load(NULL, "legacy");
+ cipher4 = EVP_CIPHER_fetch(NULL, "BF-ECB", "provider=legacy");
+ if (cipher4 == NULL) {
+ cipher4 = EVP_CIPHER_fetch(NULL, "BF-ECB", NULL);
+ }
+ res_ctx = ctx4;
+ res_crypt = cipher4;
+ break;
+ case SCRAMBLE_SHA1:
+ md4 = EVP_MD_fetch(NULL, "SHA1", NULL);
+ res_ctx = mdctx4;
+ res_crypt = md4;
+ break;
+ case SCRAMBLE_MD5:
+ md4 = EVP_MD_fetch(NULL, "MD5", NULL);
+ res_ctx = mdctx4;
+ res_crypt = md4;
+ break;
+ case SCRAMBLE_NONE:
+ break;
+ default:
+ fprintf(stderr,
+ "scramble_init(): unsupported ipv4 scrambling crypto: %d\n", s->c4);
+ return -1;
+ }
+ if (s->c4 != SCRAMBLE_NONE && (res_ctx == NULL || res_crypt == NULL)) {
+ fprintf(stderr,
+ "scramble_init(): EVP ip4 init failures %p %p\n", res_ctx, res_crypt);
+ return -1;
+ }
+
+ res_ctx = res_crypt = NULL;
+
+ switch(s->c6) {
+ case SCRAMBLE_AES:
+ OSSL_PROVIDER_load(NULL, "default");
+ cipher6 = EVP_CIPHER_fetch(NULL, "AES-128-ECB", "provider=default");
+ res_ctx = ctx6;
+ res_crypt = cipher6;
+ break;
+ case SCRAMBLE_BLOWFISH:
+ OSSL_PROVIDER_load(NULL, "legacy");
+ cipher6 = EVP_CIPHER_fetch(NULL, "BF-CBC", "provider=legacy");
+ if (cipher6 == NULL) {
+ cipher6 = EVP_CIPHER_fetch(NULL, "BF-ECB", NULL);
+ }
+ res_ctx = ctx6;
+ res_crypt = cipher6;
+ break;
+ case SCRAMBLE_SHA1:
+ md6 = EVP_MD_fetch(NULL, "SHA1", NULL);
+ res_ctx = mdctx6;
+ res_crypt = md6;
+ break;
+ case SCRAMBLE_MD5:
+ md6 = EVP_MD_fetch(NULL, "MD5", NULL);
+ res_ctx = mdctx6;
+ res_crypt = md6;
+ break;
+ case SCRAMBLE_NONE:
+ break;
+ default:
+ fprintf(stderr,
+ "scramble_init(): unsupported ipv6 scrambling crypto: %d\n", s->c6);
+ return -1;
+
+ }
+ if (s->c6 != SCRAMBLE_NONE && (res_ctx == NULL || res_crypt == NULL)) {
+ fprintf(stderr,
+ "scramble_init(): EVP ip6 init failures %p %p\n", res_ctx, res_crypt);
+ return -1;
+ }
+ if (cipher4 != NULL) {
+ if (!EVP_EncryptInit_ex2(ctx4, cipher4, scramble_key, ivec, NULL)) {
+ fprintf(stderr,
+ "scramble_init(): EVP_EncryptInit_ex2 failed:");
+ ERR_print_errors_fp(stderr);
+ return -1;
+ }
+ }
+ if (cipher6 != NULL) {
+ if (!EVP_EncryptInit_ex2(ctx6, cipher6, scramble_key, ivec, NULL)) {
+ fprintf(stderr,
+ "scramble_init(): EVP_EncryptInit_ex2 failed:");
+ ERR_print_errors_fp(stderr);
+ return -1;
+ }
+ }
+ // don't need to init anything for digests
+
+ scramble_mac = 0;
+
+ memcpy(scramble_mac_buf, s->mac, s->mlen);
+
+ if (s->mlen > 0) {
+ scramble_mac = 1;
+ if (s->mlen < ETHER_ADDR_LEN + ETHER_VLAN_LEN) {
+ fprintf(stderr,
+ "scramble_init(): mac string is too short (%d)\n",
+ s->mlen);
+ return -1;
+ }
+ }
+ memcpy(scramble_ether_addr, scramble_mac_buf, ETHER_ADDR_LEN);
+
+ /* we don't want to map ether unicast to multicast and visa versa */
+ RESET_ETHER_MCAST(scramble_ether_addr);
+
+ memcpy(&scramble_ether_vlan, scramble_mac_buf + ETHER_ADDR_LEN, ETHER_VLAN_LEN);
+ return 0;
+}
+
+/* init everything from file, if it doesn't exist, create it */
+int
+scramble_init_from_file(const char *fn, scramble_crypt_t c4, scramble_crypt_t c6, int *do_mac)
+{
+ // OSSL_PROVIDER *legacy_, *default_;
+ u_char pad[MAX_BLK_LENGTH];
+ u_char key[MAX_BLK_LENGTH];
+ u_char mac[MAX_BLK_LENGTH];
+ u_char iv[MAX_BLK_LENGTH];
+
+ scramble_state_t s;
+ FILE *f;
+
+ s.pad = pad;
+ s.key = key;
+ s.mac = mac;
+ s.iv = iv;
+ if ((f = fopen(fn, "r")) == NULL) {
+ if (errno != ENOENT) {
+ perror("scamble_init_file(): fopen");
+ return -1;
+ }
+ if (c4 == SCRAMBLE_NONE || c6 == SCRAMBLE_NONE)
+ return -1;
+
+ /* file doesn't exist, create it */
+ s.c4 = c4;
+ s.c6 = c6;
+ s.plen = MAX_BLK_LENGTH;
+ s.klen = 16; /* XXX */
+ s.ivlen = 16;
+
+ if (scramble_newpad(pad, s.plen) < 0)
+ return -1;
+ if (scramble_newkey(key, s.klen) < 0)
+ return -1;
+ if (scramble_newiv(iv, s.ivlen) < 0)
+ return -1;
+ if (do_mac && *do_mac) {
+ s.mlen = ETHER_ADDR_LEN + ETHER_VLAN_LEN;
+ if (scramble_newmac(mac, s.mlen) < 0)
+ return -1;
+ } else
+ s.mlen = 0;
+ if (scramble_savestate(fn, &s) < 0)
+ return -1;
+ } else {
+ fclose(f);
+ s.plen = MAX_BLK_LENGTH;
+ s.klen = MAX_BLK_LENGTH;
+ s.mlen = MAX_BLK_LENGTH;
+ s.ivlen = MAX_BLK_LENGTH;
+ if (scramble_readstate(fn, &s) < 0)
+ return -1;
+ if (do_mac)
+ *do_mac = (s.mlen > 0);
+ }
+
+
+ if (scramble_init(&s) < 0)
+ return -1;
+ return 0;
+}
+
+/* scramble IPv4 addresses, input and output are in network byte order */
+uint32_t
+scramble_ip4(uint32_t input, int pass_bits) {
+ uint32_t output = 0;
+ uint32_t m = 0xffffffff << 1;
+ int i = 31;
+ int class_bits = 0;
+ int pbits = 0;
+ int outlen;
+#define MAX_CLASS_BITS 4
+ static int _class_bits[1<<MAX_CLASS_BITS] = {
+ 1,1,1,1,1,1,1,1, /* class A: preserve 1 bit */
+ 2,2,2,2, /* class B: preserve 2 bits */
+ 3,3, /* class C: preserve 3 bits */
+ 4, /* class D: preserve 4 bits */
+ 32 /* class bad, preserve all */
+ };
+ uint32_t *cp;
+
+ input = ntohl(input);
+ cp = ip4cache + (input >> (32 - CACHE_BITS));
+
+ assert(pass_bits >= 0 && pass_bits < 33);
+
+ ++ipv4_anon_calls;
+
+ b4_in.ip4 = input;
+
+ class_bits = _class_bits[input >> (32-MAX_CLASS_BITS)];
+
+ // check cache first
+ output = *cp;
+ if (output != 0) {
+ output <<= (32 - CACHE_BITS);
+ if (class_bits < CACHE_BITS)
+ class_bits = CACHE_BITS;
+ ++ipv4_cache_hits;
+ }
+
+ pbits = MAX(pass_bits, class_bits);
+
+ for (i = 31; i > pbits - 1; --i) {
+ /* pass through 'i' highest bits of ip4 */
+ b4_in.ip4 &= m;
+ /* the following could be:
+ * b4_in.ip4 |= (ip4pad & ~m); */
+ b4_in.ip4 |= (ip4pad >> i);
+ b4_in.ip4 = cryptopant_swap32(b4_in.ip4);
+ uint mdlen = MD5_DIGEST_LENGTH;
+ switch (scramble_crypto4) {
+ case SCRAMBLE_BLOWFISH:
+ if (!EVP_CipherUpdate(ctx4, (u_char*)&b4_out, &outlen, (u_char*)&b4_in, BF_BLOCK)) {
+ /* Error */
+ fprintf(stderr, "scramble_ip4(): EVP_CipherUpdate failed");
+ abort();
+ }
+ case SCRAMBLE_AES:
+ if (!EVP_CipherUpdate(ctx4, (u_char*)&b4_out, &outlen, (u_char*)&b4_in, AES_BLOCK_SIZE)) {
+ /* Error */
+ fprintf(stderr, "scramble_ip4(): EVP_CipherUpdate failed");
+ abort();
+ }
+ break;
+ case SCRAMBLE_SHA1:
+ mdlen = SHA_DIGEST_LENGTH;
+ // fallthrough
+ case SCRAMBLE_MD5:
+ if (!EVP_DigestInit_ex2(mdctx4, md4, NULL) ||
+ !EVP_DigestUpdate(mdctx4, (u_char*)&b4_in, mdlen) ||
+ !EVP_DigestFinal_ex(mdctx4, (u_char*)&b4_out, &mdlen)) {
+ /* Error */
+ fprintf(stderr, "scramble_ip4(): EVP_Digest* failed");
+ abort();
+ }
+ break;
+ default:
+ abort();
+ }
+ output |= (( *((u_char*)&b4_out.ip4) & 1) << (31 - i));
+ b4_in.ip4 = cryptopant_swap32(b4_in.ip4);
+ m <<= 1;
+ }
+
+ /* output == 0 is OK, means pass address unchanged */
+
+ *cp = (output >> (32 - CACHE_BITS));
+
+ return htonl(output ^ input);
+}
+
+/* scramble ipv6 address in place, in network byte order */
+void
+scramble_ip6(struct in6_addr *input, int pass_bits)
+{
+ struct in6_addr output;
+ int i, w;
+ int pbits = pass_bits;
+
+ ++ipv6_anon_calls;
+ b6_in.ip6.s6_addr32[0] = ip6pad[0]; /* XXX this one not needed */
+ b6_in.ip6.s6_addr32[1] = ip6pad[1];
+ b6_in.ip6.s6_addr32[2] = ip6pad[2];
+ b6_in.ip6.s6_addr32[3] = ip6pad[3];
+ int outlen = AES_BLOCK_SIZE;
+ uint mdlen;
+
+ for (w = 0; w < 4; ++w) {
+ uint32_t m = 0xffffffff << 1;
+ uint32_t x = ntohl(input->s6_addr32[w]);
+ uint32_t hpad = ntohl(ip6pad[w]);
+ output.s6_addr32[w] = 0;
+ /* anonymize x, using hpad */
+ for (i = 31; i > pbits - 1; --i) {
+ /* pass through 'i' highest bits of the word */
+ x &= m;
+ /* the following could be:
+ * x |= (hpad & ~m); */
+ x |= (hpad >> i);
+ b6_in.ip6.s6_addr32[w] = htonl(x);
+ /* hashing proper */
+ switch (scramble_crypto6) {
+ case SCRAMBLE_BLOWFISH:
+ /* use BF in chain mode */
+ EVP_CIPHER_CTX_reset(ctx6);
+ if (!EVP_EncryptInit_ex2(ctx6, cipher6, scramble_key, ivec, NULL)) {
+ fprintf(stderr,
+ "scramble_init(): EVP_EncryptInit_ex2 failed:");
+ ERR_print_errors_fp(stderr);
+ }
+ if (!EVP_CipherUpdate(ctx6, (u_char*)&b6_out, &outlen, (u_char*)&b6_in, sizeof(b6_in))) {
+ /* Error */
+ fprintf(stderr, "scramble_ip6(): EVP_CipherUpdate failed\n");
+ ERR_print_errors_fp(stderr);
+ }
+ break;
+ case SCRAMBLE_AES:
+ if (!EVP_CipherUpdate(ctx6, (u_char*)&b6_out, &outlen, (u_char*)&b6_in, AES_BLOCK_SIZE)) {
+ /* Error */
+ fprintf(stderr, "scramble_ip6(): EVP_CipherUpdate failed");
+ }
+ break;
+ case SCRAMBLE_SHA1:
+ case SCRAMBLE_MD5:
+ if (scramble_crypto6 == SCRAMBLE_SHA1) {
+ mdlen = SHA_DIGEST_LENGTH;
+ } else {
+ mdlen = MD5_DIGEST_LENGTH;
+ }
+ // fallthrough
+ if (!EVP_DigestInit_ex2(mdctx6, md6, NULL) ||
+ !EVP_DigestUpdate(mdctx6, (u_char*)&b6_in, mdlen) ||
+ !EVP_DigestFinal_ex(mdctx6, (u_char*)&b6_out, &mdlen)) {
+ /* Error */
+ fprintf(stderr, "scramble_ip6(): EVP_Digest* failed");
+ }
+ break;
+ default:
+ abort();
+ }
+ output.s6_addr32[w] |= ((ntohl(b6_out.ip6.s6_addr32[3]) & 1)
+ << (31 - i));
+ m <<= 1;
+ }
+ pbits = (pbits >= 32) ? pbits - 32 : 0;
+ /* pbits >= 32 this means the above for-loop wasn't executed */
+
+ output.s6_addr32[w] = htonl(output.s6_addr32[w]) ^ input->s6_addr32[w];
+
+ /* restore the word */
+ b6_in.ip6.s6_addr32[w] = input->s6_addr32[w];
+ }
+ *input = output;
+}
+
+/* reverse map scrambled IP addresses, all network byte order */
+uint32_t
+unscramble_ip4(uint32_t input, int pass_bits)
+{
+ int i;
+ uint32_t guess, res;
+
+ guess = input; /* Starting with the input seems
+ * a good idea because some bits
+ * may be passed through
+ * unchanged */
+ for (i=32; i>0; --i) {
+ res = scramble_ip4(guess, pass_bits);
+ /* we're only interested in flipping the
+ * higher bit, don't care about the rest */
+ res ^= input;
+ if (res == 0)
+ return guess;
+ guess ^= res;
+ }
+ //unreachable, since there should be always a match
+ //(since we're zeroing out at least one bit per iteration)
+ assert(0);
+ return (0xffffffff); /* cannot find the match */
+}
+
+/* unscramble ipv6 address in place, in network byte order */
+void
+unscramble_ip6(struct in6_addr *input, int pass_bits)
+{
+ struct in6_addr guess;
+ struct in6_addr res;
+ uint32_t r = 0;
+
+ int i;
+
+ guess = *input;
+ for (i = 0; i < 4; ++i) {
+ for (;;) {
+ res = guess;
+ scramble_ip6(&res, pass_bits);
+ r = res.s6_addr32[i] ^ input->s6_addr32[i];
+
+ if (r == 0) break;
+
+ guess.s6_addr32[i] ^= r;
+ }
+
+ }
+ *input = guess;
+ return;
+}
diff --git a/src/cryptopANT.h b/src/cryptopANT.h
new file mode 100644
index 0000000..8f596ff
--- /dev/null
+++ b/src/cryptopANT.h
@@ -0,0 +1,92 @@
+/* -*- Mode:C; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
+/*
+ * Copyright (C) 2004-2024 by the University of Southern California
+ * $Id: c23494984e1ae44af55668feda8232282c9473d0 $
+ *
+ * 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.
+ *
+ */
+
+
+#ifndef _SCRAMBLE_CRYPT_H
+#define _SCRAMBLE_CRYPT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ETHER_ADDR_LEN 6
+#define ETHER_VLAN_LEN 2
+
+#define _XOR16(a, b, i) (((uint16_t *)(a))[i] ^= ((uint16_t *)(b))[i])
+#define _XOR32(a, b, i) (((uint32_t *)(a))[i] ^= ((uint32_t *)(b))[i])
+
+#define SCRAMBLE_ETHER_ADDR(a) if (1) { \
+ _XOR32(a, scramble_ether_addr, 0); \
+ _XOR16(a, scramble_ether_addr, 2); \
+ }
+
+#define SCRAMBLE_ETHER_VLAN(v) ((v) ^= scramble_ether_vlan);
+
+#define SCRAMBLE_RANDOM_DEV "/dev/urandom"
+
+typedef enum {
+ SCRAMBLE_NONE = 0x00,
+ SCRAMBLE_MD5 = 0x01,
+ SCRAMBLE_BLOWFISH = 0x02,
+ SCRAMBLE_AES = 0x03,
+ SCRAMBLE_SHA1 = 0x04
+} scramble_crypt_t;
+
+typedef struct {
+ scramble_crypt_t c4;
+ scramble_crypt_t c6;
+ u_char *key;
+ int klen;
+ u_char *pad;
+ int plen;
+ u_char *mac;
+ int mlen;
+ u_char *iv;
+ int ivlen;
+} scramble_state_t;
+
+/* external vars exported by mac scrambling macros */
+extern uint8_t scramble_ether_addr[ETHER_ADDR_LEN];
+extern uint16_t scramble_ether_vlan;
+extern int scramble_mac; /* 0/1 */
+
+/* public functions */
+extern scramble_crypt_t scramble_crypto_ip4 (void);
+extern scramble_crypt_t scramble_crypto_ip6 (void);
+extern scramble_crypt_t scramble_name2type (const char *);
+extern const char* scramble_type2name (scramble_crypt_t);
+extern int scramble_newkey (u_char *, int);
+extern int scramble_newpad (u_char *, int);
+extern int scramble_newmac (u_char *, int);
+extern int scramble_readstate (const char *, scramble_state_t *);
+extern int scramble_savestate (const char *, const scramble_state_t *);
+extern int scramble_init (const scramble_state_t *s);
+extern int scramble_init_from_file (const char *, scramble_crypt_t, scramble_crypt_t, int *);
+extern void scramble_cleanup ();
+extern uint32_t scramble_ip4 (uint32_t, int);
+extern uint32_t unscramble_ip4 (uint32_t, int);
+extern void scramble_ip6 (struct in6_addr *, int);
+extern void unscramble_ip6 (struct in6_addr *, int);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SCRAMBLE_CRYPT_H */
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;
+}