]> jfr.im git - irc/quakenet/newserv.git/blobdiff - lib/irc_ipv6.c
Fix a potential invalid memory access in iptobase64.
[irc/quakenet/newserv.git] / lib / irc_ipv6.c
index 122c420eee39bbb236b2f1c38040fc9c9cf0d910..1be9d40d85f6bfebcba53a2194645b244122efc2 100644 (file)
@@ -3,46 +3,13 @@
 #include <stdlib.h>
 #include <string.h>
 #include <assert.h>
+#include <arpa/inet.h>
 #include <netdb.h>
 #include "irc_ipv6.h"
+#include <stdio.h>
 
 #warning This source file is probably GPLed, it needs relicensing.
 
-/*
- * this new faster inet_ntoa was ripped from:
- * From: Thomas Helvey <tomh@inxpress.net>
- */
-/** Array of text strings for dotted quads. */
-static const char* IpQuadTab[] =
-{
-    "0",   "1",   "2",   "3",   "4",   "5",   "6",   "7",   "8",   "9",
-   "10",  "11",  "12",  "13",  "14",  "15",  "16",  "17",  "18",  "19",
-   "20",  "21",  "22",  "23",  "24",  "25",  "26",  "27",  "28",  "29",
-   "30",  "31",  "32",  "33",  "34",  "35",  "36",  "37",  "38",  "39",
-   "40",  "41",  "42",  "43",  "44",  "45",  "46",  "47",  "48",  "49",
-   "50",  "51",  "52",  "53",  "54",  "55",  "56",  "57",  "58",  "59",
-   "60",  "61",  "62",  "63",  "64",  "65",  "66",  "67",  "68",  "69",
-   "70",  "71",  "72",  "73",  "74",  "75",  "76",  "77",  "78",  "79",
-   "80",  "81",  "82",  "83",  "84",  "85",  "86",  "87",  "88",  "89",
-   "90",  "91",  "92",  "93",  "94",  "95",  "96",  "97",  "98",  "99",
-  "100", "101", "102", "103", "104", "105", "106", "107", "108", "109",
-  "110", "111", "112", "113", "114", "115", "116", "117", "118", "119",
-  "120", "121", "122", "123", "124", "125", "126", "127", "128", "129",
-  "130", "131", "132", "133", "134", "135", "136", "137", "138", "139",
-  "140", "141", "142", "143", "144", "145", "146", "147", "148", "149",
-  "150", "151", "152", "153", "154", "155", "156", "157", "158", "159",
-  "160", "161", "162", "163", "164", "165", "166", "167", "168", "169",
-  "170", "171", "172", "173", "174", "175", "176", "177", "178", "179",
-  "180", "181", "182", "183", "184", "185", "186", "187", "188", "189",
-  "190", "191", "192", "193", "194", "195", "196", "197", "198", "199",
-  "200", "201", "202", "203", "204", "205", "206", "207", "208", "209",
-  "210", "211", "212", "213", "214", "215", "216", "217", "218", "219",
-  "220", "221", "222", "223", "224", "225", "226", "227", "228", "229",
-  "230", "231", "232", "233", "234", "235", "236", "237", "238", "239",
-  "240", "241", "242", "243", "244", "245", "246", "247", "248", "249",
-  "250", "251", "252", "253", "254", "255"
-};
-
 /** Convert an IP address to printable ASCII form.
  * This is generally deprecated in favor of ircd_ntoa_r().
  * @param[in] in Address to convert.
@@ -65,28 +32,12 @@ const char* ircd_ntoa_r(char* buf, const struct irc_in_addr* in)
     assert(in != NULL);
 
     if (irc_in_addr_is_ipv4(in)) {
-      unsigned int pos, len;
       unsigned char *pch;
 
       pch = (unsigned char*)&in->in6_16[6];
-      len = strlen(IpQuadTab[*pch]);
-      memcpy(buf, IpQuadTab[*pch++], len);
-      pos = len;
-      buf[pos++] = '.';
-      len = strlen(IpQuadTab[*pch]);
-      memcpy(buf+pos, IpQuadTab[*pch++], len);
-      pos += len;
-      buf[pos++] = '.';
-      len = strlen(IpQuadTab[*pch]);
-      memcpy(buf+pos, IpQuadTab[*pch++], len);
-      pos += len;
-      buf[pos++] = '.';
-      len = strlen(IpQuadTab[*pch]);
-      memcpy(buf+pos, IpQuadTab[*pch++], len);
-      buf[pos + len] = '\0';
+      sprintf(buf,"%d.%d.%d.%d",pch[0],pch[1],pch[2],pch[3]);
       return buf;
     } else {
-      static const char hexdigits[] = "0123456789abcdef";
       unsigned int pos, part, max_start, max_zeros, curr_zeros, ii;
 
       /* Find longest run of zeros. */
