Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 89 additions & 0 deletions core/pva/src/main/java/org/epics/pva/PVASettings.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
******************************************************************************/
package org.epics.pva;

import java.io.File;
import java.util.logging.Level;
import java.util.logging.Logger;

Expand Down Expand Up @@ -259,6 +260,16 @@



/** Timeout [seconds] for waiting for certificate status PV to confirm VALID
*
* <p>After a TLS handshake, if the certificate includes a status PV extension
* (OID 1.3.6.1.4.1.37427.1), the connection will wait for the status PV
* to report VALID before allowing data operations.
* If not confirmed within this timeout, the connection enters degraded mode:
* data operations are released with a warning.
*/
public static int EPICS_PVA_CERT_STATUS_TMO = 30;

Check warning on line 271 in core/pva/src/main/java/org/epics/pva/PVASettings.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Make EPICS_PVA_CERT_STATUS_TMO a static final constant or non-public and provide accessors if needed.

See more on https://sonarcloud.io/project/issues?id=ControlSystemStudio_phoebus&issues=AZ2RnMjsyQ1ZEneHtSBt&open=AZ2RnMjsyQ1ZEneHtSBt&pullRequest=3786

Check warning on line 271 in core/pva/src/main/java/org/epics/pva/PVASettings.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Rename this field "EPICS_PVA_CERT_STATUS_TMO" to match the regular expression '^[a-z][a-zA-Z0-9]*$'.

See more on https://sonarcloud.io/project/issues?id=ControlSystemStudio_phoebus&issues=AZ2RnMjsyQ1ZEneHtSBx&open=AZ2RnMjsyQ1ZEneHtSBx&pullRequest=3786

Check warning on line 271 in core/pva/src/main/java/org/epics/pva/PVASettings.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Make this "public static EPICS_PVA_CERT_STATUS_TMO" field final

See more on https://sonarcloud.io/project/issues?id=ControlSystemStudio_phoebus&issues=AZ2RnMjsyQ1ZEneHtSBw&open=AZ2RnMjsyQ1ZEneHtSBw&pullRequest=3786

/** Whether to allow PVA to use IPv6
*
* <p> If this is false then PVA will not attempt to
Expand All @@ -281,6 +292,15 @@
EPICS_PVA_TCP_SOCKET_TMO = get("EPICS_PVA_TCP_SOCKET_TMO", EPICS_PVA_TCP_SOCKET_TMO);
EPICS_PVA_MAX_ARRAY_FORMATTING = get("EPICS_PVA_MAX_ARRAY_FORMATTING", EPICS_PVA_MAX_ARRAY_FORMATTING);
EPICS_PVAS_TLS_KEYCHAIN = get("EPICS_PVAS_TLS_KEYCHAIN", EPICS_PVAS_TLS_KEYCHAIN);
if (EPICS_PVAS_TLS_KEYCHAIN.isEmpty())
{
final String xdg_server = getXdgPvaKeychainPath("server.p12");
if (!xdg_server.isEmpty())
{
EPICS_PVAS_TLS_KEYCHAIN = xdg_server;
logger.log(Level.CONFIG, "EPICS_PVAS_TLS_KEYCHAIN auto-discovered at " + xdg_server);

Check warning on line 301 in core/pva/src/main/java/org/epics/pva/PVASettings.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use the built-in formatting to construct this argument.

See more on https://sonarcloud.io/project/issues?id=ControlSystemStudio_phoebus&issues=AZ2RnMjsyQ1ZEneHtSBu&open=AZ2RnMjsyQ1ZEneHtSBu&pullRequest=3786

Check warning on line 301 in core/pva/src/main/java/org/epics/pva/PVASettings.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Format specifiers or lambda should be used instead of string concatenation.

See more on https://sonarcloud.io/project/issues?id=ControlSystemStudio_phoebus&issues=AZ2RnMjsyQ1ZEneHtSBy&open=AZ2RnMjsyQ1ZEneHtSBy&pullRequest=3786
}
}
EPICS_PVAS_TLS_OPTIONS = get("EPICS_PVAS_TLS_OPTIONS", EPICS_PVAS_TLS_OPTIONS);
require_client_cert = EPICS_PVAS_TLS_OPTIONS.contains("client_cert=require");
EPICS_PVA_TLS_KEYCHAIN = get("EPICS_PVA_TLS_KEYCHAIN", EPICS_PVA_TLS_KEYCHAIN);
Expand All @@ -289,10 +309,20 @@
EPICS_PVA_TLS_KEYCHAIN = EPICS_PVAS_TLS_KEYCHAIN;
logger.log(Level.CONFIG, "EPICS_PVA_TLS_KEYCHAIN (empty) updated from EPICS_PVAS_TLS_KEYCHAIN");
}
if (EPICS_PVA_TLS_KEYCHAIN.isEmpty())
{
final String xdg_client = getXdgPvaKeychainPath("client.p12");
if (!xdg_client.isEmpty())
{
EPICS_PVA_TLS_KEYCHAIN = xdg_client;
logger.log(Level.CONFIG, "EPICS_PVA_TLS_KEYCHAIN auto-discovered at " + xdg_client);

Check warning on line 318 in core/pva/src/main/java/org/epics/pva/PVASettings.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Format specifiers or lambda should be used instead of string concatenation.

See more on https://sonarcloud.io/project/issues?id=ControlSystemStudio_phoebus&issues=AZ2RnMjsyQ1ZEneHtSBz&open=AZ2RnMjsyQ1ZEneHtSBz&pullRequest=3786

Check warning on line 318 in core/pva/src/main/java/org/epics/pva/PVASettings.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use the built-in formatting to construct this argument.

See more on https://sonarcloud.io/project/issues?id=ControlSystemStudio_phoebus&issues=AZ2RnMjsyQ1ZEneHtSBv&open=AZ2RnMjsyQ1ZEneHtSBv&pullRequest=3786
}
}
EPICS_PVA_SEND_BUFFER_SIZE = get("EPICS_PVA_SEND_BUFFER_SIZE", EPICS_PVA_SEND_BUFFER_SIZE);
EPICS_PVA_FAST_BEACON_MIN = get("EPICS_PVA_FAST_BEACON_MIN", EPICS_PVA_FAST_BEACON_MIN);
EPICS_PVA_FAST_BEACON_MAX = get("EPICS_PVA_FAST_BEACON_MAX", EPICS_PVA_FAST_BEACON_MAX);
EPICS_PVA_MAX_BEACON_AGE = get("EPICS_PVA_MAX_BEACON_AGE", EPICS_PVA_MAX_BEACON_AGE);
EPICS_PVA_CERT_STATUS_TMO = get("EPICS_PVA_CERT_STATUS_TMO", EPICS_PVA_CERT_STATUS_TMO);
EPICS_PVA_ENABLE_IPV6 = get("EPICS_PVA_ENABLE_IPV6", EPICS_PVA_ENABLE_IPV6);
}

Expand Down Expand Up @@ -339,4 +369,63 @@
{
return Integer.parseInt(get(name, Integer.toString(default_value)));
}

/** PVA protocol version for XDG path construction.
*
* <p>Matches PVXS versionString() so that the Java client
* and PVXS share the same well-known keychain locations.
*/
private static final String PVA_VERSION = "1.5";

