android_net_LocalSocketImpl.cpp revision c80af6d84d8fb729f17028ac533fac07bb7c4c5d
1/* 2 * Copyright (C) 2006 The Android Open Source Project 3 * 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 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 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. 15 */ 16 17#define LOG_TAG "LocalSocketImpl" 18 19#include "JNIHelp.h" 20#include "jni.h" 21#include "utils/Log.h" 22#include "utils/misc.h" 23 24#include <stdio.h> 25#include <string.h> 26#include <sys/types.h> 27#include <sys/socket.h> 28#include <sys/un.h> 29#include <arpa/inet.h> 30#include <netinet/in.h> 31#include <stdlib.h> 32#include <errno.h> 33#include <unistd.h> 34#include <sys/ioctl.h> 35 36#include <cutils/sockets.h> 37#include <netinet/tcp.h> 38#include <ScopedUtfChars.h> 39 40namespace android { 41 42template <typename T> 43void UNUSED(T t) {} 44 45static jfieldID field_inboundFileDescriptors; 46static jfieldID field_outboundFileDescriptors; 47static jclass class_Credentials; 48static jclass class_FileDescriptor; 49static jmethodID method_CredentialsInit; 50 51/* private native void connectLocal(FileDescriptor fd, 52 * String name, int namespace) throws IOException 53 */ 54static void 55socket_connect_local(JNIEnv *env, jobject object, 56 jobject fileDescriptor, jstring name, jint namespaceId) 57{ 58 int ret; 59 int fd; 60 61 fd = jniGetFDFromFileDescriptor(env, fileDescriptor); 62 63 if (env->ExceptionCheck()) { 64 return; 65 } 66 67 ScopedUtfChars nameUtf8(env, name); 68 69 ret = socket_local_client_connect( 70 fd, 71 nameUtf8.c_str(), 72 namespaceId, 73 SOCK_STREAM); 74 75 if (ret < 0) { 76 jniThrowIOException(env, errno); 77 return; 78 } 79} 80 81#define DEFAULT_BACKLOG 4 82 83/* private native void bindLocal(FileDescriptor fd, String name, namespace) 84 * throws IOException; 85 */ 86 87static void 88socket_bind_local (JNIEnv *env, jobject object, jobject fileDescriptor, 89 jstring name, jint namespaceId) 90{ 91 int ret; 92 int fd; 93 94 if (name == NULL) { 95 jniThrowNullPointerException(env, NULL); 96 return; 97 } 98 99 fd = jniGetFDFromFileDescriptor(env, fileDescriptor); 100 101 if (env->ExceptionCheck()) { 102 return; 103 } 104 105 ScopedUtfChars nameUtf8(env, name); 106 107 ret = socket_local_server_bind(fd, nameUtf8.c_str(), namespaceId); 108 109 if (ret < 0) { 110 jniThrowIOException(env, errno); 111 return; 112 } 113} 114 115/* private native void listen_native(int fd, int backlog) throws IOException; */ 116static void 117socket_listen (JNIEnv *env, jobject object, jobject fileDescriptor, jint backlog) 118{ 119 int ret; 120 int fd; 121 122 fd = jniGetFDFromFileDescriptor(env, fileDescriptor); 123 124 if (env->ExceptionCheck()) { 125 return; 126 } 127 128 ret = listen(fd, backlog); 129 130 if (ret < 0) { 131 jniThrowIOException(env, errno); 132 return; 133 } 134} 135 136/* private native FileDescriptor 137** accept (FileDescriptor fd, LocalSocketImpl s) 138** throws IOException; 139*/ 140static jobject 141socket_accept (JNIEnv *env, jobject object, jobject fileDescriptor, jobject s) 142{ 143 union { 144 struct sockaddr address; 145 struct sockaddr_un un_address; 146 } sa; 147 148 int ret; 149 int retFD; 150 int fd; 151 socklen_t addrlen; 152 153 if (s == NULL) { 154 jniThrowNullPointerException(env, NULL); 155 return NULL; 156 } 157 158 fd = jniGetFDFromFileDescriptor(env, fileDescriptor); 159 160 if (env->ExceptionCheck()) { 161 return NULL; 162 } 163 164 do { 165 addrlen = sizeof(sa); 166 ret = accept(fd, &(sa.address), &addrlen); 167 } while (ret < 0 && errno == EINTR); 168 169 if (ret < 0) { 170 jniThrowIOException(env, errno); 171 return NULL; 172 } 173 174 retFD = ret; 175 176 return jniCreateFileDescriptor(env, retFD); 177} 178 179/* private native void shutdown(FileDescriptor fd, boolean shutdownInput) */ 180 181static void 182socket_shutdown (JNIEnv *env, jobject object, jobject fileDescriptor, 183 jboolean shutdownInput) 184{ 185 int ret; 186 int fd; 187 188 fd = jniGetFDFromFileDescriptor(env, fileDescriptor); 189 190 if (env->ExceptionCheck()) { 191 return; 192 } 193 194 ret = shutdown(fd, shutdownInput ? SHUT_RD : SHUT_WR); 195 196 if (ret < 0) { 197 jniThrowIOException(env, errno); 198 return; 199 } 200} 201 202static jint socket_pending (JNIEnv *env, jobject object, 203 jobject fileDescriptor) 204{ 205 int fd; 206 207 fd = jniGetFDFromFileDescriptor(env, fileDescriptor); 208 209 if (env->ExceptionCheck()) { 210 return (jint)-1; 211 } 212 213 int pending; 214 int ret = ioctl(fd, TIOCOUTQ, &pending); 215 216 // If this were a non-socket fd, there would be other cases to worry 217 // about... 218 219 //ALOGD("socket_pending, ioctl ret:%d, pending:%d", ret, pending); 220 if (ret < 0) { 221 jniThrowIOException(env, errno); 222 return (jint) 0; 223 } 224 225 return (jint)pending; 226} 227static jint socket_available (JNIEnv *env, jobject object, 228 jobject fileDescriptor) 229{ 230 int fd; 231 232 fd = jniGetFDFromFileDescriptor(env, fileDescriptor); 233 234 if (env->ExceptionCheck()) { 235 return (jint)-1; 236 } 237 238#if 1 239 int avail; 240 int ret = ioctl(fd, FIONREAD, &avail); 241 242 // If this were a non-socket fd, there would be other cases to worry 243 // about... 244 245 if (ret < 0) { 246 jniThrowIOException(env, errno); 247 return (jint) 0; 248 } 249 250 return (jint)avail; 251#else 252// there appears to be a bionic bug that prevents this version from working. 253 254 ssize_t ret; 255 struct msghdr msg; 256 257 memset(&msg, 0, sizeof(msg)); 258 259 do { 260 ret = recvmsg(fd, &msg, MSG_PEEK | MSG_DONTWAIT | MSG_NOSIGNAL); 261 } while (ret < 0 && errno == EINTR); 262 263 264 // MSG_PEEK returns 0 on EOF and EWOULDBLOCK on none available 265 if (ret < 0 && errno == EWOULDBLOCK) { 266 return 0; 267 } if (ret < 0) { 268 jniThrowIOException(env, errno); 269 return -1; 270 } 271 272 return (jint)ret; 273#endif 274} 275 276/** 277 * Processes ancillary data, handling only 278 * SCM_RIGHTS. Creates appropriate objects and sets appropriate 279 * fields in the LocalSocketImpl object. Returns 0 on success 280 * or -1 if an exception was thrown. 281 */ 282static int socket_process_cmsg(JNIEnv *env, jobject thisJ, struct msghdr * pMsg) 283{ 284 struct cmsghdr *cmsgptr; 285 286 for (cmsgptr = CMSG_FIRSTHDR(pMsg); 287 cmsgptr != NULL; cmsgptr = CMSG_NXTHDR(pMsg, cmsgptr)) { 288 289 if (cmsgptr->cmsg_level != SOL_SOCKET) { 290 continue; 291 } 292 293 if (cmsgptr->cmsg_type == SCM_RIGHTS) { 294 int *pDescriptors = (int *)CMSG_DATA(cmsgptr); 295 jobjectArray fdArray; 296 int count 297 = ((cmsgptr->cmsg_len - CMSG_LEN(0)) / sizeof(int)); 298 299 if (count < 0) { 300 jniThrowException(env, "java/io/IOException", 301 "invalid cmsg length"); 302 return -1; 303 } 304 305 fdArray = env->NewObjectArray(count, class_FileDescriptor, NULL); 306 307 if (fdArray == NULL) { 308 return -1; 309 } 310 311 for (int i = 0; i < count; i++) { 312 jobject fdObject 313 = jniCreateFileDescriptor(env, pDescriptors[i]); 314 315 if (env->ExceptionCheck()) { 316 return -1; 317 } 318 319 env->SetObjectArrayElement(fdArray, i, fdObject); 320 321 if (env->ExceptionCheck()) { 322 return -1; 323 } 324 } 325 326 env->SetObjectField(thisJ, field_inboundFileDescriptors, fdArray); 327 328 if (env->ExceptionCheck()) { 329 return -1; 330 } 331 } 332 } 333 334 return 0; 335} 336 337/** 338 * Reads data from a socket into buf, processing any ancillary data 339 * and adding it to thisJ. 340 * 341 * Returns the length of normal data read, or -1 if an exception has 342 * been thrown in this function. 343 */ 344static ssize_t socket_read_all(JNIEnv *env, jobject thisJ, int fd, 345 void *buffer, size_t len) 346{ 347 ssize_t ret; 348 struct msghdr msg; 349 struct iovec iv; 350 unsigned char *buf = (unsigned char *)buffer; 351 // Enough buffer for a pile of fd's. We throw an exception if 352 // this buffer is too small. 353 struct cmsghdr cmsgbuf[2*sizeof(cmsghdr) + 0x100]; 354 355 memset(&msg, 0, sizeof(msg)); 356 memset(&iv, 0, sizeof(iv)); 357 358 iv.iov_base = buf; 359 iv.iov_len = len; 360 361 msg.msg_iov = &iv; 362 msg.msg_iovlen = 1; 363 msg.msg_control = cmsgbuf; 364 msg.msg_controllen = sizeof(cmsgbuf); 365 366 do { 367 ret = recvmsg(fd, &msg, MSG_NOSIGNAL); 368 } while (ret < 0 && errno == EINTR); 369 370 if (ret < 0 && errno == EPIPE) { 371 // Treat this as an end of stream 372 return 0; 373 } 374 375 if (ret < 0) { 376 jniThrowIOException(env, errno); 377 return -1; 378 } 379 380 if ((msg.msg_flags & (MSG_CTRUNC | MSG_OOB | MSG_ERRQUEUE)) != 0) { 381 // To us, any of the above flags are a fatal error 382 383 jniThrowException(env, "java/io/IOException", 384 "Unexpected error or truncation during recvmsg()"); 385 386 return -1; 387 } 388 389 if (ret >= 0) { 390 socket_process_cmsg(env, thisJ, &msg); 391 } 392 393 return ret; 394} 395 396/** 397 * Writes all the data in the specified buffer to the specified socket. 398 * 399 * Returns 0 on success or -1 if an exception was thrown. 400 */ 401static int socket_write_all(JNIEnv *env, jobject object, int fd, 402 void *buf, size_t len) 403{ 404 ssize_t ret; 405 struct msghdr msg; 406 unsigned char *buffer = (unsigned char *)buf; 407 memset(&msg, 0, sizeof(msg)); 408 409 jobjectArray outboundFds 410 = (jobjectArray)env->GetObjectField( 411 object, field_outboundFileDescriptors); 412 413 if (env->ExceptionCheck()) { 414 return -1; 415 } 416 417 struct cmsghdr *cmsg; 418 int countFds = outboundFds == NULL ? 0 : env->GetArrayLength(outboundFds); 419 int fds[countFds]; 420 char msgbuf[CMSG_SPACE(countFds)]; 421 422 // Add any pending outbound file descriptors to the message 423 if (outboundFds != NULL) { 424 425 if (env->ExceptionCheck()) { 426 return -1; 427 } 428 429 for (int i = 0; i < countFds; i++) { 430 jobject fdObject = env->GetObjectArrayElement(outboundFds, i); 431 if (env->ExceptionCheck()) { 432 return -1; 433 } 434 435 fds[i] = jniGetFDFromFileDescriptor(env, fdObject); 436 if (env->ExceptionCheck()) { 437 return -1; 438 } 439 } 440 441 // See "man cmsg" really 442 msg.msg_control = msgbuf; 443 msg.msg_controllen = sizeof msgbuf; 444 cmsg = CMSG_FIRSTHDR(&msg); 445 cmsg->cmsg_level = SOL_SOCKET; 446 cmsg->cmsg_type = SCM_RIGHTS; 447 cmsg->cmsg_len = CMSG_LEN(sizeof fds); 448 memcpy(CMSG_DATA(cmsg), fds, sizeof fds); 449 } 450 451 // We only write our msg_control during the first write 452 while (len > 0) { 453 struct iovec iv; 454 memset(&iv, 0, sizeof(iv)); 455 456 iv.iov_base = buffer; 457 iv.iov_len = len; 458 459 msg.msg_iov = &iv; 460 msg.msg_iovlen = 1; 461 462 do { 463 ret = sendmsg(fd, &msg, MSG_NOSIGNAL); 464 } while (ret < 0 && errno == EINTR); 465 466 if (ret < 0) { 467 jniThrowIOException(env, errno); 468 return -1; 469 } 470 471 buffer += ret; 472 len -= ret; 473 474 // Wipes out any msg_control too 475 memset(&msg, 0, sizeof(msg)); 476 } 477 478 return 0; 479} 480 481static jint socket_read (JNIEnv *env, jobject object, jobject fileDescriptor) 482{ 483 int fd; 484 int err; 485 486 if (fileDescriptor == NULL) { 487 jniThrowNullPointerException(env, NULL); 488 return (jint)-1; 489 } 490 491 fd = jniGetFDFromFileDescriptor(env, fileDescriptor); 492 493 if (env->ExceptionCheck()) { 494 return (jint)0; 495 } 496 497 unsigned char buf; 498 499 err = socket_read_all(env, object, fd, &buf, 1); 500 501 if (err < 0) { 502 jniThrowIOException(env, errno); 503 return (jint)0; 504 } 505 506 if (err == 0) { 507 // end of file 508 return (jint)-1; 509 } 510 511 return (jint)buf; 512} 513 514static jint socket_readba (JNIEnv *env, jobject object, 515 jbyteArray buffer, jint off, jint len, jobject fileDescriptor) 516{ 517 int fd; 518 jbyte* byteBuffer; 519 int ret; 520 521 if (fileDescriptor == NULL || buffer == NULL) { 522 jniThrowNullPointerException(env, NULL); 523 return (jint)-1; 524 } 525 526 if (off < 0 || len < 0 || (off + len) > env->GetArrayLength(buffer)) { 527 jniThrowException(env, "java/lang/ArrayIndexOutOfBoundsException", NULL); 528 return (jint)-1; 529 } 530 531 if (len == 0) { 532 // because socket_read_all returns 0 on EOF 533 return 0; 534 } 535 536 fd = jniGetFDFromFileDescriptor(env, fileDescriptor); 537 538 if (env->ExceptionCheck()) { 539 return (jint)-1; 540 } 541 542 byteBuffer = env->GetByteArrayElements(buffer, NULL); 543 544 if (NULL == byteBuffer) { 545 // an exception will have been thrown 546 return (jint)-1; 547 } 548 549 ret = socket_read_all(env, object, 550 fd, byteBuffer + off, len); 551 552 // A return of -1 above means an exception is pending 553 554 env->ReleaseByteArrayElements(buffer, byteBuffer, 0); 555 556 return (jint) ((ret == 0) ? -1 : ret); 557} 558 559static void socket_write (JNIEnv *env, jobject object, 560 jint b, jobject fileDescriptor) 561{ 562 int fd; 563 int err; 564 565 if (fileDescriptor == NULL) { 566 jniThrowNullPointerException(env, NULL); 567 return; 568 } 569 570 fd = jniGetFDFromFileDescriptor(env, fileDescriptor); 571 572 if (env->ExceptionCheck()) { 573 return; 574 } 575 576 err = socket_write_all(env, object, fd, &b, 1); 577 UNUSED(err); 578 // A return of -1 above means an exception is pending 579} 580 581static void socket_writeba (JNIEnv *env, jobject object, 582 jbyteArray buffer, jint off, jint len, jobject fileDescriptor) 583{ 584 int fd; 585 int err; 586 jbyte* byteBuffer; 587 588 if (fileDescriptor == NULL || buffer == NULL) { 589 jniThrowNullPointerException(env, NULL); 590 return; 591 } 592 593 if (off < 0 || len < 0 || (off + len) > env->GetArrayLength(buffer)) { 594 jniThrowException(env, "java/lang/ArrayIndexOutOfBoundsException", NULL); 595 return; 596 } 597 598 fd = jniGetFDFromFileDescriptor(env, fileDescriptor); 599 600 if (env->ExceptionCheck()) { 601 return; 602 } 603 604 byteBuffer = env->GetByteArrayElements(buffer,NULL); 605 606 if (NULL == byteBuffer) { 607 // an exception will have been thrown 608 return; 609 } 610 611 err = socket_write_all(env, object, fd, 612 byteBuffer + off, len); 613 UNUSED(err); 614 // A return of -1 above means an exception is pending 615 616 env->ReleaseByteArrayElements(buffer, byteBuffer, JNI_ABORT); 617} 618 619static jobject socket_get_peer_credentials(JNIEnv *env, 620 jobject object, jobject fileDescriptor) 621{ 622 int err; 623 int fd; 624 625 if (fileDescriptor == NULL) { 626 jniThrowNullPointerException(env, NULL); 627 return NULL; 628 } 629 630 fd = jniGetFDFromFileDescriptor(env, fileDescriptor); 631 632 if (env->ExceptionCheck()) { 633 return NULL; 634 } 635 636 struct ucred creds; 637 638 memset(&creds, 0, sizeof(creds)); 639 socklen_t szCreds = sizeof(creds); 640 641 err = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &creds, &szCreds); 642 643 if (err < 0) { 644 jniThrowIOException(env, errno); 645 return NULL; 646 } 647 648 if (szCreds == 0) { 649 return NULL; 650 } 651 652 return env->NewObject(class_Credentials, method_CredentialsInit, 653 creds.pid, creds.uid, creds.gid); 654} 655 656/* 657 * JNI registration. 658 */ 659static JNINativeMethod gMethods[] = { 660 /* name, signature, funcPtr */ 661 {"connectLocal", "(Ljava/io/FileDescriptor;Ljava/lang/String;I)V", 662 (void*)socket_connect_local}, 663 {"bindLocal", "(Ljava/io/FileDescriptor;Ljava/lang/String;I)V", (void*)socket_bind_local}, 664 {"listen_native", "(Ljava/io/FileDescriptor;I)V", (void*)socket_listen}, 665 {"accept", "(Ljava/io/FileDescriptor;Landroid/net/LocalSocketImpl;)Ljava/io/FileDescriptor;", (void*)socket_accept}, 666 {"shutdown", "(Ljava/io/FileDescriptor;Z)V", (void*)socket_shutdown}, 667 {"available_native", "(Ljava/io/FileDescriptor;)I", (void*) socket_available}, 668 {"pending_native", "(Ljava/io/FileDescriptor;)I", (void*) socket_pending}, 669 {"read_native", "(Ljava/io/FileDescriptor;)I", (void*) socket_read}, 670 {"readba_native", "([BIILjava/io/FileDescriptor;)I", (void*) socket_readba}, 671 {"writeba_native", "([BIILjava/io/FileDescriptor;)V", (void*) socket_writeba}, 672 {"write_native", "(ILjava/io/FileDescriptor;)V", (void*) socket_write}, 673 {"getPeerCredentials_native", 674 "(Ljava/io/FileDescriptor;)Landroid/net/Credentials;", 675 (void*) socket_get_peer_credentials} 676}; 677 678int register_android_net_LocalSocketImpl(JNIEnv *env) 679{ 680 jclass clazz; 681 682 clazz = env->FindClass("android/net/LocalSocketImpl"); 683 684 if (clazz == NULL) { 685 goto error; 686 } 687 688 field_inboundFileDescriptors = env->GetFieldID(clazz, 689 "inboundFileDescriptors", "[Ljava/io/FileDescriptor;"); 690 691 if (field_inboundFileDescriptors == NULL) { 692 goto error; 693 } 694 695 field_outboundFileDescriptors = env->GetFieldID(clazz, 696 "outboundFileDescriptors", "[Ljava/io/FileDescriptor;"); 697 698 if (field_outboundFileDescriptors == NULL) { 699 goto error; 700 } 701 702 class_Credentials = env->FindClass("android/net/Credentials"); 703 704 if (class_Credentials == NULL) { 705 goto error; 706 } 707 708 class_Credentials = (jclass)env->NewGlobalRef(class_Credentials); 709 710 class_FileDescriptor = env->FindClass("java/io/FileDescriptor"); 711 712 if (class_FileDescriptor == NULL) { 713 goto error; 714 } 715 716 class_FileDescriptor = (jclass)env->NewGlobalRef(class_FileDescriptor); 717 718 method_CredentialsInit 719 = env->GetMethodID(class_Credentials, "<init>", "(III)V"); 720 721 if (method_CredentialsInit == NULL) { 722 goto error; 723 } 724 725 return jniRegisterNativeMethods(env, 726 "android/net/LocalSocketImpl", gMethods, NELEM(gMethods)); 727 728error: 729 ALOGE("Error registering android.net.LocalSocketImpl"); 730 return -1; 731} 732 733}; 734