11/*******************************************************************************
2- * Copyright (c) 2019-2023 Oak Ridge National Laboratory.
2+ * Copyright (c) 2019-2025 Oak Ridge National Laboratory.
33 * All rights reserved. This program and the accompanying materials
44 * are made available under the terms of the Eclipse Public License v1.0
55 * which accompanies this distribution, and is available at
@@ -63,12 +63,29 @@ public String toString()
6363 }
6464 };
6565
66+ /** Server should reply with its GUID and empty CID list
67+ * even if it does not host any of the searched channels
68+ */
69+ public static final byte FLAG_SEARCH_MUST_REPLY = 0x01 ;
70+
71+ /** Client should ignore the 'port' in the reply and
72+ * simply use the port of the 'source', that is the peer port
73+ * of the UDP message or TCP connection
74+ * @since Version 3
75+ */
76+ public static final byte FLAG_REPLY_SRC_PORT = 0x02 ;
77+
78+ /** Indicates that search message was unicast */
79+ public static final byte FLAG_SEARCH_UNICAST = (byte )0x80 ;
80+
6681 /** Sequence number */
6782 public int seq ;
6883 /** Is it a unicast? */
6984 public boolean unicast ;
7085 /** Is reply required? */
7186 public boolean reply_required ;
87+ /** Reply to source port instead of port listed in the search request? */
88+ public boolean reply_to_src_port ;
7289 /** Address of client */
7390 public InetSocketAddress client ;
7491 /** Use TLS, or plain TCP? */
@@ -106,10 +123,10 @@ public static SearchRequest decode(final InetSocketAddress from, final byte vers
106123 // Search Sequence ID
107124 search .seq = buffer .getInt ();
108125
109- // 0-bit for replyRequired, 7-th bit for "sent as unicast" (1)/"sent as broadcast/multicast" (0)
110126 final byte flags = buffer .get ();
111- search .unicast = (flags & 0x80 ) == 0x80 ;
112- search .reply_required = (flags & 0x01 ) == 0x01 ;
127+ search .unicast = (flags & FLAG_SEARCH_UNICAST ) == FLAG_SEARCH_UNICAST ;
128+ search .reply_required = (flags & FLAG_SEARCH_MUST_REPLY ) == FLAG_SEARCH_MUST_REPLY ;
129+ search .reply_to_src_port = (flags & FLAG_REPLY_SRC_PORT ) == FLAG_REPLY_SRC_PORT ;
113130
114131 // reserved
115132 buffer .get ();
@@ -127,7 +144,13 @@ public static SearchRequest decode(final InetSocketAddress from, final byte vers
127144 logger .log (Level .WARNING , "PVA Client " + from + " sent search #" + search .seq + " with invalid address" );
128145 return null ;
129146 }
130- final int port = Short .toUnsignedInt (buffer .getShort ());
147+ int port = Short .toUnsignedInt (buffer .getShort ());
148+ // Since version 3, flag can ask us to ignore the reply port in the message
149+ // and instead use the peer's port.
150+ // This should help with NAT where we get the message from an intermediate
151+ // and need to reply via that same intermediate
152+ if (version >= 3 && search .reply_to_src_port )
153+ port = from .getPort ();
131154
132155 // Use address from message unless it's a generic local address
133156 if (addr .isAnyLocalAddress () || port <= 0 )
@@ -201,8 +224,9 @@ public static void encode(final boolean unicast, final int seq, final Collection
201224 // only the one started last will see the unicast.
202225 // Mark search message as unicast so that receiver will forward
203226 // it via local broadcast to other local listeners.
204- // 0-bit for replyRequired, 7-th bit for "sent as unicast" (1)/"sent as broadcast/multicast" (0)
205- buffer .put ((byte ) ((unicast ? 0x80 : 0x00 ) | (channels == null ? 0x01 : 0x00 )));
227+ buffer .put ((byte ) ((unicast ? FLAG_SEARCH_UNICAST : 0x00 ) |
228+ ((channels == null || channels .isEmpty ()) ? FLAG_SEARCH_MUST_REPLY : 0x00 ) |
229+ FLAG_REPLY_SRC_PORT ));
206230
207231 // reserved
208232 buffer .put ((byte ) 0 );
0 commit comments