/** Get XDG config home directory.
*
* <p>Uses {@code XDG_CONFIG_HOME} environment variable if set.
* Falls back to {@code $HOME/.config} on Unix or
* {@code %USERPROFILE%} on Windows,
* matching the PVXS {@code getXdgConfigHome()} behavior.
*
* @return XDG config home path, or empty string if home cannot be determined
*/
private static String getXdgConfigHome()
{
final String xdg = System.getenv("XDG_CONFIG_HOME");
if (xdg != null && !xdg.isEmpty())
return xdg;

final String home = System.getProperty("user.home");
if (home == null || home.isEmpty())
return "";

if (System.getProperty("os.name", "").toLowerCase().startsWith("win"))
return home;

return home + File.separator + ".config";
}

/** Try to find a PVA keychain at the XDG well-known location.
*
* <p>Constructs the path
* {@code <xdg_config_home>/pva/<PVA_VERSION>/<filename>}
* and returns it if the file exists, otherwise returns empty string.
* This mirrors the PVXS fallback in {@code config.cpp} when
* {@code EPICS_PVA_TLS_KEYCHAIN} / {@code EPICS_PVAS_TLS_KEYCHAIN}
* are not configured.
*
* @param filename Keychain filename, e.g. "client.p12" or "server.p12"
* @return Absolute path to the keychain file, or empty string if not found
*/
private static String getXdgPvaKeychainPath(final String filename)
{
final String config_home = getXdgConfigHome();
if (config_home.isEmpty())
return "";

final String path = config_home + File.separator + "pva"
+ File.separator + PVA_VERSION
+ File.separator + filename;
final File file = new File(path);
if (file.isFile() && file.canRead())
return path;
return "";
}
}
15 changes: 11 additions & 4 deletions core/pva/src/main/java/org/epics/pva/client/ChannelSearch.java
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,9 @@ public boolean equals(Object obj)

private final ClientUDPHandler udp;

/** When true, TLS is excluded from search requests and ignored in responses */
private final boolean tls_disabled;

/** Create ClientTCPHandler from IP address and 'tls' flag */
private final BiFunction<InetSocketAddress, Boolean, ClientTCPHandler> tcp_provider;

Expand All @@ -206,15 +209,18 @@ public boolean equals(Object obj)
* @param udp_addresses UDP addresses to search
* @param tcp_provider Function that creates ClientTCPHandler for IP address and 'tls' flag
* @param name_server_addresses TCP addresses to search
* @param tls_disabled When true, exclude TLS from search protocol list
* @throws Exception on error
*/
public ChannelSearch(final ClientUDPHandler udp,
final List<AddressInfo> udp_addresses,
final BiFunction<InetSocketAddress, Boolean, ClientTCPHandler> tcp_provider,
final List<AddressInfo> name_server_addresses) throws Exception
final List<AddressInfo> name_server_addresses,
final boolean tls_disabled) throws Exception
{
this.udp = udp;
this.tcp_provider = tcp_provider;
this.tls_disabled = tls_disabled;

// Each bucket holds set of channels to search in that time slot
for (int i=0; i<MAX_SEARCH_PERIOD+2; ++i)
Expand Down Expand Up @@ -434,7 +440,7 @@ else if (count == 0)
/** Issue a PVA server list request */
public void list()
{
final boolean tls = !PVASettings.EPICS_PVA_TLS_KEYCHAIN.isBlank();
final boolean tls = !tls_disabled && !PVASettings.EPICS_PVA_TLS_KEYCHAIN.isBlank();

// Search is invoked for new SearchedChannel(channel, now)
// as well as by regular, timed search.
Expand All @@ -452,8 +458,9 @@ public void list()
private void search(final Collection<SearchRequest.Channel> channels)
{
// Do we support TLS? This will be encoded in the search requests
// to tell server if we can support TLS?
final boolean tls = !PVASettings.EPICS_PVA_TLS_KEYCHAIN.isBlank();
// to tell server if we can support TLS.
// When tls_disabled, never advertise TLS so servers respond with TCP only.
final boolean tls = !tls_disabled && !PVASettings.EPICS_PVA_TLS_KEYCHAIN.isBlank();

// Search via TCP
for (AddressInfo name_server : name_server_addresses)
Expand Down
Loading