static jint OSNetworkSystem_sendDirect(JNIEnv* env, jobject, jobject fileDescriptor, jint address, jint offset, jint length, jint port, jobject inetAddress) { NetFd fd(env, fileDescriptor); if (fd.isClosed()) { return -1; } sockaddr_storage receiver; if (inetAddress != NULL && !inetAddressToSocketAddress(env, inetAddress, port, &receiver)) { return -1; } int flags = 0; char* buf = reinterpret_cast<char*>(static_cast<uintptr_t>(address + offset)); sockaddr* to = inetAddress ? reinterpret_cast<sockaddr*>(&receiver) : NULL; socklen_t toLength = inetAddress ? sizeof(receiver) : 0; ssize_t bytesSent; { int intFd = fd.get(); AsynchronousSocketCloseMonitor monitor(intFd); bytesSent = NET_FAILURE_RETRY(fd, sendto(intFd, buf, length, flags, to, toLength)); } if (env->ExceptionOccurred()) { return -1; } if (bytesSent == -1) { if (errno == ECONNRESET || errno == ECONNREFUSED) { return 0; } else { jniThrowSocketException(env, errno); } } return bytesSent; }
// TODO: can we merge this with recvDirect? static jint OSNetworkSystem_readDirect(JNIEnv* env, jobject, jobject fileDescriptor, jint address, jint count) { NetFd fd(env, fileDescriptor); if (fd.isClosed()) { return 0; } jbyte* dst = reinterpret_cast<jbyte*>(static_cast<uintptr_t>(address)); ssize_t bytesReceived; { int intFd = fd.get(); AsynchronousSocketCloseMonitor monitor(intFd); bytesReceived = NET_FAILURE_RETRY(fd, read(intFd, dst, count)); } if (env->ExceptionOccurred()) { return -1; } if (bytesReceived == 0) { return -1; } else if (bytesReceived == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) { // We were asked to read a non-blocking socket with no data // available, so report "no bytes read". return 0; } else { jniThrowSocketException(env, errno); return 0; } } else { return bytesReceived; } }
static jint OSNetworkSystem_writeDirect(JNIEnv* env, jobject, jobject fileDescriptor, jint address, jint offset, jint count) { if (count <= 0) { return 0; } NetFd fd(env, fileDescriptor); if (fd.isClosed()) { return 0; } jbyte* src = reinterpret_cast<jbyte*>(static_cast<uintptr_t>(address + offset)); ssize_t bytesSent; { int intFd = fd.get(); AsynchronousSocketCloseMonitor monitor(intFd); bytesSent = NET_FAILURE_RETRY(fd, write(intFd, src, count)); } if (env->ExceptionOccurred()) { return -1; } if (bytesSent == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) { // We were asked to write to a non-blocking socket, but were told // it would block, so report "no bytes written". return 0; } else { jniThrowSocketException(env, errno); return 0; } } return bytesSent; }
/* * Reads a network packet into the user-supplied buffer. Return the * length of the packet, or a 0 if there was a timeout or an * unacceptable packet was acquired. * * Assumes that the caller has validated the offset & byteCount values. */ static jint RawSocket_recvPacket(JNIEnv* env, jclass, jobject fileDescriptor, jbyteArray packet, jint offset, jint byteCount, jint port, jint timeout_millis) { NetFd fd(env, fileDescriptor); if (fd.isClosed()) { return 0; } ScopedByteArrayRW body(env, packet); jbyte* packetData = body.get(); if (packetData == NULL) { return 0; } packetData += offset; pollfd fds[1]; fds[0].fd = fd.get(); fds[0].events = POLLIN; int retval = poll(fds, 1, timeout_millis); if (retval <= 0) { return 0; } unsigned int size = 0; { int packetSize = byteCount; int intFd = fd.get(); AsynchronousSocketCloseMonitor monitor(intFd); size = NET_FAILURE_RETRY(fd, read(intFd, packetData, packetSize)); } if (env->ExceptionOccurred()) { return 0; } if (port != -1) { // quick check for UDP type & UDP port // the packet is an IP header, UDP header, and UDP payload if ((size < (sizeof(struct iphdr) + sizeof(struct udphdr)))) { return 0; // runt packet } u_int8_t ip_proto = ((iphdr *) packetData)->protocol; if (ip_proto != IPPROTO_UDP) { return 0; // something other than UDP } __be16 destPort = htons((reinterpret_cast<udphdr*>(packetData + sizeof(iphdr)))->dest); if (destPort != port) { return 0; // something other than requested port } } return size; }
// TODO: can we merge this with readDirect? static jint OSNetworkSystem_recvDirect(JNIEnv* env, jobject, jobject fileDescriptor, jobject packet, jint address, jint offset, jint length, jboolean peek, jboolean connected) { NetFd fd(env, fileDescriptor); if (fd.isClosed()) { return 0; } char* buf = reinterpret_cast<char*>(static_cast<uintptr_t>(address + offset)); const int flags = peek ? MSG_PEEK : 0; sockaddr_storage ss; memset(&ss, 0, sizeof(ss)); socklen_t sockAddrLen = sizeof(ss); sockaddr* from = connected ? NULL : reinterpret_cast<sockaddr*>(&ss); socklen_t* fromLength = connected ? NULL : &sockAddrLen; ssize_t bytesReceived; { int intFd = fd.get(); AsynchronousSocketCloseMonitor monitor(intFd); bytesReceived = NET_FAILURE_RETRY(fd, recvfrom(intFd, buf, length, flags, from, fromLength)); } if (env->ExceptionOccurred()) { return -1; } if (bytesReceived == -1) { if (connected && errno == ECONNREFUSED) { jniThrowException(env, "java/net/PortUnreachableException", ""); } else if (errno == EAGAIN || errno == EWOULDBLOCK) { jniThrowSocketTimeoutException(env, errno); } else { jniThrowSocketException(env, errno); } return 0; } if (packet != NULL) { env->SetIntField(packet, gCachedFields.dpack_length, bytesReceived); if (!connected) { jbyteArray addr = socketAddressToByteArray(env, &ss); if (addr == NULL) { return 0; } int port = getSocketAddressPort(&ss); jobject sender = byteArrayToInetAddress(env, addr); if (sender == NULL) { return 0; } env->SetObjectField(packet, gCachedFields.dpack_address, sender); env->SetIntField(packet, gCachedFields.dpack_port, port); } } return bytesReceived; }
/* * Writes the L3 (IP) packet to the raw socket supplied in the * FileDescriptor instance. * * Assumes that the caller has validated the offset & byteCount values. */ static int RawSocket_sendPacket(JNIEnv* env, jclass, jobject fileDescriptor, jstring interfaceName, jshort protocolType, jbyteArray destMac, jbyteArray packet, jint offset, jint byteCount) { NetFd fd(env, fileDescriptor); if (fd.isClosed()) { return 0; } ScopedUtfChars ifname(env, interfaceName); if (ifname.c_str() == NULL) { return 0; } ScopedByteArrayRO byteArray(env, packet); if (byteArray.get() == NULL) { return 0; } ScopedByteArrayRO mac(env, destMac); if (mac.get() == NULL) { return 0; } sockunion su; memset(&su, 0, sizeof(su)); su.sll.sll_hatype = htons(1); // ARPHRD_ETHER su.sll.sll_halen = mac.size(); memcpy(&su.sll.sll_addr, mac.get(), mac.size()); su.sll.sll_family = AF_PACKET; su.sll.sll_protocol = htons(protocolType); su.sll.sll_ifindex = if_nametoindex(ifname.c_str()); int err; { int intFd = fd.get(); AsynchronousSocketCloseMonitor monitor(intFd); err = NET_FAILURE_RETRY(fd, sendto(intFd, byteArray.get() + offset, byteCount, 0, &su.sa, sizeof(su))); } return err; }
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); }