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>
42 static jfieldID field_inboundFileDescriptors;
43 static jfieldID field_outboundFileDescriptors;
44 static jclass class_Credentials;
45 static jclass class_FileDescriptor;
46 static jmethodID method_CredentialsInit;
48 /* private native void connectLocal(FileDescriptor fd,
49 * String name, int namespace) throws IOException
52 socket_connect_local(JNIEnv *env, jobject object,
53 jobject fileDescriptor, jstring name, jint namespaceId)
58 fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
60 if (env->ExceptionOccurred() != NULL) {
64 ScopedUtfChars nameUtf8(env, name);
66 ret = socket_local_client_connect(
73 jniThrowIOException(env, errno);
78 #define DEFAULT_BACKLOG 4
80 /* private native void bindLocal(FileDescriptor fd, String name, namespace)
85 socket_bind_local (JNIEnv *env, jobject object, jobject fileDescriptor,
86 jstring name, jint namespaceId)
92 jniThrowNullPointerException(env, NULL);
96 fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
98 if (env->ExceptionOccurred() != NULL) {
102 ScopedUtfChars nameUtf8(env, name);
104 ret = socket_local_server_bind(fd, nameUtf8.c_str(), namespaceId);
107 jniThrowIOException(env, errno);
112 /* private native void listen_native(int fd, int backlog) throws IOException; */
114 socket_listen (JNIEnv *env, jobject object, jobject fileDescriptor, jint backlog)
119 fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
121 if (env->ExceptionOccurred() != NULL) {
125 ret = listen(fd, backlog);
128 jniThrowIOException(env, errno);
133 /* private native FileDescriptor
134 ** accept (FileDescriptor fd, LocalSocketImpl s)
135 ** throws IOException;
138 socket_accept (JNIEnv *env, jobject object, jobject fileDescriptor, jobject s)
141 struct sockaddr address;
142 struct sockaddr_un un_address;
151 jniThrowNullPointerException(env, NULL);
155 fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
157 if (env->ExceptionOccurred() != NULL) {
162 addrlen = sizeof(sa);
163 ret = accept(fd, &(sa.address), &addrlen);
164 } while (ret < 0 && errno == EINTR);
167 jniThrowIOException(env, errno);
173 return jniCreateFileDescriptor(env, retFD);
176 /* private native void shutdown(FileDescriptor fd, boolean shutdownInput) */
179 socket_shutdown (JNIEnv *env, jobject object, jobject fileDescriptor,
180 jboolean shutdownInput)
185 fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
187 if (env->ExceptionOccurred() != NULL) {
191 ret = shutdown(fd, shutdownInput ? SHUT_RD : SHUT_WR);
194 jniThrowIOException(env, errno);
200 java_opt_to_real(int optID, int* opt, int* level)
222 *level = IPPROTO_TCP;
234 socket_getOption(JNIEnv *env, jobject object, jobject fileDescriptor, jint optID)
240 socklen_t size = sizeof(int);
242 if (!java_opt_to_real(optID, &opt, &level)) {
243 jniThrowIOException(env, -1);
247 fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
249 if (env->ExceptionOccurred() != NULL) {
258 size = sizeof(lingr);
259 ret = getsockopt(fd, level, opt, &lingr, &size);
260 if (!lingr.l_onoff) {
263 value = lingr.l_linger;
268 ret = getsockopt(fd, level, opt, &value, &size);
274 jniThrowIOException(env, errno);
281 static void socket_setOption(
282 JNIEnv *env, jobject object, jobject fileDescriptor, jint optID,
283 jint boolValue, jint intValue) {
289 if (!java_opt_to_real(optID, &optname, &level)) {
290 jniThrowIOException(env, -1);
294 fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
296 if (env->ExceptionOccurred() != NULL) {
303 * SO_LINGER is special because it needs to use a special
304 * "linger" struct as well as use the incoming boolean
305 * argument specially.
308 lingr.l_onoff = boolValue ? 1 : 0; // Force it to be 0 or 1.
309 lingr.l_linger = intValue;
310 ret = setsockopt(fd, level, optname, &lingr, sizeof(lingr));
315 * SO_TIMEOUT from the core library gets converted to
316 * SO_SNDTIMEO, but the option is supposed to set both
317 * send and receive timeouts. Note: The incoming timeout
318 * value is in milliseconds.
320 struct timeval timeout;
321 timeout.tv_sec = intValue / 1000;
322 timeout.tv_usec = (intValue % 1000) * 1000;
324 ret = setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO,
325 (void *)&timeout, sizeof(timeout));
328 ret = setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO,
329 (void *)&timeout, sizeof(timeout));
336 * In all other cases, the translated option level and
337 * optname may be used directly for a call to setsockopt().
339 ret = setsockopt(fd, level, optname, &intValue, sizeof(intValue));
345 jniThrowIOException(env, errno);
349 static jint socket_pending (JNIEnv *env, jobject object,
350 jobject fileDescriptor)
354 fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
356 if (env->ExceptionOccurred() != NULL) {
361 int ret = ioctl(fd, TIOCOUTQ, &pending);
363 // If this were a non-socket fd, there would be other cases to worry
366 //ALOGD("socket_pending, ioctl ret:%d, pending:%d", ret, pending);
368 jniThrowIOException(env, errno);
372 return (jint)pending;
374 static jint socket_available (JNIEnv *env, jobject object,
375 jobject fileDescriptor)
379 fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
381 if (env->ExceptionOccurred() != NULL) {
387 int ret = ioctl(fd, FIONREAD, &avail);
389 // If this were a non-socket fd, there would be other cases to worry
393 jniThrowIOException(env, errno);
399 // there appears to be a bionic bug that prevents this version from working.
404 memset(&msg, 0, sizeof(msg));
407 ret = recvmsg(fd, &msg, MSG_PEEK | MSG_DONTWAIT | MSG_NOSIGNAL);
408 } while (ret < 0 && errno == EINTR);
411 // MSG_PEEK returns 0 on EOF and EWOULDBLOCK on none available
412 if (ret < 0 && errno == EWOULDBLOCK) {
415 jniThrowIOException(env, errno);
424 * Processes ancillary data, handling only
425 * SCM_RIGHTS. Creates appropriate objects and sets appropriate
426 * fields in the LocalSocketImpl object. Returns 0 on success
427 * or -1 if an exception was thrown.
429 static int socket_process_cmsg(JNIEnv *env, jobject thisJ, struct msghdr * pMsg)
431 struct cmsghdr *cmsgptr;
433 for (cmsgptr = CMSG_FIRSTHDR(pMsg);
434 cmsgptr != NULL; cmsgptr = CMSG_NXTHDR(pMsg, cmsgptr)) {
436 if (cmsgptr->cmsg_level != SOL_SOCKET) {
440 if (cmsgptr->cmsg_type == SCM_RIGHTS) {
441 int *pDescriptors = (int *)CMSG_DATA(cmsgptr);
442 jobjectArray fdArray;
444 = ((cmsgptr->cmsg_len - CMSG_LEN(0)) / sizeof(int));
447 jniThrowException(env, "java/io/IOException",
448 "invalid cmsg length");
452 fdArray = env->NewObjectArray(count, class_FileDescriptor, NULL);
454 if (fdArray == NULL) {
458 for (int i = 0; i < count; i++) {
460 = jniCreateFileDescriptor(env, pDescriptors[i]);
462 if (env->ExceptionOccurred() != NULL) {
466 env->SetObjectArrayElement(fdArray, i, fdObject);
468 if (env->ExceptionOccurred() != NULL) {
473 env->SetObjectField(thisJ, field_inboundFileDescriptors, fdArray);
475 if (env->ExceptionOccurred() != NULL) {
485 * Reads data from a socket into buf, processing any ancillary data
486 * and adding it to thisJ.
488 * Returns the length of normal data read, or -1 if an exception has
489 * been thrown in this function.
491 static ssize_t socket_read_all(JNIEnv *env, jobject thisJ, int fd,
492 void *buffer, size_t len)
495 ssize_t bytesread = 0;
498 unsigned char *buf = (unsigned char *)buffer;
499 // Enough buffer for a pile of fd's. We throw an exception if
500 // this buffer is too small.
501 struct cmsghdr cmsgbuf[2*sizeof(cmsghdr) + 0x100];
503 memset(&msg, 0, sizeof(msg));
504 memset(&iv, 0, sizeof(iv));
511 msg.msg_control = cmsgbuf;
512 msg.msg_controllen = sizeof(cmsgbuf);
515 ret = recvmsg(fd, &msg, MSG_NOSIGNAL);
516 } while (ret < 0 && errno == EINTR);
518 if (ret < 0 && errno == EPIPE) {
519 // Treat this as an end of stream
524 jniThrowIOException(env, errno);
528 if ((msg.msg_flags & (MSG_CTRUNC | MSG_OOB | MSG_ERRQUEUE)) != 0) {
529 // To us, any of the above flags are a fatal error
531 jniThrowException(env, "java/io/IOException",
532 "Unexpected error or truncation during recvmsg()");
538 socket_process_cmsg(env, thisJ, &msg);
545 * Writes all the data in the specified buffer to the specified socket.
547 * Returns 0 on success or -1 if an exception was thrown.
549 static int socket_write_all(JNIEnv *env, jobject object, int fd,
550 void *buf, size_t len)
554 unsigned char *buffer = (unsigned char *)buf;
555 memset(&msg, 0, sizeof(msg));
557 jobjectArray outboundFds
558 = (jobjectArray)env->GetObjectField(
559 object, field_outboundFileDescriptors);
561 if (env->ExceptionOccurred() != NULL) {
565 struct cmsghdr *cmsg;
566 int countFds = outboundFds == NULL ? 0 : env->GetArrayLength(outboundFds);
568 char msgbuf[CMSG_SPACE(countFds)];
570 // Add any pending outbound file descriptors to the message
571 if (outboundFds != NULL) {
573 if (env->ExceptionOccurred() != NULL) {
577 for (int i = 0; i < countFds; i++) {
578 jobject fdObject = env->GetObjectArrayElement(outboundFds, i);
579 if (env->ExceptionOccurred() != NULL) {
583 fds[i] = jniGetFDFromFileDescriptor(env, fdObject);
584 if (env->ExceptionOccurred() != NULL) {
589 // See "man cmsg" really
590 msg.msg_control = msgbuf;
591 msg.msg_controllen = sizeof msgbuf;
592 cmsg = CMSG_FIRSTHDR(&msg);
593 cmsg->cmsg_level = SOL_SOCKET;
594 cmsg->cmsg_type = SCM_RIGHTS;
595 cmsg->cmsg_len = CMSG_LEN(sizeof fds);
596 memcpy(CMSG_DATA(cmsg), fds, sizeof fds);
599 // We only write our msg_control during the first write
602 memset(&iv, 0, sizeof(iv));
604 iv.iov_base = buffer;
611 ret = sendmsg(fd, &msg, MSG_NOSIGNAL);
612 } while (ret < 0 && errno == EINTR);
615 jniThrowIOException(env, errno);
622 // Wipes out any msg_control too
623 memset(&msg, 0, sizeof(msg));
629 static jint socket_read (JNIEnv *env, jobject object, jobject fileDescriptor)
634 if (fileDescriptor == NULL) {
635 jniThrowNullPointerException(env, NULL);
639 fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
641 if (env->ExceptionOccurred() != NULL) {
647 err = socket_read_all(env, object, fd, &buf, 1);
650 jniThrowIOException(env, errno);
662 static jint socket_readba (JNIEnv *env, jobject object,
663 jbyteArray buffer, jint off, jint len, jobject fileDescriptor)
669 if (fileDescriptor == NULL || buffer == NULL) {
670 jniThrowNullPointerException(env, NULL);
674 if (off < 0 || len < 0 || (off + len) > env->GetArrayLength(buffer)) {
675 jniThrowException(env, "java/lang/ArrayIndexOutOfBoundsException", NULL);
680 // because socket_read_all returns 0 on EOF
684 fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
686 if (env->ExceptionOccurred() != NULL) {
690 byteBuffer = env->GetByteArrayElements(buffer, NULL);
692 if (NULL == byteBuffer) {
693 // an exception will have been thrown
697 ret = socket_read_all(env, object,
698 fd, byteBuffer + off, len);
700 // A return of -1 above means an exception is pending
702 env->ReleaseByteArrayElements(buffer, byteBuffer, 0);
704 return (jint) ((ret == 0) ? -1 : ret);
707 static void socket_write (JNIEnv *env, jobject object,
708 jint b, jobject fileDescriptor)
713 if (fileDescriptor == NULL) {
714 jniThrowNullPointerException(env, NULL);
718 fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
720 if (env->ExceptionOccurred() != NULL) {
724 err = socket_write_all(env, object, fd, &b, 1);
726 // A return of -1 above means an exception is pending
729 static void socket_writeba (JNIEnv *env, jobject object,
730 jbyteArray buffer, jint off, jint len, jobject fileDescriptor)
736 if (fileDescriptor == NULL || buffer == NULL) {
737 jniThrowNullPointerException(env, NULL);
741 if (off < 0 || len < 0 || (off + len) > env->GetArrayLength(buffer)) {
742 jniThrowException(env, "java/lang/ArrayIndexOutOfBoundsException", NULL);
746 fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
748 if (env->ExceptionOccurred() != NULL) {
752 byteBuffer = env->GetByteArrayElements(buffer,NULL);
754 if (NULL == byteBuffer) {
755 // an exception will have been thrown
759 err = socket_write_all(env, object, fd,
760 byteBuffer + off, len);
762 // A return of -1 above means an exception is pending
764 env->ReleaseByteArrayElements(buffer, byteBuffer, JNI_ABORT);
767 static jobject socket_get_peer_credentials(JNIEnv *env,
768 jobject object, jobject fileDescriptor)
773 if (fileDescriptor == NULL) {
774 jniThrowNullPointerException(env, NULL);
778 fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
780 if (env->ExceptionOccurred() != NULL) {
786 memset(&creds, 0, sizeof(creds));
787 socklen_t szCreds = sizeof(creds);
789 err = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &creds, &szCreds);
792 jniThrowIOException(env, errno);
800 return env->NewObject(class_Credentials, method_CredentialsInit,
801 creds.pid, creds.uid, creds.gid);
805 //TODO change this to return an instance of LocalSocketAddress
806 static jobject socket_getSockName(JNIEnv *env,
807 jobject object, jobject fileDescriptor)
812 if (fileDescriptor == NULL) {
813 jniThrowNullPointerException(env, NULL);
817 fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
819 if (env->ExceptionOccurred() != NULL) {
824 struct sockaddr address;
825 struct sockaddr_un un_address;
828 memset(&sa, 0, sizeof(sa));
830 socklen_t namelen = sizeof(sa);
831 err = getsockname(fd, &(sa.address), &namelen);
834 jniThrowIOException(env, errno);
838 if (sa.address.sa_family != AF_UNIX) {
839 // We think we're an impl only for AF_UNIX, so this should never happen.
841 jniThrowIOException(env, EINVAL);
845 if (sa.un_address.sun_path[0] == '\0') {
858 static JNINativeMethod gMethods[] = {
859 /* name, signature, funcPtr */
860 {"getOption_native", "(Ljava/io/FileDescriptor;I)I", (void*)socket_getOption},
861 {"setOption_native", "(Ljava/io/FileDescriptor;III)V", (void*)socket_setOption},
862 {"connectLocal", "(Ljava/io/FileDescriptor;Ljava/lang/String;I)V",
863 (void*)socket_connect_local},
864 {"bindLocal", "(Ljava/io/FileDescriptor;Ljava/lang/String;I)V", (void*)socket_bind_local},
865 {"listen_native", "(Ljava/io/FileDescriptor;I)V", (void*)socket_listen},
866 {"accept", "(Ljava/io/FileDescriptor;Landroid/net/LocalSocketImpl;)Ljava/io/FileDescriptor;", (void*)socket_accept},
867 {"shutdown", "(Ljava/io/FileDescriptor;Z)V", (void*)socket_shutdown},
868 {"available_native", "(Ljava/io/FileDescriptor;)I", (void*) socket_available},
869 {"pending_native", "(Ljava/io/FileDescriptor;)I", (void*) socket_pending},
870 {"read_native", "(Ljava/io/FileDescriptor;)I", (void*) socket_read},
871 {"readba_native", "([BIILjava/io/FileDescriptor;)I", (void*) socket_readba},
872 {"writeba_native", "([BIILjava/io/FileDescriptor;)V", (void*) socket_writeba},
873 {"write_native", "(ILjava/io/FileDescriptor;)V", (void*) socket_write},
874 {"getPeerCredentials_native",
875 "(Ljava/io/FileDescriptor;)Landroid/net/Credentials;",
876 (void*) socket_get_peer_credentials}
877 //,{"getSockName_native", "(Ljava/io/FileDescriptor;)Ljava/lang/String;",
878 // (void *) socket_getSockName}
882 int register_android_net_LocalSocketImpl(JNIEnv *env)
886 clazz = env->FindClass("android/net/LocalSocketImpl");
892 field_inboundFileDescriptors = env->GetFieldID(clazz,
893 "inboundFileDescriptors", "[Ljava/io/FileDescriptor;");
895 if (field_inboundFileDescriptors == NULL) {
899 field_outboundFileDescriptors = env->GetFieldID(clazz,
900 "outboundFileDescriptors", "[Ljava/io/FileDescriptor;");
902 if (field_outboundFileDescriptors == NULL) {
906 class_Credentials = env->FindClass("android/net/Credentials");
908 if (class_Credentials == NULL) {
912 class_Credentials = (jclass)env->NewGlobalRef(class_Credentials);
914 class_FileDescriptor = env->FindClass("java/io/FileDescriptor");
916 if (class_FileDescriptor == NULL) {
920 class_FileDescriptor = (jclass)env->NewGlobalRef(class_FileDescriptor);
922 method_CredentialsInit
923 = env->GetMethodID(class_Credentials, "<init>", "(III)V");
925 if (method_CredentialsInit == NULL) {
929 return jniRegisterNativeMethods(env,
930 "android/net/LocalSocketImpl", gMethods, NELEM(gMethods));
933 ALOGE("Error registering android.net.LocalSocketImpl");