2 * Copyright (C) 2006 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #define LOG_TAG "LocalSocketImpl"
21 #include "utils/Log.h"
22 #include "utils/misc.h"
26 #include <sys/types.h>
27 #include <sys/socket.h>
29 #include <arpa/inet.h>
30 #include <netinet/in.h>
34 #include <sys/ioctl.h>
36 #include <cutils/sockets.h>
37 #include <netinet/tcp.h>
38 #include <ScopedUtfChars.h>
45 static jfieldID field_inboundFileDescriptors;
46 static jfieldID field_outboundFileDescriptors;
47 static jclass class_Credentials;
48 static jclass class_FileDescriptor;
49 static jmethodID method_CredentialsInit;
51 /* private native void connectLocal(FileDescriptor fd,
52 * String name, int namespace) throws IOException
55 socket_connect_local(JNIEnv *env, jobject object,
56 jobject fileDescriptor, jstring name, jint namespaceId)
61 fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
63 if (env->ExceptionCheck()) {
67 ScopedUtfChars nameUtf8(env, name);
69 ret = socket_local_client_connect(
76 jniThrowIOException(env, errno);
81 #define DEFAULT_BACKLOG 4
83 /* private native void bindLocal(FileDescriptor fd, String name, namespace)
88 socket_bind_local (JNIEnv *env, jobject object, jobject fileDescriptor,
89 jstring name, jint namespaceId)
95 jniThrowNullPointerException(env, NULL);
99 fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
101 if (env->ExceptionCheck()) {
105 ScopedUtfChars nameUtf8(env, name);
107 ret = socket_local_server_bind(fd, nameUtf8.c_str(), namespaceId);
110 jniThrowIOException(env, errno);
116 * Processes ancillary data, handling only
117 * SCM_RIGHTS. Creates appropriate objects and sets appropriate
118 * fields in the LocalSocketImpl object. Returns 0 on success
119 * or -1 if an exception was thrown.
121 static int socket_process_cmsg(JNIEnv *env, jobject thisJ, struct msghdr * pMsg)
123 struct cmsghdr *cmsgptr;
125 for (cmsgptr = CMSG_FIRSTHDR(pMsg);
126 cmsgptr != NULL; cmsgptr = CMSG_NXTHDR(pMsg, cmsgptr)) {
128 if (cmsgptr->cmsg_level != SOL_SOCKET) {
132 if (cmsgptr->cmsg_type == SCM_RIGHTS) {
133 int *pDescriptors = (int *)CMSG_DATA(cmsgptr);
134 jobjectArray fdArray;
136 = ((cmsgptr->cmsg_len - CMSG_LEN(0)) / sizeof(int));
139 jniThrowException(env, "java/io/IOException",
140 "invalid cmsg length");
144 fdArray = env->NewObjectArray(count, class_FileDescriptor, NULL);
146 if (fdArray == NULL) {
150 for (int i = 0; i < count; i++) {
152 = jniCreateFileDescriptor(env, pDescriptors[i]);
154 if (env->ExceptionCheck()) {
158 env->SetObjectArrayElement(fdArray, i, fdObject);
160 if (env->ExceptionCheck()) {
165 env->SetObjectField(thisJ, field_inboundFileDescriptors, fdArray);
167 if (env->ExceptionCheck()) {
177 * Reads data from a socket into buf, processing any ancillary data
178 * and adding it to thisJ.
180 * Returns the length of normal data read, or -1 if an exception has
181 * been thrown in this function.
183 static ssize_t socket_read_all(JNIEnv *env, jobject thisJ, int fd,
184 void *buffer, size_t len)
189 unsigned char *buf = (unsigned char *)buffer;
190 // Enough buffer for a pile of fd's. We throw an exception if
191 // this buffer is too small.
192 struct cmsghdr cmsgbuf[2*sizeof(cmsghdr) + 0x100];
194 memset(&msg, 0, sizeof(msg));
195 memset(&iv, 0, sizeof(iv));
202 msg.msg_control = cmsgbuf;
203 msg.msg_controllen = sizeof(cmsgbuf);
206 ret = recvmsg(fd, &msg, MSG_NOSIGNAL);
207 } while (ret < 0 && errno == EINTR);
209 if (ret < 0 && errno == EPIPE) {
210 // Treat this as an end of stream
215 jniThrowIOException(env, errno);
219 if ((msg.msg_flags & (MSG_CTRUNC | MSG_OOB | MSG_ERRQUEUE)) != 0) {
220 // To us, any of the above flags are a fatal error
222 jniThrowException(env, "java/io/IOException",
223 "Unexpected error or truncation during recvmsg()");
229 socket_process_cmsg(env, thisJ, &msg);
236 * Writes all the data in the specified buffer to the specified socket.
238 * Returns 0 on success or -1 if an exception was thrown.
240 static int socket_write_all(JNIEnv *env, jobject object, int fd,
241 void *buf, size_t len)
245 unsigned char *buffer = (unsigned char *)buf;
246 memset(&msg, 0, sizeof(msg));
248 jobjectArray outboundFds
249 = (jobjectArray)env->GetObjectField(
250 object, field_outboundFileDescriptors);
252 if (env->ExceptionCheck()) {
256 struct cmsghdr *cmsg;
257 int countFds = outboundFds == NULL ? 0 : env->GetArrayLength(outboundFds);
259 char msgbuf[CMSG_SPACE(countFds)];
261 // Add any pending outbound file descriptors to the message
262 if (outboundFds != NULL) {
264 if (env->ExceptionCheck()) {
268 for (int i = 0; i < countFds; i++) {
269 jobject fdObject = env->GetObjectArrayElement(outboundFds, i);
270 if (env->ExceptionCheck()) {
274 fds[i] = jniGetFDFromFileDescriptor(env, fdObject);
275 if (env->ExceptionCheck()) {
280 // See "man cmsg" really
281 msg.msg_control = msgbuf;
282 msg.msg_controllen = sizeof msgbuf;
283 cmsg = CMSG_FIRSTHDR(&msg);
284 cmsg->cmsg_level = SOL_SOCKET;
285 cmsg->cmsg_type = SCM_RIGHTS;
286 cmsg->cmsg_len = CMSG_LEN(sizeof fds);
287 memcpy(CMSG_DATA(cmsg), fds, sizeof fds);
290 // We only write our msg_control during the first write
293 memset(&iv, 0, sizeof(iv));
295 iv.iov_base = buffer;
302 ret = sendmsg(fd, &msg, MSG_NOSIGNAL);
303 } while (ret < 0 && errno == EINTR);
306 jniThrowIOException(env, errno);
313 // Wipes out any msg_control too
314 memset(&msg, 0, sizeof(msg));
320 static jint socket_read (JNIEnv *env, jobject object, jobject fileDescriptor)
325 if (fileDescriptor == NULL) {
326 jniThrowNullPointerException(env, NULL);
330 fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
332 if (env->ExceptionCheck()) {
338 err = socket_read_all(env, object, fd, &buf, 1);
341 jniThrowIOException(env, errno);
353 static jint socket_readba (JNIEnv *env, jobject object,
354 jbyteArray buffer, jint off, jint len, jobject fileDescriptor)
360 if (fileDescriptor == NULL || buffer == NULL) {
361 jniThrowNullPointerException(env, NULL);
365 if (off < 0 || len < 0 || (off + len) > env->GetArrayLength(buffer)) {
366 jniThrowException(env, "java/lang/ArrayIndexOutOfBoundsException", NULL);
371 // because socket_read_all returns 0 on EOF
375 fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
377 if (env->ExceptionCheck()) {
381 byteBuffer = env->GetByteArrayElements(buffer, NULL);
383 if (NULL == byteBuffer) {
384 // an exception will have been thrown
388 ret = socket_read_all(env, object,
389 fd, byteBuffer + off, len);
391 // A return of -1 above means an exception is pending
393 env->ReleaseByteArrayElements(buffer, byteBuffer, 0);
395 return (jint) ((ret == 0) ? -1 : ret);
398 static void socket_write (JNIEnv *env, jobject object,
399 jint b, jobject fileDescriptor)
404 if (fileDescriptor == NULL) {
405 jniThrowNullPointerException(env, NULL);
409 fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
411 if (env->ExceptionCheck()) {
415 err = socket_write_all(env, object, fd, &b, 1);
417 // A return of -1 above means an exception is pending
420 static void socket_writeba (JNIEnv *env, jobject object,
421 jbyteArray buffer, jint off, jint len, jobject fileDescriptor)
427 if (fileDescriptor == NULL || buffer == NULL) {
428 jniThrowNullPointerException(env, NULL);
432 if (off < 0 || len < 0 || (off + len) > env->GetArrayLength(buffer)) {
433 jniThrowException(env, "java/lang/ArrayIndexOutOfBoundsException", NULL);
437 fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
439 if (env->ExceptionCheck()) {
443 byteBuffer = env->GetByteArrayElements(buffer,NULL);
445 if (NULL == byteBuffer) {
446 // an exception will have been thrown
450 err = socket_write_all(env, object, fd,
451 byteBuffer + off, len);
453 // A return of -1 above means an exception is pending
455 env->ReleaseByteArrayElements(buffer, byteBuffer, JNI_ABORT);
458 static jobject socket_get_peer_credentials(JNIEnv *env,
459 jobject object, jobject fileDescriptor)
464 if (fileDescriptor == NULL) {
465 jniThrowNullPointerException(env, NULL);
469 fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
471 if (env->ExceptionCheck()) {
477 memset(&creds, 0, sizeof(creds));
478 socklen_t szCreds = sizeof(creds);
480 err = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &creds, &szCreds);
483 jniThrowIOException(env, errno);
491 return env->NewObject(class_Credentials, method_CredentialsInit,
492 creds.pid, creds.uid, creds.gid);
498 static const JNINativeMethod gMethods[] = {
499 /* name, signature, funcPtr */
500 {"connectLocal", "(Ljava/io/FileDescriptor;Ljava/lang/String;I)V",
501 (void*)socket_connect_local},
502 {"bindLocal", "(Ljava/io/FileDescriptor;Ljava/lang/String;I)V", (void*)socket_bind_local},
503 {"read_native", "(Ljava/io/FileDescriptor;)I", (void*) socket_read},
504 {"readba_native", "([BIILjava/io/FileDescriptor;)I", (void*) socket_readba},
505 {"writeba_native", "([BIILjava/io/FileDescriptor;)V", (void*) socket_writeba},
506 {"write_native", "(ILjava/io/FileDescriptor;)V", (void*) socket_write},
507 {"getPeerCredentials_native",
508 "(Ljava/io/FileDescriptor;)Landroid/net/Credentials;",
509 (void*) socket_get_peer_credentials}
512 int register_android_net_LocalSocketImpl(JNIEnv *env)
516 clazz = env->FindClass("android/net/LocalSocketImpl");
522 field_inboundFileDescriptors = env->GetFieldID(clazz,
523 "inboundFileDescriptors", "[Ljava/io/FileDescriptor;");
525 if (field_inboundFileDescriptors == NULL) {
529 field_outboundFileDescriptors = env->GetFieldID(clazz,
530 "outboundFileDescriptors", "[Ljava/io/FileDescriptor;");
532 if (field_outboundFileDescriptors == NULL) {
536 class_Credentials = env->FindClass("android/net/Credentials");
538 if (class_Credentials == NULL) {
542 class_Credentials = (jclass)env->NewGlobalRef(class_Credentials);
544 class_FileDescriptor = env->FindClass("java/io/FileDescriptor");
546 if (class_FileDescriptor == NULL) {
550 class_FileDescriptor = (jclass)env->NewGlobalRef(class_FileDescriptor);
552 method_CredentialsInit
553 = env->GetMethodID(class_Credentials, "<init>", "(III)V");
555 if (method_CredentialsInit == NULL) {
559 return jniRegisterNativeMethods(env,
560 "android/net/LocalSocketImpl", gMethods, NELEM(gMethods));
563 ALOGE("Error registering android.net.LocalSocketImpl");