android_net_LocalSocketImpl.cpp revision 4fa438ef72d125b4c0e7eb37a6667ca51ed325e4
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 FileDescriptor 116** accept (FileDescriptor fd, LocalSocketImpl s) 117** throws IOException; 118*/ 119static jobject 120socket_accept (JNIEnv *env, jobject object, jobject fileDescriptor, jobject s) 121{ 122 union { 123 struct sockaddr address; 124 struct sockaddr_un un_address; 125 } sa; 126 127 int ret; 128 int retFD; 129 int fd; 130 socklen_t addrlen; 131 132 if (s == NULL) { 133 jniThrowNullPointerException(env, NULL); 134 return NULL; 135 } 136 137 fd = jniGetFDFromFileDescriptor(env, fileDescriptor); 138 139 if (env->ExceptionCheck()) { 140 return NULL; 141 } 142 143 do { 144 addrlen = sizeof(sa); 145 ret = accept(fd, &(sa.address), &addrlen); 146 } while (ret < 0 && errno == EINTR); 147 148 if (ret < 0) { 149 jniThrowIOException(env, errno); 150 return NULL; 151 } 152 153 retFD = ret; 154 155 return jniCreateFileDescriptor(env, retFD); 156} 157 158/* private native void shutdown(FileDescriptor fd, boolean shutdownInput) */ 159 160static void 161socket_shutdown (JNIEnv *env, jobject object, jobject fileDescriptor, 162 jboolean shutdownInput) 163{ 164 int ret; 165 int fd; 166 167 fd = jniGetFDFromFileDescriptor(env, fileDescriptor); 168 169 if (env->ExceptionCheck()) { 170 return; 171 } 172 173 ret = shutdown(fd, shutdownInput ? SHUT_RD : SHUT_WR); 174 175 if (ret < 0) { 176 jniThrowIOException(env, errno); 177 return; 178 } 179} 180 181/** 182 * Processes ancillary data, handling only 183 * SCM_RIGHTS. Creates appropriate objects and sets appropriate 184 * fields in the LocalSocketImpl object. Returns 0 on success 185 * or -1 if an exception was thrown. 186 */ 187static int socket_process_cmsg(JNIEnv *env, jobject thisJ, struct msghdr * pMsg) 188{ 189 struct cmsghdr *cmsgptr; 190 191 for (cmsgptr = CMSG_FIRSTHDR(pMsg); 192 cmsgptr != NULL; cmsgptr = CMSG_NXTHDR(pMsg, cmsgptr)) { 193 194 if (cmsgptr->cmsg_level != SOL_SOCKET) { 195 continue; 196 } 197 198 if (cmsgptr->cmsg_type == SCM_RIGHTS) { 199 int *pDescriptors = (int *)CMSG_DATA(cmsgptr); 200 jobjectArray fdArray; 201 int count 202 = ((cmsgptr->cmsg_len - CMSG_LEN(0)) / sizeof(int)); 203 204 if (count < 0) { 205 jniThrowException(env, "java/io/IOException", 206 "invalid cmsg length"); 207 return -1; 208 } 209 210 fdArray = env->NewObjectArray(count, class_FileDescriptor, NULL); 211 212 if (fdArray == NULL) { 213 return -1; 214 } 215 216 for (int i = 0; i < count; i++) { 217 jobject fdObject 218 = jniCreateFileDescriptor(env, pDescriptors[i]); 219 220 if (env->ExceptionCheck()) { 221 return -1; 222 } 223 224 env->SetObjectArrayElement(fdArray, i, fdObject); 225 226 if (env->ExceptionCheck()) { 227 return -1; 228 } 229 } 230 231 env->SetObjectField(thisJ, field_inboundFileDescriptors, fdArray); 232 233 if (env->ExceptionCheck()) { 234 return -1; 235 } 236 } 237 } 238 239 return 0; 240} 241 242/** 243 * Reads data from a socket into buf, processing any ancillary data 244 * and adding it to thisJ. 245 * 246 * Returns the length of normal data read, or -1 if an exception has 247 * been thrown in this function. 248 */ 249static ssize_t socket_read_all(JNIEnv *env, jobject thisJ, int fd, 250 void *buffer, size_t len) 251{ 252 ssize_t ret; 253 struct msghdr msg; 254 struct iovec iv; 255 unsigned char *buf = (unsigned char *)buffer; 256 // Enough buffer for a pile of fd's. We throw an exception if 257 // this buffer is too small. 258 struct cmsghdr cmsgbuf[2*sizeof(cmsghdr) + 0x100]; 259 260 memset(&msg, 0, sizeof(msg)); 261 memset(&iv, 0, sizeof(iv)); 262 263 iv.iov_base = buf; 264 iv.iov_len = len; 265 266 msg.msg_iov = &iv; 267 msg.msg_iovlen = 1; 268 msg.msg_control = cmsgbuf; 269 msg.msg_controllen = sizeof(cmsgbuf); 270 271 do { 272 ret = recvmsg(fd, &msg, MSG_NOSIGNAL); 273 } while (ret < 0 && errno == EINTR); 274 275 if (ret < 0 && errno == EPIPE) { 276 // Treat this as an end of stream 277 return 0; 278 } 279 280 if (ret < 0) { 281 jniThrowIOException(env, errno); 282 return -1; 283 } 284 285 if ((msg.msg_flags & (MSG_CTRUNC | MSG_OOB | MSG_ERRQUEUE)) != 0) { 286 // To us, any of the above flags are a fatal error 287 288 jniThrowException(env, "java/io/IOException", 289 "Unexpected error or truncation during recvmsg()"); 290 291 return -1; 292 } 293 294 if (ret >= 0) { 295 socket_process_cmsg(env, thisJ, &msg); 296 } 297 298 return ret; 299} 300 301/** 302 * Writes all the data in the specified buffer to the specified socket. 303 * 304 * Returns 0 on success or -1 if an exception was thrown. 305 */ 306static int socket_write_all(JNIEnv *env, jobject object, int fd, 307 void *buf, size_t len) 308{ 309 ssize_t ret; 310 struct msghdr msg; 311 unsigned char *buffer = (unsigned char *)buf; 312 memset(&msg, 0, sizeof(msg)); 313 314 jobjectArray outboundFds 315 = (jobjectArray)env->GetObjectField( 316 object, field_outboundFileDescriptors); 317 318 if (env->ExceptionCheck()) { 319 return -1; 320 } 321 322 struct cmsghdr *cmsg; 323 int countFds = outboundFds == NULL ? 0 : env->GetArrayLength(outboundFds); 324 int fds[countFds]; 325 char msgbuf[CMSG_SPACE(countFds)]; 326 327 // Add any pending outbound file descriptors to the message 328 if (outboundFds != NULL) { 329 330 if (env->ExceptionCheck()) { 331 return -1; 332 } 333 334 for (int i = 0; i < countFds; i++) { 335 jobject fdObject = env->GetObjectArrayElement(outboundFds, i); 336 if (env->ExceptionCheck()) { 337 return -1; 338 } 339 340 fds[i] = jniGetFDFromFileDescriptor(env, fdObject); 341 if (env->ExceptionCheck()) { 342 return -1; 343 } 344 } 345 346 // See "man cmsg" really 347 msg.msg_control = msgbuf; 348 msg.msg_controllen = sizeof msgbuf; 349 cmsg = CMSG_FIRSTHDR(&msg); 350 cmsg->cmsg_level = SOL_SOCKET; 351 cmsg->cmsg_type = SCM_RIGHTS; 352 cmsg->cmsg_len = CMSG_LEN(sizeof fds); 353 memcpy(CMSG_DATA(cmsg), fds, sizeof fds); 354 } 355 356 // We only write our msg_control during the first write 357 while (len > 0) { 358 struct iovec iv; 359 memset(&iv, 0, sizeof(iv)); 360 361 iv.iov_base = buffer; 362 iv.iov_len = len; 363 364 msg.msg_iov = &iv; 365 msg.msg_iovlen = 1; 366 367 do { 368 ret = sendmsg(fd, &msg, MSG_NOSIGNAL); 369 } while (ret < 0 && errno == EINTR); 370 371 if (ret < 0) { 372 jniThrowIOException(env, errno); 373 return -1; 374 } 375 376 buffer += ret; 377 len -= ret; 378 379 // Wipes out any msg_control too 380 memset(&msg, 0, sizeof(msg)); 381 } 382 383 return 0; 384} 385 386static jint socket_read (JNIEnv *env, jobject object, jobject fileDescriptor) 387{ 388 int fd; 389 int err; 390 391 if (fileDescriptor == NULL) { 392 jniThrowNullPointerException(env, NULL); 393 return (jint)-1; 394 } 395 396 fd = jniGetFDFromFileDescriptor(env, fileDescriptor); 397 398 if (env->ExceptionCheck()) { 399 return (jint)0; 400 } 401 402 unsigned char buf; 403 404 err = socket_read_all(env, object, fd, &buf, 1); 405 406 if (err < 0) { 407 jniThrowIOException(env, errno); 408 return (jint)0; 409 } 410 411 if (err == 0) { 412 // end of file 413 return (jint)-1; 414 } 415 416 return (jint)buf; 417} 418 419static jint socket_readba (JNIEnv *env, jobject object, 420 jbyteArray buffer, jint off, jint len, jobject fileDescriptor) 421{ 422 int fd; 423 jbyte* byteBuffer; 424 int ret; 425 426 if (fileDescriptor == NULL || buffer == NULL) { 427 jniThrowNullPointerException(env, NULL); 428 return (jint)-1; 429 } 430 431 if (off < 0 || len < 0 || (off + len) > env->GetArrayLength(buffer)) { 432 jniThrowException(env, "java/lang/ArrayIndexOutOfBoundsException", NULL); 433 return (jint)-1; 434 } 435 436 if (len == 0) { 437 // because socket_read_all returns 0 on EOF 438 return 0; 439 } 440 441 fd = jniGetFDFromFileDescriptor(env, fileDescriptor); 442 443 if (env->ExceptionCheck()) { 444 return (jint)-1; 445 } 446 447 byteBuffer = env->GetByteArrayElements(buffer, NULL); 448 449 if (NULL == byteBuffer) { 450 // an exception will have been thrown 451 return (jint)-1; 452 } 453 454 ret = socket_read_all(env, object, 455 fd, byteBuffer + off, len); 456 457 // A return of -1 above means an exception is pending 458 459 env->ReleaseByteArrayElements(buffer, byteBuffer, 0); 460 461 return (jint) ((ret == 0) ? -1 : ret); 462} 463 464static void socket_write (JNIEnv *env, jobject object, 465 jint b, jobject fileDescriptor) 466{ 467 int fd; 468 int err; 469 470 if (fileDescriptor == NULL) { 471 jniThrowNullPointerException(env, NULL); 472 return; 473 } 474 475 fd = jniGetFDFromFileDescriptor(env, fileDescriptor); 476 477 if (env->ExceptionCheck()) { 478 return; 479 } 480 481 err = socket_write_all(env, object, fd, &b, 1); 482 UNUSED(err); 483 // A return of -1 above means an exception is pending 484} 485 486static void socket_writeba (JNIEnv *env, jobject object, 487 jbyteArray buffer, jint off, jint len, jobject fileDescriptor) 488{ 489 int fd; 490 int err; 491 jbyte* byteBuffer; 492 493 if (fileDescriptor == NULL || buffer == NULL) { 494 jniThrowNullPointerException(env, NULL); 495 return; 496 } 497 498 if (off < 0 || len < 0 || (off + len) > env->GetArrayLength(buffer)) { 499 jniThrowException(env, "java/lang/ArrayIndexOutOfBoundsException", NULL); 500 return; 501 } 502 503 fd = jniGetFDFromFileDescriptor(env, fileDescriptor); 504 505 if (env->ExceptionCheck()) { 506 return; 507 } 508 509 byteBuffer = env->GetByteArrayElements(buffer,NULL); 510 511 if (NULL == byteBuffer) { 512 // an exception will have been thrown 513 return; 514 } 515 516 err = socket_write_all(env, object, fd, 517 byteBuffer + off, len); 518 UNUSED(err); 519 // A return of -1 above means an exception is pending 520 521 env->ReleaseByteArrayElements(buffer, byteBuffer, JNI_ABORT); 522} 523 524static jobject socket_get_peer_credentials(JNIEnv *env, 525 jobject object, jobject fileDescriptor) 526{ 527 int err; 528 int fd; 529 530 if (fileDescriptor == NULL) { 531 jniThrowNullPointerException(env, NULL); 532 return NULL; 533 } 534 535 fd = jniGetFDFromFileDescriptor(env, fileDescriptor); 536 537 if (env->ExceptionCheck()) { 538 return NULL; 539 } 540 541 struct ucred creds; 542 543 memset(&creds, 0, sizeof(creds)); 544 socklen_t szCreds = sizeof(creds); 545 546 err = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &creds, &szCreds); 547 548 if (err < 0) { 549 jniThrowIOException(env, errno); 550 return NULL; 551 } 552 553 if (szCreds == 0) { 554 return NULL; 555 } 556 557 return env->NewObject(class_Credentials, method_CredentialsInit, 558 creds.pid, creds.uid, creds.gid); 559} 560 561/* 562 * JNI registration. 563 */ 564static JNINativeMethod gMethods[] = { 565 /* name, signature, funcPtr */ 566 {"connectLocal", "(Ljava/io/FileDescriptor;Ljava/lang/String;I)V", 567 (void*)socket_connect_local}, 568 {"bindLocal", "(Ljava/io/FileDescriptor;Ljava/lang/String;I)V", (void*)socket_bind_local}, 569 {"accept", "(Ljava/io/FileDescriptor;Landroid/net/LocalSocketImpl;)Ljava/io/FileDescriptor;", (void*)socket_accept}, 570 {"shutdown", "(Ljava/io/FileDescriptor;Z)V", (void*)socket_shutdown}, 571 {"read_native", "(Ljava/io/FileDescriptor;)I", (void*) socket_read}, 572 {"readba_native", "([BIILjava/io/FileDescriptor;)I", (void*) socket_readba}, 573 {"writeba_native", "([BIILjava/io/FileDescriptor;)V", (void*) socket_writeba}, 574 {"write_native", "(ILjava/io/FileDescriptor;)V", (void*) socket_write}, 575 {"getPeerCredentials_native", 576 "(Ljava/io/FileDescriptor;)Landroid/net/Credentials;", 577 (void*) socket_get_peer_credentials} 578}; 579 580int register_android_net_LocalSocketImpl(JNIEnv *env) 581{ 582 jclass clazz; 583 584 clazz = env->FindClass("android/net/LocalSocketImpl"); 585 586 if (clazz == NULL) { 587 goto error; 588 } 589 590 field_inboundFileDescriptors = env->GetFieldID(clazz, 591 "inboundFileDescriptors", "[Ljava/io/FileDescriptor;"); 592 593 if (field_inboundFileDescriptors == NULL) { 594 goto error; 595 } 596 597 field_outboundFileDescriptors = env->GetFieldID(clazz, 598 "outboundFileDescriptors", "[Ljava/io/FileDescriptor;"); 599 600 if (field_outboundFileDescriptors == NULL) { 601 goto error; 602 } 603 604 class_Credentials = env->FindClass("android/net/Credentials"); 605 606 if (class_Credentials == NULL) { 607 goto error; 608 } 609 610 class_Credentials = (jclass)env->NewGlobalRef(class_Credentials); 611 612 class_FileDescriptor = env->FindClass("java/io/FileDescriptor"); 613 614 if (class_FileDescriptor == NULL) { 615 goto error; 616 } 617 618 class_FileDescriptor = (jclass)env->NewGlobalRef(class_FileDescriptor); 619 620 method_CredentialsInit 621 = env->GetMethodID(class_Credentials, "<init>", "(III)V"); 622 623 if (method_CredentialsInit == NULL) { 624 goto error; 625 } 626 627 return jniRegisterNativeMethods(env, 628 "android/net/LocalSocketImpl", gMethods, NELEM(gMethods)); 629 630error: 631 ALOGE("Error registering android.net.LocalSocketImpl"); 632 return -1; 633} 634 635}; 636