jobject jniCreateFileDescriptor(C_JNIEnv* env, int fd) { JNIEnv* e = reinterpret_cast<JNIEnv*>(env); static jmethodID ctor = e->GetMethodID(JniConstants::fileDescriptorClass, "<init>", "()V"); jobject fileDescriptor = (*env)->NewObject(e, JniConstants::fileDescriptorClass, ctor); jniSetFileDescriptorOfFD(env, fileDescriptor, fd); return fileDescriptor; }
/* * Creates a socket suitable for raw socket operations. The socket is * bound to the interface specified by the supplied name. The socket * value is placed into the supplied FileDescriptor instance. * * TODO(chesnutt): consider breaking this into pieces: create a * variety of constructors for different socket types, then a generic * setBlocking() method followed by polymorphic bind(). */ static void RawSocket_create(JNIEnv* env, jclass, jobject fileDescriptor, jshort protocolType, jstring interfaceName) { ScopedUtfChars ifname(env, interfaceName); if (ifname.c_str() == NULL) { return; } memset(&su, 0, sizeof(su)); su.sll.sll_family = PF_PACKET; su.sll.sll_protocol = htons(protocolType); su.sll.sll_ifindex = if_nametoindex(ifname.c_str()); int sock = socket(PF_PACKET, SOCK_DGRAM, htons(protocolType)); if (sock == -1) { ALOGE("Can't create socket %s", strerror(errno)); jniThrowSocketException(env, errno); return; } jniSetFileDescriptorOfFD(env, fileDescriptor, sock); if (!setBlocking(sock, false)) { ALOGE("Can't set non-blocking mode on socket %s", strerror(errno)); jniThrowSocketException(env, errno); return; } int err = bind(sock, &su.sa, sizeof(su)); if (err != 0) { ALOGE("Socket bind error %s", strerror(errno)); jniThrowSocketException(env, errno); return; } }
static void OSNetworkSystem_socket(JNIEnv* env, jobject, jobject fileDescriptor, jboolean stream) { if (fileDescriptor == NULL) { jniThrowNullPointerException(env, NULL); errno = EBADF; return; } // Try IPv6 but fall back to IPv4... int type = stream ? SOCK_STREAM : SOCK_DGRAM; int fd = socket(AF_INET6, type, 0); if (fd == -1 && errno == EAFNOSUPPORT) { fd = socket(AF_INET, type, 0); } if (fd == -1) { jniThrowSocketException(env, errno); return; } else { jniSetFileDescriptorOfFD(env, fileDescriptor, fd); } #ifdef __linux__ // The RFC (http://www.ietf.org/rfc/rfc3493.txt) says that IPV6_MULTICAST_HOPS defaults to 1. // The Linux kernel (at least up to 2.6.32) accidentally defaults to 64 (which would be correct // for the *unicast* hop limit). See http://www.spinics.net/lists/netdev/msg129022.html. // When that bug is fixed, we can remove this code. Until then, we manually set the hop // limit on IPv6 datagram sockets. (IPv4 is already correct.) if (type == SOCK_DGRAM && getSocketAddressFamily(fd) == AF_INET6) { int ttl = 1; setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(int)); } #endif }
static void android_os_MemoryFile_close(JNIEnv* env, jobject clazz, jobject fileDescriptor) { int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); if (fd >= 0) { jniSetFileDescriptorOfFD(env, fileDescriptor, -1); close(fd); } }
jobject jniCreateFileDescriptor(C_JNIEnv* env, int fd) { JNIEnv* e = reinterpret_cast<JNIEnv*>(env); tryInitCachedFields(e); jobject fileDescriptor = (*env)->NewObject(e, gCachedFields.fileDescriptorClass, gCachedFields.fileDescriptorCtor); jniSetFileDescriptorOfFD(env, fileDescriptor, fd); return fileDescriptor; }
static void android_os_Parcel_clearFileDescriptor(JNIEnv* env, jclass clazz, jobject object) { if (object == NULL) { jniThrowNullPointerException(env, NULL); return; } int fd = jniGetFDFromFileDescriptor(env, object); if (fd >= 0) { jniSetFileDescriptorOfFD(env, object, -1); } }
static void OSNetworkSystem_close(JNIEnv* env, jobject, jobject fileDescriptor) { NetFd fd(env, fileDescriptor); if (fd.isClosed()) { return; } int oldFd = fd.get(); jniSetFileDescriptorOfFD(env, fileDescriptor, -1); AsynchronousSocketCloseMonitor::signalBlockedThreads(oldFd); close(oldFd); }
static void android_security_cts_NetlinkSocket_create(JNIEnv* env, jclass, jobject fileDescriptor) { int sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); if (sock == -1) { ALOGE("Can't create socket %s", strerror(errno)); jclass SocketException = env->FindClass("java/net/SocketException"); env->ThrowNew(SocketException, "Can't create socket"); return; } jniSetFileDescriptorOfFD(env, fileDescriptor, sock); }
/* * Class: info_kghost_android_openvpn_ManagementSocket * Method: read * Signature: (ILjava/nio/ByteBuffer;IILinfo/kghost/android/openvpn/FileDescriptorHolder;)I */ JNIEXPORT jint JNICALL Java_info_kghost_android_openvpn_ManagementSocket_read__ILjava_nio_ByteBuffer_2IILinfo_kghost_android_openvpn_FileDescriptorHolder_2 (JNIEnv *env, jclass cls, jint socket, jobject buffer, jint offset, jint length, jobject fd) { if (socket < 0) { throwError(env, "java/lang/IllegalArgumentException", "socket"); return -1; } jlong cap = (*env)->GetDirectBufferCapacity(env, buffer); char* ptr = (*env)->GetDirectBufferAddress(env, buffer); int recvfd = -1; if (cap-offset < length) { throwError(env, "java/lang/IllegalArgumentException", "socket"); return -1; } int result = read_fd(socket, ptr+offset, length, &recvfd); if (result < 0) { switch (errno) { case EBADF: case ENOTSOCK: case ENOTCONN: case ECONNREFUSED: throwError(env, "java/lang/IllegalArgumentException", "socket"); break; case EINVAL: throwError(env, "java/lang/IllegalArgumentException", "inval"); break; case EFAULT: throwError(env, "java/lang/IllegalArgumentException", "buffer"); break; case EINTR: throwError(env, "java/lang/InterruptedException", "interrupted"); break; case ENOMEM: throwError(env, "java/lang/RuntimeException", "oom"); break; case EAGAIN: throwError(env, "java/lang/IllegalArgumentException", "non-block socket"); break; default: throwError(env, "java/lang/RuntimeException", strerror(errno)); break; } return result; } if (recvfd >= 0) { jniSetFileDescriptorOfFD(env, fd, recvfd); } return result; }
/* * Class: info_kghost_android_openvpn_OpenVpn_ControlChannel * Method: recv * Signature: (Linfo/kghost/android/openvpn/FileDescriptorHolder;Ljava/nio/Buffer;IILinfo/kghost/android/openvpn/FileDescriptorHolder;)I */ JNIEXPORT jint JNICALL Java_info_kghost_android_openvpn_OpenVpn_00024ControlChannel_recv (JNIEnv *env, jclass cls, jobject socket, jobject buffer, jint offset, jint length, jobject fd) { int s = jniGetFDFromFileDescriptor(env, socket); if (s < 0) { throwError(env, "java/lang/IllegalArgumentException", "socket"); return -1; } jlong cap = (*env)->GetDirectBufferCapacity(env, buffer); char* ptr = (*env)->GetDirectBufferAddress(env, buffer); int recvfd = -1; int result = read_fd(s, ptr+offset, min(cap-offset, length), &recvfd); if (result < 0) { switch (errno) { case EBADF: case ENOTSOCK: case ENOTCONN: case ECONNREFUSED: throwError(env, "java/lang/IllegalArgumentException", "socket"); break; case EINVAL: throwError(env, "java/lang/IllegalArgumentException", "inval"); break; case EFAULT: throwError(env, "java/lang/IllegalArgumentException", "buffer"); break; case EINTR: throwError(env, "java/lang/InterruptedException", "interrupted"); break; case ENOMEM: throwError(env, "java/lang/RuntimeException", "oom"); break; case EAGAIN: throwError(env, "java/lang/IllegalArgumentException", "non-block socket"); break; default: throwError(env, "java/lang/RuntimeException", "unknown error"); break; } return result; } if (recvfd >= 0) { jniSetFileDescriptorOfFD(env, fd, recvfd); } return result; }
/** Executes a command in a child process. */ static pid_t executeProcess(JNIEnv* env, char** commands, char** environment, const char* workingDirectory, jobject inDescriptor, jobject outDescriptor, jobject errDescriptor, jboolean redirectErrorStream) { // Keep track of the system properties fd so we don't close it. int androidSystemPropertiesFd = -1; char* fdString = getenv("ANDROID_PROPERTY_WORKSPACE"); if (fdString) { androidSystemPropertiesFd = atoi(fdString); } // Create 4 pipes: stdin, stdout, stderr, and an exec() status pipe. int pipes[PIPE_COUNT * 2] = { -1, -1, -1, -1, -1, -1, -1, -1 }; for (int i = 0; i < PIPE_COUNT; i++) { if (pipe(pipes + i * 2) == -1) { jniThrowIOException(env, errno); closePipes(pipes, -1); return -1; } } int stdinIn = pipes[0]; int stdinOut = pipes[1]; int stdoutIn = pipes[2]; int stdoutOut = pipes[3]; int stderrIn = pipes[4]; int stderrOut = pipes[5]; int statusIn = pipes[6]; int statusOut = pipes[7]; pid_t childPid = fork(); // If fork() failed... if (childPid == -1) { jniThrowIOException(env, errno); closePipes(pipes, -1); return -1; } // If this is the child process... if (childPid == 0) { /* * Note: We cannot malloc() or free() after this point! * A no-longer-running thread may be holding on to the heap lock, and * an attempt to malloc() or free() would result in deadlock. */ // Replace stdin, out, and err with pipes. dup2(stdinIn, 0); dup2(stdoutOut, 1); if (redirectErrorStream) { dup2(stdoutOut, 2); } else { dup2(stderrOut, 2); } // Close all but statusOut. This saves some work in the next step. closePipes(pipes, statusOut); // Make statusOut automatically close if execvp() succeeds. fcntl(statusOut, F_SETFD, FD_CLOEXEC); // Close remaining unwanted open fds. closeNonStandardFds(statusOut, androidSystemPropertiesFd); // Switch to working directory. if (workingDirectory != NULL) { if (chdir(workingDirectory) == -1) { goto execFailed; } } // Set up environment. if (environment != NULL) { extern char** environ; // Standard, but not in any header file. environ = environment; } // Execute process. By convention, the first argument in the arg array // should be the command itself. In fact, I get segfaults when this // isn't the case. execvp(commands[0], commands); // If we got here, execvp() failed or the working dir was invalid. execFailed: int error = errno; write(statusOut, &error, sizeof(int)); close(statusOut); exit(error); } // This is the parent process. // Close child's pipe ends. close(stdinIn); close(stdoutOut); close(stderrOut); close(statusOut); // Check status pipe for an error code. If execvp() succeeds, the other // end of the pipe should automatically close, in which case, we'll read // nothing. int result; int count = read(statusIn, &result, sizeof(int)); close(statusIn); if (count > 0) { jniThrowIOException(env, result); close(stdoutIn); close(stdinOut); close(stderrIn); return -1; } // Fill in file descriptor wrappers. jniSetFileDescriptorOfFD(env, inDescriptor, stdoutIn); jniSetFileDescriptorOfFD(env, outDescriptor, stdinOut); jniSetFileDescriptorOfFD(env, errDescriptor, stderrIn); return childPid; }
/** Executes a command in a child process. */ static pid_t ExecuteProcess(JNIEnv* env, char** commands, char** environment, const char* workingDirectory, jobject inDescriptor, jobject outDescriptor, jobject errDescriptor, jboolean redirectErrorStream) { // Create 4 pipes: stdin, stdout, stderr, and an exec() status pipe. int pipes[PIPE_COUNT * 2] = { -1, -1, -1, -1, -1, -1, -1, -1 }; for (int i = 0; i < PIPE_COUNT; i++) { if (pipe(pipes + i * 2) == -1) { jniThrowIOException(env, errno); ClosePipes(pipes, -1); return -1; } } int stdinIn = pipes[0]; int stdinOut = pipes[1]; int stdoutIn = pipes[2]; int stdoutOut = pipes[3]; int stderrIn = pipes[4]; int stderrOut = pipes[5]; int statusIn = pipes[6]; int statusOut = pipes[7]; pid_t childPid = fork(); // If fork() failed... if (childPid == -1) { jniThrowIOException(env, errno); ClosePipes(pipes, -1); return -1; } // If this is the child process... if (childPid == 0) { // Note: We cannot malloc(3) or free(3) after this point! // A thread in the parent that no longer exists in the child may have held the heap lock // when we forked, so an attempt to malloc(3) or free(3) would result in deadlock. // Replace stdin, out, and err with pipes. dup2(stdinIn, 0); dup2(stdoutOut, 1); if (redirectErrorStream) { dup2(stdoutOut, 2); } else { dup2(stderrOut, 2); } // Close all but statusOut. This saves some work in the next step. ClosePipes(pipes, statusOut); // Make statusOut automatically close if execvp() succeeds. fcntl(statusOut, F_SETFD, FD_CLOEXEC); // Close remaining unwanted open fds. CloseNonStandardFds(statusOut); // Switch to working directory. if (workingDirectory != NULL) { if (chdir(workingDirectory) == -1) { AbortChild(statusOut); } } // Set up environment. if (environment != NULL) { extern char** environ; // Standard, but not in any header file. environ = environment; } // Execute process. By convention, the first argument in the arg array // should be the command itself. execvp(commands[0], commands); AbortChild(statusOut); } // This is the parent process. // Close child's pipe ends. close(stdinIn); close(stdoutOut); close(stderrOut); close(statusOut); // Check status pipe for an error code. If execvp(2) succeeds, the other // end of the pipe should automatically close, in which case, we'll read // nothing. int child_errno; ssize_t count = TEMP_FAILURE_RETRY(read(statusIn, &child_errno, sizeof(int))); close(statusIn); if (count > 0) { // chdir(2) or execvp(2) in the child failed. // TODO: track which so we can be more specific in the detail message. jniThrowIOException(env, child_errno); close(stdoutIn); close(stdinOut); close(stderrIn); // Reap our zombie child right away. int status; int rc = TEMP_FAILURE_RETRY(waitpid(childPid, &status, 0)); if (rc == -1) { ALOGW("waitpid on failed exec failed: %s", strerror(errno)); } return -1; } // Fill in file descriptor wrappers. jniSetFileDescriptorOfFD(env, inDescriptor, stdoutIn); jniSetFileDescriptorOfFD(env, outDescriptor, stdinOut); jniSetFileDescriptorOfFD(env, errDescriptor, stderrIn); return childPid; }
static void OSNetworkSystem_accept(JNIEnv* env, jobject, jobject serverFileDescriptor, jobject newSocket, jobject clientFileDescriptor) { if (newSocket == NULL) { jniThrowNullPointerException(env, NULL); return; } NetFd serverFd(env, serverFileDescriptor); if (serverFd.isClosed()) { return; } sockaddr_storage ss; socklen_t addrLen = sizeof(ss); sockaddr* sa = reinterpret_cast<sockaddr*>(&ss); int clientFd; { int intFd = serverFd.get(); AsynchronousSocketCloseMonitor monitor(intFd); clientFd = NET_FAILURE_RETRY(serverFd, accept(intFd, sa, &addrLen)); } if (env->ExceptionOccurred()) { return; } if (clientFd == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) { jniThrowSocketTimeoutException(env, errno); } else { jniThrowSocketException(env, errno); } return; } // Reset the inherited read timeout to the Java-specified default of 0. timeval timeout(toTimeval(0)); int rc = setsockopt(clientFd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); if (rc == -1) { LOGE("couldn't reset SO_RCVTIMEO on accepted socket fd %i: %s", clientFd, strerror(errno)); jniThrowSocketException(env, errno); } /* * For network sockets, put the peer address and port in instance variables. * We don't bother to do this for UNIX domain sockets, since most peers are * anonymous anyway. */ if (ss.ss_family == AF_INET || ss.ss_family == AF_INET6) { // Remote address and port. jobject remoteAddress = socketAddressToInetAddress(env, &ss); if (remoteAddress == NULL) { close(clientFd); return; } int remotePort = getSocketAddressPort(&ss); env->SetObjectField(newSocket, gCachedFields.socketimpl_address, remoteAddress); env->SetIntField(newSocket, gCachedFields.socketimpl_port, remotePort); // Local port. memset(&ss, 0, addrLen); int rc = getsockname(clientFd, sa, &addrLen); if (rc == -1) { close(clientFd); jniThrowSocketException(env, errno); return; } int localPort = getSocketAddressPort(&ss); env->SetIntField(newSocket, gCachedFields.socketimpl_localport, localPort); } jniSetFileDescriptorOfFD(env, clientFileDescriptor, clientFd); }
/* * Create a java.io.FileDescriptor given an integer fd */ jobject jniCreateFileDescriptor(JNIEnv* env, int fd) { jobject fileDescriptor = (*env)->NewObject(env, gCachedFields.fileDescriptorClass, gCachedFields.fileDescriptorCtor); jniSetFileDescriptorOfFD(env, fileDescriptor, fd); return fileDescriptor; }