extern char *return_user_mask(char[]) ;
extern char *return_oper_mask(struct Client *) ;
extern int str2array(char **pparv, char *string, char *delim) ;
-extern char *crypt ();
-#define MAXVIRTSIZE (3 + 5 + 1)
+#include "settings.h"
void calc_mask(aClient *acptr)
{
#ifdef CLIENT_MASKING
-char *Maskchecksum(char *data, char *salt)
+/*
+ * FNV-1a 32-bit hash — simple, fast, no external dependencies.
+ */
+static uint32_t fnv_hash(const char *s, int len)
{
- char static tmp[HOSTLEN + 1] ;
-
- strncpy(tmp,crypt(data, salt),HOSTLEN) ;
- return (tmp) ;
+ uint32_t h = 0x811c9dc5;
+ int i;
+ for (i = 0; i < len; i++) {
+ h ^= (unsigned char) s[i];
+ h *= 0x01000193;
+ }
+ return h;
+}
+
+/*
+ * Write a 6-character uppercase hex tag from a 32-bit hash.
+ * Uses the low 24 bits (16 million values). Null-terminates dst.
+ */
+static void mask_hashtag(char *dst, uint32_t hash)
+{
+ static const char hex[] = "0123456789ABCDEF";
+ int i;
+ for (i = 20; i >= 0; i -= 4)
+ *dst++ = hex[(hash >> i) & 0xf];
+ *dst = '\0';
}
+/*
+ * Extract the /48 prefix (first 3 hextets) from an IPv6 string.
+ * Returns length written to dst (not counting NUL).
+ */
+static int ipv6_prefix48(char *dst, int dstlen, const char *s)
+{
+ int colons = 0, i;
+
+ for (i = 0; s[i] && colons < 3; i++) {
+ if (s[i] == ':')
+ colons++;
+ }
+ if (colons >= 3 && i > 1) {
+ /* i is past the 3rd colon; take everything before it */
+ if (i - 1 >= dstlen) i = dstlen;
+ memcpy(dst, s, i - 1);
+ dst[i - 1] = '\0';
+ return i - 1;
+ }
+ /* Short address, use what we have */
+ strncpy(dst, s, dstlen - 1);
+ dst[dstlen - 1] = '\0';
+ return strlen(dst);
+}
+
+/*
+ * Extract the /64 prefix (first 4 hextets) from an IPv6 string.
+ * This is the hash input — two users on the same /64 get the same mask.
+ */
+static int ipv6_prefix64(char *dst, int dstlen, const char *s)
+{
+ int colons = 0, i;
+
+ for (i = 0; s[i] && colons < 4; i++) {
+ if (s[i] == ':')
+ colons++;
+ }
+ if (colons >= 4 && i > 1) {
+ if (i - 1 >= dstlen) i = dstlen;
+ memcpy(dst, s, i - 1);
+ dst[i - 1] = '\0';
+ return i - 1;
+ }
+ strncpy(dst, s, dstlen - 1);
+ dst[dstlen - 1] = '\0';
+ return strlen(dst);
+}
char *return_user_mask(char *s)
{
static char mask[HOSTLEN + 1];
- char *csum;
- char *dot, *ptr ;
- static char destroy[HOSTLEN + 1], *parv[HOSTLEN + 1];
- char salt[12] = "$1$\0\0\0\0\0\0\0\0\0";
- int len = 0, overflow = 0, parc = 0;
-
- strncpy(destroy, s, HOSTLEN);
- len = strlen(destroy);
-
- if ((len + MAXVIRTSIZE) > HOSTLEN) {
- overflow = (len + MAXVIRTSIZE) - HOSTLEN;
- ptr = &destroy[overflow];
- } else {
- ptr = &destroy[0];
- }
- memset(mask, 0, HOSTLEN);
-
- parc = str2array(parv, ptr, ".");
+ char hashtag[7];
+ uint32_t hash;
+ char *dot;
- if (strlen(s) > HOSTLEN) {
+ if (strlen(s) > HOSTLEN)
s[HOSTLEN] = 0;
+
+ /* Strip ::ffff: prefix from v4-mapped addresses */
+ if (strncmp(s, "::ffff:", 7) == 0)
+ s += 7;
+
+ dot = strchr(s, '.');
+
+ if (strchr(s, ':')) {
+ /*
+ * IPv6: hash the /64, show the /48.
+ * Same /64 = same mask. Opers ban /48 with *.prefix48
+ * Result: SereneAB12CD.2601:540:ce00
+ */
+ char prefix48[20], prefix64[30];
+
+ ipv6_prefix48(prefix48, sizeof(prefix48), s);
+ ipv6_prefix64(prefix64, sizeof(prefix64), s);
+ hash = fnv_hash(prefix64, strlen(prefix64));
+ mask_hashtag(hashtag, hash);
+ snprintf(mask, HOSTLEN, "%s%s.%s", cfg_mask_prefix, hashtag, prefix48);
+ return mask;
}
-
- if (parc == 4) {
- len = strlen(parv[3]);
- if (isdigit(parv[3][len - 1])) {
- /* Numeric IP, lets use the last octets of the IP address
- * as salt */
- strcat(salt, parv[3]);
- strcat(salt, parv[2]);
- csum = Maskchecksum(s, salt);
- sprintf(mask, "%s.%s.%s.%i", parv[0], parv[1],parv[2],csum[15]+256+csum[19]);
- return mask;
- }
+
+ if (dot && !isdigit((unsigned char) s[strlen(s) - 1])) {
+ /*
+ * Hostname: hash the full hostname, show domain after first dot.
+ * Result: SereneAB12CD.example.com
+ */
+ hash = fnv_hash(s, strlen(s));
+ mask_hashtag(hashtag, hash);
+ snprintf(mask, HOSTLEN, "%s%s.%s", cfg_mask_prefix, hashtag, dot + 1);
+ return mask;
}
- /* Hostname.... lets use it as our salt... */
- strncat(salt, s, 8);
- csum = Maskchecksum(s, salt);
+ if (dot) {
+ /*
+ * IPv4: hash the full IP, show first 3 octets.
+ * Result: SereneAB12CD.192.168.1
+ */
+ char *last_dot = strrchr(s, '.');
+ char net_prefix[HOSTLEN + 1];
- dot = (char *) strchr(s, '.');
-
- if (dot == NULL) {
- sprintf(mask, "%s%i%i%i%i%i%i.%s", cfg_mask_prefix, csum[14]%10,csum[15]%10,csum[16]%10,csum[17]%10,csum[18]%10,csum[19]%10, s);
- return mask;
- } else {
- sprintf(mask, "%s%i%i%i%i%i%i.%s", cfg_mask_prefix, csum[14]%10,csum[15]%10,csum[16]%10,csum[17]%10,csum[18]%10,csum[19]%10, dot + 1);
+ if (last_dot && last_dot != s) {
+ strncpy(net_prefix, s, last_dot - s);
+ net_prefix[last_dot - s] = '\0';
+ } else {
+ strncpy(net_prefix, s, HOSTLEN);
+ net_prefix[HOSTLEN] = '\0';
+ }
+ hash = fnv_hash(s, strlen(s));
+ mask_hashtag(hashtag, hash);
+ snprintf(mask, HOSTLEN, "%s%s.%s", cfg_mask_prefix, hashtag, net_prefix);
return mask;
}
+
+ /*
+ * Single-label host (no dots, no colons).
+ * Result: SereneAB12CD.hostname
+ */
+ hash = fnv_hash(s, strlen(s));
+ mask_hashtag(hashtag, hash);
+ snprintf(mask, HOSTLEN, "%s%s.%s", cfg_mask_prefix, hashtag, s);
+ return mask;
}
#endif /* CLIENT_MASKING */