@@ -114,13 +65,7 @@ const char* ircd_ntoa_r(char* buf, const struct irc_in_addr* in)
           continue;
         }
         part = ntohs(in->in6_16[ii]);
-        if (part >= 0x1000)
-          APPEND(hexdigits[part >> 12]);
-        if (part >= 0x100)
-          APPEND(hexdigits[(part >> 8) & 15]);
-        if (part >= 0x10)
-          APPEND(hexdigits[(part >> 4) & 15]);
-        APPEND(hexdigits[part & 15]);
+        pos+=sprintf(buf+pos,"%x",part);
         if (ii < 7)
           APPEND(':');
       }
@@ -132,6 +77,48 @@ const char* ircd_ntoa_r(char* buf, const struct irc_in_addr* in)
     }
 }
 
+/** Convert a CIDR mask to printable ASCII form.
+ * This is generally deprecated in favor of ircd_ntoa_masked_r().
+ * @param[in] in Address to convert.
+ * @param[in] bits Mask bits.
+ * @return Pointer to a static buffer containing the readable form.
+ */
+const char* ircd_ntoa_masked(const struct irc_in_addr* in, unsigned char bits)
+{
+  static char buf[CIDRLEN];
+  return ircd_ntoa_masked_r(buf, in, bits);
+}
+
+/** Convert a CIDR mask to printable ASCII form.
+ * @param[out] buf Output buffer to write to.
+ * @param[in] in Address to format.
+ * @param[in] bits Mask bits.
+ * @return Pointer to the output buffer \a buf.
+ */
+const char* ircd_ntoa_masked_r(char* buf, const struct irc_in_addr* in, unsigned char bits)
+{
+  char inname[SOCKIPLEN];
+  struct irc_in_addr intemp;
+  int i;
+
+  for(i=0;i<8;i++) {
+    int curbits = bits - i * 16;
+
+    if (curbits<0)
+      curbits = 0;
+    else if (curbits>16)
+      curbits = 16;
+
+    uint16_t mask = 0xffff & ~((1 << (16 - curbits)) - 1);
+    intemp.in6_16[i] = htons(ntohs(in->in6_16[i]) & mask);
+  }
+
+  ircd_ntoa_r(inname, &intemp);
+  sprintf(buf, "%s/%u", inname, irc_bitlen(in, bits));
+
+  return buf;
+}
+
 /** Attempt to parse an IPv4 address into a network-endian form.
  * @param[in] input Input string.
  * @param[out] output Network-endian representation of the address.
@@ -161,12 +148,14 @@ ircd_aton_ip4(const char *input, unsigned int *output, unsigned char *pbits)
       *pbits = bits;
     return pos;
   case '.':
+    if (++dots > 3)
+      return 0;
     if (input[++pos] == '.')
       return 0;
-    ip |= part << (24 - 8 * dots++);
+    ip |= part << (32 - 8 * dots);
     part = 0;
     if (input[pos] == '*') {
-      while (input[++pos] == '*') ;
+      while (input[++pos] == '*' || input[pos] == '.') ;
       if (input[pos] != '\0')
         return 0;
       if (pbits)
@@ -284,7 +273,7 @@ ipmask_parse(const char *input, struct irc_in_addr *ip, unsigned char *pbits)
       *pbits = part;
       goto finish;
     case '*':
-      while (input[++pos] == '*') ;
+      while (input[++pos] == '*' || input[pos] == ':') ;
       if (input[pos] != '\0' || colon < 8)
         return 0;
       if (pbits)
@@ -300,6 +289,8 @@ ipmask_parse(const char *input, struct irc_in_addr *ip, unsigned char *pbits)
     default:
       return 0;
     }
+    if (input[pos] != '\0')
+      return 0;
   finish:
     if (colon < 8) {
       unsigned int jj;
@@ -449,7 +440,7 @@ const char* iptobase64(char* buf, const struct irc_in_addr* addr, unsigned int c
 
     assert(count >= 25);
     /* Can start by printing out the leading non-zero parts. */
