jnr.unixsocket.SockAddrUnix Maven / Gradle / Ivy
/*
* Copyright (C) 2009 Wayne Meissner
*
* This file is part of the JNR project.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jnr.unixsocket;
import static java.nio.charset.StandardCharsets.UTF_8;
import jnr.constants.platform.ProtocolFamily;
import jnr.ffi.Platform;
import jnr.ffi.Platform.OS;
import jnr.ffi.Runtime;
import jnr.ffi.Struct;
/**
* Native unix domain socket address structure.
*/
abstract class SockAddrUnix extends Struct {
private static transient OS currentOS = Platform.getNativePlatform().getOS();
public final static int ADDR_LENGTH = 108;
public final static int HEADER_LENGTH = 2;
protected abstract UTF8String getPathField();
protected abstract NumberField getFamilyField();
// This is important to be cached here for supporting abstract namespace on Linux
// (which starts with a NUL byte. path is NOT NUL terminated in this case!)
private java.lang.String cachedPath;
SockAddrUnix() {
super(Runtime.getSystemRuntime());
}
/**
* Sets the protocol family of this unix socket address.
*
* @param family The protocol family, usually {@link ProtocolFamily#PF_UNIX}
*/
final void setFamily(ProtocolFamily family) {
getFamilyField().set(family.intValue());
}
/**
* Gets the protocol family of this unix socket address.
*
* @return The protocol family
*/
final ProtocolFamily getFamily() {
return ProtocolFamily.valueOf(getFamilyField().intValue());
}
/**
* Sets the file system path of this socket address
*
* @param path The unix socket address
*/
void setPath(java.lang.String path) {
cachedPath = path;
getPathField().set(cachedPath);
}
/**
* Updates the file system path of this socket address.
* In order to support abstract namespaces, this MUST be
* called after any native syscall that sets this
* path struct like getsockname(), getpeername(), accept().
*
* @param len the value of the addrlen var, set by the above syscalls.
*/
void updatePath(final int len) {
if (currentOS == OS.LINUX) {
// Linux always returns an accurate length in
// order to support abstract namespace, where
// path STARTS with a NUL byte.
cachedPath = len == HEADER_LENGTH ? "" : getPath(len - HEADER_LENGTH);
} else {
// All others might return a len > 0 (typically 14) AND the path is terminated
// by a NUL byte if it is shorter than sizeof(sun_path)
cachedPath = getPathField().get();
int slen = len - HEADER_LENGTH;
if (slen <= 0) {
cachedPath = "";
} else {
if (slen < getPathField().length() && slen < cachedPath.length()) {
cachedPath = cachedPath.substring(0, slen);
}
}
}
}
/**
* Gets the file system path of this socket address
*
* @return A String
*/
final java.lang.String getPath() {
if (null == cachedPath) {
cachedPath = getPathField().get();
}
return cachedPath;
}
/**
* Gets the path of this socket address, supporting abstract namespace on Linux.
*
* @param len The desired length of the string.
* If the first character of the path is NUL, then this value ist considered
* exact, otherwise it includes a trailing NUL charater and therefore the actual
* string length is len - 1.
*/
final java.lang.String getPath(int len) {
UTF8String str = getPathField();
byte [] ba = new byte[str.length()];
str.getMemory().get(str.offset(), ba, 0, len);
if (0 != ba[0]) {
len -= 1;
}
return new java.lang.String(java.util.Arrays.copyOf(ba, len), UTF_8);
}
/**
* Gets the maximum length of this address (including len/family header)
*
* @return The maximum size of the address in bytes
*/
int getMaximumLength() {
return HEADER_LENGTH + getPathField().length();
}
/**
* Gets the actual length of this address (including len/family header)
*
* @return The actual size of this address, in bytes
*/
int length() {
if (currentOS == OS.LINUX && null != cachedPath) {
return HEADER_LENGTH + cachedPath.length();
}
return HEADER_LENGTH + strlen(getPathField());
}
/**
* Gets len/family header length
*
* @return The size of header, in bytes
*/
int getHeaderLength() {
return HEADER_LENGTH;
}
/**
* Creates a new instance of SockAddrUnix
*
* @return An instance of SockAddrUnix
*/
static SockAddrUnix create() {
return Platform.getNativePlatform().isBSD() ? new BSDSockAddrUnix() : new DefaultSockAddrUnix();
}
private static final int strlen(UTF8String str) {
int end = str.getMemory().indexOf(str.offset(), (byte) 0);
return end >= 0 ? end : str.length();
}
/**
* An implementation of {@link SockAddrUnix} for BSD systems
*/
static final class BSDSockAddrUnix extends SockAddrUnix {
public final Unsigned8 sun_len = new Unsigned8();
public final Unsigned8 sun_family = new Unsigned8();
public final UTF8String sun_addr = new UTF8String(ADDR_LENGTH);
@Override
public void setPath(java.lang.String path) {
super.setPath(path);
sun_len.set(path.length());
}
protected UTF8String getPathField() {
return sun_addr;
}
protected NumberField getFamilyField() {
return sun_family;
}
}
/**
* An implementation of {@link SockAddrUnix} for Linux, Solaris, et, al
*/
static final class DefaultSockAddrUnix extends SockAddrUnix {
public final Unsigned16 sun_family = new Unsigned16();
public final UTF8String sun_addr = new UTF8String(ADDR_LENGTH);
protected UTF8String getPathField() {
return sun_addr;
}
protected NumberField getFamilyField() {
return sun_family;
}
}
}