/*
* Copyright (C) 2016 Fritz Elfert
*
* 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 java.io.
File;
import java.io.
IOException;
import java.net.
SocketOption;
import java.nio.file.
Files;
import java.util.
Map;
import java.util.
HashMap;
import jnr.constants.platform.
ProtocolFamily;
import jnr.constants.platform.
SocketLevel;
import jnr.ffi.
Platform;
import jnr.ffi.
Platform.
OS;
import jnr.ffi.byref.
IntByReference;
/**
* Helper class, providing common methods.
*/
final class
Common {
private static
OS currentOS =
Platform.
getNativePlatform().
getOS();
private
Common() {
}
static
UnixSocketAddress bind(int
fd,
UnixSocketAddress local) throws
IOException {
SockAddrUnix sa;
if (null ==
local) {
// Support autobind
sa =
SockAddrUnix.
create();
sa.
setFamily(
ProtocolFamily.
PF_UNIX);
if (
currentOS ==
OS.
LINUX) {
// On Linux, we simply set an empty path
sa.
setPath("");
} else {
// Emulate something similar (bind to some random unique address),
// but use regular namespace
File f =
Files.
createTempFile("jnr-unixsocket-tmp", ".sock").
toFile();
f.
deleteOnExit();
f.
delete();
sa.
setPath(
f.
getPath());
}
} else {
sa =
local.
getStruct();
}
if (
Native.
bind(
fd,
sa,
sa.
length()) < 0) {
throw new
IOException(
Native.
getLastErrorString());
}
return
getsockname(
fd);
}
static
UnixSocketAddress getsockname(int
sockfd) {
UnixSocketAddress local = new
UnixSocketAddress();
SockAddrUnix addr =
local.
getStruct();
IntByReference len = new
IntByReference(
addr.
getMaximumLength());
if (
Native.
libc().
getsockname(
sockfd,
addr,
len) < 0) {
throw new
Error(
Native.
getLastErrorString());
}
addr.
updatePath(
len.
getValue());
return
local;
}
static
UnixSocketAddress getpeername(int
sockfd) {
UnixSocketAddress remote = new
UnixSocketAddress();
SockAddrUnix addr =
remote.
getStruct();
IntByReference len = new
IntByReference(
addr.
getMaximumLength());
if (
Native.
libc().
getpeername(
sockfd,
addr,
len) < 0) {
throw new
Error(
Native.
getLastErrorString());
}
addr.
updatePath(
len.
getValue());
return
remote;
}
static <T> T
getSocketOption(int
fd,
SocketOption<?>
name) throws
IOException {
jnr.constants.platform.
SocketOption optname =
rMap.
get(
name);
if (null ==
optname) {
throw new
AssertionError("Option not found");
}
Class<?>
type =
name.
type();
if (
type ==
Credentials.class) {
return (T)
Credentials.
getCredentials(
fd);
}
if (
type ==
Integer.class) {
return (T)
Integer.
valueOf(
Native.
getsockopt(
fd,
SocketLevel.
SOL_SOCKET,
optname.
intValue()));
}
return (T)
Boolean.
valueOf(
Native.
getboolsockopt(
fd,
SocketLevel.
SOL_SOCKET,
optname.
intValue()));
}
static void
setSocketOption(int
fd,
SocketOption<?>
name,
Object value) throws
IOException {
if (null ==
value) {
throw new
IllegalArgumentException("Invalid option value");
}
jnr.constants.platform.
SocketOption optname =
wMap.
get(
name);
if (null ==
optname) {
throw new
AssertionError("Option not found or not writable");
}
Class<?>
type =
name.
type();
if (
type !=
Integer.class &&
type !=
Boolean.class) {
throw new
AssertionError("Unsupported option type");
}
int
optvalue;
if (
type ==
Integer.class) {
optvalue = ((
Integer)
value).
intValue();
} else {
optvalue = ((
Boolean)
value).
booleanValue() ? 1 : 0;
}
if (
name ==
UnixSocketOptions.
SO_RCVBUF ||
name ==
UnixSocketOptions.
SO_SNDBUF) {
int
i = ((
Integer)
value).
intValue();
if (
i < 0) {
throw new
IllegalArgumentException("Invalid send/receive buffer size");
}
}
if (
name ==
UnixSocketOptions.
SO_RCVTIMEO ||
name ==
UnixSocketOptions.
SO_SNDTIMEO) {
int
i = ((
Integer)
value).
intValue();
if (
i < 0) {
throw new
IllegalArgumentException("Invalid send/receive timeout");
}
}
if (0 !=
Native.
setsockopt(
fd,
SocketLevel.
SOL_SOCKET,
optname,
optvalue)) {
throw new
IOException(
Native.
getLastErrorString());
}
}
private static final
Map<
SocketOption<?>,jnr.constants.platform.
SocketOption>
wMap = new
HashMap<>();
private static final
Map<
SocketOption<?>,jnr.constants.platform.
SocketOption>
rMap = new
HashMap<>();
static {
wMap.
put(
UnixSocketOptions.
SO_RCVBUF, jnr.constants.platform.
SocketOption.
SO_RCVBUF);
wMap.
put(
UnixSocketOptions.
SO_SNDBUF, jnr.constants.platform.
SocketOption.
SO_SNDBUF);
wMap.
put(
UnixSocketOptions.
SO_RCVTIMEO, jnr.constants.platform.
SocketOption.
SO_RCVTIMEO);
wMap.
put(
UnixSocketOptions.
SO_SNDTIMEO, jnr.constants.platform.
SocketOption.
SO_SNDTIMEO);
wMap.
put(
UnixSocketOptions.
SO_KEEPALIVE, jnr.constants.platform.
SocketOption.
SO_KEEPALIVE);
rMap.
putAll(
wMap);
rMap.
put(
UnixSocketOptions.
SO_PEERCRED, jnr.constants.platform.
SocketOption.
SO_PEERCRED);
}
}