-    for (ii = 0; (addr->in6_16[ii]) && (ii < 8); ++ii) {
+    for (ii = 0; (ii < 8) && (addr->in6_16[ii]); ++ii) {
       inttobase64(output, ntohs(addr->in6_16[ii]), 3);
       output += 3;
     }
@@ -466,7 +457,6 @@ const char* iptobase64(char* buf, const struct irc_in_addr* addr, unsigned int c
     if (curr_zeros > max_zeros) {
       max_start = ii - curr_zeros;
       max_zeros = curr_zeros;
-      curr_zeros = 0;
     }
     /* Print the rest of the address */
     for (ii = zero; ii < 8; ) {
@@ -517,3 +507,48 @@ void base64toip(const char* input, struct irc_in_addr* addr)
   }
 }
 
+/** Test whether an address matches the most significant bits of a mask.
+ * @param[in] addr Address to test.
+ * @param[in] mask Address to test against.
+ * @param[in] bits Number of bits to test.
+ * @return 0 on mismatch, 1 if bits < 128 and all bits match; -1 if
+ * bits == 128 and all bits match.
+ */
+int ipmask_check(const struct irc_in_addr *addr, const struct irc_in_addr *mask, unsigned char bits)
+{
+  int k;
+
+  for (k = 0; k < 8; k++) {
+    if (bits < 16)
+      return !(htons(addr->in6_16[k] ^ mask->in6_16[k]) >> (16-bits));
+    if (addr->in6_16[k] != mask->in6_16[k])
+      return 0;
+    if (!(bits -= 16))
+      return 1;
+  }
+  return -1;
+}
+
+/** Convert IP addresses to canonical form for comparison.  6to4 and Teredo addresses
+ * are converted to IPv4 addresses. All other addresses are left alone.
+ * @param[out] out Receives canonical format for address.
+ * @param[in] in IP address to canonicalize.
+ */
+void ip_canonicalize_tunnel(struct irc_in_addr *out, const struct irc_in_addr *in)
+{
+    if (in->in6_16[0] == htons(0x2002)) { /* 6to4 */
+        out->in6_16[0] = out->in6_16[1] = out->in6_16[2] = 0;
+        out->in6_16[3] = out->in6_16[4] = 0;
+        out->in6_16[5] = 0xffff;
+        out->in6_16[6] = in->in6_16[1];
+        out->in6_16[7] = in->in6_16[2];
+    } else if(in->in6_16[0] == htons(0x2001) && in->in6_16[1] == 0) { /* Teredo */
+        out->in6_16[0]  = out->in6_16[1] = out->in6_16[2] = 0;
+        out->in6_16[3] = out->in6_16[4] = 0;
+        out->in6_16[5] = 0xffff;
+        out->in6_16[6] = ~(in->in6_16[6]);
+        out->in6_16[7] = ~(in->in6_16[7]);
+    } else
+        memcpy(out, in, sizeof(*out));
+}
+