gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[GNUnet-SVN] r33127 - in gnunet/src: core include


From: gnunet
Subject: [GNUnet-SVN] r33127 - in gnunet/src: core include
Date: Wed, 23 Apr 2014 13:04:54 +0200

Author: grothoff
Date: 2014-04-23 13:04:53 +0200 (Wed, 23 Apr 2014)
New Revision: 33127

Modified:
   gnunet/src/core/gnunet-service-core_kx.c
   gnunet/src/core/gnunet-service-core_sessions.c
   gnunet/src/core/gnunet-service-core_sessions.h
   gnunet/src/core/gnunet-service-core_typemap.c
   gnunet/src/core/gnunet-service-core_typemap.h
   gnunet/src/include/gnunet_protocols.h
Log:
fix #3348: send typemap confirmation messages, perform faster typemap 
retransmissions for unconfirmed typemaps, restart retransmissions on reconnect

Modified: gnunet/src/core/gnunet-service-core_kx.c
===================================================================
--- gnunet/src/core/gnunet-service-core_kx.c    2014-04-23 10:47:47 UTC (rev 
33126)
+++ gnunet/src/core/gnunet-service-core_kx.c    2014-04-23 11:04:53 UTC (rev 
33127)
@@ -821,13 +821,15 @@
         (GNUNET_CORE_KX_STATE_REKEY_SENT == kx->status) ) &&
        (end_t.abs_value_us <= kx->foreign_key_expires.abs_value_us) )
   {
-    GNUNET_STATISTICS_update (GSC_stats, gettext_noop ("# old ephemeral keys 
ignored"),
+    GNUNET_STATISTICS_update (GSC_stats,
+                              gettext_noop ("# old ephemeral keys ignored"),
                              1, GNUNET_NO);
     return;
   }
   start_t = GNUNET_TIME_absolute_ntoh (m->creation_time);
 
-  GNUNET_STATISTICS_update (GSC_stats, gettext_noop ("# ephemeral keys 
received"),
+  GNUNET_STATISTICS_update (GSC_stats,
+                            gettext_noop ("# ephemeral keys received"),
                             1, GNUNET_NO);
 
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
@@ -884,11 +886,17 @@
     break;
   case GNUNET_CORE_KX_STATE_KEY_SENT:
     /* fine, need to send our key after updating our status, see below */
+    GSC_SESSIONS_reinit (&kx->peer);
     break;
   case GNUNET_CORE_KX_STATE_KEY_RECEIVED:
+    /* other peer already got our key, but typemap did go down */
+    GSC_SESSIONS_reinit (&kx->peer);
+    break;
   case GNUNET_CORE_KX_STATE_UP:
+    /* other peer already got our key, typemap NOT down */
+    break;
   case GNUNET_CORE_KX_STATE_REKEY_SENT:
-    /* other peer already got our key */
+    /* other peer already got our key, typemap NOT down */
     break;
   default:
     GNUNET_break (0);
@@ -1509,6 +1517,9 @@
   case GNUNET_MESSAGE_TYPE_CORE_COMPRESSED_TYPE_MAP:
     GSC_SESSIONS_set_typemap (dmc->peer, m);
     return GNUNET_OK;
+  case GNUNET_MESSAGE_TYPE_CORE_CONFIRM_TYPE_MAP:
+    GSC_SESSIONS_confirm_typemap (dmc->peer, m);
+    return GNUNET_OK;
   default:
     GSC_CLIENTS_deliver_message (dmc->peer, m,
                                  ntohs (m->size),

Modified: gnunet/src/core/gnunet-service-core_sessions.c
===================================================================
--- gnunet/src/core/gnunet-service-core_sessions.c      2014-04-23 10:47:47 UTC 
(rev 33126)
+++ gnunet/src/core/gnunet-service-core_sessions.c      2014-04-23 11:04:53 UTC 
(rev 33127)
@@ -137,6 +137,12 @@
   GNUNET_SCHEDULER_TaskIdentifier typemap_task;
 
   /**
+   * Retransmission delay we currently use for the typemap
+   * transmissions (if not confirmed).
+   */
+  struct GNUNET_TIME_Relative typemap_delay;
+
+  /**
    * Is the neighbour queue empty and thus ready for us
    * to transmit an encrypted message?
    */
@@ -151,7 +157,35 @@
 };
 
 
+GNUNET_NETWORK_STRUCT_BEGIN
+
 /**
+ * Message sent to confirm that a typemap was received.
+ */
+struct TypeMapConfirmationMessage
+{
+
+  /**
+   * Header with type #GNUNET_MESSAGE_TYPE_CORE_CONFIRM_TYPE_MAP.
+   */
+  struct GNUNET_MessageHeader header;
+
+  /**
+   * Reserved, always zero.
+   */
+  uint32_t reserved GNUNET_PACKED;
+
+  /**
+   * Hash of the (decompressed) type map that was received.
+   */
+  struct GNUNET_HashCode tm_hash;
+
+};
+
+GNUNET_NETWORK_STRUCT_END
+
+
+/**
  * Map of peer identities to `struct Session`.
  */
 static struct GNUNET_CONTAINER_MultiPeerMap *sessions;
@@ -203,10 +237,16 @@
   }
   while (NULL != (sme = session->sme_head))
   {
-    GNUNET_CONTAINER_DLL_remove (session->sme_head, session->sme_tail, sme);
+    GNUNET_CONTAINER_DLL_remove (session->sme_head,
+                                 session->sme_tail,
+                                 sme);
     GNUNET_free (sme);
   }
-  GNUNET_SCHEDULER_cancel (session->typemap_task);
+  if (GNUNET_SCHEDULER_NO_TASK != session->typemap_task)
+  {
+    GNUNET_SCHEDULER_cancel (session->typemap_task);
+    session->typemap_task = GNUNET_SCHEDULER_NO_TASK;
+  }
   GSC_CLIENTS_notify_clients_about_neighbour (&session->peer,
                                               session->tmap, NULL);
   GNUNET_assert (GNUNET_YES ==
@@ -224,7 +264,7 @@
 
 /**
  * Transmit our current typemap message to the other peer.
- * (Done periodically in case an update got lost).
+ * (Done periodically until the typemap is confirmed).
  *
  * @param cls the `struct Session *`
  * @param tc unused
@@ -237,20 +277,14 @@
   struct GNUNET_MessageHeader *hdr;
   struct GNUNET_TIME_Relative delay;
 
-  if (0 == session->first_typemap)
-  {
-    delay = TYPEMAP_FREQUENCY_FIRST;
-    session->first_typemap = 1;
-  }
-  else
-  {
-    delay = TYPEMAP_FREQUENCY;
-  }
+  session->typemap_delay = GNUNET_TIME_STD_BACKOFF (session->typemap_delay);
+  delay = session->typemap_delay;
   /* randomize a bit to avoid spont. sync */
   delay.rel_value_us +=
       GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 1000 * 1000);
   session->typemap_task =
-      GNUNET_SCHEDULER_add_delayed (delay, &transmit_typemap_task, session);
+      GNUNET_SCHEDULER_add_delayed (delay,
+                                    &transmit_typemap_task, session);
   GNUNET_STATISTICS_update (GSC_stats,
                             gettext_noop ("# type map refreshes sent"), 1,
                             GNUNET_NO);
@@ -261,6 +295,24 @@
 
 
 /**
+ * Restart the typemap task for the given session.
+ *
+ * @param session session to restart typemap transmission for
+ */
+static void
+start_typemap_task (struct Session *session)
+{
+  if (GNUNET_SCHEDULER_NO_TASK != session->typemap_task)
+    GNUNET_SCHEDULER_cancel (session->typemap_task);
+  session->typemap_delay = GNUNET_TIME_UNIT_SECONDS;
+  session->typemap_task =
+    GNUNET_SCHEDULER_add_delayed (session->typemap_delay,
+                                  &transmit_typemap_task,
+                                  session);
+}
+
+
+/**
  * Create a session, a key exchange was just completed.
  *
  * @param peer peer that is now connected
@@ -279,10 +331,9 @@
   session->tmap = GSC_TYPEMAP_create ();
   session->peer = *peer;
   session->kxinfo = kx;
-  session->typemap_task =
-      GNUNET_SCHEDULER_add_now (&transmit_typemap_task, session);
   GNUNET_assert (GNUNET_OK ==
-                 GNUNET_CONTAINER_multipeermap_put (sessions, peer,
+                 GNUNET_CONTAINER_multipeermap_put (sessions,
+                                                    &session->peer,
                                                     session,
                                                     
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
   GNUNET_STATISTICS_set (GSC_stats, gettext_noop ("# peers connected"),
@@ -290,10 +341,83 @@
                          GNUNET_NO);
   GSC_CLIENTS_notify_clients_about_neighbour (peer,
                                               NULL, session->tmap);
+  start_typemap_task (session);
 }
 
 
 /**
+ * The other peer has indicated that he 'lost' the session
+ * (KX down), reinitialize the session on our end, in particular
+ * this means to restart the typemap transmission.
+ *
+ * @param peer peer that is now connected
+ */
+void
+GSC_SESSIONS_reinit (const struct GNUNET_PeerIdentity *peer)
+{
+  struct Session *session;
+
+  session = find_session (peer);
+  if (NULL == session)
+  {
+    /* KX/session is new for both sides; thus no need to restart what
+       has not yet begun */
+    return;
+  }
+  start_typemap_task (session);
+}
+
+
+/**
+ * The other peer has confirmed receiving our type map,
+ * check if it is current and if so, stop retransmitting it.
+ *
+ * @param peer peer that confirmed the type map
+ * @param msg confirmation message we received
+ */
+void
+GSC_SESSIONS_confirm_typemap (const struct GNUNET_PeerIdentity *peer,
+                              const struct GNUNET_MessageHeader *msg)
+{
+  const struct TypeMapConfirmationMessage *cmsg;
+  struct Session *session;
+
+  session = find_session (peer);
+  if (NULL == session)
+  {
+    GNUNET_break (0);
+    return;
+  }
+  if (ntohs (msg->size) != sizeof (struct TypeMapConfirmationMessage))
+  {
+    GNUNET_break_op (0);
+    return;
+  }
+  cmsg = (const struct TypeMapConfirmationMessage *) msg;
+  if (GNUNET_YES !=
+      GSC_TYPEMAP_check_hash (&cmsg->tm_hash))
+  {
+    /* our typemap has changed in the meantime, do not
+       accept confirmation */
+    GNUNET_STATISTICS_update (GSC_stats,
+                              gettext_noop
+                              ("# outdated typemap confirmations received"),
+                              1, GNUNET_NO);
+    return;
+  }
+  if (GNUNET_SCHEDULER_NO_TASK != session->typemap_task)
+  {
+    GNUNET_SCHEDULER_cancel (session->typemap_task);
+    session->typemap_task = GNUNET_SCHEDULER_NO_TASK;
+  }
+  GNUNET_STATISTICS_update (GSC_stats,
+                            gettext_noop
+                            ("# valid typemap confirmations received"),
+                            1, GNUNET_NO);
+}
+
+
+/**
  * Notify the given client about the session (client is new).
  *
  * @param cls the `struct GSC_Client`
@@ -325,7 +449,8 @@
 GSC_SESSIONS_notify_client_about_sessions (struct GSC_Client *client)
 {
   /* notify new client about existing sessions */
-  GNUNET_CONTAINER_multipeermap_iterate (sessions, 
&notify_client_about_session,
+  GNUNET_CONTAINER_multipeermap_iterate (sessions,
+                                         &notify_client_about_session,
                                          client);
 }
 
@@ -592,11 +717,14 @@
     size_t used;
 
     used = 0;
-    while ((NULL != (pos = session->sme_head)) && (used + pos->size <= msize))
+    while ( (NULL != (pos = session->sme_head)) &&
+            (used + pos->size <= msize) )
     {
       memcpy (&pbuf[used], &pos[1], pos->size);
       used += pos->size;
-      GNUNET_CONTAINER_DLL_remove (session->sme_head, session->sme_tail, pos);
+      GNUNET_CONTAINER_DLL_remove (session->sme_head,
+                                   session->sme_tail,
+                                   pos);
       GNUNET_free (pos);
     }
     /* compute average payload size */
@@ -619,7 +747,8 @@
 
 
 /**
- * Send a message to the neighbour now.
+ * Send an updated typemap message to the neighbour now,
+ * and restart typemap transmissions.
  *
  * @param cls the message
  * @param key neighbour's identity
@@ -627,38 +756,42 @@
  * @return always #GNUNET_OK
  */
 static int
-do_send_message (void *cls,
-                 const struct GNUNET_PeerIdentity *key,
-                 void *value)
+do_restart_typemap_message (void *cls,
+                            const struct GNUNET_PeerIdentity *key,
+                            void *value)
 {
   const struct GNUNET_MessageHeader *hdr = cls;
   struct Session *session = value;
-  struct SessionMessageEntry *m;
+  struct SessionMessageEntry *sme;
   uint16_t size;
 
   size = ntohs (hdr->size);
-  m = GNUNET_malloc (sizeof (struct SessionMessageEntry) + size);
-  memcpy (&m[1], hdr, size);
-  m->size = size;
-  m->priority = GNUNET_CORE_PRIO_CRITICAL_CONTROL;
-  GNUNET_CONTAINER_DLL_insert_tail (session->sme_head, session->sme_tail, m);
+  sme = GNUNET_malloc (sizeof (struct SessionMessageEntry) + size);
+  memcpy (&sme[1], hdr, size);
+  sme->size = size;
+  sme->priority = GNUNET_CORE_PRIO_CRITICAL_CONTROL;
+  GNUNET_CONTAINER_DLL_insert (session->sme_head,
+                               session->sme_tail,
+                               sme);
   try_transmission (session);
+  start_typemap_task (session);
   return GNUNET_OK;
 }
 
 
 /**
- * Broadcast a message to all neighbours.
+ * Broadcast an updated typemap message to all neighbours.
+ * Restarts the retransmissions until the typemaps are confirmed.
  *
  * @param msg message to transmit
  */
 void
-GSC_SESSIONS_broadcast (const struct GNUNET_MessageHeader *msg)
+GSC_SESSIONS_broadcast_typemap (const struct GNUNET_MessageHeader *msg)
 {
   if (NULL == sessions)
     return;
   GNUNET_CONTAINER_multipeermap_iterate (sessions,
-                                         &do_send_message,
+                                         &do_restart_typemap_message,
                                          (void *) msg);
 }
 
@@ -732,7 +865,7 @@
 
 
 /**
- * We've received a typemap message from a peer, update ours.
+ * We have received a typemap message from a peer, update ours.
  * Notifies clients about the session.
  *
  * @param peer peer this is about
@@ -744,6 +877,8 @@
 {
   struct Session *session;
   struct GSC_TypeMap *nmap;
+  struct SessionMessageEntry *sme;
+  struct TypeMapConfirmationMessage *tmc;
 
   nmap = GSC_TYPEMAP_get_from_message (msg);
   if (NULL == nmap)
@@ -754,8 +889,24 @@
     GNUNET_break (0);
     return;
   }
+  sme = GNUNET_malloc (sizeof (struct SessionMessageEntry) +
+                       sizeof (struct TypeMapConfirmationMessage));
+  sme->deadline = GNUNET_TIME_absolute_get ();
+  sme->size = sizeof (struct TypeMapConfirmationMessage);
+  sme->priority = GNUNET_CORE_PRIO_CRITICAL_CONTROL;
+  tmc = (struct TypeMapConfirmationMessage *) &sme[1];
+  tmc->header.size = htons (sizeof (struct TypeMapConfirmationMessage));
+  tmc->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_CONFIRM_TYPE_MAP);
+  tmc->reserved = htonl (0);
+  GSC_TYPEMAP_hash (nmap,
+                    &tmc->tm_hash);
+  GNUNET_CONTAINER_DLL_insert (session->sme_head,
+                               session->sme_tail,
+                               sme);
+  try_transmission (session);
   GSC_CLIENTS_notify_clients_about_neighbour (peer,
-                                              session->tmap, nmap);
+                                              session->tmap,
+                                              nmap);
   GSC_TYPEMAP_destroy (session->tmap);
   session->tmap = nmap;
 }
@@ -776,7 +927,9 @@
   struct Session *session;
   struct GSC_TypeMap *nmap;
 
-  if (0 == memcmp (peer, &GSC_my_identity, sizeof (struct 
GNUNET_PeerIdentity)))
+  if (0 == memcmp (peer,
+                   &GSC_my_identity,
+                   sizeof (struct GNUNET_PeerIdentity)))
     return;
   session = find_session (peer);
   GNUNET_assert (NULL != session);
@@ -796,12 +949,14 @@
 void
 GSC_SESSIONS_init ()
 {
-  sessions = GNUNET_CONTAINER_multipeermap_create (128, GNUNET_NO);
+  sessions = GNUNET_CONTAINER_multipeermap_create (128,
+                                                   GNUNET_YES);
 }
 
 
 /**
- * Helper function for GSC_SESSIONS_handle_client_iterate_peers.
+ * Helper function for #GSC_SESSIONS_done() to free all
+ * active sessions.
  *
  * @param cls NULL
  * @param key identity of the connected peer
@@ -828,7 +983,8 @@
 {
   if (NULL != sessions)
   {
-    GNUNET_CONTAINER_multipeermap_iterate (sessions, &free_session_helper, 
NULL);
+    GNUNET_CONTAINER_multipeermap_iterate (sessions,
+                                           &free_session_helper, NULL);
     GNUNET_CONTAINER_multipeermap_destroy (sessions);
     sessions = NULL;
   }

Modified: gnunet/src/core/gnunet-service-core_sessions.h
===================================================================
--- gnunet/src/core/gnunet-service-core_sessions.h      2014-04-23 10:47:47 UTC 
(rev 33126)
+++ gnunet/src/core/gnunet-service-core_sessions.h      2014-04-23 11:04:53 UTC 
(rev 33127)
@@ -42,6 +42,29 @@
 
 
 /**
+ * The other peer has indicated that he 'lost' the session
+ * (KX down), reinitialize the session on our end, in particular
+ * this means to restart the typemap transmission.
+ *
+ * @param peer peer that is now connected
+ */
+void
+GSC_SESSIONS_reinit (const struct GNUNET_PeerIdentity *peer);
+
+
+/**
+ * The other peer has confirmed receiving our type map,
+ * check if it is current and if so, stop retransmitting it.
+ *
+ * @param peer peer that confirmed the type map
+ * @param msg confirmation message we received
+ */
+void
+GSC_SESSIONS_confirm_typemap (const struct GNUNET_PeerIdentity *peer,
+                              const struct GNUNET_MessageHeader *msg);
+
+
+/**
  * End the session with the given peer (we are no longer
  * connected).
  *
@@ -102,12 +125,13 @@
 
 
 /**
- * Broadcast a message to all neighbours.
+ * Broadcast an updated typemap message to all neighbours.
+ * Restarts the retransmissions until the typemaps are confirmed.
  *
  * @param msg message to transmit
  */
 void
-GSC_SESSIONS_broadcast (const struct GNUNET_MessageHeader *msg);
+GSC_SESSIONS_broadcast_typemap (const struct GNUNET_MessageHeader *msg);
 
 
 /**

Modified: gnunet/src/core/gnunet-service-core_typemap.c
===================================================================
--- gnunet/src/core/gnunet-service-core_typemap.c       2014-04-23 10:47:47 UTC 
(rev 33126)
+++ gnunet/src/core/gnunet-service-core_typemap.c       2014-04-23 11:04:53 UTC 
(rev 33127)
@@ -1,6 +1,6 @@
 /*
      This file is part of GNUnet.
-     (C) 2011 Christian Grothoff (and other contributing authors)
+     (C) 2011-2014 Christian Grothoff (and other contributing authors)
 
      GNUnet is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published
@@ -51,8 +51,65 @@
  */
 static uint8_t map_counters[UINT16_MAX + 1];
 
+/**
+ * Current hash of our (uncompressed) type map.
+ * Lazily computed when needed.
+ */
+static struct GNUNET_HashCode my_tm_hash;
 
 /**
+ * Is #my_tm_hash() current with respect to our type map?
+ */
+static int hash_current;
+
+
+/**
+ * Our type map changed, recompute its hash.
+ */
+static void
+rehash_typemap ()
+{
+  hash_current = GNUNET_NO;
+}
+
+
+/**
+ * Hash the contents of a type map.
+ *
+ * @param tm map to hash
+ * @param hc where to store the hash code
+ */
+void
+GSC_TYPEMAP_hash (const struct GSC_TypeMap *tm,
+                  struct GNUNET_HashCode *hc)
+{
+  GNUNET_CRYPTO_hash (tm,
+                      sizeof (struct GSC_TypeMap),
+                      hc);
+}
+
+
+/**
+ * Check if the given hash matches our current type map.
+ *
+ * @param hc hash code to check if it matches our type map
+ * @return #GNUNET_YES if the hash matches, #GNUNET_NO if not
+ */
+int
+GSC_TYPEMAP_check_hash (const struct GNUNET_HashCode *hc)
+{
+  if (GNUNET_NO == hash_current)
+  {
+    GSC_TYPEMAP_hash (&my_type_map,
+                      &my_tm_hash);
+    hash_current = GNUNET_YES;
+  }
+  return (0 == memcmp (hc, &my_tm_hash, sizeof (struct GNUNET_HashCode)))
+    ? GNUNET_YES : GNUNET_NO;
+}
+
+
+/**
  * Compute a type map message for this peer.
  *
  * @return this peers current type map message.
@@ -152,7 +209,7 @@
   GNUNET_STATISTICS_update (GSC_stats,
                             gettext_noop ("# updates to my type map"), 1,
                             GNUNET_NO);
-  GSC_SESSIONS_broadcast (hdr);
+  GSC_SESSIONS_broadcast_typemap (hdr);
   GNUNET_free (hdr);
 }
 
@@ -180,7 +237,10 @@
     }
   }
   if (GNUNET_YES == changed)
+  {
+    rehash_typemap ();
     broadcast_my_type_map ();
+  }
 }
 
 
@@ -207,7 +267,10 @@
     }
   }
   if (GNUNET_YES == changed)
+  {
+    rehash_typemap ();
     broadcast_my_type_map ();
+  }
 }
 
 

Modified: gnunet/src/core/gnunet-service-core_typemap.h
===================================================================
--- gnunet/src/core/gnunet-service-core_typemap.h       2014-04-23 10:47:47 UTC 
(rev 33126)
+++ gnunet/src/core/gnunet-service-core_typemap.h       2014-04-23 11:04:53 UTC 
(rev 33127)
@@ -67,6 +67,27 @@
 
 
 /**
+ * Check if the given hash matches our current type map.
+ *
+ * @param hc hash code to check if it matches our type map
+ * @return #GNUNET_YES if the hash matches, #GNUNET_NO if not
+ */
+int
+GSC_TYPEMAP_check_hash (const struct GNUNET_HashCode *hc);
+
+
+/**
+ * Hash the contents of a type map.
+ *
+ * @param tm map to hash
+ * @param hc where to store the hash code
+ */
+void
+GSC_TYPEMAP_hash (const struct GSC_TypeMap *tm,
+                  struct GNUNET_HashCode *hc);
+
+
+/**
  * Extract a type map from a
  * #GNUNET_MESSAGE_TYPE_CORE_COMRESSED_TYPE_MAP or
  * #GNUNET_MESSAGE_TYPE_CORE_BINARY_TYPE_MAP message.

Modified: gnunet/src/include/gnunet_protocols.h
===================================================================
--- gnunet/src/include/gnunet_protocols.h       2014-04-23 10:47:47 UTC (rev 
33126)
+++ gnunet/src/include/gnunet_protocols.h       2014-04-23 11:04:53 UTC (rev 
33127)
@@ -378,7 +378,12 @@
  */
 #define GNUNET_MESSAGE_TYPE_CORE_EPHEMERAL_KEY 88
 
+/**
+ * Other peer confirms having received the type map
+ */
+#define GNUNET_MESSAGE_TYPE_CORE_CONFIRM_TYPE_MAP 89
 
+
 
/*******************************************************************************
  * DATASTORE message types
  
******************************************************************************/
@@ -636,7 +641,7 @@
 #define GNUNET_MESSAGE_TYPE_DHT_P2P_GET_RESULT                 162
 
 /**
- * Trail Rejection Message. 
+ * Trail Rejection Message.
  */
 #define GNUNET_MESSAGE_TYPE_DHT_P2P_TRAIL_REJECTION            163
 
/*******************************************************************************




reply via email to

[Prev in Thread] Current Thread [Next in Thread]