Files
libunix-dbus-java/unix-java.c
2016-02-26 16:05:44 -03:00

457 lines
12 KiB
C

/*
* Java Unix Sockets Library
*
* Copyright (c) Matthew Johnson 2005
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation, version 2 only.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* To Contact the author, please email src@matthew.ath.cx
*
*/
/* _GNU_SOURCE is required to use struct ucred in glibc 2.8 */
#define _GNU_SOURCE
#include "unix-java.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <errno.h>
#include <string.h>
#include <sys/un.h>
#ifndef IOV_MAX
#define IOV_MAX 1024
#endif
#ifdef __cplusplus
extern "C" {
#endif
void throw(JNIEnv* env, int err, const char* msg)
{
jstring jmsg = (*env)->NewStringUTF(env, msg);
jclass exc = (*env)->FindClass(env, "cx/ath/matthew/unix/UnixIOException");
jmethodID cons = (*env)->GetMethodID(env, exc, "<init>", "(ILjava/lang/String;)V");
jobject exo = (*env)->NewObject(env, exc, cons, err, jmsg);
(*env)->DeleteLocalRef(env, exc);
(*env)->DeleteLocalRef(env, jmsg);
(*env)->Throw(env, exo);
(*env)->DeleteLocalRef(env, exo);
}
void handleerrno(JNIEnv *env)
{
if (0 == errno) return;
int err = errno;
if (EAGAIN == err) return; // we read 0 bytes due to a timeout
const char* msg = strerror(err);
throw(env, err, msg);
}
/*
* Class: cx_ath_matthew_unix_UnixServerSocket
* Method: native_bind
* Signature: (Ljava/lang/String;)I
*/
JNIEXPORT jint JNICALL Java_cx_ath_matthew_unix_UnixServerSocket_native_1bind
(JNIEnv *env, jobject o, jstring address, jboolean abstract)
{
int sock = socket(PF_UNIX, SOCK_STREAM, 0);
if (-1 == sock) { handleerrno(env); return -1; }
const char* caddr = (*env)->GetStringUTFChars(env, address, 0);
int slen = (*env)->GetStringUTFLength(env, address)+1;
struct sockaddr_un *sad = malloc(sizeof(sa_family_t)+slen);
if (abstract) {
char* shifted = sad->sun_path+1;
strncpy(shifted, caddr, slen-1);
sad->sun_path[0] = 0;
} else
strncpy(sad->sun_path, caddr, slen);
(*env)->ReleaseStringUTFChars(env, address, caddr);
sad->sun_family = AF_UNIX;
int rv = bind(sock, (const struct sockaddr*) sad, sizeof(sa_family_t)+slen);
free(sad);
if (-1 == rv) { handleerrno(env); return -1; }
rv = listen(sock, 10);
if (-1 == rv) { handleerrno(env); return -1; }
return sock;
}
/*
* Class: cx_ath_matthew_unix_UnixServerSocket
* Method: native_close
* Signature: (I)V
*/
JNIEXPORT void JNICALL Java_cx_ath_matthew_unix_UnixServerSocket_native_1close
(JNIEnv * env, jobject o, jint sock)
{
if (0 == sock) return;
int rv = shutdown(sock, SHUT_RDWR);
if (-1 == rv) { handleerrno(env); }
else {
rv = close(sock);
if (-1 == rv) { handleerrno(env); }
}
}
/*
* Class: cx_ath_matthew_unix_UnixServerSocket
* Method: native_accept
* Signature: (I)I
*/
JNIEXPORT jint JNICALL Java_cx_ath_matthew_unix_UnixServerSocket_native_1accept
(JNIEnv * env, jobject o, jint sock)
{
int newsock = accept(sock, NULL, NULL);
if (-1 == newsock) handleerrno(env);
return newsock;
}
/*
* Class: cx_ath_matthew_unix_UnixSocket
* Method: native_set_pass_cred
* Signature: (IZ)V
*/
JNIEXPORT void JNICALL Java_cx_ath_matthew_unix_UnixSocket_native_1set_1pass_1cred
(JNIEnv *env, jobject o, jint sock, jboolean enable)
{
#ifdef SO_PASSCRED
int opt = enable;
int rv = setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &opt, sizeof(int));
if (-1 == rv) { handleerrno(env);}
#endif
}
/*
* Class: cx_ath_matthew_unix_UnixSocket
* Method: native_connect
* Signature: (Ljava/lang/String;)I
*/
JNIEXPORT jint JNICALL Java_cx_ath_matthew_unix_UnixSocket_native_1connect
(JNIEnv *env, jobject o, jstring address, jboolean abstract)
{
int sock = socket(PF_UNIX, SOCK_STREAM, 0);
if (-1 == sock) { handleerrno(env); return -1; }
const char* caddr = (*env)->GetStringUTFChars(env, address, 0);
int slen = (*env)->GetStringUTFLength(env, address)+1;
struct sockaddr_un *sad = malloc(sizeof(sa_family_t)+slen);
if (abstract) {
char* shifted = sad->sun_path+1;
strncpy(shifted, caddr, slen-1);
sad->sun_path[0] = 0;
} else
strncpy(sad->sun_path, caddr, slen);
(*env)->ReleaseStringUTFChars(env, address, caddr);
sad->sun_family = AF_UNIX;
int rv = connect(sock, (const struct sockaddr*) sad, sizeof(sa_family_t)+slen);
free(sad);
if (-1 == rv) { handleerrno(env); return -1; }
return sock;
}
/*
* Class: cx_ath_matthew_unix_UnixSocket
* Method: native_close
* Signature: (I)V
*/
JNIEXPORT void JNICALL Java_cx_ath_matthew_unix_UnixSocket_native_1close
(JNIEnv *env, jobject o, jint sock)
{
if (0 == sock) return;
int rv = shutdown(sock, SHUT_RDWR);
if (-1 == rv) { handleerrno(env); }
else {
rv = close(sock);
if (-1 == rv) { handleerrno(env); }
}
}
/*
* Class: cx_ath_matthew_unix_USInputStream
* Method: native_recv
* Signature: ([BII)I
*/
JNIEXPORT jint JNICALL Java_cx_ath_matthew_unix_USInputStream_native_1recv
(JNIEnv *env, jobject o, jint sock, jbyteArray buf, jint offs, jint len, jint flags, jint timeout)
{
fd_set rfds;
struct timeval tv;
jbyte* cbuf = (*env)->GetByteArrayElements(env, buf, NULL);
void* recvb = cbuf + offs;
int rv;
if (timeout > 0) {
FD_ZERO(&rfds);
FD_SET(sock, &rfds);
tv.tv_sec = 0;
tv.tv_usec = timeout;
rv = select(sock+1, &rfds, NULL, NULL, &tv);
rv = recv(sock, recvb, len, flags);
if (-1 == rv) { handleerrno(env); rv = -1; }
(*env)->ReleaseByteArrayElements(env, buf, cbuf, 0);
return rv;
} else {
rv = recv(sock, recvb, len, flags);
(*env)->ReleaseByteArrayElements(env, buf, cbuf, 0);
if (-1 == rv) { handleerrno(env); return -1; }
return rv;
}
}
/*
* Class: cx_ath_matthew_unix_USOutputStream
* Method: native_send
* Signature: (I[BII)I
*/
JNIEXPORT jint JNICALL Java_cx_ath_matthew_unix_USOutputStream_native_1send__I_3BII
(JNIEnv *env, jobject o, jint sock, jbyteArray buf, jint offs, jint len)
{
jbyte* cbuf = (*env)->GetByteArrayElements(env, buf, NULL);
void* sendb = cbuf + offs;
int rv = send(sock, sendb, len, 0);
(*env)->ReleaseByteArrayElements(env, buf, cbuf, 0);
if (-1 == rv) { handleerrno(env); return -1; }
return rv;
}
/*
* Class: cx_ath_matthew_unix_USOutputStream
* Method: native_send
* Signature: (I[[B)I
*/
JNIEXPORT jint JNICALL Java_cx_ath_matthew_unix_USOutputStream_native_1send__I_3_3B
(JNIEnv *env, jobject o, jint sock, jobjectArray bufs)
{
size_t sblen = 1;
socklen_t sblen_size = sizeof(sblen);
getsockopt(sock, SOL_SOCKET, SO_SNDBUF, &sblen, &sblen_size);
struct msghdr msg;
struct iovec *iov;
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_flags = 0;
size_t els = (*env)->GetArrayLength(env, bufs);
iov = (struct iovec*) malloc((els<IOV_MAX?els:IOV_MAX) * sizeof(struct iovec));
msg.msg_iov = iov;
jbyteArray *b = (jbyteArray*) malloc(els * sizeof(jbyteArray));
int rv = 0;
for (int i = 0, j = 0, s = 0; i <= els; i++, j++) {
if (i == els) {
msg.msg_iovlen = j;
rv = sendmsg(sock, &msg, 0);
for (int k = i-1, l = j-1; l >= 0; k--, l--)
(*env)->ReleaseByteArrayElements(env, b[k], iov[l].iov_base, 0);
if (-1 == rv) { handleerrno(env); return -1; }
break;
}
b[i] = (*env)->GetObjectArrayElement(env, bufs, i);
if (NULL == b[i]) {
msg.msg_iovlen = j;
rv = sendmsg(sock, &msg, 0);
for (int k = i-1, l = j-1; l >= 0; k--, l--)
(*env)->ReleaseByteArrayElements(env, b[k], iov[l].iov_base, 0);
if (-1 == rv) { handleerrno(env); return -1; }
break;
}
size_t l = (*env)->GetArrayLength(env, b[i]);
if (s+l > sblen || j == IOV_MAX) {
msg.msg_iovlen = j;
rv = sendmsg(sock, &msg, 0);
j = 0;
s = 0;
for (int k = i-1, l = j-1; l >= 0; k--, l--)
(*env)->ReleaseByteArrayElements(env, b[k], iov[l].iov_base, 0);
if (-1 == rv) { handleerrno(env); return -1; }
}
iov[j].iov_base = (*env)->GetByteArrayElements(env, b[i], NULL);
iov[j].iov_len = l;
s += l;
}
free(iov);
free(b);
return rv;
}
/*
* Class: cx_ath_matthew_unix_UnixSocket
* Method: native_getPID
* Signature: (I)I
*/
JNIEXPORT jint JNICALL Java_cx_ath_matthew_unix_UnixSocket_native_1getPID
(JNIEnv * env, jobject o, jint sock)
{
#ifdef SO_PEERCRED
struct ucred cr;
socklen_t cl=sizeof(cr);
if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &cr, &cl)==0)
return cr.pid;
else
return -1;
#else
return -1;
#endif
}
/*
* Class: cx_ath_matthew_unix_UnixSocket
* Method: native_getUID
* Signature: (I)I
*/
JNIEXPORT jint JNICALL Java_cx_ath_matthew_unix_UnixSocket_native_1getUID
(JNIEnv * env, jobject o, jint sock)
{
#ifdef SO_PEERCRED
struct ucred cr;
socklen_t cl=sizeof(cr);
if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &cr, &cl)==0)
return cr.uid;
else
return -1;
#else
return -1;
#endif
}
/*
* Class: cx_ath_matthew_unix_UnixSocket
* Method: native_getGID
* Signature: (I)I
*/
JNIEXPORT jint JNICALL Java_cx_ath_matthew_unix_UnixSocket_native_1getGID
(JNIEnv * env, jobject o, jint sock)
{
#ifdef SO_PEERCRED
struct ucred cr;
socklen_t cl=sizeof(cr);
if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &cr, &cl)==0)
return cr.gid;
else
return -1;
#else
return -1;
#endif
}
/*
* Class: cx_ath_matthew_unix_UnixSocket
* Method: native_send_creds
* Signature: (B)V
*/
JNIEXPORT void JNICALL Java_cx_ath_matthew_unix_UnixSocket_native_1send_1creds
(JNIEnv * env, jobject o, jint sock, jbyte data)
{
struct msghdr msg;
struct iovec iov;
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_flags = 0;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = NULL;
msg.msg_controllen = 0;
iov.iov_base = &data;
iov.iov_len = 1;
#ifdef SCM_CREDENTIALS
char buf[CMSG_SPACE(sizeof(struct ucred))];
msg.msg_control = buf;
msg.msg_controllen = sizeof buf;
struct cmsghdr *cmsg;
struct ucred *creds;
cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_CREDENTIALS;
cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
/* Initialize the payload: */
creds = (struct ucred *)CMSG_DATA(cmsg);
creds->pid = getpid();
creds->uid = getuid();
creds->gid = getgid();
#endif
int rv = sendmsg(sock, &msg, 0);
if (-1 == rv) { handleerrno(env); }
}
/*
* Class: cx_ath_matthew_unix_UnixSocket
* Method: native_recv_creds
* Signature: ([I)B
*/
JNIEXPORT jbyte JNICALL Java_cx_ath_matthew_unix_UnixSocket_native_1recv_1creds
(JNIEnv *env, jobject o, jint sock, jintArray jcreds)
{
struct msghdr msg;
char iov_buf = 0;
struct iovec iov;
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_flags = 0;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = NULL;
msg.msg_controllen = 0;
iov.iov_base = &iov_buf;
iov.iov_len = 1;
#ifdef SCM_CREDENTIALS
char buf[CMSG_SPACE(sizeof(struct ucred))];
msg.msg_control = buf;
msg.msg_controllen = sizeof buf;
struct cmsghdr *cmsg;
struct ucred *creds = NULL;
#endif
recvmsg(sock, &msg, 0);
#ifdef SCM_CREDENTIALS
for (cmsg = CMSG_FIRSTHDR(&msg);
cmsg != NULL;
cmsg = CMSG_NXTHDR(&msg,cmsg)) {
if (cmsg->cmsg_level == SOL_SOCKET
&& cmsg->cmsg_type == SCM_CREDENTIALS) {
creds = (struct ucred *) CMSG_DATA(cmsg);
break;
}
}
if (NULL != creds) {
jint cred_array[3];
cred_array[0] = creds->pid;
cred_array[1] = creds->uid;
cred_array[2] = creds->gid;
(*env)->SetIntArrayRegion(env, jcreds, 0, 3, &cred_array[0]);
}
#endif
return iov_buf;
}
#ifdef __cplusplus
}
#endif