[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

[tor-commits] [tor/master] Get the client side of receiving an IPv6 address to work



commit cac5335195d3bb9a39af77886992ffa98b8c7817
Author: Nick Mathewson <nickm@xxxxxxxxxxxxxx>
Date:   Wed Oct 31 18:27:48 2012 -0400

    Get the client side of receiving an IPv6 address to work
    
    This makes it so we can handle getting an IPv6 in the 3 different
    formats we specified it for in RESOLVED cells,
    END_STREAM_REASON_EXITPOLICY cells, and CONNECTED cells.
    
    We don't cache IPv6 addresses yet, since proposal 205 isn't
    implemented.
    
    There's a refactored function for parsing connected cells; it has unit
    tests.
---
 src/or/addressmap.c          |   35 ++++++-----
 src/or/addressmap.h          |    2 +-
 src/or/connection_edge.c     |   15 ++++-
 src/or/connection_edge.h     |    1 +
 src/or/relay.c               |  134 ++++++++++++++++++++++++++++++++---------
 src/or/relay.h               |    2 +
 src/test/test_cell_formats.c |  114 +++++++++++++++++++++++++++++++++++
 7 files changed, 254 insertions(+), 49 deletions(-)

diff --git a/src/or/addressmap.c b/src/or/addressmap.c
index 4aa46fc..45e29c1 100644
--- a/src/or/addressmap.c
+++ b/src/or/addressmap.c
@@ -576,10 +576,7 @@ client_dns_set_addressmap_impl(origin_circuit_t *on_circ,
                                const char *exitname,
                                int ttl)
 {
-  /* <address>.<hex or nickname>.exit\0  or just  <address>\0 */
-  char extendedaddress[MAX_SOCKS_ADDR_LEN+MAX_VERBOSE_NICKNAME_LEN+10];
-  /* 123.123.123.123.<hex or nickname>.exit\0  or just  123.123.123.123\0 */
-  char extendedval[INET_NTOA_BUF_LEN+MAX_VERBOSE_NICKNAME_LEN+10];
+  char *extendedaddress=NULL, *extendedval=NULL;
   (void)on_circ;
 
   tor_assert(address);
@@ -594,18 +591,19 @@ client_dns_set_addressmap_impl(origin_circuit_t *on_circ,
     /* XXXX fails to ever get attempts to get an exit address of
      * google.com.digest[=~]nickname.exit; we need a syntax for this that
      * won't make strict RFC952-compliant applications (like us) barf. */
-    tor_snprintf(extendedaddress, sizeof(extendedaddress),
+    tor_asprintf(&extendedaddress,
                  "%s.%s.exit", address, exitname);
-    tor_snprintf(extendedval, sizeof(extendedval),
+    tor_asprintf(&extendedval,
                  "%s.%s.exit", name, exitname);
   } else {
-    tor_snprintf(extendedaddress, sizeof(extendedaddress),
+    tor_asprintf(&extendedaddress,
                  "%s", address);
-    tor_snprintf(extendedval, sizeof(extendedval),
+    tor_asprintf(&extendedval,
                  "%s", name);
   }
-  addressmap_register(extendedaddress, tor_strdup(extendedval),
+  addressmap_register(extendedaddress, extendedval,
                       time(NULL) + ttl, ADDRMAPSRC_DNS, 0, 0);
+  tor_free(extendedaddress);
 }
 
 /** Record the fact that <b>address</b> resolved to <b>val</b>.
@@ -620,19 +618,26 @@ client_dns_set_addressmap_impl(origin_circuit_t *on_circ,
  */
 void
 client_dns_set_addressmap(origin_circuit_t *on_circ,
-                          const char *address, uint32_t val,
+                          const char *address,
+                          const tor_addr_t *val,
                           const char *exitname,
                           int ttl)
 {
-  struct in_addr in;
-  char valbuf[INET_NTOA_BUF_LEN];
+  tor_addr_t addr_tmp;
+  char valbuf[TOR_ADDR_BUF_LEN];
 
   tor_assert(address);
+  tor_assert(val);
 
-  if (tor_inet_aton(address, &in))
+  if (tor_addr_parse(&addr_tmp, address) == 0)
     return; /* If address was an IP address already, don't add a mapping. */
-  in.s_addr = htonl(val);
-  tor_inet_ntoa(&in,valbuf,sizeof(valbuf));
+
+  /* XXXXX For now, don't cache IPv6 addresses. */
+  if (tor_addr_family(val) != AF_INET)
+    return;
+
+  if (! tor_addr_to_str(valbuf, val, sizeof(valbuf), 0)) /* XXXX decorate? */
+    return;
 
   client_dns_set_addressmap_impl(on_circ, address, valbuf, exitname, ttl);
 }
diff --git a/src/or/addressmap.h b/src/or/addressmap.h
index dd8fc9b..9b07341 100644
--- a/src/or/addressmap.h
+++ b/src/or/addressmap.h
@@ -29,7 +29,7 @@ int parse_virtual_addr_network(const char *val, int validate_only,
 int client_dns_incr_failures(const char *address);
 void client_dns_clear_failures(const char *address);
 void client_dns_set_addressmap(origin_circuit_t *on_circ,
-                               const char *address, uint32_t val,
+                               const char *address, const tor_addr_t *val,
                                const char *exitname, int ttl);
 const char *addressmap_register_virtual_address(int type, char *new_address);
 void addressmap_get_mappings(smartlist_t *sl, time_t min_expires,
diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c
index dc7d863..969128f 100644
--- a/src/or/connection_edge.c
+++ b/src/or/connection_edge.c
@@ -1949,10 +1949,19 @@ connection_ap_handshake_socks_resolved(entry_connection_t *conn,
     if (CIRCUIT_IS_ORIGIN(circ)) /* should always be true */
       origin_circ = TO_ORIGIN_CIRCUIT(circ);
     if (answer_type == RESOLVED_TYPE_IPV4 && answer_len == 4) {
-      uint32_t a = ntohl(get_uint32(answer));
-      if (a) {
+      tor_addr_t a;
+      tor_addr_from_ipv4n(&a, get_uint32(answer));
+      if (! tor_addr_is_null(&a)) {
         client_dns_set_addressmap(origin_circ,
-                                  conn->socks_request->address, a,
+                                  conn->socks_request->address, &a,
+                                  conn->chosen_exit_name, ttl);
+      }
+    } else if (answer_type == RESOLVED_TYPE_IPV6 && answer_len == 16) {
+      tor_addr_t a;
+      tor_addr_from_ipv6_bytes(&a, (char*)answer);
+      if (! tor_addr_is_null(&a)) {
+        client_dns_set_addressmap(origin_circ,
+                                  conn->socks_request->address, &a,
                                   conn->chosen_exit_name, ttl);
       }
     } else if (answer_type == RESOLVED_TYPE_HOSTNAME && answer_len < 256) {
diff --git a/src/or/connection_edge.h b/src/or/connection_edge.h
index 39f492d..9c4318c 100644
--- a/src/or/connection_edge.h
+++ b/src/or/connection_edge.h
@@ -108,6 +108,7 @@ typedef struct begin_cell_t {
 
 int begin_cell_parse(const cell_t *cell, begin_cell_t *bcell,
                      uint8_t *end_reason_out);
+
 #endif
 
 
diff --git a/src/or/relay.c b/src/or/relay.c
index 76e9d25..a05e955 100644
--- a/src/or/relay.c
+++ b/src/or/relay.c
@@ -705,21 +705,37 @@ connection_ap_process_end_not_open(
     switch (reason) {
       case END_STREAM_REASON_EXITPOLICY:
         if (rh->length >= 5) {
-          uint32_t addr = ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE+1));
-          int ttl;
-          if (!addr) {
+          tor_addr_t addr;
+          int ttl = -1;
+          tor_addr_make_unspec(&addr);
+          if (rh->length == 5 || rh->length == 9) {
+            tor_addr_from_ipv4n(&addr,
+                                get_uint32(cell->payload+RELAY_HEADER_SIZE+1));
+            if (rh->length == 9)
+              ttl = (int)ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE+5));
+          } else if (rh->length == 17 || rh->length == 21) {
+            tor_addr_from_ipv6_bytes(&addr,
+                                (char*)(cell->payload+RELAY_HEADER_SIZE+1));
+            if (rh->length == 21)
+              ttl = (int)ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE+17));
+          }
+          if (tor_addr_is_null(&addr)) {
             log_info(LD_APP,"Address '%s' resolved to 0.0.0.0. Closing,",
                      safe_str(conn->socks_request->address));
             connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL);
             return 0;
           }
-          if (rh->length >= 9)
-            ttl = (int)ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE+5));
-          else
-            ttl = -1;
 
+          if ((tor_addr_family(&addr) == AF_INET && !conn->ipv4_traffic_ok) ||
+              (tor_addr_family(&addr) == AF_INET6 && !conn->ipv6_traffic_ok)) {
+            log_fn(LOG_PROTOCOL_WARN, LD_APP,
+                   "Got an EXITPOLICY failure on a connection with a "
+                   "mismatched family. Closing.");
+            connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL);
+            return 0;
+          }
           if (get_options()->ClientDNSRejectInternalAddresses &&
-              is_internal_IP(addr, 0)) {
+              tor_addr_is_internal(&addr, 0)) {
             log_info(LD_APP,"Address '%s' resolved to internal. Closing,",
                      safe_str(conn->socks_request->address));
             connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL);
@@ -727,7 +743,7 @@ connection_ap_process_end_not_open(
           }
 
           client_dns_set_addressmap(circ,
-                                    conn->socks_request->address, addr,
+                                    conn->socks_request->address, &addr,
                                     conn->chosen_exit_name, ttl);
         }
         /* check if he *ought* to have allowed it */
@@ -830,20 +846,56 @@ connection_ap_process_end_not_open(
 }
 
 /** Helper: change the socks_request-&gt;address field on conn to the
- * dotted-quad representation of <b>new_addr</b> (given in host order),
+ * dotted-quad representation of <b>new_addr</b>,
  * and send an appropriate REMAP event. */
 static void
-remap_event_helper(entry_connection_t *conn, uint32_t new_addr)
+remap_event_helper(entry_connection_t *conn, const tor_addr_t *new_addr)
 {
-  struct in_addr in;
-
-  in.s_addr = htonl(new_addr);
-  tor_inet_ntoa(&in, conn->socks_request->address,
-                sizeof(conn->socks_request->address));
+  tor_addr_to_str(conn->socks_request->address, new_addr,
+                  sizeof(conn->socks_request->address),
+                  0); /* XXXXX Should decorate be 1? */
   control_event_stream_status(conn, STREAM_EVENT_REMAP,
                               REMAP_STREAM_SOURCE_EXIT);
 }
 
+/**DOCDOC*/
+int
+connected_cell_parse(const relay_header_t *rh, const cell_t *cell,
+                     tor_addr_t *addr_out, int *ttl_out)
+{
+  uint32_t bytes;
+  const uint8_t *payload = cell->payload + RELAY_HEADER_SIZE;
+
+  tor_addr_make_unspec(addr_out);
+  *ttl_out = -1;
+  if (rh->length == 0)
+    return 0;
+  if (rh->length < 4)
+    return -1;
+  bytes = ntohl(get_uint32(payload));
+
+  /* If bytes is 0, this is maybe a v6 address. Otherwise it's a v4 address */
+  if (bytes != 0) {
+    /* v4 address */
+    tor_addr_from_ipv4h(addr_out, bytes);
+    if (rh->length >= 8) {
+      bytes = ntohl(get_uint32(payload + 4));
+      if (bytes <= INT32_MAX)
+        *ttl_out = bytes;
+    }
+  } else {
+    if (rh->length < 25) /* 4 bytes of 0s, 1 addr, 16 ipv4, 4 ttl. */
+      return -1;
+    if (get_uint8(payload + 4) != 6)
+      return -1;
+    tor_addr_from_ipv6_bytes(addr_out, (char*)(payload + 5));
+    bytes = ntohl(get_uint32(payload + 21));
+    if (bytes <= INT32_MAX)
+      *ttl_out = (int) bytes;
+  }
+  return 0;
+}
+
 /** An incoming relay cell has arrived from circuit <b>circ</b> to
  * stream <b>conn</b>.
  *
@@ -874,6 +926,8 @@ connection_edge_process_relay_cell_not_open(
 
   if (conn->base_.type == CONN_TYPE_AP &&
       rh->command == RELAY_COMMAND_CONNECTED) {
+    tor_addr_t addr;
+    int ttl;
     entry_connection_t *entry_conn = EDGE_TO_ENTRY_CONN(conn);
     tor_assert(CIRCUIT_IS_ORIGIN(circ));
     if (conn->base_.state != AP_CONN_STATE_CONNECT_WAIT) {
@@ -884,27 +938,41 @@ connection_edge_process_relay_cell_not_open(
     conn->base_.state = AP_CONN_STATE_OPEN;
     log_info(LD_APP,"'connected' received after %d seconds.",
              (int)(time(NULL) - conn->base_.timestamp_lastread));
-    if (rh->length >= 4) {
-      uint32_t addr = ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE));
-      int ttl;
-      if (!addr || (get_options()->ClientDNSRejectInternalAddresses &&
-                    is_internal_IP(addr, 0))) {
+    if (connected_cell_parse(rh, cell, &addr, &ttl) < 0) {
+      log_fn(LOG_PROTOCOL_WARN, LD_APP,
+             "Got a badly formatted connected cell. Closing.");
+      connection_edge_end(conn, END_STREAM_REASON_TORPROTOCOL);
+      connection_mark_unattached_ap(entry_conn, END_STREAM_REASON_TORPROTOCOL);
+    }
+    if (tor_addr_family(&addr) != AF_UNSPEC) {
+      const sa_family_t family = tor_addr_family(&addr);
+      if (tor_addr_is_null(&addr) ||
+          (get_options()->ClientDNSRejectInternalAddresses &&
+           tor_addr_is_internal(&addr, 0))) {
         log_info(LD_APP, "...but it claims the IP address was %s. Closing.",
-                 fmt_addr32(addr));
+                 fmt_addr(&addr));
+        connection_edge_end(conn, END_STREAM_REASON_TORPROTOCOL);
+        connection_mark_unattached_ap(entry_conn,
+                                      END_STREAM_REASON_TORPROTOCOL);
+        return 0;
+      }
+
+      if ((family == AF_INET && ! entry_conn->ipv4_traffic_ok) ||
+          (family == AF_INET6 && ! entry_conn->ipv6_traffic_ok)) {
+        log_fn(LOG_PROTOCOL_WARN, LD_APP,
+               "Got a connected cell to %s with unsupported address family."
+               " Closing.", fmt_addr(&addr));
         connection_edge_end(conn, END_STREAM_REASON_TORPROTOCOL);
         connection_mark_unattached_ap(entry_conn,
                                       END_STREAM_REASON_TORPROTOCOL);
         return 0;
       }
-      if (rh->length >= 8)
-        ttl = (int)ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE+4));
-      else
-        ttl = -1;
+
       client_dns_set_addressmap(TO_ORIGIN_CIRCUIT(circ),
-                                entry_conn->socks_request->address, addr,
+                                entry_conn->socks_request->address, &addr,
                                 entry_conn->chosen_exit_name, ttl);
 
-      remap_event_helper(entry_conn, addr);
+      remap_event_helper(entry_conn, &addr);
     }
     circuit_log_path(LOG_INFO,LD_APP,TO_ORIGIN_CIRCUIT(circ));
     /* don't send a socks reply to transparent conns */
@@ -994,8 +1062,14 @@ connection_edge_process_relay_cell_not_open(
                    ttl,
                    -1);
     if (answer_type == RESOLVED_TYPE_IPV4 && answer_len == 4) {
-      uint32_t addr = ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE+2));
-      remap_event_helper(entry_conn, addr);
+      tor_addr_t addr;
+      tor_addr_from_ipv4n(&addr, get_uint32(cell->payload+RELAY_HEADER_SIZE+2));
+      remap_event_helper(entry_conn, &addr);
+    } else if (answer_type == RESOLVED_TYPE_IPV6 && answer_len == 16) {
+      tor_addr_t addr;
+      tor_addr_from_ipv6_bytes(&addr,
+                               (char*)(cell->payload+RELAY_HEADER_SIZE+2));
+      remap_event_helper(entry_conn, &addr);
     }
     connection_mark_unattached_ap(entry_conn,
                               END_STREAM_REASON_DONE |
diff --git a/src/or/relay.h b/src/or/relay.h
index 0f7b45f..57400fd 100644
--- a/src/or/relay.h
+++ b/src/or/relay.h
@@ -68,6 +68,8 @@ void circuit_clear_cell_queue(circuit_t *circ, channel_t *chan);
 #ifdef RELAY_PRIVATE
 int relay_crypt(circuit_t *circ, cell_t *cell, cell_direction_t cell_direction,
                 crypt_path_t **layer_hint, char *recognized);
+int connected_cell_parse(const relay_header_t *rh, const cell_t *cell,
+                         tor_addr_t *addr_out, int *ttl_out);
 #endif
 
 #endif
diff --git a/src/test/test_cell_formats.c b/src/test/test_cell_formats.c
index 2199a05..3fac745 100644
--- a/src/test/test_cell_formats.c
+++ b/src/test/test_cell_formats.c
@@ -6,6 +6,7 @@
 #include "orconfig.h"
 
 #define CONNECTION_EDGE_PRIVATE
+#define RELAY_PRIVATE
 #include "or.h"
 #include "connection_edge.h"
 #include "relay.h"
@@ -223,12 +224,125 @@ test_cfmt_begin_cells(void *arg)
   tor_free(bcell.address);
 }
 
+static void
+test_cfmt_connected_cells(void *arg)
+{
+  relay_header_t rh;
+  cell_t cell;
+  tor_addr_t addr;
+  int ttl, r;
+  (void)arg;
+
+  /* Let's try an oldschool one with nothing in it. */
+  make_relay_cell(&cell, RELAY_COMMAND_CONNECTED, "", 0);
+  relay_header_unpack(&rh, cell.payload);
+  r = connected_cell_parse(&rh, &cell, &addr, &ttl);
+  tt_int_op(r, ==, 0);
+  tt_int_op(tor_addr_family(&addr), ==, AF_UNSPEC);
+  tt_int_op(ttl, ==, -1);
+
+  /* A slightly less oldschool one: only an IPv4 address */
+  make_relay_cell(&cell, RELAY_COMMAND_CONNECTED, "\x20\x30\x40\x50", 4);
+  relay_header_unpack(&rh, cell.payload);
+  r = connected_cell_parse(&rh, &cell, &addr, &ttl);
+  tt_int_op(r, ==, 0);
+  tt_int_op(tor_addr_family(&addr), ==, AF_INET);
+  tt_str_op(fmt_addr(&addr), ==, "32.48.64.80");
+  tt_int_op(ttl, ==, -1);
+
+  /* Bogus but understandable: truncated TTL */
+  make_relay_cell(&cell, RELAY_COMMAND_CONNECTED, "\x11\x12\x13\x14\x15", 5);
+  relay_header_unpack(&rh, cell.payload);
+  r = connected_cell_parse(&rh, &cell, &addr, &ttl);
+  tt_int_op(r, ==, 0);
+  tt_int_op(tor_addr_family(&addr), ==, AF_INET);
+  tt_str_op(fmt_addr(&addr), ==, "17.18.19.20");
+  tt_int_op(ttl, ==, -1);
+
+  /* Regular IPv4 one: address and TTL */
+  make_relay_cell(&cell, RELAY_COMMAND_CONNECTED,
+                  "\x02\x03\x04\x05\x00\x00\x0e\x10", 8);
+  relay_header_unpack(&rh, cell.payload);
+  r = connected_cell_parse(&rh, &cell, &addr, &ttl);
+  tt_int_op(r, ==, 0);
+  tt_int_op(tor_addr_family(&addr), ==, AF_INET);
+  tt_str_op(fmt_addr(&addr), ==, "2.3.4.5");
+  tt_int_op(ttl, ==, 3600);
+
+  /* IPv4 with too-big TTL */
+  make_relay_cell(&cell, RELAY_COMMAND_CONNECTED,
+                  "\x02\x03\x04\x05\xf0\x00\x00\x00", 8);
+  relay_header_unpack(&rh, cell.payload);
+  r = connected_cell_parse(&rh, &cell, &addr, &ttl);
+  tt_int_op(r, ==, 0);
+  tt_int_op(tor_addr_family(&addr), ==, AF_INET);
+  tt_str_op(fmt_addr(&addr), ==, "2.3.4.5");
+  tt_int_op(ttl, ==, -1);
+
+  /* IPv6 (ttl is mandatory) */
+  make_relay_cell(&cell, RELAY_COMMAND_CONNECTED,
+                  "\x00\x00\x00\x00\x06"
+                  "\x26\x07\xf8\xb0\x40\x0c\x0c\x02"
+                  "\x00\x00\x00\x00\x00\x00\x00\x68"
+                  "\x00\x00\x02\x58", 25);
+  relay_header_unpack(&rh, cell.payload);
+  r = connected_cell_parse(&rh, &cell, &addr, &ttl);
+  tt_int_op(r, ==, 0);
+  tt_int_op(tor_addr_family(&addr), ==, AF_INET6);
+  tt_str_op(fmt_addr(&addr), ==, "2607:f8b0:400c:c02::68");
+  tt_int_op(ttl, ==, 600);
+
+  /* IPv6 (ttl too big) */
+  make_relay_cell(&cell, RELAY_COMMAND_CONNECTED,
+                  "\x00\x00\x00\x00\x06"
+                  "\x26\x07\xf8\xb0\x40\x0c\x0c\x02"
+                  "\x00\x00\x00\x00\x00\x00\x00\x68"
+                  "\x90\x00\x02\x58", 25);
+  relay_header_unpack(&rh, cell.payload);
+  r = connected_cell_parse(&rh, &cell, &addr, &ttl);
+  tt_int_op(r, ==, 0);
+  tt_int_op(tor_addr_family(&addr), ==, AF_INET6);
+  tt_str_op(fmt_addr(&addr), ==, "2607:f8b0:400c:c02::68");
+  tt_int_op(ttl, ==, -1);
+
+  /* Bogus size: 3. */
+  make_relay_cell(&cell, RELAY_COMMAND_CONNECTED,
+                  "\x00\x01\x02", 3);
+  relay_header_unpack(&rh, cell.payload);
+  r = connected_cell_parse(&rh, &cell, &addr, &ttl);
+  tt_int_op(r, ==, -1);
+
+  /* Bogus family: 7. */
+  make_relay_cell(&cell, RELAY_COMMAND_CONNECTED,
+                  "\x00\x00\x00\x00\x07"
+                  "\x26\x07\xf8\xb0\x40\x0c\x0c\x02"
+                  "\x00\x00\x00\x00\x00\x00\x00\x68"
+                  "\x90\x00\x02\x58", 25);
+  relay_header_unpack(&rh, cell.payload);
+  r = connected_cell_parse(&rh, &cell, &addr, &ttl);
+  tt_int_op(r, ==, -1);
+
+  /* Truncated IPv6. */
+  make_relay_cell(&cell, RELAY_COMMAND_CONNECTED,
+                  "\x00\x00\x00\x00\x06"
+                  "\x26\x07\xf8\xb0\x40\x0c\x0c\x02"
+                  "\x00\x00\x00\x00\x00\x00\x00\x68"
+                  "\x00\x00\x02", 24);
+  relay_header_unpack(&rh, cell.payload);
+  r = connected_cell_parse(&rh, &cell, &addr, &ttl);
+  tt_int_op(r, ==, -1);
+
+ done:
+  ;
+}
+
 #define TEST(name, flags)                                               \
   { #name, test_cfmt_ ## name, flags, 0, NULL }
 
 struct testcase_t cell_format_tests[] = {
   TEST(relay_header, 0),
   TEST(begin_cells, 0),
+  TEST(connected_cells, 0),
   END_OF_TESTCASES
 };
 



_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits